Skip to content

Commit

Permalink
Add a flag for toggling swap split
Browse files Browse the repository at this point in the history
gadial committed Jan 22, 2025
1 parent e30b617 commit 93b59f9
Showing 3 changed files with 28 additions and 10 deletions.
3 changes: 2 additions & 1 deletion crates/accelerate/src/split_2q_unitaries.rs
Original file line number Diff line number Diff line change
@@ -85,6 +85,7 @@ pub fn split_2q_unitaries(
py: Python,
dag: &mut DAGCircuit,
requested_fidelity: f64,
split_swaps: bool,
) -> PyResult<Option<(DAGCircuit, Vec<usize>)>> {
if !dag.get_op_counts().contains_key("unitary") {
return Ok(None);
@@ -130,7 +131,7 @@ pub fn split_2q_unitaries(
}
}
}
if !has_swaps {
if !split_swaps || !has_swaps {
return Ok(None);
}
// We have swap-like unitaries, so we create a new DAG in a manner similar to
6 changes: 4 additions & 2 deletions qiskit/transpiler/passes/optimization/split_2q_unitaries.py
Original file line number Diff line number Diff line change
@@ -31,17 +31,19 @@ class Split2QUnitaries(TransformationPass):
to how it's done in :class:`ElidePermutations`.
"""

def __init__(self, fidelity: float = 1.0 - 1e-16):
def __init__(self, fidelity: float = 1.0 - 1e-16, split_swap: bool = False):
"""
Args:
fidelity: Allowed tolerance for splitting two-qubit unitaries and gate decompositions.
split_swap: Whether to attempt to split swap gates, resulting in a permutation of the qubits.
"""
super().__init__()
self.requested_fidelity = fidelity
self.split_swap = split_swap

def run(self, dag: DAGCircuit) -> DAGCircuit:
"""Run the Split2QUnitaries pass on `dag`."""
result = split_2q_unitaries(dag, self.requested_fidelity)
result = split_2q_unitaries(dag, self.requested_fidelity, self.split_swap)
if result is None:
return dag

29 changes: 22 additions & 7 deletions test/python/transpiler/test_split_2q_unitaries.py
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
from qiskit.circuit import Parameter, CircuitInstruction, Gate
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.quantum_info import Operator
from qiskit.transpiler import PassManager, generate_preset_pass_manager
from qiskit.transpiler import PassManager
from qiskit.quantum_info.operators.predicates import matrix_equal
from qiskit.transpiler.passes import Collect2qBlocks, ConsolidateBlocks
from qiskit.transpiler.passes.optimization.split_2q_unitaries import Split2QUnitaries
@@ -278,12 +278,14 @@ def test_2q_swap(self):
pm = PassManager()
pm.append(Collect2qBlocks())
pm.append(ConsolidateBlocks())
pm.append(Split2QUnitaries())
pm.append(Split2QUnitaries(split_swap=True))
res = pm.run(qc_split)
res_op = Operator.from_circuit(res)
expected_op = Operator(qc_split)

self.assertNotIn("swap", res.count_ops())
self.assertEqual(
res.count_ops()["unitary"], 2
) # the original 2-qubit unitary should be split into 2 1-qubit unitaries.
self.assertTrue(expected_op.equiv(res_op))
self.assertTrue(matrix_equal(expected_op.data, res_op.data, ignore_phase=False))

@@ -303,17 +305,20 @@ def test_2q_swap_with_1_qubit_gates(self):
pm = PassManager()
pm.append(Collect2qBlocks())
pm.append(ConsolidateBlocks())
pm.append(Split2QUnitaries())
pm.append(Split2QUnitaries(split_swap=True))
res = pm.run(qc_split)
res_op = Operator.from_circuit(res)
expected_op = Operator(qc_split)

self.assertNotIn("swap", res.count_ops())
self.assertEqual(
res.count_ops()["unitary"], 2
) # the original 2-qubit unitary should be split into 2 1-qubit unitaries.
self.assertTrue(expected_op.equiv(res_op))
self.assertTrue(matrix_equal(expected_op.data, res_op.data, ignore_phase=False))

def test_2q_swap_with_non_unitary_swaps(self):
"""Test that a 2q unitary matching a swap gate in a
"""Test a 2q unitary matching a swap gate in a
circuit containing explicit swap gates."""
qc = QuantumCircuit(2)
qc.swap(0, 1)
@@ -324,11 +329,17 @@ def test_2q_swap_with_non_unitary_swaps(self):

qc_split.append(UnitaryGate(Operator(qc)), [0, 1])

pm = generate_preset_pass_manager(optimization_level=2)
pm = PassManager()
pm.append(Collect2qBlocks())
pm.append(ConsolidateBlocks())
pm.append(Split2QUnitaries(split_swap=True))
res = pm.run(qc_split)
res_op = Operator.from_circuit(res)
expected_op = Operator(qc_split)

self.assertEqual(
res.count_ops()["unitary"], 2
) # the original 2-qubit unitary should be split into 2 1-qubit unitaries.
self.assertTrue(expected_op.equiv(res_op))
self.assertTrue(matrix_equal(expected_op.data, res_op.data, ignore_phase=False))

@@ -355,10 +366,14 @@ def test_2q_swap_with_large_circuit(self):
qc_split.cx(0, 4)
qc_split.h(1)

pm = generate_preset_pass_manager(optimization_level=2)
pm = PassManager()
pm.append(Split2QUnitaries(split_swap=True))
res = pm.run(qc_split)
res_op = Operator.from_circuit(res)
expected_op = Operator(qc_split)

self.assertEqual(
res.count_ops()["unitary"], 2
) # the original 2-qubit unitary should be split into 2 1-qubit unitaries.
self.assertTrue(expected_op.equiv(res_op))
self.assertTrue(matrix_equal(expected_op.data, res_op.data, ignore_phase=False))

0 comments on commit 93b59f9

Please sign in to comment.