Skip to content

Commit f71d201

Browse files
authored
Expose Target::operation_from_name with native types (Qiskit#14219)
* Expose `Target::operation_from_name` with native types The previous version of `operation_from_name` made assumptions about what the Rust-space caller needed, and hid some of the information with two different paths ending up in a `TargetKeyError` (not present and present but variadic). Some callers need to know if the instruction was variadic (as the Python-space version does), and the more typical error return for "not present" in lookups is `Option`, to avoid constructing potentially costly error objects. * Use existing `ToPyObject` impl
1 parent 13db91c commit f71d201

File tree

2 files changed

+66
-88
lines changed

2 files changed

+66
-88
lines changed

crates/accelerate/src/target_transpiler/mod.rs

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -428,9 +428,11 @@ impl Target {
428428
py: Python<'py>,
429429
instruction: &str,
430430
) -> PyResult<Bound<'py, PyAny>> {
431-
match self._operation_from_name(instruction) {
432-
Ok(instruction) => instruction.into_pyobject(py),
433-
Err(e) => Err(PyKeyError::new_err(e.message)),
431+
match self.operation_from_name(instruction) {
432+
Some(op) => op.into_bound_py_any(py),
433+
None => Err(PyKeyError::new_err(format!(
434+
"Instruction {instruction} not in target"
435+
))),
434436
}
435437
}
436438

@@ -1154,33 +1156,9 @@ impl Target {
11541156
}
11551157
}
11561158

