Skip to content

Commit 8dce146

Browse files
committed
Fix performance regression in UnitarySynthesis
This commit fixes a performance regression that was introduced in PR Qiskit#13141. When the pass is looking up the preferred synthesis direction for a unitary based on the connectvity constraints the connectivity was being provided as a PyList. To look up the edge in connectivity set this meant we needed to iterate over the list and then create a set that rust could lookup if it contains an edge or it's reverse. This has significant overhead because its iterating via python and also iterating per decomposition. This commit addresses this by changing the input type to be a HashSet from Python so Pyo3 will convert a pyset directly to a HashSet once at call time and that's used by reference for lookups directly instead of needing to iterate over the list each time.
1 parent 19c5c06 commit 8dce146

File tree

2 files changed

+9
-15
lines changed

2 files changed

+9
-15
lines changed

crates/accelerate/src/unitary_synthesis.rs

+8-14
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use smallvec::{smallvec, SmallVec};
2727

2828
use pyo3::intern;
2929
use pyo3::prelude::*;
30-
use pyo3::types::{IntoPyDict, PyDict, PyList, PyString};
30+
use pyo3::types::{IntoPyDict, PyDict, PyString};
3131
use pyo3::wrap_pyfunction;
3232
use pyo3::Python;
3333

@@ -225,7 +225,7 @@ fn py_run_main_loop(
225225
qubit_indices: Vec<usize>,
226226
min_qubits: usize,
227227
target: &Target,
228-
coupling_edges: &Bound<'_, PyList>,
228+
coupling_edges: HashSet<[usize; 2]>,
229229
approximation_degree: Option<f64>,
230230
natural_direction: Option<bool>,
231231
) -> PyResult<DAGCircuit> {
@@ -268,7 +268,7 @@ fn py_run_main_loop(
268268
new_ids,
269269
min_qubits,
270270
target,
271-
coupling_edges,
271+
coupling_edges.clone(),
272272
approximation_degree,
273273
natural_direction,
274274
)?;
@@ -352,7 +352,7 @@ fn py_run_main_loop(
352352
py,
353353
unitary,
354354
ref_qubits,
355-
coupling_edges,
355+
&coupling_edges,
356356
target,
357357
approximation_degree,
358358
natural_direction,
@@ -383,7 +383,7 @@ fn run_2q_unitary_synthesis(
383383
py: Python,
384384
unitary: Array2<Complex64>,
385385
ref_qubits: &[PhysicalQubit; 2],
386-
coupling_edges: &Bound<'_, PyList>,
386+
coupling_edges: &HashSet<[usize; 2]>,
387387
target: &Target,
388388
approximation_degree: Option<f64>,
389389
natural_direction: Option<bool>,
@@ -794,7 +794,7 @@ fn preferred_direction(
794794
decomposer: &DecomposerElement,
795795
ref_qubits: &[PhysicalQubit; 2],
796796
natural_direction: Option<bool>,
797-
coupling_edges: &Bound<'_, PyList>,
797+
coupling_edges: &HashSet<[usize; 2]>,
798798
target: &Target,
799799
) -> PyResult<Option<bool>> {
800800
// Returns:
@@ -830,14 +830,8 @@ fn preferred_direction(
830830
Some(false) => None,
831831
_ => {
832832
// None or Some(true)
833-
let mut edge_set = HashSet::new();
834-
for item in coupling_edges.iter() {
835-
if let Ok(tuple) = item.extract::<(usize, usize)>() {
836-
edge_set.insert(tuple);
837-
}
838-
}
839-
let zero_one = edge_set.contains(&(qubits[0].0 as usize, qubits[1].0 as usize));
840-
let one_zero = edge_set.contains(&(qubits[1].0 as usize, qubits[0].0 as usize));
833+
let zero_one = coupling_edges.contains(&[qubits[0].0 as usize, qubits[1].0 as usize]);
834+
let one_zero = coupling_edges.contains(&[qubits[1].0 as usize, qubits[0].0 as usize]);
841835

842836
match (zero_one, one_zero) {
843837
(true, false) => Some(true),

qiskit/transpiler/passes/synthesis/unitary_synthesis.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
505505

506506
if self.method == "default" and isinstance(kwargs["target"], Target):
507507
_coupling_edges = (
508-
list(self._coupling_map.get_edges()) if self._coupling_map is not None else []
508+
set(self._coupling_map.get_edges()) if self._coupling_map is not None else set()
509509
)
510510

511511
out = run_default_main_loop(

0 commit comments

Comments
 (0)