Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix performance regression in UnitarySynthesis #13375

Merged
merged 3 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 8 additions & 14 deletions crates/accelerate/src/unitary_synthesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use smallvec::{smallvec, SmallVec};

use pyo3::intern;
use pyo3::prelude::*;
use pyo3::types::{IntoPyDict, PyDict, PyList, PyString};
use pyo3::types::{IntoPyDict, PyDict, PyString};
use pyo3::wrap_pyfunction;
use pyo3::Python;

Expand Down Expand Up @@ -225,7 +225,7 @@ fn py_run_main_loop(
qubit_indices: Vec<usize>,
min_qubits: usize,
target: &Target,
coupling_edges: &Bound<'_, PyList>,
coupling_edges: HashSet<[PhysicalQubit; 2]>,
approximation_degree: Option<f64>,
natural_direction: Option<bool>,
) -> PyResult<DAGCircuit> {
Expand Down Expand Up @@ -268,7 +268,7 @@ fn py_run_main_loop(
new_ids,
min_qubits,
target,
coupling_edges,
coupling_edges.clone(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can avoid this clone if we refactor the functions a bit to split the python interface out and use a reference for the recursion. Not sure how important this is because this only comes up during control flow processing and we're already slower there because of the python usage and circuit to dag conversion. Maybe something to fix for when we make a dag native control flow op.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make sense to revisit this when we get to have rust-native control flow op.

approximation_degree,
natural_direction,
)?;
Expand Down Expand Up @@ -352,7 +352,7 @@ fn py_run_main_loop(
py,
unitary,
ref_qubits,
coupling_edges,
&coupling_edges,
target,
approximation_degree,
natural_direction,
Expand Down Expand Up @@ -383,7 +383,7 @@ fn run_2q_unitary_synthesis(
py: Python,
unitary: Array2<Complex64>,
ref_qubits: &[PhysicalQubit; 2],
coupling_edges: &Bound<'_, PyList>,
coupling_edges: &HashSet<[PhysicalQubit; 2]>,
target: &Target,
approximation_degree: Option<f64>,
natural_direction: Option<bool>,
Expand Down Expand Up @@ -794,7 +794,7 @@ fn preferred_direction(
decomposer: &DecomposerElement,
ref_qubits: &[PhysicalQubit; 2],
natural_direction: Option<bool>,
coupling_edges: &Bound<'_, PyList>,
coupling_edges: &HashSet<[PhysicalQubit; 2]>,
target: &Target,
) -> PyResult<Option<bool>> {
// Returns:
Expand Down Expand Up @@ -830,14 +830,8 @@ fn preferred_direction(
Some(false) => None,
_ => {
// None or Some(true)
let mut edge_set = HashSet::new();
for item in coupling_edges.iter() {
if let Ok(tuple) = item.extract::<(usize, usize)>() {
edge_set.insert(tuple);
}
}
let zero_one = edge_set.contains(&(qubits[0].0 as usize, qubits[1].0 as usize));
let one_zero = edge_set.contains(&(qubits[1].0 as usize, qubits[0].0 as usize));
let zero_one = coupling_edges.contains(&qubits);
let one_zero = coupling_edges.contains(&[qubits[1], qubits[0]]);

match (zero_one, one_zero) {
(true, false) => Some(true),
Expand Down
72 changes: 37 additions & 35 deletions qiskit/transpiler/passes/synthesis/unitary_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,37 +457,6 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
default_kwargs = {}
method_list = [(plugin_method, plugin_kwargs), (default_method, default_kwargs)]

for method, kwargs in method_list:
if method.supports_basis_gates:
kwargs["basis_gates"] = self._basis_gates
if method.supports_natural_direction:
kwargs["natural_direction"] = self._natural_direction
if method.supports_pulse_optimize:
kwargs["pulse_optimize"] = self._pulse_optimize
if method.supports_gate_lengths:
_gate_lengths = _gate_lengths or _build_gate_lengths(
self._backend_props, self._target
)
kwargs["gate_lengths"] = _gate_lengths
if method.supports_gate_errors:
_gate_errors = _gate_errors or _build_gate_errors(self._backend_props, self._target)
kwargs["gate_errors"] = _gate_errors
if method.supports_gate_lengths_by_qubit:
_gate_lengths_by_qubit = _gate_lengths_by_qubit or _build_gate_lengths_by_qubit(
self._backend_props, self._target
)
kwargs["gate_lengths_by_qubit"] = _gate_lengths_by_qubit
if method.supports_gate_errors_by_qubit:
_gate_errors_by_qubit = _gate_errors_by_qubit or _build_gate_errors_by_qubit(
self._backend_props, self._target
)
kwargs["gate_errors_by_qubit"] = _gate_errors_by_qubit
supported_bases = method.supported_bases
if supported_bases is not None:
kwargs["matched_basis"] = _choose_bases(self._basis_gates, supported_bases)
if method.supports_target:
kwargs["target"] = self._target

# Handle approximation degree as a special case for backwards compatibility, it's
# not part of the plugin interface and only something needed for the default
# pass.
Expand All @@ -503,22 +472,55 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
else {}
)

if self.method == "default" and isinstance(kwargs["target"], Target):
if self.method == "default" and self._target is not None:
_coupling_edges = (
list(self._coupling_map.get_edges()) if self._coupling_map is not None else []
set(self._coupling_map.get_edges()) if self._coupling_map is not None else set()
)

out = run_default_main_loop(
dag,
list(qubit_indices.values()),
self._min_qubits,
kwargs["target"],
self._target,
_coupling_edges,
self._approximation_degree,
kwargs["natural_direction"],
self._natural_direction,
)
return out
else:
for method, kwargs in method_list:
if method.supports_basis_gates:
kwargs["basis_gates"] = self._basis_gates
if method.supports_natural_direction:
kwargs["natural_direction"] = self._natural_direction
if method.supports_pulse_optimize:
kwargs["pulse_optimize"] = self._pulse_optimize
if method.supports_gate_lengths:
_gate_lengths = _gate_lengths or _build_gate_lengths(
self._backend_props, self._target
)
kwargs["gate_lengths"] = _gate_lengths
if method.supports_gate_errors:
_gate_errors = _gate_errors or _build_gate_errors(
self._backend_props, self._target
)
kwargs["gate_errors"] = _gate_errors
if method.supports_gate_lengths_by_qubit:
_gate_lengths_by_qubit = _gate_lengths_by_qubit or _build_gate_lengths_by_qubit(
self._backend_props, self._target
)
kwargs["gate_lengths_by_qubit"] = _gate_lengths_by_qubit
if method.supports_gate_errors_by_qubit:
_gate_errors_by_qubit = _gate_errors_by_qubit or _build_gate_errors_by_qubit(
self._backend_props, self._target
)
kwargs["gate_errors_by_qubit"] = _gate_errors_by_qubit
supported_bases = method.supported_bases
if supported_bases is not None:
kwargs["matched_basis"] = _choose_bases(self._basis_gates, supported_bases)
if method.supports_target:
kwargs["target"] = self._target

out = self._run_main_loop(
dag, qubit_indices, plugin_method, plugin_kwargs, default_method, default_kwargs
)
Expand Down