diff --git a/crates/accelerate/src/twirling.rs b/crates/accelerate/src/twirling.rs index c2a755c81ac6..fc02cb1d3a8b 100644 --- a/crates/accelerate/src/twirling.rs +++ b/crates/accelerate/src/twirling.rs @@ -31,7 +31,9 @@ use qiskit_circuit::packed_instruction::PackedInstruction; use crate::QiskitError; use qiskit_circuit::{BlocksMode, NoBlocks, VarsMode}; -use qiskit_transpiler::passes::run_optimize_1q_gates_decomposition; +use qiskit_transpiler::passes::{ + Optimize1qGatesDecompositionState, run_optimize_1q_gates_decomposition, +}; use qiskit_transpiler::target::Target; use rand::prelude::*; use rand_pcg::Pcg64Mcg; @@ -295,9 +297,12 @@ fn generate_twirled_circuit( } } } - if optimizer_target.is_some() { + if let Some(optimizer_target) = optimizer_target { let mut dag = DAGCircuit::from_circuit_data(&out_circ, false, None, None, None, None)?; - run_optimize_1q_gates_decomposition(&mut dag, optimizer_target, None, None)?; + let state = Optimize1qGatesDecompositionState::new( + optimizer_target.num_qubits.unwrap_or(0) as usize, + ); + run_optimize_1q_gates_decomposition(&mut dag, &state, Some(optimizer_target), None, None)?; Ok(CircuitData::from_dag_ref(&dag)?) } else { Ok(out_circ) diff --git a/crates/cext/src/transpiler/passes/optimize_1q_sequences.rs b/crates/cext/src/transpiler/passes/optimize_1q_sequences.rs index 1c80e428c1e6..c3de5e37b18e 100644 --- a/crates/cext/src/transpiler/passes/optimize_1q_sequences.rs +++ b/crates/cext/src/transpiler/passes/optimize_1q_sequences.rs @@ -12,7 +12,10 @@ use crate::pointers::{const_ptr_as_ref, mut_ptr_as_ref}; use qiskit_circuit::{circuit_data::CircuitData, dag_circuit::DAGCircuit}; -use qiskit_transpiler::{passes::run_optimize_1q_gates_decomposition, target::Target}; +use qiskit_transpiler::{ + passes::{Optimize1qGatesDecompositionState, run_optimize_1q_gates_decomposition}, + target::Target, +}; /// @ingroup QkTranspilerPassesStandalone /// Runs the Optimize1qGatesDecomposition pass in standalone mode on a circuit. @@ -76,8 +79,11 @@ pub unsafe extern "C" fn qk_transpiler_pass_standalone_optimize_1q_sequences( let mut circuit_as_dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None) .expect("Error while converting the circuit to a dag."); + let state = Optimize1qGatesDecompositionState::new( + target.map(|x| x.num_qubits.unwrap_or(0)).unwrap_or(0) as usize, + ); // Run the pass - run_optimize_1q_gates_decomposition(&mut circuit_as_dag, target, None, None) + run_optimize_1q_gates_decomposition(&mut circuit_as_dag, &state, target, None, None) .expect("Error while running the pass."); // Convert the DAGCircuit back to an instance of CircuitData @@ -154,7 +160,16 @@ pub unsafe extern "C" fn qk_transpiler_pass_optimize_1q_sequences( // SAFETY: Per documentation, the pointer is non-null and aligned. let dag = unsafe { mut_ptr_as_ref(dag) }; + let state = Optimize1qGatesDecompositionState::new( + target + .map(|x| { + x.num_qubits + .map(|num_qubits| num_qubits as usize) + .unwrap_or(0) + }) + .unwrap_or(0), + ); // Run the pass - run_optimize_1q_gates_decomposition(dag, target, None, None) + run_optimize_1q_gates_decomposition(dag, &state, target, None, None) .expect("Error while running the pass."); } diff --git a/crates/transpiler/src/passes/mod.rs b/crates/transpiler/src/passes/mod.rs index 64c71b75fbe8..ca8de897ae6a 100644 --- a/crates/transpiler/src/passes/mod.rs +++ b/crates/transpiler/src/passes/mod.rs @@ -87,7 +87,8 @@ pub use instruction_duration_check::{ pub use inverse_cancellation::{inverse_cancellation_mod, run_inverse_cancellation_standard_gates}; pub use litinski_transformation::{litinski_transformation_mod, run_litinski_transformation}; pub use optimize_1q_gates_decomposition::{ - optimize_1q_gates_decomposition_mod, run_optimize_1q_gates_decomposition, + Optimize1qGatesDecompositionState, optimize_1q_gates_decomposition_mod, + run_optimize_1q_gates_decomposition, }; pub use optimize_clifford_t::{optimize_clifford_t_mod, run_optimize_clifford_t}; pub use remove_diagonal_gates_before_measure::{ diff --git a/crates/transpiler/src/passes/optimize_1q_gates_decomposition.rs b/crates/transpiler/src/passes/optimize_1q_gates_decomposition.rs index f4d2b3fb2577..8fbb02ac71ae 100644 --- a/crates/transpiler/src/passes/optimize_1q_gates_decomposition.rs +++ b/crates/transpiler/src/passes/optimize_1q_gates_decomposition.rs @@ -10,6 +10,9 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. +use std::str::FromStr; +use std::sync::OnceLock; + use hashbrown::HashSet; use num_complex::Complex64; @@ -17,9 +20,11 @@ use pyo3::prelude::*; use pyo3::wrap_pyfunction; use ndarray::prelude::*; +use rayon::prelude::*; use rustworkx_core::petgraph::stable_graph::NodeIndex; use qiskit_circuit::dag_circuit::{DAGCircuit, NodeType}; +use qiskit_circuit::getenv_use_multiple_threads; use qiskit_circuit::operations::{Operation, OperationRef, Param}; use crate::target::{Target, TargetOperation}; @@ -53,103 +58,241 @@ fn compute_error_from_target_one_qubit_sequence( } } -#[pyfunction] -#[pyo3(name = "optimize_1q_gates_decomposition", signature = (dag, *, target=None, basis_gates=None, global_decomposers=None))] -pub fn run_optimize_1q_gates_decomposition( - dag: &mut DAGCircuit, - target: Option<&Target>, - basis_gates: Option>, - global_decomposers: Option>, -) -> PyResult<()> { - let runs: Vec> = dag.collect_1q_runs().unwrap().collect(); - let dag_qubits = dag.num_qubits(); - let mut target_basis_per_qubit: Vec = vec![EulerBasisSet::new(); dag_qubits]; - let mut basis_gates_per_qubit: Vec>> = vec![None; dag_qubits]; - for raw_run in runs { - let mut error = match target { - Some(_) => 1., - None => raw_run.len() as f64, - }; - let qubit: PhysicalQubit = if let NodeType::Operation(inst) = &dag[raw_run[0]] { - PhysicalQubit::new(dag.get_qargs(inst.qubits)[0].0) - } else { - unreachable!("nodes in runs will always be op nodes") - }; - if basis_gates_per_qubit[qubit.index()].is_none() { - let basis_gates = match target { +#[pyclass(module = "qiskit._accelerate.optimize_1q_gates_decomposition")] +pub struct Optimize1qGatesDecompositionState { + target_basis_per_qubit: Vec>, + basis_gates_per_qubit: Vec>>>, + global: bool, +} + +type Optimize1qGatesDecompositionStatePickle = ( + Vec>>, + Vec>>>, + bool, +); + +#[pymethods] +impl Optimize1qGatesDecompositionState { + fn __getstate__(&self) -> Optimize1qGatesDecompositionStatePickle { + ( + self.target_basis_per_qubit + .iter() + .map(|raw_set| { + raw_set.get().map(|basis_set| { + basis_set + .get_bases() + .map(|x| x.as_str().to_string()) + .collect() + }) + }) + .collect(), + self.basis_gates_per_qubit + .iter() + .map(|x| x.get().cloned()) + .collect(), + self.global, + ) + } + + fn __setstate__(&mut self, state: Optimize1qGatesDecompositionStatePickle) { + self.target_basis_per_qubit = state + .0 + .into_iter() + .map(|set| match set { + Some(set) => { + let mut euler_set = EulerBasisSet::new(); + for basis_str in set { + euler_set.add_basis(EulerBasis::from_str(basis_str.as_str()).unwrap()); + } + let out_set = OnceLock::new(); + out_set.set(euler_set).expect("Qubit already populated"); + out_set + } + None => OnceLock::new(), + }) + .collect(); + self.basis_gates_per_qubit = state + .1 + .into_iter() + .map(|gates| match gates { + Some(gates) => { + let out_list = OnceLock::new(); + out_list.set(gates).expect("Qubit already populated"); + out_list + } + None => OnceLock::new(), + }) + .collect(); + self.global = state.2; + } + + #[new] + #[pyo3(signature = (num_qubits=0))] + pub fn new(num_qubits: usize) -> Self { + Self { + target_basis_per_qubit: vec![OnceLock::new(); num_qubits.max(1)], + basis_gates_per_qubit: vec![OnceLock::new(); num_qubits.max(1)], + global: num_qubits == 0, + } + } +} + +impl Optimize1qGatesDecompositionState { + fn get_basis_set( + &self, + qubit: PhysicalQubit, + target: Option<&Target>, + basis_gates: Option<&HashSet>, + ) -> Option> { + if self.global { + match target { Some(target) => Some( target - .operation_names_for_qargs(&[qubit])? - .into_iter() - .filter(|gate_name| { - let target_op = target.operation_from_name(gate_name).unwrap(); - let TargetOperation::Normal(gate) = target_op else { - return false; - }; - if let OperationRef::StandardGate(_) = gate.operation.view() { - // For standard gates check that the target entry accepts any - // params and if so then we can use the gate in the pass - // else filter the operation since arbitrary angles are not - // supported - gate.params_view() - .iter() - .all(|x| matches!(x, Param::ParameterExpression(_))) + .operations() + .filter_map(|op| { + if op.operation.num_qubits() == 1 { + Some(op.operation.name().to_string()) } else { - // For all other gates pass it through - true + None } }) - .collect(), + .collect::>(), ), - None => { - let basis = basis_gates.as_ref(); - basis.map(|basis| basis.iter().map(|x| x.as_str()).collect()) - } + None => basis_gates.cloned(), + } + } else { + let Some(target) = target else { + unreachable!("Target must be true to be non-global"); }; - basis_gates_per_qubit[qubit.index()] = basis_gates; + Some( + target + .operation_names_for_qargs(&[qubit]) + .expect("Qubit has operations") + .into_iter() + .map(|x| x.to_string()) + .filter(|gate_name| { + let target_op = target.operation_from_name(gate_name).unwrap(); + let TargetOperation::Normal(gate) = target_op else { + return false; + }; + if let OperationRef::StandardGate(_) = gate.operation.view() { + // For standard gates check that the target entry accepts any + // params and if so then we can use the gate in the pass + // else filter the operation since arbitrary angles are not + // supported + gate.params_view() + .iter() + .all(|x| matches!(x, Param::ParameterExpression(_))) + } else { + // For all other gates pass it through + true + } + }) + .collect(), + ) } - let basis_gates = basis_gates_per_qubit[qubit.index()].as_ref(); + } - let target_basis_set = &mut target_basis_per_qubit[qubit.index()]; - if !target_basis_set.initialized() { + fn get_euler_basis_set( + &self, + qubit: PhysicalQubit, + target: Option<&Target>, + global_decomposers: Option<&Vec>, + ) -> EulerBasisSet { + if self.global { + let mut target_basis_set = EulerBasisSet::new(); match target { - Some(_target) => EULER_BASES - .iter() - .enumerate() - .filter_map(|(idx, gates)| { - if !gates.iter().all(|gate| { - basis_gates - .expect("the target path always provides a hash set") - .contains(gate) - }) { - return None; - } - let basis = EULER_BASIS_NAMES[idx]; - Some(basis) - }) - .for_each(|basis| target_basis_set.add_basis(basis)), - None => match &global_decomposers { - Some(bases) => { - for basis in bases.iter() { - target_basis_set.add_basis(EulerBasis::__new__(basis)?) + Some(_target) => { + EULER_BASES + .iter() + .enumerate() + .filter_map(|(idx, gates)| { + if !gates.iter().all(|gate| { + self.basis_gates_per_qubit[0] + .get() + .unwrap() + .as_ref() + .expect("the target path always provides a hash set") + .contains(*gate) + }) { + return None; + } + let basis = EULER_BASIS_NAMES[idx]; + Some(basis) + }) + .for_each(|basis| target_basis_set.add_basis(basis)); + if target_basis_set.basis_supported(EulerBasis::U3) + && target_basis_set.basis_supported(EulerBasis::U321) + { + target_basis_set.remove(EulerBasis::U3); + } + if target_basis_set.basis_supported(EulerBasis::ZSX) + && target_basis_set.basis_supported(EulerBasis::ZSXX) + { + target_basis_set.remove(EulerBasis::ZSX); + } + } + None => { + match &global_decomposers { + Some(bases) => { + for basis in bases.iter() { + target_basis_set.add_basis( + EulerBasis::__new__(basis).expect("a valid basis string"), + ) + } } + None => match self.basis_gates_per_qubit[0].get().unwrap() { + Some(gates) => EULER_BASES + .iter() + .enumerate() + .filter_map(|(idx, basis_gates)| { + if !gates + .iter() + .all(|gate| basis_gates.as_ref().contains(&gate.as_str())) + { + return None; + } + let basis = EULER_BASIS_NAMES[idx]; + Some(basis) + }) + .for_each(|basis| target_basis_set.add_basis(basis)), + None => target_basis_set.support_all(), + }, } - None => match basis_gates { - Some(gates) => EULER_BASES - .iter() - .enumerate() - .filter_map(|(idx, basis_gates)| { - if !gates.iter().all(|gate| basis_gates.as_ref().contains(gate)) { - return None; - } - let basis = EULER_BASIS_NAMES[idx]; - Some(basis) - }) - .for_each(|basis| target_basis_set.add_basis(basis)), - None => target_basis_set.support_all(), - }, - }, - }; + if target_basis_set.basis_supported(EulerBasis::U3) + && target_basis_set.basis_supported(EulerBasis::U321) + { + target_basis_set.remove(EulerBasis::U3); + } + if target_basis_set.basis_supported(EulerBasis::ZSX) + && target_basis_set.basis_supported(EulerBasis::ZSXX) + { + target_basis_set.remove(EulerBasis::ZSX); + } + } + } + target_basis_set + } else { + let mut target_basis_set = EulerBasisSet::new(); + EULER_BASES + .iter() + .enumerate() + .filter_map(|(idx, gates)| { + if !gates.iter().all(|gate| { + self.basis_gates_per_qubit[qubit.index()] + .get() + .as_ref() + .unwrap() + .as_ref() + .expect("the target path always provides a hash set") + .contains(*gate) + }) { + return None; + } + let basis = EULER_BASIS_NAMES[idx]; + Some(basis) + }) + .for_each(|basis| target_basis_set.add_basis(basis)); if target_basis_set.basis_supported(EulerBasis::U3) && target_basis_set.basis_supported(EulerBasis::U321) { @@ -160,68 +303,141 @@ pub fn run_optimize_1q_gates_decomposition( { target_basis_set.remove(EulerBasis::ZSX); } + target_basis_set } - let target_basis_set = &target_basis_per_qubit[qubit.index()]; - let operator = raw_run - .iter() - .map(|node_index| { - let node = &dag[*node_index]; - if let NodeType::Operation(inst) = node { - if let Some(target) = target { - error *= compute_error_term_from_target(inst.op.name(), target, qubit); + } +} + +#[pyfunction] +#[pyo3(name = "optimize_1q_gates_decomposition", signature = (dag, state, *, target=None, basis_gates=None, global_decomposers=None))] +pub fn run_optimize_1q_gates_decomposition( + dag: &mut DAGCircuit, + state: &Optimize1qGatesDecompositionState, + target: Option<&Target>, + basis_gates: Option>, + global_decomposers: Option>, +) -> PyResult<()> { + let runs: Vec> = dag.collect_1q_runs().unwrap().collect(); + let process_run = + |raw_run: &[NodeIndex], dag: &DAGCircuit| -> PyResult> { + let mut error = match target { + Some(_) => 1., + None => raw_run.len() as f64, + }; + let qubit: PhysicalQubit = if let NodeType::Operation(inst) = &dag[raw_run[0]] { + PhysicalQubit::new(dag.get_qargs(inst.qubits)[0].0) + } else { + unreachable!("nodes in runs will always be op nodes") + }; + let basis_gates = if state.global { + state.basis_gates_per_qubit[0].get_or_init(|| { + state.get_basis_set(PhysicalQubit::new(0), target, basis_gates.as_ref()) + }) + } else { + state.basis_gates_per_qubit[qubit.index()] + .get_or_init(|| state.get_basis_set(qubit, target, basis_gates.as_ref())) + }; + let target_basis_set = if state.global { + state.target_basis_per_qubit[0].get_or_init(|| { + state.get_euler_basis_set( + PhysicalQubit::new(0), + target, + global_decomposers.as_ref(), + ) + }) + } else { + state.target_basis_per_qubit[qubit.index()].get_or_init(|| { + state.get_euler_basis_set(qubit, target, global_decomposers.as_ref()) + }) + }; + let operator = raw_run + .iter() + .map(|node_index| { + let node = &dag[*node_index]; + if let NodeType::Operation(inst) = node { + if let Some(target) = target { + error *= compute_error_term_from_target(inst.op.name(), target, qubit); + } + inst.try_matrix_as_static_1q() + .expect("collect_1q_runs only collects gates that can produce a matrix") + } else { + unreachable!("Can only have op nodes here") } - inst.try_matrix_as_static_1q() - .expect("collect_1q_runs only collects gates that can produce a matrix") - } else { - unreachable!("Can only have op nodes here") - } - }) - .fold(gate_matrix::ONE_QUBIT_IDENTITY, |mut operator, node| { - matmul_1q_with_slice(&mut operator, &node); - operator - }); + }) + .fold(gate_matrix::ONE_QUBIT_IDENTITY, |mut operator, node| { + matmul_1q_with_slice(&mut operator, &node); + operator + }); - let old_error = if target.is_some() { - (1. - error, raw_run.len()) - } else { - (error, raw_run.len()) - }; - let sequence = unitary_to_gate_sequence_inner( - aview2(&operator), - target_basis_set, - qubit.index(), - None, - true, - None, - ); - let sequence = match sequence { - Some(seq) => seq, - None => continue, - }; - let new_error = compute_error_from_target_one_qubit_sequence(&sequence, qubit, target); + let old_error = if target.is_some() { + (1. - error, raw_run.len()) + } else { + (error, raw_run.len()) + }; + let sequence = unitary_to_gate_sequence_inner( + aview2(&operator), + target_basis_set, + qubit.index(), + None, + true, + None, + ); + let Some(sequence) = sequence else { + return Ok(None); + }; + let new_error = compute_error_from_target_one_qubit_sequence(&sequence, qubit, target); - let mut outside_basis = false; - if let Some(basis) = basis_gates { - for node in &raw_run { - if let NodeType::Operation(inst) = &dag[*node] { - if !basis.contains(inst.op.name()) { - outside_basis = true; - break; + let mut outside_basis = false; + if let Some(basis) = basis_gates { + for node in raw_run { + if let NodeType::Operation(inst) = &dag[*node] { + if !basis.contains(inst.op.name()) { + outside_basis = true; + break; + } } } + } else { + outside_basis = false; } - } else { - outside_basis = false; - } - if outside_basis - || new_error < old_error - || new_error.0.abs() < 1e-9 && old_error.0.abs() >= 1e-9 - { - for gate in sequence.gates { - dag.insert_1q_on_incoming_qubit((gate.0, &gate.1), raw_run[0]); + if outside_basis + || new_error < old_error + || new_error.0.abs() < 1e-9 && old_error.0.abs() >= 1e-9 + { + Ok(Some(sequence)) + } else { + Ok(None) + } + }; + // TODO: Add a line like: + // if runs.len() > X && getenv_use_multiple_threads() { + // where X is a good value for switching between serial and parallel versions + if getenv_use_multiple_threads() { + let sequences = runs + .par_iter() + .map(|raw_run| process_run(raw_run, dag)) + .collect::>>()?; + runs.into_iter() + .zip(sequences) + .filter_map(|(raw_run, sequence)| sequence.map(|x| (raw_run, x))) + .for_each(|(raw_run, sequence)| { + for gate in sequence.gates { + dag.insert_1q_on_incoming_qubit((gate.0, &gate.1), raw_run[0]); + } + dag.add_global_phase(&Param::Float(sequence.global_phase)) + .unwrap(); + dag.remove_1q_sequence(&raw_run); + }); + } else { + for raw_run in runs { + let sequence = process_run(&raw_run, dag)?; + if let Some(sequence) = sequence { + for gate in sequence.gates { + dag.insert_1q_on_incoming_qubit((gate.0, &gate.1), raw_run[0]); + } + dag.add_global_phase(&Param::Float(sequence.global_phase))?; + dag.remove_1q_sequence(&raw_run); } - dag.add_global_phase(&Param::Float(sequence.global_phase))?; - dag.remove_1q_sequence(&raw_run); } } Ok(()) @@ -258,5 +474,6 @@ pub fn matmul_1q_with_slice(operator: &mut [[Complex64; 2]; 2], other: &[[Comple pub fn optimize_1q_gates_decomposition_mod(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(run_optimize_1q_gates_decomposition))?; + m.add_class::()?; Ok(()) } diff --git a/crates/transpiler/src/transpiler.rs b/crates/transpiler/src/transpiler.rs index f8d4fa8462a9..639b8d1c9aac 100644 --- a/crates/transpiler/src/transpiler.rs +++ b/crates/transpiler/src/transpiler.rs @@ -353,10 +353,12 @@ pub fn optimization_stage( if optimization_level == OptimizationLevel::Level1 { new_depth = Some(dag.depth(false)?); new_size = Some(dag.size(false)?); + let optimize_1q_state = + Optimize1qGatesDecompositionState::new(target.num_qubits.unwrap_or(0) as usize); while new_depth != depth || new_size != size { depth = new_depth; size = new_size; - run_optimize_1q_gates_decomposition(dag, Some(target), None, None)?; + run_optimize_1q_gates_decomposition(dag, &optimize_1q_state, Some(target), None, None)?; run_inverse_cancellation_standard_gates(dag); if gates_missing_from_target(dag, target)? { translation_stage(dag, target, synthesis_state, equivalence_library)?; @@ -376,11 +378,13 @@ pub fn optimization_stage( )?; new_depth = Some(dag.depth(false)?); new_size = Some(dag.size(false)?); + let optimize_1q_state = + Optimize1qGatesDecompositionState::new(target.num_qubits.unwrap_or(0) as usize); while new_depth != depth || new_size != size { depth = new_depth; size = new_size; run_remove_identity_equiv(dag, approximation_degree, Some(target))?; - run_optimize_1q_gates_decomposition(dag, Some(target), None, None)?; + run_optimize_1q_gates_decomposition(dag, &optimize_1q_state, Some(target), None, None)?; cancel_commutations(dag, commutation_checker, None, 1.0)?; if gates_missing_from_target(dag, target)? { translation_stage(dag, target, synthesis_state, equivalence_library)?; @@ -391,7 +395,8 @@ pub fn optimization_stage( } else if optimization_level == OptimizationLevel::Level3 { let mut continue_loop: bool = true; let mut min_state = MinPointState::new(dag); - + let optimize_1q_state = + Optimize1qGatesDecompositionState::new(target.num_qubits.unwrap_or(0) as usize); while continue_loop { run_consolidate_blocks(dag, false, approximation_degree, Some(target))?; *dag = run_unitary_synthesis( @@ -403,7 +408,7 @@ pub fn optimization_stage( target.into(), )?; run_remove_identity_equiv(dag, approximation_degree, Some(target))?; - run_optimize_1q_gates_decomposition(dag, Some(target), None, None)?; + run_optimize_1q_gates_decomposition(dag, &optimize_1q_state, Some(target), None, None)?; cancel_commutations(dag, commutation_checker, None, 1.0)?; if gates_missing_from_target(dag, target)? { translation_stage(dag, target, synthesis_state, equivalence_library)?; diff --git a/qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py b/qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py index 3d432943e145..c4b3baf801d8 100644 --- a/qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +++ b/qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py @@ -98,6 +98,12 @@ def __init__(self, basis=None, target=None): self._basis_gates = None self.error_map = self._build_error_map() + num_qubits = 0 + if self._target: + num_qubits = self._target.num_qubits + if num_qubits is None: + num_qubits = 0 + self._state = optimize_1q_gates_decomposition.Optimize1qGatesDecompositionState(num_qubits) def _build_error_map(self): # include path for when target exists but target.num_qubits is None (BasicSimulator) @@ -210,9 +216,10 @@ def run(self, dag): """ optimize_1q_gates_decomposition.optimize_1q_gates_decomposition( dag, + self._state, target=self._target, - global_decomposers=self._global_decomposers, basis_gates=self._basis_gates, + global_decomposers=self._global_decomposers, ) return dag