qiskit/test/python/transpiler/test_optimize_1q_decomposit...

767 lines
27 KiB
Python

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""Test the optimize-1q-gate pass"""
import unittest
import ddt
import numpy as np
from qiskit.circuit import QuantumRegister, QuantumCircuit, ClassicalRegister, Parameter
from qiskit.circuit.library.standard_gates import (
UGate,
SXGate,
PhaseGate,
U3Gate,
U2Gate,
U1Gate,
RZGate,
RXGate,
RYGate,
HGate,
)
from qiskit.circuit.random import random_circuit
from qiskit.compiler import transpile
from qiskit.transpiler import PassManager, Target, InstructionProperties
from qiskit.transpiler.passes import Optimize1qGatesDecomposition
from qiskit.transpiler.passes import BasisTranslator
from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel
from qiskit.quantum_info import Operator
from test import QiskitTestCase # pylint: disable=wrong-import-order
theta = Parameter("θ")
phi = Parameter("ϕ")
lambda_ = Parameter("λ")
# a typical target where u1 is cheaper than u2 is cheaper than u3
u1_props = {(0,): InstructionProperties(error=0)}
u2_props = {(0,): InstructionProperties(error=1e-4)}
u3_props = {(0,): InstructionProperties(error=2e-4)}
target_u1_u2_u3 = Target()
target_u1_u2_u3.add_instruction(U1Gate(theta), u1_props, name="u1")
target_u1_u2_u3.add_instruction(U2Gate(theta, phi), u2_props, name="u2")
target_u1_u2_u3.add_instruction(U3Gate(theta, phi, lambda_), u3_props, name="u3")
# a typical target where continuous rz and rx are available; rz is cheaper
rz_props = {(0,): InstructionProperties(duration=0, error=0)}
rx_props = {(0,): InstructionProperties(duration=0.5e-8, error=0.00025)}
target_rz_rx = Target()
target_rz_rx.add_instruction(RZGate(theta), rz_props, name="rz")
target_rz_rx.add_instruction(RXGate(theta), rx_props, name="rx")
# a typical target where continuous rz, and discrete sx are available; rz is cheaper
rz_props = {(0,): InstructionProperties(duration=0, error=0)}
sx_props = {(0,): InstructionProperties(duration=0.5e-8, error=0.00025)}
target_rz_sx = Target()
target_rz_sx.add_instruction(RZGate(theta), rz_props, name="rz")
target_rz_sx.add_instruction(SXGate(), sx_props, name="sx")
# a target with overcomplete basis, rz is cheaper than ry is cheaper than u
rz_props = {(0,): InstructionProperties(duration=0.1e-8, error=0.0001)}
ry_props = {(0,): InstructionProperties(duration=0.5e-8, error=0.0002)}
u_props = {(0,): InstructionProperties(duration=0.9e-8, error=0.0005)}
target_rz_ry_u = Target()
target_rz_ry_u.add_instruction(RZGate(theta), rz_props, name="rz")
target_rz_ry_u.add_instruction(RYGate(theta), ry_props, name="ry")
target_rz_ry_u.add_instruction(UGate(theta, phi, lambda_), u_props, name="u")
# a target with hadamard and phase, we don't yet have an explicit decomposer
# but we can at least recognize circuits that are native for it
h_props = {(0,): InstructionProperties(duration=0.3e-8, error=0.0003)}
p_props = {(0,): InstructionProperties(duration=0, error=0)}
target_h_p = Target()
target_h_p.add_instruction(HGate(), h_props, name="h")
target_h_p.add_instruction(PhaseGate(theta), p_props, name="p")
# a target with rz, ry, and u. Error are not specified so we should prefer
# shorter decompositions.
rz_props = {(0,): None}
ry_props = {(0,): None}
u_props = {(0,): None}
target_rz_ry_u_noerror = Target()
target_rz_ry_u_noerror.add_instruction(RZGate(theta), rz_props, name="rz")
target_rz_ry_u_noerror.add_instruction(RYGate(theta), ry_props, name="ry")
target_rz_ry_u_noerror.add_instruction(UGate(theta, phi, lambda_), u_props, name="u")
@ddt.ddt
class TestOptimize1qGatesDecomposition(QiskitTestCase):
"""Test for 1q gate optimizations."""
def test_run_pass_in_parallel(self):
"""Test running pass on multiple circuits in parallel."""
qr = QuantumRegister(1, "qr")
circuit = QuantumCircuit(qr)
passmanager = PassManager([Optimize1qGatesDecomposition(target=target_u1_u2_u3)])
results = passmanager.run([circuit, circuit])
for result in results:
self.assertTrue(Operator(circuit).equiv(Operator(result)))
@ddt.data(target_u1_u2_u3, target_rz_rx, target_rz_sx, target_rz_ry_u, target_h_p)
def test_optimize_h_gates_target(self, target):
"""Transpile: qr:--[H]-[H]-[H]--"""
qr = QuantumRegister(1, "qr")
circuit = QuantumCircuit(qr)
circuit.h(qr[0])
circuit.h(qr[0])
circuit.h(qr[0])
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(target=target))
result = passmanager.run(circuit)
self.assertTrue(Operator(circuit).equiv(Operator(result)))
@ddt.data(
target_u1_u2_u3,
target_rz_rx,
target_rz_sx,
target_rz_ry_u,
)
def test_optimize_identity_target(self, target):
"""Transpile: qr:--[RY(θ), RY(-θ)]-- to null."""
qr = QuantumRegister(1, "qr")
circuit = QuantumCircuit(qr)
circuit.ry(np.pi / 7, qr[0])
circuit.ry(-np.pi / 7, qr[0])
expected = QuantumCircuit(qr)
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(target=target))
result = passmanager.run(circuit)
self.assertEqual(expected, result)
def test_optimize_away_idenity_no_target(self):
"""Test identity run is removed for no target specified."""
circuit = QuantumCircuit(1)
circuit.h(0)
circuit.h(0)
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition())
result = passmanager.run(circuit)
self.assertEqual(QuantumCircuit(1), result)
def test_optimize_error_over_target_3(self):
"""U is shorter than RZ-RY-RZ or RY-RZ-RY so use it when no error given."""
qr = QuantumRegister(1, "qr")
circuit = QuantumCircuit(qr)
circuit.u(np.pi / 7, np.pi / 4, np.pi / 3, qr[0])
target = target_rz_ry_u_noerror
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(target=target))
result = passmanager.run(circuit)
self.assertEqual(len(result), 1)
self.assertIsInstance(result[0].operation, UGate)
@ddt.data(
["cx", "u3"],
["cz", "u3"],
["cx", "u"],
["p", "sx", "u", "cx"],
["cz", "rx", "rz"],
["rxx", "rx", "ry"],
["iswap", "rx", "rz"],
["rz", "rx"],
["rz", "sx"],
["p", "sx"],
["r"],
)
def test_optimize_h_gates_pass_manager(self, basis):
"""Transpile: qr:--[H]-[H]-[H]--"""
qr = QuantumRegister(1, "qr")
circuit = QuantumCircuit(qr)
circuit.h(qr[0])
circuit.h(qr[0])
circuit.h(qr[0])
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
self.assertTrue(Operator(circuit).equiv(Operator(result)))
self.assertLessEqual(result.depth(), circuit.depth())
@ddt.data(
["cx", "u3"],
["cz", "u3"],
["cx", "u"],
["p", "sx", "u", "cx"],
["cz", "rx", "rz"],
["rxx", "rx", "ry"],
["iswap", "rx", "rz"],
["rz", "rx"],
["rz", "sx"],
["p", "sx"],
["r"],
)
def test_ignores_conditional_rotations(self, basis):
"""Conditional rotations should not be considered in the chain."""
qr = QuantumRegister(1, "qr")
cr = ClassicalRegister(2, "cr")
circuit = QuantumCircuit(qr, cr)
with self.assertWarns(DeprecationWarning):
circuit.p(0.1, qr).c_if(cr, 1)
with self.assertWarns(DeprecationWarning):
circuit.p(0.2, qr).c_if(cr, 3)
circuit.p(0.3, qr)
circuit.p(0.4, qr)
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
with self.assertWarns(DeprecationWarning):
self.assertTrue(Operator(circuit).equiv(Operator(result)))
@ddt.data(
["cx", "u3"],
["cz", "u3"],
["cx", "u"],
["p", "sx", "u", "cx"],
["cz", "rx", "rz"],
["rxx", "rx", "ry"],
["iswap", "rx", "rz"],
["rz", "rx"],
["rz", "sx"],
["p", "sx"],
["r"],
)
def test_in_the_back(self, basis):
"""Optimizations can be in the back of the circuit.
See https://github.com/Qiskit/qiskit-terra/issues/2004.
qr0:--[U1]-[U1]-[H]--
"""
qr = QuantumRegister(1, "qr")
circuit = QuantumCircuit(qr)
circuit.p(0.3, qr)
circuit.p(0.4, qr)
circuit.h(qr)
expected = QuantumCircuit(qr)
expected.p(0.7, qr)
expected.h(qr)
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
self.assertTrue(Operator(circuit).equiv(Operator(result)))
@ddt.data(
["cx", "u3"],
["cz", "u3"],
["cx", "u"],
["p", "sx", "u", "cx"],
["cz", "rx", "rz"],
["rxx", "rx", "ry"],
["iswap", "rx", "rz"],
["rz", "sx"],
["rz", "rx"],
["p", "sx"],
)
def test_single_parameterized_circuit(self, basis):
"""Parameters should be treated as opaque gates."""
qr = QuantumRegister(1)
qc = QuantumCircuit(qr)
theta_p = Parameter("theta")
qc.p(0.3, qr)
qc.p(0.4, qr)
qc.p(theta_p, qr)
qc.p(0.1, qr)
qc.p(0.2, qr)
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(qc)
self.assertTrue(
Operator(qc.assign_parameters({theta_p: 3.14})).equiv(
Operator(result.assign_parameters({theta_p: 3.14}))
)
)
@ddt.data(
["cx", "u3"],
["cz", "u3"],
["cx", "u"],
["p", "sx", "u", "cx"],
["cz", "rx", "rz"],
["rxx", "rx", "ry"],
["iswap", "rx", "rz"],
["rz", "rx"],
["rz", "sx"],
["p", "sx"],
)
def test_parameterized_circuits(self, basis):
"""Parameters should be treated as opaque gates."""
qr = QuantumRegister(1)
qc = QuantumCircuit(qr)
theta_p = Parameter("theta")
qc.p(0.3, qr)
qc.p(0.4, qr)
qc.p(theta_p, qr)
qc.p(0.1, qr)
qc.p(0.2, qr)
qc.p(theta_p, qr)
qc.p(0.3, qr)
qc.p(0.2, qr)
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(qc)
self.assertTrue(
Operator(qc.assign_parameters({theta_p: 3.14})).equiv(
Operator(result.assign_parameters({theta_p: 3.14}))
)
)
@ddt.data(
["cx", "u3"],
["cz", "u3"],
["cx", "u"],
["p", "sx", "u", "cx"],
["cz", "rx", "rz"],
["rxx", "rx", "ry"],
["iswap", "rx", "rz"],
["rz", "rx"],
["rz", "sx"],
["p", "sx"],
)
def test_parameterized_expressions_in_circuits(self, basis):
"""Expressions of Parameters should be treated as opaque gates."""
qr = QuantumRegister(1)
qc = QuantumCircuit(qr)
theta_p = Parameter("theta")
phi_p = Parameter("phi")
sum_ = theta_p + phi_p
product_ = theta_p * phi_p
qc.p(0.3, qr)
qc.p(0.4, qr)
qc.p(theta_p, qr)
qc.p(phi_p, qr)
qc.p(sum_, qr)
qc.p(product_, qr)
qc.p(0.3, qr)
qc.p(0.2, qr)
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(qc)
self.assertTrue(
Operator(qc.assign_parameters({theta_p: 3.14, phi_p: 10})).equiv(
Operator(result.assign_parameters({theta_p: 3.14, phi_p: 10}))
)
)
def test_identity_xyx(self):
"""Test lone identity gates in rx ry basis are removed."""
circuit = QuantumCircuit(2)
circuit.rx(0, 1)
circuit.ry(0, 0)
basis = ["rxx", "rx", "ry"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
self.assertEqual([], result.data)
def test_identity_zxz(self):
"""Test lone identity gates in rx rz basis are removed."""
circuit = QuantumCircuit(2)
circuit.rx(0, 1)
circuit.rz(0, 0)
basis = ["cz", "rx", "rz"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
self.assertEqual([], result.data)
def test_identity_psx(self):
"""Test lone identity gates in p sx basis are removed."""
circuit = QuantumCircuit(1)
circuit.p(0, 0)
basis = ["cx", "p", "sx"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
self.assertEqual([], result.data)
def test_identity_u(self):
"""Test lone identity gates in u basis are removed."""
circuit = QuantumCircuit(1)
circuit.u(0, 0, 0, 0)
basis = ["cx", "u"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
self.assertEqual([], result.data)
def test_identity_u3(self):
"""Test lone identity gates in u3 basis are removed."""
circuit = QuantumCircuit(1)
circuit.append(U3Gate(0, 0, 0), [0])
basis = ["cx", "u3"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
self.assertEqual([], result.data)
def test_identity_r(self):
"""Test lone identity gates in r basis are removed."""
circuit = QuantumCircuit(1)
circuit.r(0, 0, 0)
basis = ["r"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
self.assertEqual([], result.data)
def test_identity_rzx(self):
"""Test lone identity gates in rz rx basis are removed."""
circuit = QuantumCircuit(2)
circuit.rz(0, 0)
circuit.rx(0, 1)
basis = ["cx", "rz", "rx"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
self.assertEqual([], result.data)
def test_overcomplete_basis(self):
"""Test optimization with an overcomplete basis."""
circuit = random_circuit(3, 3, seed=42)
basis = ["rz", "rxx", "rx", "ry", "p", "sx", "u", "cx"]
passmanager = PassManager()
passmanager.append(BasisTranslator(sel, basis))
basis_translated = passmanager.run(circuit)
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result_full = passmanager.run(basis_translated)
self.assertTrue(Operator(circuit).equiv(Operator(result_full)))
self.assertGreater(basis_translated.depth(), result_full.depth())
def test_euler_decomposition_worse(self):
"""Ensure we don't decompose to a deeper circuit."""
circuit = QuantumCircuit(1)
circuit.rx(-np.pi / 2, 0)
circuit.rz(-np.pi / 2, 0)
basis = ["rx", "rz"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
# decomposition of circuit will result in 3 gates instead of 2
# assert optimization pass doesn't use it.
self.assertEqual(circuit, result, f"Circuit:\n{circuit}\nResult:\n{result}")
def test_euler_decomposition_worse_2(self):
"""Ensure we don't decompose to a deeper circuit in an edge case."""
circuit = QuantumCircuit(1)
circuit.rz(0.13, 0)
circuit.ry(-0.14, 0)
basis = ["ry", "rz"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
self.assertEqual(circuit, result, f"Circuit:\n{circuit}\nResult:\n{result}")
def test_euler_decomposition_zsx(self):
"""Ensure we don't decompose to a deeper circuit in the ZSX basis."""
circuit = QuantumCircuit(1)
circuit.rz(0.3, 0)
circuit.sx(0)
circuit.rz(0.2, 0)
circuit.sx(0)
basis = ["sx", "rz"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
self.assertEqual(circuit, result, f"Circuit:\n{circuit}\nResult:\n{result}")
def test_euler_decomposition_zsx_2(self):
"""Ensure we don't decompose to a deeper circuit in the ZSX basis."""
circuit = QuantumCircuit(1)
circuit.sx(0)
circuit.rz(0.2, 0)
circuit.sx(0)
circuit.rz(0.3, 0)
basis = ["sx", "rz"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
self.assertEqual(circuit, result, f"Circuit:\n{circuit}\nResult:\n{result}")
def test_optimize_run_of_u_to_single_u_on_target_no_error(self):
"""U(pi/3, 0, 0) * U(pi/3, 0, 0) * U(pi/3, 0, 0) -> U(pi, 0, 0). See #9701."""
qr = QuantumRegister(1, "qr")
circuit = QuantumCircuit(qr)
for _ in range(3):
circuit.append(UGate(np.pi / 3, 0.0, 0.0), [qr[0]])
expected = QuantumCircuit(qr)
expected.append(UGate(np.pi, 0.0, 0.0), [qr[0]])
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(target=target_rz_ry_u_noerror))
result = passmanager.run(circuit)
msg = f"expected:\n{expected}\nresult:\n{result}"
self.assertEqual(expected, result, msg=msg)
def test_optimize_run_of_u_to_single_u_no_target(self):
"""U(pi/3, 0, 0) * U(pi/3, 0, 0) * U(pi/3, 0, 0) -> U(pi, 0, 0). See #9701."""
qr = QuantumRegister(1, "qr")
circuit = QuantumCircuit(qr)
for _ in range(3):
circuit.append(UGate(np.pi / 3, 0.0, 0.0), [qr[0]])
expected = QuantumCircuit(qr)
expected.append(UGate(np.pi, 0.0, 0.0), [qr[0]])
basis = ["u"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
msg = f"expected:\n{expected}\nresult:\n{result}"
self.assertEqual(expected, result, msg=msg)
def test_optimize_u_to_phase_gate(self):
"""U(0, 0, pi/4) -> p(pi/4). Basis [p, sx]."""
qr = QuantumRegister(2, "qr")
circuit = QuantumCircuit(qr)
circuit.append(UGate(0, 0, np.pi / 4), [qr[0]])
expected = QuantumCircuit(qr)
expected.append(PhaseGate(np.pi / 4), [qr[0]])
basis = ["p", "sx"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
msg = f"expected:\n{expected}\nresult:\n{result}"
self.assertEqual(expected, result, msg=msg)
def test_optimize_u_to_p_sx_p(self):
"""U(pi/2, 0, pi/4) -> p(-pi/4)-sx-p(p/2). Basis [p, sx]."""
qr = QuantumRegister(2, "qr")
circuit = QuantumCircuit(qr)
circuit.append(UGate(np.pi / 2, 0, np.pi / 4), [qr[0]])
expected = QuantumCircuit(qr, global_phase=-np.pi / 4)
expected.append(PhaseGate(-np.pi / 4), [qr[0]])
expected.append(SXGate(), [qr[0]])
expected.append(PhaseGate(np.pi / 2), [qr[0]])
basis = ["p", "sx"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(circuit)
msg = f"expected:\n{expected}\nresult:\n{result}"
self.assertEqual(expected, result, msg=msg)
def test_optimize_u3_to_u1(self):
"""U3(0, 0, pi/4) -> U1(pi/4). Basis [u1, u2, u3]."""
qr = QuantumRegister(2, "qr")
circuit = QuantumCircuit(qr)
circuit.append(U3Gate(0, 0, np.pi / 4), [qr[0]])
expected = QuantumCircuit(qr)
expected.append(U1Gate(np.pi / 4), [qr[0]])
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(target=target_u1_u2_u3))
result = passmanager.run(circuit)
msg = f"expected:\n{expected}\nresult:\n{result}"
self.assertEqual(expected, result, msg=msg)
def test_optimize_u3_to_u2(self):
"""U3(pi/2, 0, pi/4) -> U2(0, pi/4). Basis [u1, u2, u3]."""
qr = QuantumRegister(2, "qr")
circuit = QuantumCircuit(qr)
circuit.append(U3Gate(np.pi / 2, 0, np.pi / 4), [qr[0]])
expected = QuantumCircuit(qr)
expected.append(U2Gate(0, np.pi / 4), [qr[0]])
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(target=target_u1_u2_u3))
result = passmanager.run(circuit)
self.assertEqual(expected, result)
msg = f"expected:\n{expected}\nresult:\n{result}"
self.assertEqual(expected, result, msg=msg)
def test_y_simplification_rz_sx_x(self):
"""Test that a y gate gets decomposed to x-zx with ibmq basis."""
qc = QuantumCircuit(1)
qc.y(0)
basis = ["id", "rz", "sx", "x", "cx"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(qc)
expected = QuantumCircuit(1)
expected.rz(-np.pi, 0)
expected.x(0)
msg = f"expected:\n{expected}\nresult:\n{result}"
self.assertEqual(expected, result, msg=msg)
def test_short_string(self):
"""Test that a shorter-than-universal string is still rewritten."""
qc = QuantumCircuit(1)
qc.h(0)
qc.ry(np.pi / 2, 0)
basis = ["sx", "rz"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(qc)
expected = QuantumCircuit(1)
expected.sx(0)
expected.sx(0)
msg = f"expected:\n{expected}\nresult:\n{result}"
self.assertEqual(expected, result, msg=msg)
def test_u_rewrites_to_rz(self):
"""Test that a phase-like U-gate gets rewritten into an RZ gate."""
qc = QuantumCircuit(1)
qc.u(0, 0, np.pi / 6, 0)
basis = ["sx", "rz"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(qc)
expected = QuantumCircuit(1, global_phase=np.pi / 12)
expected.rz(np.pi / 6, 0)
msg = f"expected:\n{expected}\nresult:\n{result}"
self.assertEqual(expected, result, msg=msg)
def test_u_rewrites_to_phase(self):
"""Test that a phase-like U-gate gets rewritten into an RZ gate."""
qc = QuantumCircuit(1)
qc.u(0, 0, np.pi / 6, 0)
basis = ["sx", "p"]
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(qc)
expected = QuantumCircuit(1)
expected.p(np.pi / 6, 0)
msg = f"expected:\n{expected}\nresult:\n{result}"
self.assertEqual(expected, result, msg=msg)
def test_no_warning_on_hadamard(self):
"""
Test Hadamards don't trigger the inefficiency warning when they're part of the basis.
See #6848.
"""
qc = QuantumCircuit(2)
qc.cz(0, 1)
basis = ["h", "cx", "rz", "sx", "x"]
result = transpile(qc, basis_gates=basis)
expected = QuantumCircuit(2)
expected.h(1)
expected.cx(0, 1)
expected.h(1)
msg = f"expected:\n{expected}\nresult:\n{result}"
self.assertEqual(expected, result, msg=msg)
def test_if_else(self):
"""Test that the pass recurses in a simple if-else."""
basis = ["cx", "sx", "u", "rz", "if_else"]
num_qubits = 4
qr = QuantumRegister(num_qubits)
cr = ClassicalRegister(1)
test = QuantumCircuit(qr, cr)
test.h(0)
test.measure(0, 0)
test_true = QuantumCircuit(qr, cr)
test_true.h(qr[0])
test_true.h(qr[0])
test_true.h(qr[0])
test.if_else((0, True), test_true.copy(), None, range(num_qubits), [0])
expected = QuantumCircuit(qr, cr)
expected.u(np.pi / 2, 0, -np.pi, 0)
expected.measure(0, 0)
expected_true = QuantumCircuit(qr, cr)
expected_true.u(np.pi / 2, 0, -np.pi, qr[0])
expected.if_else((0, True), expected_true, None, range(num_qubits), [0])
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(test)
self.assertEqual(result, expected)
def test_nested_control_flow(self):
"""Test that collection recurses into nested control flow."""
basis = ["cx", "u", "if_else", "for_loop"]
num_qubits = 4
qr = QuantumRegister(num_qubits)
cr = ClassicalRegister(1)
test = QuantumCircuit(qr, cr)
test.h(0)
test.measure(0, 0)
test_true = QuantumCircuit(qr, cr)
test_for = QuantumCircuit(qr, cr)
test_for.h(qr[0])
test_for.h(qr[0])
test_for.h(qr[0])
test_true.for_loop(range(4), body=test_for, qubits=qr, clbits=cr)
test.if_else((0, True), test_true.copy(), None, range(num_qubits), [0])
expected = QuantumCircuit(qr, cr)
expected.u(np.pi / 2, 0, -np.pi, 0)
expected.measure(0, 0)
expected_true = QuantumCircuit(qr, cr)
expected_for = QuantumCircuit(qr, cr)
expected_for.u(np.pi / 2, 0, -np.pi, qr[0])
expected_true.for_loop(range(4), body=expected_for, qubits=qr, clbits=cr)
expected.if_else((0, True), expected_true, None, range(num_qubits), [0])
passmanager = PassManager()
passmanager.append(Optimize1qGatesDecomposition(basis))
result = passmanager.run(test)
self.assertEqual(result, expected)
def test_prefer_no_substitution_if_all_ideal(self):
"""Test that gates are not substituted if all our ideal gates in basis."""
target = Target(num_qubits=1)
target.add_instruction(HGate(), {(0,): InstructionProperties(error=0)})
target.add_instruction(
UGate(Parameter("a"), Parameter("b"), Parameter("c")),
{(0,): InstructionProperties(error=0)},
)
qc = QuantumCircuit(1)
qc.h(0)
opt_pass = Optimize1qGatesDecomposition(target)
res = opt_pass(qc)
self.assertEqual(res, qc)
if __name__ == "__main__":
unittest.main()