Skip to content

Commit

Permalink
Filter possible 2q decomposers more carefully in unitary synthesis (#…
Browse files Browse the repository at this point in the history
…10008) (#10074)

* Detect invalid input in XXDecomposer and raise errors

* Allow passing weyl decomposition to 2q decomposers

This is an optimization. We plan to compute this decomposition before
calling the 2q decomposers. So we want to be able to reuse it.

* Decompose unitary to synthesize to filter 2q decomposers

* Format with black

* Remove obsolete function

* Reformat
* Fix lint complaints

* Add tests for new error raised in XXDecomposer

* lighter-weight bug fix

* update test case for product of unitaries

---------

Co-authored-by: Ali Javadi <[email protected]>
(cherry picked from commit 93b561c)

Co-authored-by: John Lapeyre <[email protected]>
  • Loading branch information
mergify[bot] and jlapeyre authored May 3, 2023
1 parent b174fc5 commit 16bcd4c
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 17 deletions.
7 changes: 7 additions & 0 deletions qiskit/quantum_info/synthesis/xx_decompose/decomposer.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@ def _best_decomposition(canonical_coordinate, available_strengths):
canonical_coordinate = np.array(canonical_coordinate)

while True:
if len(priority_queue) == 0:
if len(available_strengths) == 0:
raise QiskitError(
"Attempting to synthesize entangling gate with no controlled gates in basis set."
)
raise QiskitError("Unable to synthesize a 2q unitary with the supplied basis set.")

sequence_cost, sequence = heapq.heappop(priority_queue)

strength_polytope = XXPolytope.from_strengths(*[x / 2 for x in sequence])
Expand Down
33 changes: 17 additions & 16 deletions qiskit/transpiler/passes/synthesis/unitary_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -701,24 +701,25 @@ def is_controlled(gate):
# if we are using the approximation_degree knob, use it to scale already-given fidelities
if approximation_degree is not None:
basis_2q_fidelity = {k: v * approximation_degree for k, v in basis_2q_fidelity.items()}
for basis_1q in available_1q_basis:
if isinstance(pi2_basis, CXGate) and basis_1q == "ZSX":
pi2_decomposer = TwoQubitBasisDecomposer(
pi2_basis,
euler_basis=basis_1q,
if basis_2q_fidelity:
for basis_1q in available_1q_basis:
if isinstance(pi2_basis, CXGate) and basis_1q == "ZSX":
pi2_decomposer = TwoQubitBasisDecomposer(
pi2_basis,
euler_basis=basis_1q,
basis_fidelity=basis_2q_fidelity,
pulse_optimize=True,
)
embodiments.update({pi / 2: XXEmbodiments[type(pi2_decomposer.gate)]})
else:
pi2_decomposer = None
decomposer = XXDecomposer(
basis_fidelity=basis_2q_fidelity,
pulse_optimize=True,
euler_basis=basis_1q,
embodiments=embodiments,
backup_optimizer=pi2_decomposer,
)
embodiments.update({pi / 2: XXEmbodiments[type(pi2_decomposer.gate)]})
else:
pi2_decomposer = None
decomposer = XXDecomposer(
basis_fidelity=basis_2q_fidelity,
euler_basis=basis_1q,
embodiments=embodiments,
backup_optimizer=pi2_decomposer,
)
decomposers.append(decomposer)
decomposers.append(decomposer)

self._decomposer_cache[qubits_tuple] = decomposers
return decomposers
Expand Down
23 changes: 23 additions & 0 deletions test/python/quantum_info/xx_decompose/test_decomposer.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from scipy.stats import unitary_group

import qiskit
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.quantum_info.operators import Operator
from qiskit.quantum_info.synthesis.xx_decompose.decomposer import (
XXDecomposer,
Expand Down Expand Up @@ -132,3 +133,25 @@ def test_compilation_improvement(self):
# the following are taken from Fig 14 of the XX synthesis paper
self.assertAlmostEqual(mean(clever_costs), 1.445e-2, delta=5e-3)
self.assertAlmostEqual(mean(naive_costs), 2.058e-2, delta=5e-3)

def test_error_on_empty_basis_fidelity(self):
"""Test synthesizing entangling gate with no entangling basis fails."""
decomposer = XXDecomposer(basis_fidelity={})
qc = QuantumCircuit(2)
qc.cx(0, 1)
mat = Operator(qc).to_matrix()
with self.assertRaisesRegex(
qiskit.exceptions.QiskitError,
"Attempting to synthesize entangling gate with no controlled gates in basis set.",
):
decomposer(mat)

def test_no_error_on_empty_basis_fidelity_trivial_target(self):
"""Test synthesizing non-entangling gate with no entangling basis succeeds."""
decomposer = XXDecomposer(basis_fidelity={})
qc = QuantumCircuit(2)
qc.x(0)
qc.y(1)
mat = Operator(qc).to_matrix()
dqc = decomposer(mat)
self.assertTrue(np.allclose(mat, Operator(dqc).to_matrix()))
53 changes: 52 additions & 1 deletion test/python/transpiler/test_unitary_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,20 @@
TrivialLayout,
)
from qiskit.circuit.library import (
IGate,
CXGate,
RZGate,
SXGate,
XGate,
iSwapGate,
ECRGate,
UGate,
ZGate,
RYYGate,
RZZGate,
RXXGate,
)
from qiskit.circuit import Measure
from qiskit.circuit.controlflow import IfElseOp
from qiskit.circuit import Parameter, Gate

Expand Down Expand Up @@ -855,14 +861,59 @@ def __init__(self):
unitary_synth_pass = UnitarySynthesis(target=target)
result_dag = unitary_synth_pass.run(dag)
result_qc = dag_to_circuit(result_dag)
self.assertEqual(result_qc, QuantumCircuit(2))
self.assertEqual(result_qc, qc)

def test_default_does_not_fail_on_no_syntheses(self):
qc = QuantumCircuit(1)
qc.unitary(np.eye(2), [0])
pass_ = UnitarySynthesis(["unknown", "gates"])
self.assertEqual(qc, pass_(qc))

def test_iswap_no_cx_synthesis_succeeds(self):
"""Test basis set with iswap but no cx can synthesize a circuit"""
target = Target()
theta = Parameter("theta")

i_props = {
(0,): InstructionProperties(duration=35.5e-9, error=0.000413),
(1,): InstructionProperties(duration=35.5e-9, error=0.000502),
}
target.add_instruction(IGate(), i_props)
rz_props = {
(0,): InstructionProperties(duration=0, error=0),
(1,): InstructionProperties(duration=0, error=0),
}
target.add_instruction(RZGate(theta), rz_props)
sx_props = {
(0,): InstructionProperties(duration=35.5e-9, error=0.000413),
(1,): InstructionProperties(duration=35.5e-9, error=0.000502),
}
target.add_instruction(SXGate(), sx_props)
x_props = {
(0,): InstructionProperties(duration=35.5e-9, error=0.000413),
(1,): InstructionProperties(duration=35.5e-9, error=0.000502),
}
target.add_instruction(XGate(), x_props)
iswap_props = {
(0, 1): InstructionProperties(duration=519.11e-9, error=0.01201),
(1, 0): InstructionProperties(duration=554.66e-9, error=0.01201),
}
target.add_instruction(iSwapGate(), iswap_props)
measure_props = {
(0,): InstructionProperties(duration=5.813e-6, error=0.0751),
(1,): InstructionProperties(duration=5.813e-6, error=0.0225),
}
target.add_instruction(Measure(), measure_props)

qc = QuantumCircuit(2)
cxmat = Operator(CXGate()).to_matrix()
qc.unitary(cxmat, [0, 1])
unitary_synth_pass = UnitarySynthesis(target=target)
dag = circuit_to_dag(qc)
result_dag = unitary_synth_pass.run(dag)
result_qc = dag_to_circuit(result_dag)
self.assertTrue(np.allclose(Operator(result_qc.to_gate()).to_matrix(), cxmat))


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

0 comments on commit 16bcd4c

Please sign in to comment.