Skip to content

Commit 994328a

Browse files
authored
Reduce non-determinism in UnitarySynthesis pass (Qiskit#13667)
* Make the output of the unitary synthesis pass reproducible * Add test
1 parent 327e903 commit 994328a

File tree

2 files changed

+25
-7
lines changed

2 files changed

+25
-7
lines changed

crates/accelerate/src/unitary_synthesis.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::sync::OnceLock;
1717

1818
use approx::relative_eq;
1919
use hashbrown::{HashMap, HashSet};
20-
use indexmap::IndexMap;
20+
use indexmap::{IndexMap, IndexSet};
2121
use itertools::Itertools;
2222
use ndarray::prelude::*;
2323
use num_complex::{Complex, Complex64};
@@ -627,8 +627,8 @@ fn get_2q_decomposers_from_target(
627627
}
628628

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

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

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

test/python/transpiler/test_unitary_synthesis.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import scipy
2222
from ddt import ddt, data
2323

24-
from qiskit import transpile
24+
from qiskit import transpile, generate_preset_pass_manager
2525
from qiskit.providers.fake_provider import Fake5QV1, GenericBackendV2
2626
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
2727
from qiskit.circuit.library import quantum_volume
@@ -921,7 +921,6 @@ def test_3q_measure_all(self):
921921

922922
def test_target_with_global_gates(self):
923923
"""Test that 2q decomposition can handle a target with global gates."""
924-
925924
basis_gates = ["h", "p", "cp", "rz", "cx", "ccx", "swap"]
926925
target = Target.from_configuration(basis_gates=basis_gates)
927926

@@ -931,9 +930,28 @@ def test_target_with_global_gates(self):
931930
bell_op = Operator(bell)
932931
qc = QuantumCircuit(2)
933932
qc.unitary(bell_op, [0, 1])
933+
934934
tqc = transpile(qc, target=target)
935935
self.assertTrue(set(tqc.count_ops()).issubset(basis_gates))
936936

937+
def test_determinism(self):
938+
"""Test that the decomposition is deterministic."""
939+
gate_counts = {"rx": 6, "rz": 12, "iswap": 2}
940+
basis_gates = ["rx", "rz", "iswap"]
941+
target = Target.from_configuration(basis_gates=basis_gates)
942+
pm = generate_preset_pass_manager(target=target, optimization_level=2, seed_transpiler=42)
943+
944+
qc = QuantumCircuit(2)
945+
qc.h(0)
946+
qc.cx(0, 1)
947+
948+
for _ in range(10):
949+
out = pm.run(qc)
950+
self.assertTrue(Operator(out).equiv(qc))
951+
self.assertTrue(set(out.count_ops()).issubset(basis_gates))
952+
for basis_gate in basis_gates:
953+
self.assertLessEqual(out.count_ops()[basis_gate], gate_counts[basis_gate])
954+
937955

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

0 commit comments

Comments
 (0)