Skip to content

Commit

Permalink
Reduce non-determinism in UnitarySynthesis pass (#13667)
Browse files Browse the repository at this point in the history
* Make the output of the unitary synthesis pass reproducible

* Add test
  • Loading branch information
ElePT authored Jan 15, 2025
1 parent 327e903 commit 994328a
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 7 deletions.
10 changes: 5 additions & 5 deletions crates/accelerate/src/unitary_synthesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use std::sync::OnceLock;

use approx::relative_eq;
use hashbrown::{HashMap, HashSet};
use indexmap::IndexMap;
use indexmap::{IndexMap, IndexSet};
use itertools::Itertools;
use ndarray::prelude::*;
use num_complex::{Complex, Complex64};
Expand Down Expand Up @@ -627,8 +627,8 @@ fn get_2q_decomposers_from_target(
}

let target_basis_set = get_target_basis_set(target, qubits[0]);
let available_1q_basis: HashSet<&str> =
HashSet::from_iter(target_basis_set.get_bases().map(|basis| basis.as_str()));
let available_1q_basis: IndexSet<&str> =
IndexSet::from_iter(target_basis_set.get_bases().map(|basis| basis.as_str()));
let mut decomposers: Vec<DecomposerElement> = Vec::new();

#[inline]
Expand Down Expand Up @@ -689,10 +689,10 @@ fn get_2q_decomposers_from_target(
// If our 2q basis gates are a subset of cx, ecr, or cz then we know TwoQubitBasisDecomposer
// is an ideal decomposition and there is no need to bother calculating the XX embodiments
// or try the XX decomposer
let available_basis_set: HashSet<&str> = available_2q_basis.keys().copied().collect();
let available_basis_set: IndexSet<&str> = available_2q_basis.keys().copied().collect();

#[inline]
fn check_goodbye(basis_set: &HashSet<&str>) -> bool {
fn check_goodbye(basis_set: &IndexSet<&str>) -> bool {
basis_set.iter().all(|gate| GOODBYE_SET.contains(gate))
}

Expand Down
22 changes: 20 additions & 2 deletions test/python/transpiler/test_unitary_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import scipy
from ddt import ddt, data

from qiskit import transpile
from qiskit import transpile, generate_preset_pass_manager
from qiskit.providers.fake_provider import Fake5QV1, GenericBackendV2
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.library import quantum_volume
Expand Down Expand Up @@ -921,7 +921,6 @@ def test_3q_measure_all(self):

def test_target_with_global_gates(self):
"""Test that 2q decomposition can handle a target with global gates."""

basis_gates = ["h", "p", "cp", "rz", "cx", "ccx", "swap"]
target = Target.from_configuration(basis_gates=basis_gates)

Expand All @@ -931,9 +930,28 @@ def test_target_with_global_gates(self):
bell_op = Operator(bell)
qc = QuantumCircuit(2)
qc.unitary(bell_op, [0, 1])

tqc = transpile(qc, target=target)
self.assertTrue(set(tqc.count_ops()).issubset(basis_gates))

def test_determinism(self):
"""Test that the decomposition is deterministic."""
gate_counts = {"rx": 6, "rz": 12, "iswap": 2}
basis_gates = ["rx", "rz", "iswap"]
target = Target.from_configuration(basis_gates=basis_gates)
pm = generate_preset_pass_manager(target=target, optimization_level=2, seed_transpiler=42)

qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)

for _ in range(10):
out = pm.run(qc)
self.assertTrue(Operator(out).equiv(qc))
self.assertTrue(set(out.count_ops()).issubset(basis_gates))
for basis_gate in basis_gates:
self.assertLessEqual(out.count_ops()[basis_gate], gate_counts[basis_gate])


if __name__ == "__main__":
unittest.main()

0 comments on commit 994328a

Please sign in to comment.