From 7f0282af2dc7d58dd0602146b28bb822563cad65 Mon Sep 17 00:00:00 2001 From: Raynel Sanchez Date: Mon, 22 Jul 2024 22:06:24 -0400 Subject: [PATCH 01/10] Add: Basis search function - Add rust counterpart for `basis_search`. - Consolidated the `BasisSearchVisitor` into the function due to differences in rust behavior. --- .../basis/basis_translator/basis_search.rs | 214 ++++++++++++++++++ .../src/basis/basis_translator/mod.rs | 21 ++ crates/accelerate/src/basis/mod.rs | 21 ++ crates/accelerate/src/lib.rs | 1 + crates/pyext/src/lib.rs | 1 + qiskit/__init__.py | 2 + .../passes/basis/basis_translator.py | 158 +------------ 7 files changed, 262 insertions(+), 156 deletions(-) create mode 100644 crates/accelerate/src/basis/basis_translator/basis_search.rs create mode 100644 crates/accelerate/src/basis/basis_translator/mod.rs create mode 100644 crates/accelerate/src/basis/mod.rs diff --git a/crates/accelerate/src/basis/basis_translator/basis_search.rs b/crates/accelerate/src/basis/basis_translator/basis_search.rs new file mode 100644 index 000000000000..e158630a0c62 --- /dev/null +++ b/crates/accelerate/src/basis/basis_translator/basis_search.rs @@ -0,0 +1,214 @@ +// 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 std::cell::RefCell; + +use crate::equivalence::{CircuitRep, EdgeData, Equivalence, EquivalenceLibrary, Key, NodeData}; +use ahash::RandomState; +use indexmap::{IndexMap, IndexSet}; +use pyo3::prelude::*; +use pyo3::types::PySet; +use qiskit_circuit::operations::{Operation, Param}; +use rustworkx_core::petgraph::stable_graph::{EdgeReference, NodeIndex, StableDiGraph}; +use rustworkx_core::petgraph::visit::Control; +use rustworkx_core::traversal::{dijkstra_search, DijkstraEvent}; +use smallvec::SmallVec; + +#[pyfunction] +#[pyo3(name = "basis_search")] +pub(crate) fn py_basis_search( + equiv_lib: &mut EquivalenceLibrary, + source_basis: &Bound, + target_basis: &Bound, +) -> PyResult, CircuitRep)>>> { + let source_basis: PyResult> = + source_basis.iter().map(|item| item.extract()).collect(); + let target_basis: PyResult> = + target_basis.iter().map(|item| item.extract()).collect(); + Ok(basis_search(equiv_lib, source_basis?, target_basis?)) +} + +/// Search for a set of transformations from source_basis to target_basis. +/// Args: +/// equiv_lib (EquivalenceLibrary): Source of valid translations +/// source_basis (Set[Tuple[gate_name: str, gate_num_qubits: int]]): Starting basis. +/// target_basis (Set[gate_name: str]): Target basis. +/// +/// Returns: +/// Optional[List[Tuple[gate, equiv_params, equiv_circuit]]]: List of (gate, +/// equiv_params, equiv_circuit) tuples tuples which, if applied in order +/// will map from source_basis to target_basis. Returns None if no path +/// was found. +pub(crate) fn basis_search( + equiv_lib: &mut EquivalenceLibrary, + source_basis: IndexSet<(String, u32), RandomState>, + target_basis: IndexSet, +) -> Option, CircuitRep)>> { + // Build the visitor attributes: + let mut num_gates_remaining_for_rule: IndexMap = IndexMap::default(); + let predecessors: IndexMap<(&str, u32), Equivalence, RandomState> = IndexMap::default(); + let predecessors_cell: RefCell> = + RefCell::new(predecessors); + let opt_cost_map: IndexMap<(&str, u32), u32, RandomState> = IndexMap::default(); + let opt_cost_map_cell: RefCell> = + RefCell::new(opt_cost_map); + let mut basis_transforms: Vec<(String, u32, SmallVec<[Param; 3]>, CircuitRep)> = vec![]; + + // Initialize visitor attributes: + initialize_num_gates_remain_for_rule(&equiv_lib.graph, &mut num_gates_remaining_for_rule); + // println!("{:#?}", num_gates_remaining_for_rule); + + // TODO: Logs + let mut source_basis_remain: IndexSet = source_basis + .iter() + .filter_map(|(gate_name, gate_num_qubits)| { + if !target_basis.contains(gate_name) { + Some(Key { + name: gate_name.to_string(), + num_qubits: *gate_num_qubits, + }) + } else { + None + } + }) + .collect(); + + // If source_basis is empty, no work needs to be done. + if source_basis_remain.is_empty() { + return Some(vec![]); + } + + // This is only necessary since gates in target basis are currently reported by + // their names and we need to have in addition the number of qubits they act on. + let target_basis_keys: Vec = equiv_lib + .keys() + .cloned() + .filter(|key| target_basis.contains(&key.name)) + .collect(); + // println!("Target basis keys {:#?}", target_basis_keys); + let dummy: NodeIndex = equiv_lib.graph.add_node(NodeData { + equivs: vec![], + key: Key { + name: "key".to_string(), + num_qubits: 0, + }, + }); + + let target_basis_indices: Vec = target_basis_keys + .iter() + .map(|key| equiv_lib.node_index(key)) + .collect(); + + target_basis_indices.iter().for_each(|node| { + equiv_lib.graph.add_edge(dummy, *node, None); + }); + + // Build visitor methods + + let edge_weight = + |edge: EdgeReference>| -> Result { + if edge.weight().is_none() { + return Ok(1); + } + let edge_data = edge.weight().as_ref().unwrap(); + let mut cost_tot = 0; + let borrowed_cost = opt_cost_map_cell.borrow(); + for instruction in edge_data.rule.circuit.0.iter() { + cost_tot += borrowed_cost[&(instruction.op.name(), instruction.op.num_qubits())]; + } + Ok(cost_tot + - borrowed_cost[&(edge_data.source.name.as_str(), edge_data.source.num_qubits)]) + }; + + dijkstra_search( + &equiv_lib.graph, + [dummy], + edge_weight, + |event: DijkstraEvent, u32>| { + match event { + DijkstraEvent::Discover(n, score) => { + let gate_key = &equiv_lib.graph[n].key; + let gate = &(gate_key.name.as_str(), gate_key.num_qubits); + source_basis_remain.swap_remove(gate_key); + let mut borrowed_cost_map = opt_cost_map_cell.borrow_mut(); + borrowed_cost_map + .entry(*gate) + .and_modify(|cost_ref| *cost_ref = score) + .or_insert(score); + if let Some(rule) = predecessors_cell.borrow().get(gate) { + // TODO: Logger + basis_transforms.push(( + gate_key.name.to_string(), + gate_key.num_qubits, + rule.params.clone(), + rule.circuit.clone(), + )); + } + + if source_basis_remain.is_empty() { + basis_transforms.reverse(); + return Control::Break(()); + } + } + DijkstraEvent::EdgeRelaxed(_, target, edata) => { + if let Some(edata) = edata { + let gate = &equiv_lib.graph[target].key; + predecessors_cell + .borrow_mut() + .entry((gate.name.as_str(), gate.num_qubits)) + .and_modify(|value| *value = edata.rule.clone()) + .or_insert(edata.rule.clone()); + } + } + DijkstraEvent::ExamineEdge(_, target, edata) => { + if edata.is_some() { + let edata = edata.as_ref().unwrap(); + num_gates_remaining_for_rule + .entry(edata.index) + .and_modify(|val| *val -= 1) + .or_insert(0); + let target = &equiv_lib.graph[target].key; + + if num_gates_remaining_for_rule[&edata.index] > 0 + || target_basis_keys.contains(target) + { + return Control::Prune; + } + } + } + _ => {} + }; + Control::Continue + }, + ) + .unwrap(); + // Values will have to be cloned in order for the dummy node to be removed. + // Will be revised + drop(opt_cost_map_cell); + drop(predecessors_cell); + equiv_lib.graph.remove_node(dummy); + Some(basis_transforms) +} + +fn initialize_num_gates_remain_for_rule( + graph: &StableDiGraph>, + source: &mut IndexMap, +) { + let mut save_index = usize::MAX; + for edge_data in graph.edge_weights().flatten() { + if save_index == edge_data.index { + continue; + } + source.insert(edge_data.index, edge_data.num_gates); + save_index = edge_data.index; + } +} diff --git a/crates/accelerate/src/basis/basis_translator/mod.rs b/crates/accelerate/src/basis/basis_translator/mod.rs new file mode 100644 index 000000000000..9927eb57d287 --- /dev/null +++ b/crates/accelerate/src/basis/basis_translator/mod.rs @@ -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::*; + +pub mod basis_search; + +#[pymodule] +pub fn basis_translator(m: &Bound) -> PyResult<()> { + m.add_wrapped(wrap_pyfunction!(basis_search::py_basis_search))?; + Ok(()) +} diff --git a/crates/accelerate/src/basis/mod.rs b/crates/accelerate/src/basis/mod.rs new file mode 100644 index 000000000000..b072f7848fc2 --- /dev/null +++ b/crates/accelerate/src/basis/mod.rs @@ -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) -> PyResult<()> { + m.add_wrapped(wrap_pymodule!(basis_translator::basis_translator))?; + Ok(()) +} diff --git a/crates/accelerate/src/lib.rs b/crates/accelerate/src/lib.rs index f17fea5bccfe..8a28f2a9e6c5 100644 --- a/crates/accelerate/src/lib.rs +++ b/crates/accelerate/src/lib.rs @@ -14,6 +14,7 @@ use std::env; use pyo3::import_exception; +pub mod basis; pub mod check_map; pub mod circuit_library; pub mod commutation_analysis; diff --git a/crates/pyext/src/lib.rs b/crates/pyext/src/lib.rs index 8b54c535db2f..f2afcf3e0eb9 100644 --- a/crates/pyext/src/lib.rs +++ b/crates/pyext/src/lib.rs @@ -28,6 +28,7 @@ where #[rustfmt::skip] #[pymodule] fn _accelerate(m: &Bound) -> PyResult<()> { + add_submodule(m, ::qiskit_accelerate::basis::basis, "basis")?; add_submodule(m, ::qiskit_accelerate::check_map::check_map_mod, "check_map")?; add_submodule(m, ::qiskit_accelerate::circuit_library::circuit_library, "circuit_library")?; add_submodule(m, ::qiskit_accelerate::commutation_analysis::commutation_analysis, "commutation_analysis")?; diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 0e9694217cd8..48506055344c 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -54,6 +54,8 @@ sys.modules["qiskit._accelerate.circuit"] = _accelerate.circuit sys.modules["qiskit._accelerate.circuit_library"] = _accelerate.circuit_library sys.modules["qiskit._accelerate.converters"] = _accelerate.converters +sys.modules["qiskit._accelerate.basis"] = _accelerate.basis +sys.modules["qiskit._accelerate.basis.basis_translator"] = _accelerate.basis.basis_translator 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.equivalence"] = _accelerate.equivalence diff --git a/qiskit/transpiler/passes/basis/basis_translator.py b/qiskit/transpiler/passes/basis/basis_translator.py index 0fdae973e1b9..8c0c468a2b48 100644 --- a/qiskit/transpiler/passes/basis/basis_translator.py +++ b/qiskit/transpiler/passes/basis/basis_translator.py @@ -175,7 +175,7 @@ def run(self, dag): # Search for a path from source to target basis. search_start_time = time.time() - basis_transforms = _basis_search(self._equiv_lib, source_basis, target_basis) + basis_transforms = basis_search(self._equiv_lib, source_basis, target_basis) qarg_local_basis_transforms = {} for qarg, local_source_basis in qargs_local_source_basis.items(): @@ -198,7 +198,7 @@ def run(self, dag): expanded_target, qarg, ) - local_basis_transforms = _basis_search( + local_basis_transforms = basis_search( self._equiv_lib, local_source_basis, expanded_target ) @@ -451,160 +451,6 @@ def _extract_basis_target( return source_basis, qargs_local_source_basis -class StopIfBasisRewritable(Exception): - """Custom exception that signals `rustworkx.dijkstra_search` to stop.""" - - -class BasisSearchVisitor(rustworkx.visit.DijkstraVisitor): - """Handles events emitted during `rustworkx.dijkstra_search`.""" - - def __init__(self, graph, source_basis, target_basis): - self.graph = graph - self.target_basis = set(target_basis) - self._source_gates_remain = set(source_basis) - self._num_gates_remain_for_rule = {} - save_index = -1 - for edata in self.graph.edges(): - if save_index == edata.index: - continue - self._num_gates_remain_for_rule[edata.index] = edata.num_gates - save_index = edata.index - - self._basis_transforms = [] - self._predecessors = {} - self._opt_cost_map = {} - - def discover_vertex(self, v, score): - gate = self.graph[v].key - self._source_gates_remain.discard(gate) - self._opt_cost_map[gate] = score - rule = self._predecessors.get(gate, None) - if rule is not None: - logger.debug( - "Gate %s generated using rule \n%s\n with total cost of %s.", - gate.name, - rule.circuit, - score, - ) - self._basis_transforms.append((gate.name, gate.num_qubits, rule.params, rule.circuit)) - # we can stop the search if we have found all gates in the original circuit. - if not self._source_gates_remain: - # if we start from source gates and apply `basis_transforms` in reverse order, we'll end - # up with gates in the target basis. Note though that `basis_transforms` may include - # additional transformations that are not required to map our source gates to the given - # target basis. - self._basis_transforms.reverse() - raise StopIfBasisRewritable - - def examine_edge(self, edge): - _, target, edata = edge - if edata is None: - return - - self._num_gates_remain_for_rule[edata.index] -= 1 - - target = self.graph[target].key - # if there are gates in this `rule` that we have not yet generated, we can't apply - # this `rule`. if `target` is already in basis, it's not beneficial to use this rule. - if self._num_gates_remain_for_rule[edata.index] > 0 or target in self.target_basis: - raise rustworkx.visit.PruneSearch - - def edge_relaxed(self, edge): - _, target, edata = edge - if edata is not None: - gate = self.graph[target].key - self._predecessors[gate] = edata.rule - - def edge_cost(self, edge_data): - """Returns the cost of an edge. - - This function computes the cost of this edge rule by summing - the costs of all gates in the rule equivalence circuit. In the - end, we need to subtract the cost of the source since `dijkstra` - will later add it. - """ - - if edge_data is None: - # the target of the edge is a gate in the target basis, - # so we return a default value of 1. - return 1 - - cost_tot = 0 - for instruction in edge_data.rule.circuit: - key = Key(name=instruction.name, num_qubits=len(instruction.qubits)) - cost_tot += self._opt_cost_map[key] - - return cost_tot - self._opt_cost_map[edge_data.source] - - @property - def basis_transforms(self): - """Returns the gate basis transforms.""" - return self._basis_transforms - - -def _basis_search(equiv_lib, source_basis, target_basis): - """Search for a set of transformations from source_basis to target_basis. - - Args: - equiv_lib (EquivalenceLibrary): Source of valid translations - source_basis (Set[Tuple[gate_name: str, gate_num_qubits: int]]): Starting basis. - target_basis (Set[gate_name: str]): Target basis. - - Returns: - Optional[List[Tuple[gate, equiv_params, equiv_circuit]]]: List of (gate, - equiv_params, equiv_circuit) tuples tuples which, if applied in order - will map from source_basis to target_basis. Returns None if no path - was found. - """ - - logger.debug("Begining basis search from %s to %s.", source_basis, target_basis) - - source_basis = { - Key(gate_name, gate_num_qubits) - for gate_name, gate_num_qubits in source_basis - if gate_name not in target_basis - } - - # if source basis is empty, no work to be done. - if not source_basis: - return [] - - # This is only necessary since gates in target basis are currently reported by - # their names and we need to have in addition the number of qubits they act on. - target_basis_keys = [key for key in equiv_lib.keys() if key.name in target_basis] - - graph = equiv_lib.graph - vis = BasisSearchVisitor(graph, source_basis, target_basis_keys) - - # we add a dummy node and connect it with gates in the target basis. - # we'll start the search from this dummy node. - dummy = graph.add_node( - NodeData( - key=Key("".join(chr(random.randint(0, 26) + 97) for _ in range(10)), 0), - equivs=[Equivalence([], QuantumCircuit(0, name="dummy starting node"))], - ) - ) - - try: - graph.add_edges_from_no_data( - [(dummy, equiv_lib.node_index(key)) for key in target_basis_keys] - ) - rtn = None - try: - rustworkx.digraph_dijkstra_search(graph, [dummy], vis.edge_cost, vis) - except StopIfBasisRewritable: - rtn = vis.basis_transforms - - logger.debug("Transformation path:") - for gate_name, gate_num_qubits, params, equiv in rtn: - logger.debug("%s/%s => %s\n%s", gate_name, gate_num_qubits, params, equiv) - finally: - # Remove dummy node in order to return graph to original state - graph.remove_node(dummy) - - return rtn - - def _compose_transforms(basis_transforms, source_basis, source_dag): """Compose a set of basis transforms into a set of replacements. From 37815a8f12afe2b425506fa78b812ee6fc178a35 Mon Sep 17 00:00:00 2001 From: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> Date: Mon, 22 Jul 2024 23:34:57 -0400 Subject: [PATCH 02/10] Fix: Wrong return value for `basis_search` --- .../basis/basis_translator/basis_search.rs | 79 +++++++++---------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/crates/accelerate/src/basis/basis_translator/basis_search.rs b/crates/accelerate/src/basis/basis_translator/basis_search.rs index e158630a0c62..0f596c98e24c 100644 --- a/crates/accelerate/src/basis/basis_translator/basis_search.rs +++ b/crates/accelerate/src/basis/basis_translator/basis_search.rs @@ -29,14 +29,15 @@ pub(crate) fn py_basis_search( equiv_lib: &mut EquivalenceLibrary, source_basis: &Bound, target_basis: &Bound, -) -> PyResult, CircuitRep)>>> { - let source_basis: PyResult> = +) -> PyResult { + let new_source_basis: PyResult> = source_basis.iter().map(|item| item.extract()).collect(); - let target_basis: PyResult> = + let new_target_basis: PyResult> = target_basis.iter().map(|item| item.extract()).collect(); - Ok(basis_search(equiv_lib, source_basis?, target_basis?)) + Ok(basis_search(equiv_lib, new_source_basis?, new_target_basis?).into_py(source_basis.py())) } +type BasisTransforms = Vec<(String, u32, SmallVec<[Param; 3]>, CircuitRep)>; /// Search for a set of transformations from source_basis to target_basis. /// Args: /// equiv_lib (EquivalenceLibrary): Source of valid translations @@ -52,7 +53,7 @@ pub(crate) fn basis_search( equiv_lib: &mut EquivalenceLibrary, source_basis: IndexSet<(String, u32), RandomState>, target_basis: IndexSet, -) -> Option, CircuitRep)>> { +) -> Option { // Build the visitor attributes: let mut num_gates_remaining_for_rule: IndexMap = IndexMap::default(); let predecessors: IndexMap<(&str, u32), Equivalence, RandomState> = IndexMap::default(); @@ -65,7 +66,6 @@ pub(crate) fn basis_search( // Initialize visitor attributes: initialize_num_gates_remain_for_rule(&equiv_lib.graph, &mut num_gates_remaining_for_rule); - // println!("{:#?}", num_gates_remaining_for_rule); // TODO: Logs let mut source_basis_remain: IndexSet = source_basis @@ -94,7 +94,8 @@ pub(crate) fn basis_search( .cloned() .filter(|key| target_basis.contains(&key.name)) .collect(); - // println!("Target basis keys {:#?}", target_basis_keys); + + // Dummy node is inserted in the graph. Which is where the search will start let dummy: NodeIndex = equiv_lib.graph.add_node(NodeData { equivs: vec![], key: Key { @@ -103,17 +104,18 @@ pub(crate) fn basis_search( }, }); + // Extract indices for the target_basis gates, to avoid borrowing from graph. let target_basis_indices: Vec = target_basis_keys .iter() .map(|key| equiv_lib.node_index(key)) .collect(); + // Connect each edge in the target_basis to the dummy node. target_basis_indices.iter().for_each(|node| { equiv_lib.graph.add_edge(dummy, *node, None); }); - // Build visitor methods - + // Edge cost function for Visitor let edge_weight = |edge: EdgeReference>| -> Result { if edge.weight().is_none() { @@ -129,7 +131,7 @@ pub(crate) fn basis_search( - borrowed_cost[&(edge_data.source.name.as_str(), edge_data.source.num_qubits)]) }; - dijkstra_search( + let basis_transforms = match dijkstra_search( &equiv_lib.graph, [dummy], edge_weight, @@ -159,44 +161,41 @@ pub(crate) fn basis_search( return Control::Break(()); } } - DijkstraEvent::EdgeRelaxed(_, target, edata) => { - if let Some(edata) = edata { - let gate = &equiv_lib.graph[target].key; - predecessors_cell - .borrow_mut() - .entry((gate.name.as_str(), gate.num_qubits)) - .and_modify(|value| *value = edata.rule.clone()) - .or_insert(edata.rule.clone()); - } + DijkstraEvent::EdgeRelaxed(_, target, Some(edata)) => { + let gate = &equiv_lib.graph[target].key; + predecessors_cell + .borrow_mut() + .entry((gate.name.as_str(), gate.num_qubits)) + .and_modify(|value| *value = edata.rule.clone()) + .or_insert(edata.rule.clone()); } - DijkstraEvent::ExamineEdge(_, target, edata) => { - if edata.is_some() { - let edata = edata.as_ref().unwrap(); - num_gates_remaining_for_rule - .entry(edata.index) - .and_modify(|val| *val -= 1) - .or_insert(0); - let target = &equiv_lib.graph[target].key; - - if num_gates_remaining_for_rule[&edata.index] > 0 - || target_basis_keys.contains(target) - { - return Control::Prune; - } + DijkstraEvent::ExamineEdge(_, target, Some(edata)) => { + num_gates_remaining_for_rule + .entry(edata.index) + .and_modify(|val| *val -= 1) + .or_insert(0); + let target = &equiv_lib.graph[target].key; + + // If there are gates in this `rule` that we have not yet generated, we can't apply + // this `rule`. if `target` is already in basis, it's not beneficial to use this rule. + if num_gates_remaining_for_rule[&edata.index] > 0 + || target_basis_keys.contains(target) + { + return Control::Prune; } } _ => {} }; Control::Continue }, - ) - .unwrap(); - // Values will have to be cloned in order for the dummy node to be removed. - // Will be revised - drop(opt_cost_map_cell); - drop(predecessors_cell); + ) { + Ok(Control::Break(_)) => Some(basis_transforms), + _ => None, + }; + + // TODO: Values will have to be cloned in order for the dummy node to be removed. equiv_lib.graph.remove_node(dummy); - Some(basis_transforms) + basis_transforms } fn initialize_num_gates_remain_for_rule( From dbf9131f3d9af92468416579fe59c6c41e8b0e5a Mon Sep 17 00:00:00 2001 From: Raynel Sanchez Date: Tue, 23 Jul 2024 10:46:44 -0400 Subject: [PATCH 03/10] Fix: Remove `IndexMap` and duplicate declarations. --- .../basis/basis_translator/basis_search.rs | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/crates/accelerate/src/basis/basis_translator/basis_search.rs b/crates/accelerate/src/basis/basis_translator/basis_search.rs index 0f596c98e24c..0897d3f09012 100644 --- a/crates/accelerate/src/basis/basis_translator/basis_search.rs +++ b/crates/accelerate/src/basis/basis_translator/basis_search.rs @@ -12,29 +12,33 @@ use std::cell::RefCell; -use crate::equivalence::{CircuitRep, EdgeData, Equivalence, EquivalenceLibrary, Key, NodeData}; -use ahash::RandomState; -use indexmap::{IndexMap, IndexSet}; +use hashbrown::{HashMap, HashSet}; use pyo3::prelude::*; -use pyo3::types::PySet; use qiskit_circuit::operations::{Operation, Param}; use rustworkx_core::petgraph::stable_graph::{EdgeReference, NodeIndex, StableDiGraph}; use rustworkx_core::petgraph::visit::Control; use rustworkx_core::traversal::{dijkstra_search, DijkstraEvent}; use smallvec::SmallVec; +use crate::equivalence::{CircuitRep, EdgeData, Equivalence, EquivalenceLibrary, Key, NodeData}; + #[pyfunction] #[pyo3(name = "basis_search")] pub(crate) fn py_basis_search( + py: Python, equiv_lib: &mut EquivalenceLibrary, - source_basis: &Bound, - target_basis: &Bound, -) -> PyResult { - let new_source_basis: PyResult> = - source_basis.iter().map(|item| item.extract()).collect(); - let new_target_basis: PyResult> = - target_basis.iter().map(|item| item.extract()).collect(); - Ok(basis_search(equiv_lib, new_source_basis?, new_target_basis?).into_py(source_basis.py())) + source_basis: HashSet<(String, u32)>, + target_basis: HashSet, +) -> PyObject { + basis_search( + equiv_lib, + source_basis + .iter() + .map(|(name, num_qubits)| (name.as_str(), *num_qubits)) + .collect(), + target_basis.iter().map(|name| name.as_str()).collect(), + ) + .into_py(py) } type BasisTransforms = Vec<(String, u32, SmallVec<[Param; 3]>, CircuitRep)>; @@ -51,24 +55,20 @@ type BasisTransforms = Vec<(String, u32, SmallVec<[Param; 3]>, CircuitRep)>; /// was found. pub(crate) fn basis_search( equiv_lib: &mut EquivalenceLibrary, - source_basis: IndexSet<(String, u32), RandomState>, - target_basis: IndexSet, + source_basis: HashSet<(&str, u32)>, + target_basis: HashSet<&str>, ) -> Option { // Build the visitor attributes: - let mut num_gates_remaining_for_rule: IndexMap = IndexMap::default(); - let predecessors: IndexMap<(&str, u32), Equivalence, RandomState> = IndexMap::default(); - let predecessors_cell: RefCell> = - RefCell::new(predecessors); - let opt_cost_map: IndexMap<(&str, u32), u32, RandomState> = IndexMap::default(); - let opt_cost_map_cell: RefCell> = - RefCell::new(opt_cost_map); + let mut num_gates_remaining_for_rule: HashMap = HashMap::default(); + let predecessors: RefCell> = RefCell::new(HashMap::default()); + let opt_cost_map: RefCell> = RefCell::new(HashMap::default()); let mut basis_transforms: Vec<(String, u32, SmallVec<[Param; 3]>, CircuitRep)> = vec![]; // Initialize visitor attributes: initialize_num_gates_remain_for_rule(&equiv_lib.graph, &mut num_gates_remaining_for_rule); // TODO: Logs - let mut source_basis_remain: IndexSet = source_basis + let mut source_basis_remain: HashSet = source_basis .iter() .filter_map(|(gate_name, gate_num_qubits)| { if !target_basis.contains(gate_name) { @@ -92,7 +92,7 @@ pub(crate) fn basis_search( let target_basis_keys: Vec = equiv_lib .keys() .cloned() - .filter(|key| target_basis.contains(&key.name)) + .filter(|key| target_basis.contains(key.name.as_str())) .collect(); // Dummy node is inserted in the graph. Which is where the search will start @@ -140,13 +140,13 @@ pub(crate) fn basis_search( DijkstraEvent::Discover(n, score) => { let gate_key = &equiv_lib.graph[n].key; let gate = &(gate_key.name.as_str(), gate_key.num_qubits); - source_basis_remain.swap_remove(gate_key); - let mut borrowed_cost_map = opt_cost_map_cell.borrow_mut(); + source_basis_remain.remove(gate_key); + let mut borrowed_cost_map = opt_cost_map.borrow_mut(); borrowed_cost_map .entry(*gate) .and_modify(|cost_ref| *cost_ref = score) .or_insert(score); - if let Some(rule) = predecessors_cell.borrow().get(gate) { + if let Some(rule) = predecessors.borrow().get(gate) { // TODO: Logger basis_transforms.push(( gate_key.name.to_string(), @@ -163,7 +163,7 @@ pub(crate) fn basis_search( } DijkstraEvent::EdgeRelaxed(_, target, Some(edata)) => { let gate = &equiv_lib.graph[target].key; - predecessors_cell + predecessors .borrow_mut() .entry((gate.name.as_str(), gate.num_qubits)) .and_modify(|value| *value = edata.rule.clone()) @@ -200,7 +200,7 @@ pub(crate) fn basis_search( fn initialize_num_gates_remain_for_rule( graph: &StableDiGraph>, - source: &mut IndexMap, + source: &mut HashMap, ) { let mut save_index = usize::MAX; for edge_data in graph.edge_weights().flatten() { From d457ddb040c60cdb3164f00c1efdfcd138dd0533 Mon Sep 17 00:00:00 2001 From: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> Date: Wed, 24 Jul 2024 09:36:56 -0400 Subject: [PATCH 04/10] Fix: Adapt to #12730 --- qiskit/transpiler/passes/basis/basis_translator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qiskit/transpiler/passes/basis/basis_translator.py b/qiskit/transpiler/passes/basis/basis_translator.py index 8c0c468a2b48..0d5e8911e526 100644 --- a/qiskit/transpiler/passes/basis/basis_translator.py +++ b/qiskit/transpiler/passes/basis/basis_translator.py @@ -37,6 +37,8 @@ from qiskit.transpiler.basepasses import TransformationPass from qiskit.transpiler.exceptions import TranspilerError from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES +from qiskit._accelerate.circuit import StandardGate +from qiskit._accelerate.basis.basis_translator import basis_search logger = logging.getLogger(__name__) From ed1fd5a05e74c0493b77fa8aca672aafb38db7d5 Mon Sep 17 00:00:00 2001 From: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> Date: Wed, 24 Jul 2024 10:11:57 -0400 Subject: [PATCH 05/10] Remove: unused imports --- qiskit/transpiler/passes/basis/basis_translator.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/qiskit/transpiler/passes/basis/basis_translator.py b/qiskit/transpiler/passes/basis/basis_translator.py index 0d5e8911e526..2fa6bf7d0d61 100644 --- a/qiskit/transpiler/passes/basis/basis_translator.py +++ b/qiskit/transpiler/passes/basis/basis_translator.py @@ -21,8 +21,6 @@ from itertools import zip_longest from collections import defaultdict -import rustworkx - from qiskit.circuit import ( Gate, ParameterVector, @@ -33,11 +31,9 @@ ) from qiskit.dagcircuit import DAGCircuit, DAGOpNode from qiskit.converters import circuit_to_dag, dag_to_circuit -from qiskit.circuit.equivalence import Key, NodeData, Equivalence from qiskit.transpiler.basepasses import TransformationPass from qiskit.transpiler.exceptions import TranspilerError from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES -from qiskit._accelerate.circuit import StandardGate from qiskit._accelerate.basis.basis_translator import basis_search logger = logging.getLogger(__name__) From c0d1f19247c22500a0f71eea599f74963ceb87df Mon Sep 17 00:00:00 2001 From: Raynel Sanchez Date: Tue, 6 Aug 2024 17:17:43 -0400 Subject: [PATCH 06/10] Docs: Edit docstring for rust native `basis_search` --- .../basis/basis_translator/basis_search.rs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/crates/accelerate/src/basis/basis_translator/basis_search.rs b/crates/accelerate/src/basis/basis_translator/basis_search.rs index 0897d3f09012..420d5dc9d198 100644 --- a/crates/accelerate/src/basis/basis_translator/basis_search.rs +++ b/crates/accelerate/src/basis/basis_translator/basis_search.rs @@ -24,6 +24,17 @@ use crate::equivalence::{CircuitRep, EdgeData, Equivalence, EquivalenceLibrary, #[pyfunction] #[pyo3(name = "basis_search")] +/// Search for a set of transformations from source_basis to target_basis. +/// Args: +/// equiv_lib (EquivalenceLibrary): Source of valid translations +/// source_basis (Set[Tuple[gate_name: str, gate_num_qubits: int]]): Starting basis. +/// target_basis (Set[gate_name: str]): Target basis. +/// +/// Returns: +/// Optional[List[Tuple[gate, equiv_params, equiv_circuit]]]: List of (gate, +/// equiv_params, equiv_circuit) tuples tuples which, if applied in order +/// will map from source_basis to target_basis. Returns None if no path +/// was found. pub(crate) fn py_basis_search( py: Python, equiv_lib: &mut EquivalenceLibrary, @@ -43,16 +54,13 @@ pub(crate) fn py_basis_search( type BasisTransforms = Vec<(String, u32, SmallVec<[Param; 3]>, CircuitRep)>; /// Search for a set of transformations from source_basis to target_basis. -/// Args: -/// equiv_lib (EquivalenceLibrary): Source of valid translations -/// source_basis (Set[Tuple[gate_name: str, gate_num_qubits: int]]): Starting basis. -/// target_basis (Set[gate_name: str]): Target basis. /// -/// Returns: -/// Optional[List[Tuple[gate, equiv_params, equiv_circuit]]]: List of (gate, -/// equiv_params, equiv_circuit) tuples tuples which, if applied in order -/// will map from source_basis to target_basis. Returns None if no path -/// was found. +/// Performs a Dijkstra search algorithm on the `EquivalenceLibrary`'s core graph +/// to rate and classify different possible equivalent circuits to the provided gates. +/// +/// This is done by connecting all the nodes represented in the `target_basis` to a dummy +/// node, and then traversing the graph until all the nodes described in the `source +/// basis` are reached. pub(crate) fn basis_search( equiv_lib: &mut EquivalenceLibrary, source_basis: HashSet<(&str, u32)>, From 48966e9620557cb3ad780f79533ac4d9fdba983b Mon Sep 17 00:00:00 2001 From: Raynel Sanchez Date: Thu, 29 Aug 2024 13:03:15 -0400 Subject: [PATCH 07/10] Fix: Use owned Strings. - Due to the nature of `hashbrown` we must use owned Strings instead of `&str`. --- .../basis/basis_translator/basis_search.rs | 58 +++++++++++-------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/crates/accelerate/src/basis/basis_translator/basis_search.rs b/crates/accelerate/src/basis/basis_translator/basis_search.rs index 420d5dc9d198..9823847d1cfe 100644 --- a/crates/accelerate/src/basis/basis_translator/basis_search.rs +++ b/crates/accelerate/src/basis/basis_translator/basis_search.rs @@ -14,14 +14,14 @@ use std::cell::RefCell; use hashbrown::{HashMap, HashSet}; use pyo3::prelude::*; + +use crate::equivalence::{CircuitRep, EdgeData, Equivalence, EquivalenceLibrary, Key, NodeData}; use qiskit_circuit::operations::{Operation, Param}; use rustworkx_core::petgraph::stable_graph::{EdgeReference, NodeIndex, StableDiGraph}; use rustworkx_core::petgraph::visit::Control; use rustworkx_core::traversal::{dijkstra_search, DijkstraEvent}; use smallvec::SmallVec; -use crate::equivalence::{CircuitRep, EdgeData, Equivalence, EquivalenceLibrary, Key, NodeData}; - #[pyfunction] #[pyo3(name = "basis_search")] /// Search for a set of transformations from source_basis to target_basis. @@ -68,8 +68,9 @@ pub(crate) fn basis_search( ) -> Option { // Build the visitor attributes: let mut num_gates_remaining_for_rule: HashMap = HashMap::default(); - let predecessors: RefCell> = RefCell::new(HashMap::default()); - let opt_cost_map: RefCell> = RefCell::new(HashMap::default()); + let predecessors: RefCell> = + RefCell::new(HashMap::default()); + let opt_cost_map: RefCell> = RefCell::new(HashMap::default()); let mut basis_transforms: Vec<(String, u32, SmallVec<[Param; 3]>, CircuitRep)> = vec![]; // Initialize visitor attributes: @@ -124,20 +125,26 @@ pub(crate) fn basis_search( }); // Edge cost function for Visitor - let edge_weight = - |edge: EdgeReference>| -> Result { - if edge.weight().is_none() { - return Ok(1); - } - let edge_data = edge.weight().as_ref().unwrap(); - let mut cost_tot = 0; - let borrowed_cost = opt_cost_map_cell.borrow(); - for instruction in edge_data.rule.circuit.0.iter() { - cost_tot += borrowed_cost[&(instruction.op.name(), instruction.op.num_qubits())]; - } - Ok(cost_tot - - borrowed_cost[&(edge_data.source.name.as_str(), edge_data.source.num_qubits)]) - }; + let edge_weight = |edge: EdgeReference>| -> Result { + if edge.weight().is_none() { + return Ok(1); + } + let edge_data = edge.weight().as_ref().unwrap(); + let mut cost_tot = 0; + let borrowed_cost = opt_cost_map.borrow(); + for instruction in edge_data.rule.circuit.0.iter() { + let instruction_op = instruction.op.view(); + cost_tot += borrowed_cost[&( + instruction_op.name().to_string(), + instruction_op.num_qubits(), + )]; + } + Ok(cost_tot + - borrowed_cost[&( + edge_data.source.name.to_string(), + edge_data.source.num_qubits, + )]) + }; let basis_transforms = match dijkstra_search( &equiv_lib.graph, @@ -147,14 +154,15 @@ pub(crate) fn basis_search( match event { DijkstraEvent::Discover(n, score) => { let gate_key = &equiv_lib.graph[n].key; - let gate = &(gate_key.name.as_str(), gate_key.num_qubits); + let gate = (gate_key.name.to_string(), gate_key.num_qubits); source_basis_remain.remove(gate_key); let mut borrowed_cost_map = opt_cost_map.borrow_mut(); - borrowed_cost_map - .entry(*gate) - .and_modify(|cost_ref| *cost_ref = score) - .or_insert(score); - if let Some(rule) = predecessors.borrow().get(gate) { + if let Some(entry) = borrowed_cost_map.get_mut(&gate) { + *entry = score; + } else { + borrowed_cost_map.insert(gate.clone(), score); + } + if let Some(rule) = predecessors.borrow().get(&gate) { // TODO: Logger basis_transforms.push(( gate_key.name.to_string(), @@ -173,7 +181,7 @@ pub(crate) fn basis_search( let gate = &equiv_lib.graph[target].key; predecessors .borrow_mut() - .entry((gate.name.as_str(), gate.num_qubits)) + .entry((gate.name.to_string(), gate.num_qubits)) .and_modify(|value| *value = edata.rule.clone()) .or_insert(edata.rule.clone()); } From d96de8ae68511f332eeb1428694d32d751a69349 Mon Sep 17 00:00:00 2001 From: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> Date: Thu, 26 Sep 2024 09:42:53 -0400 Subject: [PATCH 08/10] Add: mutable graph view that the `BasisTranslator` can access in Rust. - Remove import of `random` in `basis_translator`. --- .../src/basis/basis_translator/basis_search.rs | 18 +++++++++--------- crates/accelerate/src/equivalence.rs | 5 +++++ .../passes/basis/basis_translator.py | 1 - 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/crates/accelerate/src/basis/basis_translator/basis_search.rs b/crates/accelerate/src/basis/basis_translator/basis_search.rs index 9823847d1cfe..b0d641e6fe1f 100644 --- a/crates/accelerate/src/basis/basis_translator/basis_search.rs +++ b/crates/accelerate/src/basis/basis_translator/basis_search.rs @@ -74,7 +74,7 @@ pub(crate) fn basis_search( let mut basis_transforms: Vec<(String, u32, SmallVec<[Param; 3]>, CircuitRep)> = vec![]; // Initialize visitor attributes: - initialize_num_gates_remain_for_rule(&equiv_lib.graph, &mut num_gates_remaining_for_rule); + initialize_num_gates_remain_for_rule(equiv_lib.graph(), &mut num_gates_remaining_for_rule); // TODO: Logs let mut source_basis_remain: HashSet = source_basis @@ -100,12 +100,12 @@ pub(crate) fn basis_search( // their names and we need to have in addition the number of qubits they act on. let target_basis_keys: Vec = equiv_lib .keys() + .filter(|&key| target_basis.contains(key.name.as_str())) .cloned() - .filter(|key| target_basis.contains(key.name.as_str())) .collect(); // Dummy node is inserted in the graph. Which is where the search will start - let dummy: NodeIndex = equiv_lib.graph.add_node(NodeData { + let dummy: NodeIndex = equiv_lib.mut_graph().add_node(NodeData { equivs: vec![], key: Key { name: "key".to_string(), @@ -121,7 +121,7 @@ pub(crate) fn basis_search( // Connect each edge in the target_basis to the dummy node. target_basis_indices.iter().for_each(|node| { - equiv_lib.graph.add_edge(dummy, *node, None); + equiv_lib.mut_graph().add_edge(dummy, *node, None); }); // Edge cost function for Visitor @@ -147,13 +147,13 @@ pub(crate) fn basis_search( }; let basis_transforms = match dijkstra_search( - &equiv_lib.graph, + &equiv_lib.graph(), [dummy], edge_weight, |event: DijkstraEvent, u32>| { match event { DijkstraEvent::Discover(n, score) => { - let gate_key = &equiv_lib.graph[n].key; + let gate_key = &equiv_lib.graph()[n].key; let gate = (gate_key.name.to_string(), gate_key.num_qubits); source_basis_remain.remove(gate_key); let mut borrowed_cost_map = opt_cost_map.borrow_mut(); @@ -178,7 +178,7 @@ pub(crate) fn basis_search( } } DijkstraEvent::EdgeRelaxed(_, target, Some(edata)) => { - let gate = &equiv_lib.graph[target].key; + let gate = &equiv_lib.graph()[target].key; predecessors .borrow_mut() .entry((gate.name.to_string(), gate.num_qubits)) @@ -190,7 +190,7 @@ pub(crate) fn basis_search( .entry(edata.index) .and_modify(|val| *val -= 1) .or_insert(0); - let target = &equiv_lib.graph[target].key; + let target = &equiv_lib.graph()[target].key; // If there are gates in this `rule` that we have not yet generated, we can't apply // this `rule`. if `target` is already in basis, it's not beneficial to use this rule. @@ -210,7 +210,7 @@ pub(crate) fn basis_search( }; // TODO: Values will have to be cloned in order for the dummy node to be removed. - equiv_lib.graph.remove_node(dummy); + equiv_lib.mut_graph().remove_node(dummy); basis_transforms } diff --git a/crates/accelerate/src/equivalence.rs b/crates/accelerate/src/equivalence.rs index e389151b6b04..d9d04f2d53c6 100644 --- a/crates/accelerate/src/equivalence.rs +++ b/crates/accelerate/src/equivalence.rs @@ -690,6 +690,11 @@ impl EquivalenceLibrary { pub fn graph(&self) -> &GraphType { &self.graph } + + /// Expose a mutable view of the inner graph. + pub(crate) fn mut_graph(&mut self) -> &mut GraphType { + &mut self.graph + } } fn raise_if_param_mismatch( diff --git a/qiskit/transpiler/passes/basis/basis_translator.py b/qiskit/transpiler/passes/basis/basis_translator.py index 2fa6bf7d0d61..34c128820c06 100644 --- a/qiskit/transpiler/passes/basis/basis_translator.py +++ b/qiskit/transpiler/passes/basis/basis_translator.py @@ -13,7 +13,6 @@ """Translates gates to a target basis using a given equivalence library.""" -import random import time import logging From 3b5c8dd21aa20ece464db05baad1f9ea22bec29b Mon Sep 17 00:00:00 2001 From: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> Date: Mon, 7 Oct 2024 12:41:56 -0400 Subject: [PATCH 09/10] Fix: Review comments - Rename `EquivalenceLibrary`'s `mut_graph` method to `graph_mut` to keep consistent with rust naming conventions. - Use `&HashSet` instead of `HashSet<&str>` to avoid extra conversion. - Use `u32::MAX` as num_qubits for dummy node. - Use for loop instead of foreachj to add edges to dummy node. - Add comment explaining usage of flatten in `initialize_num_gates_remain_for_rule`. - Remove stale comments. --- .../basis/basis_translator/basis_search.rs | 31 ++++++------------- crates/accelerate/src/equivalence.rs | 2 +- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/crates/accelerate/src/basis/basis_translator/basis_search.rs b/crates/accelerate/src/basis/basis_translator/basis_search.rs index 46438eae925f..92d18d36f74d 100644 --- a/crates/accelerate/src/basis/basis_translator/basis_search.rs +++ b/crates/accelerate/src/basis/basis_translator/basis_search.rs @@ -42,15 +42,7 @@ pub(crate) fn py_basis_search( source_basis: HashSet, target_basis: HashSet, ) -> PyObject { - basis_search( - equiv_lib, - source_basis - .iter() - .map(|(name, num_qubits)| (name.as_str(), *num_qubits)) - .collect(), - target_basis.iter().map(|name| name.as_str()).collect(), - ) - .into_py(py) + basis_search(equiv_lib, &source_basis, &target_basis).into_py(py) } type BasisTransforms = Vec<(GateIdentifier, BasisTransformIn)>; @@ -64,8 +56,8 @@ type BasisTransforms = Vec<(GateIdentifier, BasisTransformIn)>; /// basis` are reached. pub(crate) fn basis_search( equiv_lib: &mut EquivalenceLibrary, - source_basis: HashSet<(&str, u32)>, - target_basis: HashSet<&str>, + source_basis: &HashSet, + target_basis: &HashSet, ) -> Option { // Build the visitor attributes: let mut num_gates_remaining_for_rule: HashMap = HashMap::default(); @@ -77,7 +69,6 @@ pub(crate) fn basis_search( // Initialize visitor attributes: initialize_num_gates_remain_for_rule(equiv_lib.graph(), &mut num_gates_remaining_for_rule); - // TODO: Logs let mut source_basis_remain: HashSet = source_basis .iter() .filter_map(|(gate_name, gate_num_qubits)| { @@ -106,11 +97,11 @@ pub(crate) fn basis_search( .collect(); // Dummy node is inserted in the graph. Which is where the search will start - let dummy: NodeIndex = equiv_lib.mut_graph().add_node(NodeData { + let dummy: NodeIndex = equiv_lib.graph_mut().add_node(NodeData { equivs: vec![], key: Key { name: "key".to_string(), - num_qubits: 0, + num_qubits: u32::MAX, }, }); @@ -121,9 +112,9 @@ pub(crate) fn basis_search( .collect(); // Connect each edge in the target_basis to the dummy node. - target_basis_indices.iter().for_each(|node| { - equiv_lib.mut_graph().add_edge(dummy, *node, None); - }); + for node in target_basis_indices { + equiv_lib.graph_mut().add_edge(dummy, node, None); + } // Edge cost function for Visitor let edge_weight = |edge: EdgeReference>| -> Result { @@ -164,7 +155,6 @@ pub(crate) fn basis_search( borrowed_cost_map.insert(gate.clone(), score); } if let Some(rule) = predecessors.borrow().get(&gate) { - // TODO: Logger basis_transforms.push(( (gate_key.name.to_string(), gate_key.num_qubits), (rule.params.clone(), rule.circuit.clone()), @@ -207,9 +197,7 @@ pub(crate) fn basis_search( Ok(Control::Break(_)) => Some(basis_transforms), _ => None, }; - - // TODO: Values will have to be cloned in order for the dummy node to be removed. - equiv_lib.mut_graph().remove_node(dummy); + equiv_lib.graph_mut().remove_node(dummy); basis_transforms } @@ -218,6 +206,7 @@ fn initialize_num_gates_remain_for_rule( source: &mut HashMap, ) { let mut save_index = usize::MAX; + // When iterating over the edges, ignore any none-valued ones by calling `flatten` for edge_data in graph.edge_weights().flatten() { if save_index == edge_data.index { continue; diff --git a/crates/accelerate/src/equivalence.rs b/crates/accelerate/src/equivalence.rs index 51afd9bfac3d..7ea9161a2a1f 100644 --- a/crates/accelerate/src/equivalence.rs +++ b/crates/accelerate/src/equivalence.rs @@ -699,7 +699,7 @@ impl EquivalenceLibrary { } /// Expose a mutable view of the inner graph. - pub(crate) fn mut_graph(&mut self) -> &mut GraphType { + pub(crate) fn graph_mut(&mut self) -> &mut GraphType { &mut self.graph } } From 90d83d0e5409f5ffbcc8bc108c36f8447f74dda7 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 7 Oct 2024 17:45:14 -0400 Subject: [PATCH 10/10] Update crates/accelerate/src/basis/basis_translator/basis_search.rs --- crates/accelerate/src/basis/basis_translator/basis_search.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/accelerate/src/basis/basis_translator/basis_search.rs b/crates/accelerate/src/basis/basis_translator/basis_search.rs index 92d18d36f74d..2810765db741 100644 --- a/crates/accelerate/src/basis/basis_translator/basis_search.rs +++ b/crates/accelerate/src/basis/basis_translator/basis_search.rs @@ -23,8 +23,6 @@ use rustworkx_core::traversal::{dijkstra_search, DijkstraEvent}; use super::compose_transforms::{BasisTransformIn, GateIdentifier}; -#[pyfunction] -#[pyo3(name = "basis_search")] /// Search for a set of transformations from source_basis to target_basis. /// Args: /// equiv_lib (EquivalenceLibrary): Source of valid translations @@ -36,6 +34,8 @@ use super::compose_transforms::{BasisTransformIn, GateIdentifier}; /// equiv_params, equiv_circuit) tuples tuples which, if applied in order /// will map from source_basis to target_basis. Returns None if no path /// was found. +#[pyfunction] +#[pyo3(name = "basis_search")] pub(crate) fn py_basis_search( py: Python, equiv_lib: &mut EquivalenceLibrary,