forked from Qiskit/qiskit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Oxidize BasisTranslator] Add rust-native
compose_transforms()
(Qis…
…kit#13137) * Initial: Add `compose_transforms` and `get_example_gates` to Rust. * Add: `compose_transform` logic in rust * Fix: Correct the behavior of `compose_transforms`. - Use `PackedOperation.control_flow` instead of `CONTROL_FLOW_OP_NAMES` to check if an instructuion is a control flow operation. - Remove panic statement when checking for example gates. - Use qreg when adding an instruction to the mock_dag in `compose_transforms`. - Add missing comparison check in for loop to compare the mapped instructions. - Use borrowed `DAGCircuit` instances in the recursion of `get_example_gates`, do not use extract. - Fix behavior of `get_example_gates` to return `PackedInstruction` instances instead of `NodeIndex`. * Fix: Leverage rust-based `circuit_to_dag` converters. - Use `circuit_to_dag` and `DAGCircuit::from_circuit_data` to convert `QuantumCircuit` instances. * Format: Fix indentation of import lines in `compose_transforms.rs` * Formatting: Separate complicated type aliases * Fix: Adapt to new `DAGCircuit` limitations * Format: Remove unused imports in BasisTranslator * Fix: Adapt to Qiskit#13143 * Fix: Code review comments - Remove unused `circuit_to_dag` and `OperationRef` imports. - Streamline complex types into simpler aliases. - Rename `CircuitRep` to `CircuitFromPython`. - Reshape `get_example_gates` to work with `CircuitData` instances during its recursion. - Remove unnecessary clone of `gate_name` in `compose_transform`. - Use `mapped_instructions` values when iterating in `compose_transforms`. - Rename `DAGCircuit::substitute_node_with_dag` to `DAGCircuit::py_*` in rust. - Adapted `_basis_search` to return a tuple of tuples instead of a 4-tuple. * Fix: More commments from code review - Remove stale comment related to Qiskit#3947 - Rename tuple instances to `GateIdentifier`. - Rename `doomed_nodes` to `nodes_to_replace`. - Add docstring for `get_example_gates`. - Rename `get_example_gates` to `get_gate_num_params`. Co-authored-by: Kevin Hartman <[email protected]> * Refactor: Rename `example_gates` to `gate_param_counts` - Remove `CircuitFromPython` and re-use original instance from `EquivalenceLibrary` after Qiskit#12585 merged. --------- Co-authored-by: Kevin Hartman <[email protected]>
- Loading branch information
1 parent
4e6a874
commit 43feab3
Showing
10 changed files
with
276 additions
and
120 deletions.
There are no files selected for viewing
192 changes: 192 additions & 0 deletions
192
crates/accelerate/src/basis/basis_translator/compose_transforms.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
// This code is part of Qiskit. | ||
// | ||
// (C) Copyright IBM 2024 | ||
// | ||
// This code is licensed under the Apache License, Version 2.0. You may | ||
// obtain a copy of this license in the LICENSE.txt file in the root directory | ||
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
// | ||
// Any modifications or derivative works of this code must retain this | ||
// copyright notice, and modified files need to carry a notice indicating | ||
// that they have been altered from the originals. | ||
|
||
use hashbrown::{HashMap, HashSet}; | ||
use pyo3::prelude::*; | ||
use qiskit_circuit::circuit_instruction::OperationFromPython; | ||
use qiskit_circuit::imports::{GATE, PARAMETER_VECTOR, QUANTUM_REGISTER}; | ||
use qiskit_circuit::parameter_table::ParameterUuid; | ||
use qiskit_circuit::Qubit; | ||
use qiskit_circuit::{ | ||
circuit_data::CircuitData, | ||
dag_circuit::{DAGCircuit, NodeType}, | ||
operations::{Operation, Param}, | ||
}; | ||
use smallvec::SmallVec; | ||
|
||
use crate::equivalence::CircuitFromPython; | ||
|
||
// Custom types | ||
pub type GateIdentifier = (String, u32); | ||
pub type BasisTransformIn = (SmallVec<[Param; 3]>, CircuitFromPython); | ||
pub type BasisTransformOut = (SmallVec<[Param; 3]>, DAGCircuit); | ||
|
||
#[pyfunction(name = "compose_transforms")] | ||
pub(super) fn py_compose_transforms( | ||
py: Python, | ||
basis_transforms: Vec<(GateIdentifier, BasisTransformIn)>, | ||
source_basis: HashSet<GateIdentifier>, | ||
source_dag: &DAGCircuit, | ||
) -> PyResult<HashMap<GateIdentifier, BasisTransformOut>> { | ||
compose_transforms(py, &basis_transforms, &source_basis, source_dag) | ||
} | ||
|
||
pub(super) fn compose_transforms<'a>( | ||
py: Python, | ||
basis_transforms: &'a [(GateIdentifier, BasisTransformIn)], | ||
source_basis: &'a HashSet<GateIdentifier>, | ||
source_dag: &'a DAGCircuit, | ||
) -> PyResult<HashMap<GateIdentifier, BasisTransformOut>> { | ||
let mut gate_param_counts: HashMap<GateIdentifier, usize> = HashMap::default(); | ||
get_gates_num_params(source_dag, &mut gate_param_counts)?; | ||
let mut mapped_instructions: HashMap<GateIdentifier, BasisTransformOut> = HashMap::new(); | ||
|
||
for (gate_name, gate_num_qubits) in source_basis.iter().cloned() { | ||
let num_params = gate_param_counts[&(gate_name.clone(), gate_num_qubits)]; | ||
|
||
let placeholder_params: SmallVec<[Param; 3]> = PARAMETER_VECTOR | ||
.get_bound(py) | ||
.call1((&gate_name, num_params))? | ||
.extract()?; | ||
|
||
let mut dag = DAGCircuit::new(py)?; | ||
// Create the mock gate and add to the circuit, use Python for this. | ||
let qubits = QUANTUM_REGISTER.get_bound(py).call1((gate_num_qubits,))?; | ||
dag.add_qreg(py, &qubits)?; | ||
|
||
let gate = GATE.get_bound(py).call1(( | ||
&gate_name, | ||
gate_num_qubits, | ||
placeholder_params | ||
.iter() | ||
.map(|x| x.clone_ref(py)) | ||
.collect::<SmallVec<[Param; 3]>>(), | ||
))?; | ||
let gate_obj: OperationFromPython = gate.extract()?; | ||
let qubits: Vec<Qubit> = (0..dag.num_qubits() as u32).map(Qubit).collect(); | ||
dag.apply_operation_back( | ||
py, | ||
gate_obj.operation, | ||
&qubits, | ||
&[], | ||
if gate_obj.params.is_empty() { | ||
None | ||
} else { | ||
Some(gate_obj.params) | ||
}, | ||
gate_obj.extra_attrs, | ||
#[cfg(feature = "cache_pygates")] | ||
Some(gate.into()), | ||
)?; | ||
mapped_instructions.insert((gate_name, gate_num_qubits), (placeholder_params, dag)); | ||
|
||
for ((gate_name, gate_num_qubits), (equiv_params, equiv)) in basis_transforms { | ||
for (_, dag) in &mut mapped_instructions.values_mut() { | ||
let nodes_to_replace = dag | ||
.op_nodes(true) | ||
.filter_map(|node| { | ||
if let Some(NodeType::Operation(op)) = dag.dag().node_weight(node) { | ||
if (gate_name.as_str(), *gate_num_qubits) | ||
== (op.op.name(), op.op.num_qubits()) | ||
{ | ||
Some(( | ||
node, | ||
op.params_view() | ||
.iter() | ||
.map(|x| x.clone_ref(py)) | ||
.collect::<SmallVec<[Param; 3]>>(), | ||
)) | ||
} else { | ||
None | ||
} | ||
} else { | ||
None | ||
} | ||
}) | ||
.collect::<Vec<_>>(); | ||
for (node, params) in nodes_to_replace { | ||
let param_mapping: HashMap<ParameterUuid, Param> = equiv_params | ||
.iter() | ||
.map(|x| ParameterUuid::from_parameter(x.to_object(py).bind(py))) | ||
.zip(params) | ||
.map(|(uuid, param)| -> PyResult<(ParameterUuid, Param)> { | ||
Ok((uuid?, param.clone_ref(py))) | ||
}) | ||
.collect::<PyResult<_>>()?; | ||
let mut replacement = equiv.clone(); | ||
replacement | ||
.0 | ||
.assign_parameters_from_mapping(py, param_mapping)?; | ||
let replace_dag: DAGCircuit = | ||
DAGCircuit::from_circuit_data(py, replacement.0, true)?; | ||
let op_node = dag.get_node(py, node)?; | ||
dag.py_substitute_node_with_dag( | ||
py, | ||
op_node.bind(py), | ||
&replace_dag, | ||
None, | ||
true, | ||
)?; | ||
} | ||
} | ||
} | ||
} | ||
Ok(mapped_instructions) | ||
} | ||
|
||
/// `DAGCircuit` variant. | ||
/// | ||
/// Gets the identifier of a gate instance (name, number of qubits) mapped to the | ||
/// number of parameters it contains currently. | ||
fn get_gates_num_params( | ||
dag: &DAGCircuit, | ||
example_gates: &mut HashMap<GateIdentifier, usize>, | ||
) -> PyResult<()> { | ||
for node in dag.op_nodes(true) { | ||
if let Some(NodeType::Operation(op)) = dag.dag().node_weight(node) { | ||
example_gates.insert( | ||
(op.op.name().to_string(), op.op.num_qubits()), | ||
op.params_view().len(), | ||
); | ||
if op.op.control_flow() { | ||
let blocks = op.op.blocks(); | ||
for block in blocks { | ||
get_gates_num_params_circuit(&block, example_gates)?; | ||
} | ||
} | ||
} | ||
} | ||
Ok(()) | ||
} | ||
|
||
/// `CircuitData` variant. | ||
/// | ||
/// Gets the identifier of a gate instance (name, number of qubits) mapped to the | ||
/// number of parameters it contains currently. | ||
fn get_gates_num_params_circuit( | ||
circuit: &CircuitData, | ||
example_gates: &mut HashMap<GateIdentifier, usize>, | ||
) -> PyResult<()> { | ||
for inst in circuit.iter() { | ||
example_gates.insert( | ||
(inst.op.name().to_string(), inst.op.num_qubits()), | ||
inst.params_view().len(), | ||
); | ||
if inst.op.control_flow() { | ||
let blocks = inst.op.blocks(); | ||
for block in blocks { | ||
get_gates_num_params_circuit(&block, example_gates)?; | ||
} | ||
} | ||
} | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// This code is part of Qiskit. | ||
// | ||
// (C) Copyright IBM 2024 | ||
// | ||
// This code is licensed under the Apache License, Version 2.0. You may | ||
// obtain a copy of this license in the LICENSE.txt file in the root directory | ||
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
// | ||
// Any modifications or derivative works of this code must retain this | ||
// copyright notice, and modified files need to carry a notice indicating | ||
// that they have been altered from the originals. | ||
|
||
use pyo3::prelude::*; | ||
|
||
mod compose_transforms; | ||
|
||
#[pymodule] | ||
pub fn basis_translator(m: &Bound<PyModule>) -> PyResult<()> { | ||
m.add_wrapped(wrap_pyfunction!(compose_transforms::py_compose_transforms))?; | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// This code is part of Qiskit. | ||
// | ||
// (C) Copyright IBM 2024 | ||
// | ||
// This code is licensed under the Apache License, Version 2.0. You may | ||
// obtain a copy of this license in the LICENSE.txt file in the root directory | ||
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
// | ||
// Any modifications or derivative works of this code must retain this | ||
// copyright notice, and modified files need to carry a notice indicating | ||
// that they have been altered from the originals. | ||
|
||
use pyo3::{prelude::*, wrap_pymodule}; | ||
|
||
pub mod basis_translator; | ||
|
||
#[pymodule] | ||
pub fn basis(m: &Bound<PyModule>) -> PyResult<()> { | ||
m.add_wrapped(wrap_pymodule!(basis_translator::basis_translator))?; | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.