From fa34c5a0c5dc5884590b88933588bf8997f6b209 Mon Sep 17 00:00:00 2001 From: Raynel Sanchez Date: Tue, 10 Sep 2024 13:27:35 -0400 Subject: [PATCH] Fix: Remove second re-mapping of bits - Remove second re-mapping of bits, perform this mapping at insertion of qubits instead. - Modify insertion of qubits to re-map the indices and store them after insertion should a custom ordering be provided. - Remove extra `.and_modify()` for `clbits_last_node` from `DAGCircuit::extend`. - Remove submodule addition to `circuit` module, submodule is now under `_accelerate.converters`. - Other tweaks and fixes. --- crates/circuit/src/dag_circuit.rs | 140 ++++++++++++++++------------ crates/circuit/src/lib.rs | 12 --- crates/pyext/src/lib.rs | 1 + qiskit/__init__.py | 2 +- qiskit/converters/circuit_to_dag.py | 2 +- 5 files changed, 84 insertions(+), 73 deletions(-) diff --git a/crates/circuit/src/dag_circuit.rs b/crates/circuit/src/dag_circuit.rs index f3a3bb0bf34b..ff01ab8ff1bd 100644 --- a/crates/circuit/src/dag_circuit.rs +++ b/crates/circuit/src/dag_circuit.rs @@ -6539,8 +6539,7 @@ impl DAGCircuit { }); clbit_last_nodes .entry(clbit) - .and_modify(|val| *val = new_node) - .or_insert(new_node); + .and_modify(|val| *val = new_node); self.dag .add_edge(clbit_last_node, new_node, Wire::Clbit(clbit)); } @@ -6641,42 +6640,64 @@ impl DAGCircuit { new_dag.metadata = qc.metadata.map(|meta| meta.unbind()); // Add the qubits depending on order. - let mut qubit_map: HashMap> = HashMap::new(); - if let Some(qubit_ordering) = qubit_order { - for qubit in qubit_ordering { - if new_dag.qubits.find(&qubit).is_some() { - return Err(DAGCircuitError::new_err(format!( - "duplicate qubits {}", - &qubit - ))); - } - qubit_map.insert(new_dag.add_qubit_unchecked(py, &qubit)?, qubit); - } + let qubit_map: Option> = if let Some(qubit_ordering) = qubit_order { + let mut ordered_vec = Vec::from_iter((0..num_qubits as u32).map(Qubit)); + qubit_ordering + .into_iter() + .try_for_each(|qubit| -> PyResult<()> { + if new_dag.qubits.find(&qubit).is_some() { + return Err(DAGCircuitError::new_err(format!( + "duplicate qubits {}", + &qubit + ))); + } + let qubit_index = qc_data.qubits().find(&qubit).unwrap(); + ordered_vec[qubit_index.0 as usize] = + new_dag.add_qubit_unchecked(py, &qubit)?; + Ok(()) + })?; + Some(ordered_vec) } else { - for qubit in qc_data.qubits().bits() { - let bound = qubit.clone_ref(py).into_bound(py); - qubit_map.insert(new_dag.add_qubit_unchecked(py, qubit.bind(py))?, bound); - } - } + qc_data + .qubits() + .bits() + .iter() + .try_for_each(|qubit| -> PyResult<_> { + new_dag.add_qubit_unchecked(py, qubit.bind(py))?; + Ok(()) + })?; + None + }; // Add the clbits depending on order. - let mut clbit_map: HashMap> = HashMap::new(); - if let Some(clbit_ordering) = clbit_order { - for clbit in clbit_ordering { - if new_dag.clbits.find(&clbit).is_some() { - return Err(DAGCircuitError::new_err(format!( - "duplicate clbits {}", - &clbit - ))); - } - clbit_map.insert(new_dag.add_clbit_unchecked(py, &clbit)?, clbit); - } + let clbit_map: Option> = if let Some(clbit_ordering) = clbit_order { + let mut ordered_vec = Vec::from_iter((0..num_clbits as u32).map(Clbit)); + clbit_ordering + .into_iter() + .try_for_each(|clbit| -> PyResult<()> { + if new_dag.clbits.find(&clbit).is_some() { + return Err(DAGCircuitError::new_err(format!( + "duplicate clbits {}", + &clbit + ))); + }; + let clbit_index = qc_data.clbits().find(&clbit).unwrap(); + ordered_vec[clbit_index.0 as usize] = + new_dag.add_clbit_unchecked(py, &clbit)?; + Ok(()) + })?; + Some(ordered_vec) } else { - for clbit in qc_data.clbits().bits() { - let bound = clbit.clone_ref(py).into_bound(py); - clbit_map.insert(new_dag.add_clbit_unchecked(py, clbit.bind(py))?, bound); - } - } + qc_data + .clbits() + .bits() + .iter() + .try_for_each(|clbit| -> PyResult<()> { + new_dag.add_clbit_unchecked(py, clbit.bind(py))?; + Ok(()) + })?; + None + }; // Add all of the new vars. for var in &qc.declared_vars { @@ -6704,35 +6725,36 @@ impl DAGCircuit { } } - // Collect the re-mapped indices for qubits - let remapped_qubits: HashMap = qubit_map - .into_iter() - .map(|(index, bit)| (qc_data.qubits().find(&bit).unwrap(), index)) - .collect(); - // Collect the re-mapped indices for clbits - let remapped_clbits: HashMap = clbit_map - .into_iter() - .map(|(index, bit)| (qc_data.clbits().find(&bit).unwrap(), index)) - .collect(); - // Pre-process and re-intern all indices again. let instructions: Vec = qc_data .iter() .map(|instr| -> PyResult { // Re-map the qubits - let qargs: Vec = qc_data - .get_qargs(instr.qubits) - .iter() - .map(|bit| remapped_qubits[bit]) - .collect(); - let new_qubits = new_dag.qargs_interner.insert_owned(qargs); + let new_qargs = if let Some(qubit_mapping) = &qubit_map { + let qargs = qc_data + .get_qargs(instr.qubits) + .iter() + .map(|bit| qubit_mapping[bit.0 as usize]) + .collect(); + new_dag.qargs_interner.insert_owned(qargs) + } else { + new_dag + .qargs_interner + .insert(qc_data.get_qargs(instr.qubits)) + }; // Remap the clbits - let cargs: Vec = qc_data - .get_cargs(instr.clbits) - .iter() - .map(|bit| remapped_clbits[bit]) - .collect(); - let new_clbits = new_dag.cargs_interner.insert_owned(cargs); + let new_cargs = if let Some(clbit_mapping) = &clbit_map { + let qargs = qc_data + .get_cargs(instr.clbits) + .iter() + .map(|bit| clbit_mapping[bit.0 as usize]) + .collect(); + new_dag.cargs_interner.insert_owned(qargs) + } else { + new_dag + .cargs_interner + .insert(qc_data.get_cargs(instr.clbits)) + }; // Copy the operations Ok(PackedInstruction { @@ -6741,8 +6763,8 @@ impl DAGCircuit { } else { instr.op.clone() }, - qubits: new_qubits, - clbits: new_clbits, + qubits: new_qargs, + clbits: new_cargs, params: instr.params.clone(), extra_attrs: instr.extra_attrs.clone(), #[cfg(feature = "cache_pygates")] diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index d9ffba4a0c66..dcff558ade64 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -81,17 +81,6 @@ impl From for BitType { } } -#[inline(always)] -#[doc(hidden)] -fn add_submodule(m: &Bound, constructor: F, name: &str) -> PyResult<()> -where - F: FnOnce(&Bound) -> PyResult<()>, -{ - let new_mod = PyModule::new_bound(m.py(), name)?; - constructor(&new_mod)?; - m.add_submodule(&new_mod) -} - pub fn circuit(m: &Bound) -> PyResult<()> { m.add_class::()?; m.add_class::()?; @@ -101,6 +90,5 @@ pub fn circuit(m: &Bound) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; - add_submodule(m, converters::converters, "converters")?; Ok(()) } diff --git a/crates/pyext/src/lib.rs b/crates/pyext/src/lib.rs index 03bd0202dae4..72c40bba88ec 100644 --- a/crates/pyext/src/lib.rs +++ b/crates/pyext/src/lib.rs @@ -42,6 +42,7 @@ where #[pymodule] fn _accelerate(m: &Bound) -> PyResult<()> { add_submodule(m, qiskit_circuit::circuit, "circuit")?; + add_submodule(m, qiskit_circuit::converters::converters, "converters")?; add_submodule(m, qiskit_qasm2::qasm2, "qasm2")?; add_submodule(m, qiskit_qasm3::qasm3, "qasm3")?; add_submodule(m, circuit_library, "circuit_library")?; diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 2fd10b078696..894f9710d431 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -61,7 +61,7 @@ # and not have to rely on attribute access. No action needed for top-level extension packages. sys.modules["qiskit._accelerate.circuit"] = _accelerate.circuit sys.modules["qiskit._accelerate.circuit_library"] = _accelerate.circuit_library -sys.modules["qiskit._accelerate.circuit.converters"] = _accelerate.circuit.converters +sys.modules["qiskit._accelerate.converters"] = _accelerate.converters sys.modules["qiskit._accelerate.convert_2q_block_matrix"] = _accelerate.convert_2q_block_matrix sys.modules["qiskit._accelerate.dense_layout"] = _accelerate.dense_layout sys.modules["qiskit._accelerate.error_map"] = _accelerate.error_map diff --git a/qiskit/converters/circuit_to_dag.py b/qiskit/converters/circuit_to_dag.py index f4433c468de5..5be9a721bafa 100644 --- a/qiskit/converters/circuit_to_dag.py +++ b/qiskit/converters/circuit_to_dag.py @@ -13,7 +13,7 @@ """Helper function for converting a circuit to a dag""" from qiskit.circuit.library.blueprintcircuit import BlueprintCircuit -from qiskit._accelerate.circuit.converters import circuit_to_dag as core_circuit_to_dag +from qiskit._accelerate.converters import circuit_to_dag as core_circuit_to_dag def circuit_to_dag(circuit, copy_operations=True, *, qubit_order=None, clbit_order=None):