1157-
/// Gets a tuple of Operation object and Parameters based on the operation name if present in the Target.
1158-
// TODO: Remove once `Target` is being consumed.
1159-
#[allow(dead_code)]
1160-
pub fn operation_from_name(
1161-
&self,
1162-
instruction: &str,
1163-
) -> Result<&NormalOperation, TargetKeyError> {
1164-
match self._operation_from_name(instruction) {
1165-
Ok(TargetOperation::Normal(operation)) => Ok(operation),
1166-
Ok(TargetOperation::Variadic(_)) => Err(TargetKeyError::new_err(format!(
1167-
"Instruction {:?} was found in the target, but the instruction is Varidic.",
1168-
instruction
1169-
))),
1170-
Err(e) => Err(e),
1171-
}
1172-
}
1173-
1174-
/// Gets the instruction object based on the operation name
1175-
fn _operation_from_name(&self, instruction: &str) -> Result<&TargetOperation, TargetKeyError> {
1176-
if let Some(gate_obj) = self._gate_name_map.get(instruction) {
1177-
Ok(gate_obj)
1178-
} else {
1179-
Err(TargetKeyError::new_err(format!(
1180-
"Instruction {:?} not in target",
1181-
instruction
1182-
)))
1183-
}
1159+
/// Retrieve the backing representation of an operation name in the target, if it exists.
1160+
pub fn operation_from_name(&self, instruction: &str) -> Option<&TargetOperation> {
1161+
self._gate_name_map.get(instruction)
11841162
}
11851163

11861164
/// Returns an iterator over all the qargs of a specific Target object

crates/accelerate/src/unitary_synthesis.rs

Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use crate::euler_one_qubit_decomposer::{
4242
unitary_to_gate_sequence_inner, EulerBasis, EulerBasisSet, EULER_BASES, EULER_BASIS_NAMES,
4343
};
4444
use crate::nlayout::PhysicalQubit;
45-
use crate::target_transpiler::{NormalOperation, Target};
45+
use crate::target_transpiler::{NormalOperation, Target, TargetOperation};
4646
use crate::two_qubit_decompose::{
4747
RXXEquivalent, TwoQubitBasisDecomposer, TwoQubitControlledUDecomposer, TwoQubitGateSequence,
4848
TwoQubitWeylDecomposition,
@@ -550,43 +550,41 @@ fn get_2q_decomposers_from_target(
550550
IndexMap::new();
551551
for (q_pair, gates) in qubit_gate_map {
552552
for key in gates {
553-
match target.operation_from_name(key) {
554-
Ok(op) => {
555-
match op.operation.view() {
556-
OperationRef::Gate(_) => (),
557-
OperationRef::StandardGate(_) => (),
558-
_ => continue,
559-
}
560-
// Filter out non-2q-gate candidates
561-
if op.operation.num_qubits() != 2 {
562-
continue;
563-
}
564-
// Add to param_basis if the gate parameters aren't bound (not Float)
565-
if !op.params.iter().all(|p| matches!(p, Param::Float(_))) {
566-
available_2q_param_basis.insert(
567-
key,
568-
(
569-
op.clone(),
570-
match &target[key].get(Some(q_pair)) {
571-
Some(Some(props)) => props.error,
572-
_ => None,
573-
},
574-
),
575-
);
576-
}
577-
available_2q_basis.insert(
578-
key,
579-
(
580-
op.clone(),
581-
match &target[key].get(Some(q_pair)) {
582-
Some(Some(props)) => props.error,
583-
_ => None,
584-
},
585-
),
586-
);
587-
}
553+
let Some(TargetOperation::Normal(op)) = target.operation_from_name(key) else {
554+
continue;
555+
};
556+
match op.operation.view() {
557+
OperationRef::Gate(_) => (),
558+
OperationRef::StandardGate(_) => (),
588559
_ => continue,
589560
}
561+
// Filter out non-2q-gate candidates
562+
if op.operation.num_qubits() != 2 {
563+
continue;
564+
}
565+
// Add to param_basis if the gate parameters aren't bound (not Float)
566+
if !op.params.iter().all(|p| matches!(p, Param::Float(_))) {
567+
available_2q_param_basis.insert(
568+
key,
569+
(
570+
op.clone(),
571+
match &target[key].get(Some(q_pair)) {
572+
Some(Some(props)) => props.error,
573+
_ => None,
574+
},
575+
),
576+
);
577+
}
578+
available_2q_basis.insert(
579+
key,
580+
(
581+
op.clone(),
582+
match &target[key].get(Some(q_pair)) {
583+
Some(Some(props)) => props.error,
584+
_ => None,
585+
},
586+
),
587+
);
590588
}
591589
}
592590
if available_2q_basis.is_empty() && available_2q_param_basis.is_empty() {
@@ -1112,30 +1110,32 @@ fn synth_error(
11121110
inst_qubits: &SmallVec<[PhysicalQubit; 2]>| {
11131111
if let Ok(names) = target.operation_names_for_qargs(Some(inst_qubits)) {
11141112
for name in names {
1115-
if let Ok(target_op) = target.operation_from_name(name) {
1116-
let are_params_close = if let Some(params) = inst_params {
1117-
params.iter().zip(target_op.params.iter()).all(|(p1, p2)| {
1118-
p1.is_close(py, p2, 1e-10)
1119-
.expect("Unexpected parameter expression error.")
1120-
})
1121-
} else {
1122-
false
1123-
};
1124-
let is_parametrized = target_op
1125-
.params
1126-
.iter()
1127-
.any(|param| matches!(param, Param::ParameterExpression(_)));
1128-
if target_op.operation.name() == inst_name
1129-
&& (is_parametrized || are_params_close)
1130-
{
1131-
match target[name].get(Some(inst_qubits)) {
1132-
Some(Some(props)) => {
1133-
gate_fidelities.push(1.0 - props.error.unwrap_or(0.0))
1134-
}
1135-
_ => gate_fidelities.push(1.0),
1113+
let Some(TargetOperation::Normal(target_op)) = target.operation_from_name(name)
1114+
else {
1115+
continue;
1116+
};
1117+
let are_params_close = if let Some(params) = inst_params {
1118+
params.iter().zip(target_op.params.iter()).all(|(p1, p2)| {
1119+
p1.is_close(py, p2, 1e-10)
1120+
.expect("Unexpected parameter expression error.")
1121+
})
1122+
} else {
1123+
false
1124+
};
1125+
let is_parametrized = target_op
1126+
.params
1127+
.iter()
1128+
.any(|param| matches!(param, Param::ParameterExpression(_)));
1129+
if target_op.operation.name() == inst_name
1130+
&& (is_parametrized || are_params_close)
1131+
{
1132+
match target[name].get(Some(inst_qubits)) {
1133+
Some(Some(props)) => {
1134+
gate_fidelities.push(1.0 - props.error.unwrap_or(0.0))
11361135
}
1137-
break;
1136+
_ => gate_fidelities.push(1.0),
11381137
}
1138+
break;
11391139
}
11401140
}
11411141
}

0 commit comments

Comments
 (0)