mirror of https://github.com/Qiskit/qiskit.git
1302 lines
50 KiB
Python
1302 lines
50 KiB
Python
# This code is part of Qiskit.
|
|
#
|
|
# (C) Copyright IBM 2017, 2020.
|
|
#
|
|
# 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 BasisTranslator pass"""
|
|
|
|
import os
|
|
|
|
from numpy import pi
|
|
import scipy
|
|
|
|
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
|
|
from qiskit import transpile
|
|
from qiskit.circuit import Gate, Parameter, EquivalenceLibrary, Qubit, Clbit, Measure
|
|
from qiskit.circuit.equivalence_library import StandardEquivalenceLibrary as std_eq_lib
|
|
from qiskit.circuit.classical import expr, types
|
|
from qiskit.circuit.library import (
|
|
HGate,
|
|
U1Gate,
|
|
U2Gate,
|
|
U3Gate,
|
|
CU1Gate,
|
|
CU3Gate,
|
|
UGate,
|
|
RZGate,
|
|
XGate,
|
|
SXGate,
|
|
CXGate,
|
|
RXGate,
|
|
RZZGate,
|
|
)
|
|
from qiskit.converters import circuit_to_dag, dag_to_circuit, circuit_to_instruction
|
|
from qiskit.exceptions import QiskitError
|
|
from qiskit.providers.fake_provider import GenericBackendV2
|
|
from qiskit.quantum_info import Operator
|
|
from qiskit.transpiler.target import Target, InstructionProperties
|
|
from qiskit.transpiler.exceptions import TranspilerError
|
|
from qiskit.transpiler.passes.basis import BasisTranslator, UnrollCustomDefinitions
|
|
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
|
from qiskit.circuit.library.standard_gates.equivalence_library import (
|
|
StandardEquivalenceLibrary as std_eqlib,
|
|
)
|
|
from test import QiskitTestCase # pylint: disable=wrong-import-order
|
|
|
|
|
|
class OneQubitZeroParamGate(Gate):
|
|
"""Mock one qubit zero param gate."""
|
|
|
|
def __init__(self, name="1q0p"):
|
|
super().__init__(name, 1, [])
|
|
|
|
|
|
class OneQubitOneParamGate(Gate):
|
|
"""Mock one qubit one param gate."""
|
|
|
|
def __init__(self, theta, name="1q1p"):
|
|
super().__init__(name, 1, [theta])
|
|
|
|
|
|
class OneQubitOneParamPrimeGate(Gate):
|
|
"""Mock one qubit one param gate."""
|
|
|
|
def __init__(self, alpha):
|
|
super().__init__("1q1p_prime", 1, [alpha])
|
|
|
|
|
|
class OneQubitTwoParamGate(Gate):
|
|
"""Mock one qubit two param gate."""
|
|
|
|
def __init__(self, phi, lam, name="1q2p"):
|
|
super().__init__(name, 1, [phi, lam])
|
|
|
|
|
|
class TwoQubitZeroParamGate(Gate):
|
|
"""Mock one qubit zero param gate."""
|
|
|
|
def __init__(self, name="2q0p"):
|
|
super().__init__(name, 2, [])
|
|
|
|
|
|
class VariadicZeroParamGate(Gate):
|
|
"""Mock variadic zero param gate."""
|
|
|
|
def __init__(self, num_qubits, name="vq0p"):
|
|
super().__init__(name, num_qubits, [])
|
|
|
|
|
|
class TestBasisTranslator(QiskitTestCase):
|
|
"""Test the BasisTranslator pass."""
|
|
|
|
def test_circ_in_basis_no_op(self):
|
|
"""Verify we don't change a circuit already in the target basis."""
|
|
eq_lib = EquivalenceLibrary()
|
|
qc = QuantumCircuit(1)
|
|
qc.append(OneQubitZeroParamGate(), [0])
|
|
dag = circuit_to_dag(qc)
|
|
|
|
expected = circuit_to_dag(qc)
|
|
|
|
pass_ = BasisTranslator(eq_lib, ["1q0p"])
|
|
actual = pass_.run(dag)
|
|
|
|
self.assertEqual(actual, expected)
|
|
|
|
def test_raise_if_target_basis_unreachable(self):
|
|
"""Verify we raise if the circuit cannot be transformed to the target."""
|
|
eq_lib = EquivalenceLibrary()
|
|
|
|
qc = QuantumCircuit(1)
|
|
qc.append(OneQubitZeroParamGate(), [0])
|
|
dag = circuit_to_dag(qc)
|
|
|
|
pass_ = BasisTranslator(eq_lib, ["1q1p"])
|
|
|
|
with self.assertRaises(TranspilerError):
|
|
pass_.run(dag)
|
|
|
|
def test_single_substitution(self):
|
|
"""Verify we correctly unroll gates through a single equivalence."""
|
|
eq_lib = EquivalenceLibrary()
|
|
|
|
gate = OneQubitZeroParamGate()
|
|
equiv = QuantumCircuit(1)
|
|
equiv.append(OneQubitOneParamGate(pi), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
qc = QuantumCircuit(1)
|
|
qc.append(OneQubitZeroParamGate(), [0])
|
|
dag = circuit_to_dag(qc)
|
|
|
|
expected = QuantumCircuit(1)
|
|
expected.append(OneQubitOneParamGate(pi), [0])
|
|
expected_dag = circuit_to_dag(expected)
|
|
|
|
pass_ = BasisTranslator(eq_lib, ["1q1p"])
|
|
actual = pass_.run(dag)
|
|
|
|
self.assertEqual(actual, expected_dag)
|
|
|
|
def test_double_substitution(self):
|
|
"""Verify we correctly unroll gates through multiple equivalences."""
|
|
eq_lib = EquivalenceLibrary()
|
|
|
|
gate = OneQubitZeroParamGate()
|
|
equiv = QuantumCircuit(1)
|
|
equiv.append(OneQubitOneParamGate(pi), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
theta = Parameter("theta")
|
|
gate = OneQubitOneParamGate(theta)
|
|
equiv = QuantumCircuit(1)
|
|
equiv.append(OneQubitTwoParamGate(theta, pi / 2), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
qc = QuantumCircuit(1)
|
|
qc.append(OneQubitZeroParamGate(), [0])
|
|
dag = circuit_to_dag(qc)
|
|
|
|
expected = QuantumCircuit(1)
|
|
expected.append(OneQubitTwoParamGate(pi, pi / 2), [0])
|
|
expected_dag = circuit_to_dag(expected)
|
|
|
|
pass_ = BasisTranslator(eq_lib, ["1q2p"])
|
|
actual = pass_.run(dag)
|
|
|
|
self.assertEqual(actual, expected_dag)
|
|
|
|
def test_single_substitution_with_global_phase(self):
|
|
"""Verify we correctly unroll gates through a single equivalence with global phase."""
|
|
eq_lib = EquivalenceLibrary()
|
|
|
|
gate = OneQubitZeroParamGate()
|
|
equiv = QuantumCircuit(1, global_phase=0.2)
|
|
equiv.append(OneQubitOneParamGate(pi), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
qc = QuantumCircuit(1, global_phase=0.1)
|
|
qc.append(OneQubitZeroParamGate(), [0])
|
|
dag = circuit_to_dag(qc)
|
|
|
|
expected = QuantumCircuit(1, global_phase=0.1 + 0.2)
|
|
expected.append(OneQubitOneParamGate(pi), [0])
|
|
expected_dag = circuit_to_dag(expected)
|
|
|
|
pass_ = BasisTranslator(eq_lib, ["1q1p"])
|
|
actual = pass_.run(dag)
|
|
|
|
self.assertEqual(actual, expected_dag)
|
|
|
|
def test_single_two_gate_substitution_with_global_phase(self):
|
|
"""Verify we correctly unroll gates through a single equivalence with global phase."""
|
|
eq_lib = EquivalenceLibrary()
|
|
|
|
gate = OneQubitZeroParamGate()
|
|
equiv = QuantumCircuit(1, global_phase=0.2)
|
|
equiv.append(OneQubitOneParamGate(pi), [0])
|
|
equiv.append(OneQubitOneParamGate(pi), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
qc = QuantumCircuit(1, global_phase=0.1)
|
|
qc.append(OneQubitZeroParamGate(), [0])
|
|
dag = circuit_to_dag(qc)
|
|
|
|
expected = QuantumCircuit(1, global_phase=0.1 + 0.2)
|
|
expected.append(OneQubitOneParamGate(pi), [0])
|
|
expected.append(OneQubitOneParamGate(pi), [0])
|
|
expected_dag = circuit_to_dag(expected)
|
|
|
|
pass_ = BasisTranslator(eq_lib, ["1q1p"])
|
|
actual = pass_.run(dag)
|
|
|
|
self.assertEqual(actual, expected_dag)
|
|
|
|
def test_two_substitutions_with_global_phase(self):
|
|
"""Verify we correctly unroll gates through a single equivalences with global phase."""
|
|
eq_lib = EquivalenceLibrary()
|
|
|
|
gate = OneQubitZeroParamGate()
|
|
equiv = QuantumCircuit(1, global_phase=0.2)
|
|
equiv.append(OneQubitOneParamGate(pi), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
qc = QuantumCircuit(1, global_phase=0.1)
|
|
qc.append(OneQubitZeroParamGate(), [0])
|
|
qc.append(OneQubitZeroParamGate(), [0])
|
|
dag = circuit_to_dag(qc)
|
|
|
|
expected = QuantumCircuit(1, global_phase=0.1 + 2 * 0.2)
|
|
expected.append(OneQubitOneParamGate(pi), [0])
|
|
expected.append(OneQubitOneParamGate(pi), [0])
|
|
expected_dag = circuit_to_dag(expected)
|
|
|
|
pass_ = BasisTranslator(eq_lib, ["1q1p"])
|
|
actual = pass_.run(dag)
|
|
|
|
self.assertEqual(actual, expected_dag)
|
|
|
|
def test_two_single_two_gate_substitutions_with_global_phase(self):
|
|
"""Verify we correctly unroll gates through a single equivalence with global phase."""
|
|
eq_lib = EquivalenceLibrary()
|
|
|
|
gate = OneQubitZeroParamGate()
|
|
equiv = QuantumCircuit(1, global_phase=0.2)
|
|
equiv.append(OneQubitOneParamGate(pi), [0])
|
|
equiv.append(OneQubitOneParamGate(pi), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
qc = QuantumCircuit(1, global_phase=0.1)
|
|
qc.append(OneQubitZeroParamGate(), [0])
|
|
qc.append(OneQubitZeroParamGate(), [0])
|
|
dag = circuit_to_dag(qc)
|
|
|
|
expected = QuantumCircuit(1, global_phase=0.1 + 2 * 0.2)
|
|
expected.append(OneQubitOneParamGate(pi), [0])
|
|
expected.append(OneQubitOneParamGate(pi), [0])
|
|
expected.append(OneQubitOneParamGate(pi), [0])
|
|
expected.append(OneQubitOneParamGate(pi), [0])
|
|
|
|
expected_dag = circuit_to_dag(expected)
|
|
|
|
pass_ = BasisTranslator(eq_lib, ["1q1p"])
|
|
actual = pass_.run(dag)
|
|
|
|
self.assertEqual(actual, expected_dag)
|
|
|
|
def test_double_substitution_with_global_phase(self):
|
|
"""Verify we correctly unroll gates through multiple equivalences with global phase."""
|
|
eq_lib = EquivalenceLibrary()
|
|
|
|
gate = OneQubitZeroParamGate()
|
|
equiv = QuantumCircuit(1, global_phase=0.2)
|
|
equiv.append(OneQubitOneParamGate(pi), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
theta = Parameter("theta")
|
|
gate = OneQubitOneParamGate(theta)
|
|
equiv = QuantumCircuit(1, global_phase=0.4)
|
|
equiv.append(OneQubitTwoParamGate(theta, pi / 2), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
qc = QuantumCircuit(1, global_phase=0.1)
|
|
qc.append(OneQubitZeroParamGate(), [0])
|
|
dag = circuit_to_dag(qc)
|
|
|
|
expected = QuantumCircuit(1, global_phase=0.1 + 0.2 + 0.4)
|
|
expected.append(OneQubitTwoParamGate(pi, pi / 2), [0])
|
|
expected_dag = circuit_to_dag(expected)
|
|
|
|
pass_ = BasisTranslator(eq_lib, ["1q2p"])
|
|
actual = pass_.run(dag)
|
|
|
|
self.assertEqual(actual, expected_dag)
|
|
|
|
def test_multiple_variadic(self):
|
|
"""Verify circuit with multiple instances of variadic gate."""
|
|
eq_lib = EquivalenceLibrary()
|
|
|
|
# e.g. MSGate
|
|
oneq_gate = VariadicZeroParamGate(1)
|
|
equiv = QuantumCircuit(1)
|
|
equiv.append(OneQubitZeroParamGate(), [0])
|
|
eq_lib.add_equivalence(oneq_gate, equiv)
|
|
|
|
twoq_gate = VariadicZeroParamGate(2)
|
|
equiv = QuantumCircuit(2)
|
|
equiv.append(TwoQubitZeroParamGate(), [0, 1])
|
|
eq_lib.add_equivalence(twoq_gate, equiv)
|
|
|
|
qc = QuantumCircuit(2)
|
|
qc.append(VariadicZeroParamGate(1), [0])
|
|
qc.append(VariadicZeroParamGate(2), [0, 1])
|
|
|
|
dag = circuit_to_dag(qc)
|
|
|
|
expected = QuantumCircuit(2)
|
|
expected.append(OneQubitZeroParamGate(), [0])
|
|
expected.append(TwoQubitZeroParamGate(), [0, 1])
|
|
|
|
expected_dag = circuit_to_dag(expected)
|
|
|
|
pass_ = BasisTranslator(eq_lib, ["1q0p", "2q0p"])
|
|
actual = pass_.run(dag)
|
|
|
|
self.assertEqual(actual, expected_dag)
|
|
|
|
def test_diamond_path(self):
|
|
"""Verify we find a path when there are multiple paths to the target basis."""
|
|
eq_lib = EquivalenceLibrary()
|
|
|
|
# Path 1: 1q0p -> 1q1p(pi) -> 1q2p(pi, pi/2)
|
|
|
|
gate = OneQubitZeroParamGate()
|
|
equiv = QuantumCircuit(1)
|
|
equiv.append(OneQubitOneParamGate(pi), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
theta = Parameter("theta")
|
|
gate = OneQubitOneParamGate(theta)
|
|
equiv = QuantumCircuit(1)
|
|
equiv.append(OneQubitTwoParamGate(theta, pi / 2), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
# Path 2: 1q0p -> 1q1p_prime(pi/2) -> 1q2p(2 * pi/2, pi/2)
|
|
|
|
gate = OneQubitZeroParamGate()
|
|
equiv = QuantumCircuit(1)
|
|
equiv.append(OneQubitOneParamPrimeGate(pi / 2), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
alpha = Parameter("alpha")
|
|
gate = OneQubitOneParamPrimeGate(alpha)
|
|
equiv = QuantumCircuit(1)
|
|
equiv.append(OneQubitTwoParamGate(2 * alpha, pi / 2), [0])
|
|
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
qc = QuantumCircuit(1)
|
|
qc.append(OneQubitZeroParamGate(), [0])
|
|
dag = circuit_to_dag(qc)
|
|
|
|
expected = QuantumCircuit(1)
|
|
expected.append(OneQubitTwoParamGate(pi, pi / 2), [0])
|
|
expected_dag = circuit_to_dag(expected)
|
|
|
|
pass_ = BasisTranslator(eq_lib, ["1q2p"])
|
|
actual = pass_.run(dag)
|
|
|
|
self.assertEqual(actual, expected_dag)
|
|
|
|
def test_if_else(self):
|
|
"""Test a simple if-else with parameters."""
|
|
qubits = [Qubit(), Qubit()]
|
|
clbits = [Clbit(), Clbit()]
|
|
alpha = Parameter("alpha")
|
|
beta = Parameter("beta")
|
|
gate = OneQubitOneParamGate(alpha)
|
|
equiv = QuantumCircuit([qubits[0]])
|
|
equiv.append(OneQubitZeroParamGate(name="1q0p_2"), [qubits[0]])
|
|
equiv.append(OneQubitOneParamGate(alpha, name="1q1p_2"), [qubits[0]])
|
|
|
|
eq_lib = EquivalenceLibrary()
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
circ = QuantumCircuit(qubits, clbits)
|
|
circ.append(OneQubitOneParamGate(beta), [qubits[0]])
|
|
circ.measure(qubits[0], clbits[1])
|
|
with circ.if_test((clbits[1], 0)) as else_:
|
|
circ.append(OneQubitOneParamGate(alpha), [qubits[0]])
|
|
circ.append(TwoQubitZeroParamGate(), qubits)
|
|
with else_:
|
|
circ.append(TwoQubitZeroParamGate(), [qubits[1], qubits[0]])
|
|
dag = circuit_to_dag(circ)
|
|
dag_translated = BasisTranslator(eq_lib, ["if_else", "1q0p_2", "1q1p_2", "2q0p"]).run(dag)
|
|
|
|
expected = QuantumCircuit(qubits, clbits)
|
|
expected.append(OneQubitZeroParamGate(name="1q0p_2"), [qubits[0]])
|
|
expected.append(OneQubitOneParamGate(beta, name="1q1p_2"), [qubits[0]])
|
|
expected.measure(qubits[0], clbits[1])
|
|
with expected.if_test((clbits[1], 0)) as else_:
|
|
expected.append(OneQubitZeroParamGate(name="1q0p_2"), [qubits[0]])
|
|
expected.append(OneQubitOneParamGate(alpha, name="1q1p_2"), [qubits[0]])
|
|
expected.append(TwoQubitZeroParamGate(), qubits)
|
|
with else_:
|
|
expected.append(TwoQubitZeroParamGate(), [qubits[1], qubits[0]])
|
|
dag_expected = circuit_to_dag(expected)
|
|
self.assertEqual(dag_translated, dag_expected)
|
|
|
|
def test_nested_loop(self):
|
|
"""Test a simple if-else with parameters."""
|
|
qubits = [Qubit(), Qubit()]
|
|
clbits = [Clbit(), Clbit()]
|
|
cr = ClassicalRegister(bits=clbits)
|
|
index1 = Parameter("index1")
|
|
alpha = Parameter("alpha")
|
|
|
|
gate = OneQubitOneParamGate(alpha)
|
|
equiv = QuantumCircuit([qubits[0]])
|
|
equiv.append(OneQubitZeroParamGate(name="1q0p_2"), [qubits[0]])
|
|
equiv.append(OneQubitOneParamGate(alpha, name="1q1p_2"), [qubits[0]])
|
|
|
|
eq_lib = EquivalenceLibrary()
|
|
eq_lib.add_equivalence(gate, equiv)
|
|
|
|
circ = QuantumCircuit(qubits, cr)
|
|
with circ.for_loop(range(3), loop_parameter=index1) as ind:
|
|
with circ.while_loop((cr, 0)):
|
|
circ.append(OneQubitOneParamGate(alpha * ind), [qubits[0]])
|
|
dag = circuit_to_dag(circ)
|
|
dag_translated = BasisTranslator(
|
|
eq_lib, ["if_else", "for_loop", "while_loop", "1q0p_2", "1q1p_2"]
|
|
).run(dag)
|
|
|
|
expected = QuantumCircuit(qubits, cr)
|
|
with expected.for_loop(range(3), loop_parameter=index1) as ind:
|
|
with expected.while_loop((cr, 0)):
|
|
expected.append(OneQubitZeroParamGate(name="1q0p_2"), [qubits[0]])
|
|
expected.append(OneQubitOneParamGate(alpha * ind, name="1q1p_2"), [qubits[0]])
|
|
dag_expected = circuit_to_dag(expected)
|
|
self.assertEqual(dag_translated, dag_expected)
|
|
|
|
def test_different_bits(self):
|
|
"""Test that the basis translator correctly works when the inner blocks of control-flow
|
|
operations are not over the same bits as the outer blocks."""
|
|
base = QuantumCircuit([Qubit() for _ in [None] * 4], [Clbit()])
|
|
for_body = QuantumCircuit([Qubit(), Qubit()])
|
|
for_body.h(0)
|
|
for_body.cz(0, 1)
|
|
base.for_loop((1,), None, for_body, [1, 2], [])
|
|
|
|
while_body = QuantumCircuit([Qubit(), Qubit(), Clbit()])
|
|
while_body.cz(0, 1)
|
|
|
|
true_body = QuantumCircuit([Qubit(), Qubit(), Clbit()])
|
|
true_body.measure(0, 0)
|
|
true_body.while_loop((0, True), while_body, [0, 1], [0])
|
|
false_body = QuantumCircuit([Qubit(), Qubit(), Clbit()])
|
|
false_body.cz(0, 1)
|
|
base.if_else((0, True), true_body, false_body, [0, 3], [0])
|
|
|
|
basis = {"rz", "sx", "cx", "for_loop", "if_else", "while_loop", "measure"}
|
|
out = BasisTranslator(std_eqlib, basis).run(circuit_to_dag(base))
|
|
self.assertEqual(set(out.count_ops(recurse=True)), basis)
|
|
|
|
def test_correct_parameter_assignment(self):
|
|
"""Test correct parameter assignment from an equivalence during translation"""
|
|
rx_key = next(key for key in std_eq_lib.keys() if key.name == "rx")
|
|
|
|
# The circuit doesn't need to be parametric.
|
|
qc = QuantumCircuit(1)
|
|
qc.rx(0.5, 0)
|
|
|
|
BasisTranslator(
|
|
equivalence_library=std_eq_lib,
|
|
target_basis=["cx", "id", "rz", "sx", "x"],
|
|
)(qc)
|
|
|
|
inst = std_eq_lib._get_equivalences(rx_key)[0].circuit.data[0]
|
|
self.assertEqual(inst.params, inst.operation.params)
|
|
|
|
|
|
class TestUnrollerCompatability(QiskitTestCase):
|
|
"""Tests backward compatability with the Unroller pass.
|
|
|
|
Duplicate of TestUnroller from test.python.transpiler.test_unroller with
|
|
Unroller replaced by UnrollCustomDefinitions -> BasisTranslator.
|
|
"""
|
|
|
|
def test_basic_unroll(self):
|
|
"""Test decompose a single H into u2."""
|
|
qr = QuantumRegister(1, "qr")
|
|
circuit = QuantumCircuit(qr)
|
|
circuit.h(qr[0])
|
|
dag = circuit_to_dag(circuit)
|
|
pass_ = UnrollCustomDefinitions(std_eqlib, ["u2"])
|
|
dag = pass_.run(dag)
|
|
pass_ = BasisTranslator(std_eqlib, ["u2"])
|
|
unrolled_dag = pass_.run(dag)
|
|
op_nodes = unrolled_dag.op_nodes()
|
|
self.assertEqual(len(op_nodes), 1)
|
|
self.assertEqual(op_nodes[0].name, "u2")
|
|
|
|
def test_unroll_toffoli(self):
|
|
"""Test unroll toffoli on multi regs to h, t, tdg, cx."""
|
|
qr1 = QuantumRegister(2, "qr1")
|
|
qr2 = QuantumRegister(1, "qr2")
|
|
circuit = QuantumCircuit(qr1, qr2)
|
|
circuit.ccx(qr1[0], qr1[1], qr2[0])
|
|
dag = circuit_to_dag(circuit)
|
|
pass_ = UnrollCustomDefinitions(std_eqlib, ["h", "t", "tdg", "cx"])
|
|
dag = pass_.run(dag)
|
|
pass_ = BasisTranslator(std_eqlib, ["h", "t", "tdg", "cx"])
|
|
unrolled_dag = pass_.run(dag)
|
|
op_nodes = unrolled_dag.op_nodes()
|
|
self.assertEqual(len(op_nodes), 15)
|
|
for node in op_nodes:
|
|
self.assertIn(node.name, ["h", "t", "tdg", "cx"])
|
|
|
|
def test_basic_unroll_min_qubits(self):
|
|
"""Test decompose a single H into u2."""
|
|
qr = QuantumRegister(1, "qr")
|
|
circuit = QuantumCircuit(qr)
|
|
circuit.h(qr[0])
|
|
dag = circuit_to_dag(circuit)
|
|
pass_ = UnrollCustomDefinitions(std_eqlib, ["u2"], min_qubits=3)
|
|
dag = pass_.run(dag)
|
|
pass_ = BasisTranslator(std_eqlib, ["u2"], min_qubits=3)
|
|
unrolled_dag = pass_.run(dag)
|
|
op_nodes = unrolled_dag.op_nodes()
|
|
self.assertEqual(len(op_nodes), 1)
|
|
self.assertEqual(op_nodes[0].name, "h")
|
|
|
|
def test_unroll_toffoli_min_qubits(self):
|
|
"""Test unroll toffoli on multi regs to h, t, tdg, cx."""
|
|
qr1 = QuantumRegister(2, "qr1")
|
|
qr2 = QuantumRegister(1, "qr2")
|
|
circuit = QuantumCircuit(qr1, qr2)
|
|
circuit.ccx(qr1[0], qr1[1], qr2[0])
|
|
circuit.sx(qr1[0])
|
|
dag = circuit_to_dag(circuit)
|
|
pass_ = UnrollCustomDefinitions(std_eqlib, ["h", "t", "tdg", "cx"], min_qubits=3)
|
|
dag = pass_.run(dag)
|
|
pass_ = BasisTranslator(std_eqlib, ["h", "t", "tdg", "cx"], min_qubits=3)
|
|
unrolled_dag = pass_.run(dag)
|
|
op_nodes = unrolled_dag.op_nodes()
|
|
self.assertEqual(len(op_nodes), 16)
|
|
for node in op_nodes:
|
|
self.assertIn(node.name, ["h", "t", "tdg", "cx", "sx"])
|
|
|
|
def test_unroll_1q_chain_conditional(self):
|
|
"""Test unroll chain of 1-qubit gates interrupted by conditional."""
|
|
|
|
# ┌───┐┌─────┐┌───┐┌───┐┌─────────┐┌─────────┐┌─────────┐┌─┐ ┌───┐ ┌───┐ »
|
|
# qr: ┤ H ├┤ Tdg ├┤ Z ├┤ T ├┤ Ry(0.5) ├┤ Rz(0.3) ├┤ Rx(0.1) ├┤M├─┤ X ├──┤ Y ├─»
|
|
# └───┘└─────┘└───┘└───┘└─────────┘└─────────┘└─────────┘└╥┘ └─╥─┘ └─╥─┘ »
|
|
# ║ ┌──╨──┐┌──╨──┐»
|
|
# cr: 1/══════════════════════════════════════════════════════╩═╡ 0x1 ╞╡ 0x1 ╞»
|
|
# 0 └─────┘└─────┘»
|
|
# « ┌───┐
|
|
# « qr: ─┤ Z ├─
|
|
# « └─╥─┘
|
|
# « ┌──╨──┐
|
|
# «cr: 1/╡ 0x1 ╞
|
|
# « └─────┘
|
|
qr = QuantumRegister(1, "qr")
|
|
cr = ClassicalRegister(1, "cr")
|
|
circuit = QuantumCircuit(qr, cr)
|
|
circuit.h(qr)
|
|
circuit.tdg(qr)
|
|
circuit.z(qr)
|
|
circuit.t(qr)
|
|
circuit.ry(0.5, qr)
|
|
circuit.rz(0.3, qr)
|
|
circuit.rx(0.1, qr)
|
|
circuit.measure(qr, cr)
|
|
with self.assertWarns(DeprecationWarning):
|
|
circuit.x(qr).c_if(cr, 1)
|
|
with self.assertWarns(DeprecationWarning):
|
|
circuit.y(qr).c_if(cr, 1)
|
|
with self.assertWarns(DeprecationWarning):
|
|
circuit.z(qr).c_if(cr, 1)
|
|
dag = circuit_to_dag(circuit)
|
|
pass_ = UnrollCustomDefinitions(std_eqlib, ["u1", "u2", "u3"])
|
|
dag = pass_.run(dag)
|
|
|
|
pass_ = BasisTranslator(std_eqlib, ["u1", "u2", "u3"])
|
|
unrolled_dag = pass_.run(dag)
|
|
|
|
# Pick up -1 * 0.3 / 2 global phase for one RZ -> U1.
|
|
#
|
|
# global phase: 6.1332
|
|
# ┌─────────┐┌──────────┐┌───────┐┌─────────┐┌─────────────┐┌─────────┐»
|
|
# qr: ┤ U2(0,π) ├┤ U1(-π/4) ├┤ U1(π) ├┤ U1(π/4) ├┤ U3(0.5,0,0) ├┤ U1(0.3) ├»
|
|
# └─────────┘└──────────┘└───────┘└─────────┘└─────────────┘└─────────┘»
|
|
# cr: 1/═══════════════════════════════════════════════════════════════════»
|
|
# »
|
|
# « ┌──────────────────┐┌─┐┌───────────┐┌───────────────┐┌───────┐
|
|
# « qr: ┤ U3(0.1,-π/2,π/2) ├┤M├┤ U3(π,0,π) ├┤ U3(π,π/2,π/2) ├┤ U1(π) ├
|
|
# « └──────────────────┘└╥┘└─────╥─────┘└───────╥───────┘└───╥───┘
|
|
# « ║ ┌──╨──┐ ┌──╨──┐ ┌──╨──┐
|
|
# «cr: 1/═════════════════════╩════╡ 0x1 ╞════════╡ 0x1 ╞══════╡ 0x1 ╞═
|
|
# « 0 └─────┘ └─────┘ └─────┘
|
|
ref_circuit = QuantumCircuit(qr, cr, global_phase=-0.3 / 2)
|
|
ref_circuit.append(U2Gate(0, pi), [qr[0]])
|
|
ref_circuit.append(U1Gate(-pi / 4), [qr[0]])
|
|
ref_circuit.append(U1Gate(pi), [qr[0]])
|
|
ref_circuit.append(U1Gate(pi / 4), [qr[0]])
|
|
ref_circuit.append(U3Gate(0.5, 0, 0), [qr[0]])
|
|
ref_circuit.append(U1Gate(0.3), [qr[0]])
|
|
ref_circuit.append(U3Gate(0.1, -pi / 2, pi / 2), [qr[0]])
|
|
ref_circuit.measure(qr[0], cr[0])
|
|
with self.assertWarns(DeprecationWarning):
|
|
ref_circuit.append(U3Gate(pi, 0, pi), [qr[0]]).c_if(cr, 1)
|
|
with self.assertWarns(DeprecationWarning):
|
|
ref_circuit.append(U3Gate(pi, pi / 2, pi / 2), [qr[0]]).c_if(cr, 1)
|
|
with self.assertWarns(DeprecationWarning):
|
|
ref_circuit.append(U1Gate(pi), [qr[0]]).c_if(cr, 1)
|
|
ref_dag = circuit_to_dag(ref_circuit)
|
|
|
|
self.assertEqual(unrolled_dag, ref_dag)
|
|
|
|
def test_unroll_no_basis(self):
|
|
"""Test when a given gate has no decompositions."""
|
|
qr = QuantumRegister(1, "qr")
|
|
cr = ClassicalRegister(1, "cr")
|
|
circuit = QuantumCircuit(qr, cr)
|
|
circuit.h(qr)
|
|
dag = circuit_to_dag(circuit)
|
|
pass_ = UnrollCustomDefinitions(std_eqlib, [])
|
|
dag = pass_.run(dag)
|
|
|
|
pass_ = BasisTranslator(std_eqlib, [])
|
|
|
|
with self.assertRaises(QiskitError):
|
|
pass_.run(dag)
|
|
|
|
def test_unroll_all_instructions(self):
|
|
"""Test unrolling a circuit containing all standard instructions."""
|
|
|
|
qr = QuantumRegister(3, "qr")
|
|
cr = ClassicalRegister(3, "cr")
|
|
circuit = QuantumCircuit(qr, cr)
|
|
circuit.crx(0.5, qr[1], qr[2])
|
|
circuit.cry(0.5, qr[1], qr[2])
|
|
circuit.ccx(qr[0], qr[1], qr[2])
|
|
circuit.ch(qr[0], qr[2])
|
|
circuit.crz(0.5, qr[1], qr[2])
|
|
circuit.cswap(qr[1], qr[0], qr[2])
|
|
circuit.append(CU1Gate(0.1), [qr[0], qr[2]])
|
|
circuit.append(CU3Gate(0.2, 0.1, 0.0), [qr[1], qr[2]])
|
|
circuit.cx(qr[1], qr[0])
|
|
circuit.cy(qr[1], qr[2])
|
|
circuit.cz(qr[2], qr[0])
|
|
circuit.h(qr[1])
|
|
circuit.id(qr[0])
|
|
circuit.rx(0.1, qr[0])
|
|
circuit.ry(0.2, qr[1])
|
|
circuit.rz(0.3, qr[2])
|
|
circuit.rzz(0.6, qr[1], qr[0])
|
|
circuit.s(qr[0])
|
|
circuit.sdg(qr[1])
|
|
circuit.swap(qr[1], qr[2])
|
|
circuit.t(qr[2])
|
|
circuit.tdg(qr[0])
|
|
circuit.append(U1Gate(0.1), [qr[1]])
|
|
circuit.append(U2Gate(0.2, -0.1), [qr[0]])
|
|
circuit.append(U3Gate(0.3, 0.0, -0.1), [qr[2]])
|
|
circuit.x(qr[2])
|
|
circuit.y(qr[1])
|
|
circuit.z(qr[0])
|
|
# circuit.snapshot('0')
|
|
# circuit.measure(qr, cr)
|
|
dag = circuit_to_dag(circuit)
|
|
pass_ = UnrollCustomDefinitions(std_eqlib, ["u3", "cx", "id"])
|
|
dag = pass_.run(dag)
|
|
|
|
pass_ = BasisTranslator(std_eqlib, ["u3", "cx", "id"])
|
|
unrolled_dag = pass_.run(dag)
|
|
|
|
ref_circuit = QuantumCircuit(qr, cr)
|
|
ref_circuit.append(U3Gate(0, 0, pi / 2), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.append(U3Gate(-0.25, 0, 0), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.append(U3Gate(0.25, -pi / 2, 0), [qr[2]])
|
|
ref_circuit.append(U3Gate(0.25, 0, 0), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.append(U3Gate(-0.25, 0, 0), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.append(U3Gate(pi / 2, 0, pi), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.append(U3Gate(0, 0, -pi / 4), [qr[2]])
|
|
ref_circuit.cx(qr[0], qr[2])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 4), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 4), [qr[1]])
|
|
ref_circuit.append(U3Gate(0, 0, -pi / 4), [qr[2]])
|
|
ref_circuit.cx(qr[0], qr[2])
|
|
ref_circuit.cx(qr[0], qr[1])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 4), [qr[0]])
|
|
ref_circuit.append(U3Gate(0, 0, -pi / 4), [qr[1]])
|
|
ref_circuit.cx(qr[0], qr[1])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 4), [qr[2]])
|
|
ref_circuit.append(U3Gate(pi / 2, 0, pi), [qr[2]])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 2), [qr[2]])
|
|
ref_circuit.append(U3Gate(pi / 2, 0, pi), [qr[2]])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 4), [qr[2]])
|
|
ref_circuit.cx(qr[0], qr[2])
|
|
ref_circuit.append(U3Gate(0, 0, -pi / 4), [qr[2]])
|
|
ref_circuit.append(U3Gate(pi / 2, 0, pi), [qr[2]])
|
|
ref_circuit.append(U3Gate(0, 0, -pi / 2), [qr[2]])
|
|
ref_circuit.append(U3Gate(0, 0, 0.25), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.append(U3Gate(0, 0, -0.25), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.cx(qr[2], qr[0])
|
|
ref_circuit.append(U3Gate(pi / 2, 0, pi), [qr[2]])
|
|
ref_circuit.cx(qr[0], qr[2])
|
|
ref_circuit.append(U3Gate(0, 0, -pi / 4), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 4), [qr[2]])
|
|
ref_circuit.cx(qr[0], qr[2])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 4), [qr[0]])
|
|
ref_circuit.append(U3Gate(0, 0, -pi / 4), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.cx(qr[1], qr[0])
|
|
ref_circuit.append(U3Gate(0, 0, -pi / 4), [qr[0]])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 4), [qr[1]])
|
|
ref_circuit.cx(qr[1], qr[0])
|
|
ref_circuit.append(U3Gate(0, 0, 0.05), [qr[1]])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 4), [qr[2]])
|
|
ref_circuit.append(U3Gate(pi / 2, 0, pi), [qr[2]])
|
|
ref_circuit.cx(qr[2], qr[0])
|
|
ref_circuit.append(U3Gate(0, 0, 0.05), [qr[0]])
|
|
ref_circuit.cx(qr[0], qr[2])
|
|
ref_circuit.append(U3Gate(0, 0, -0.05), [qr[2]])
|
|
ref_circuit.cx(qr[0], qr[2])
|
|
ref_circuit.append(U3Gate(0, 0, 0.05), [qr[2]])
|
|
ref_circuit.append(U3Gate(0, 0, -0.05), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.append(U3Gate(-0.1, 0, -0.05), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.cx(qr[1], qr[0])
|
|
ref_circuit.append(U3Gate(pi / 2, 0, pi), [qr[0]])
|
|
ref_circuit.append(U3Gate(0.1, 0.1, 0), [qr[2]])
|
|
ref_circuit.append(U3Gate(0, 0, -pi / 2), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.append(U3Gate(pi / 2, 0, pi), [qr[1]])
|
|
ref_circuit.append(U3Gate(0.2, 0, 0), [qr[1]])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 2), [qr[2]])
|
|
ref_circuit.cx(qr[2], qr[0])
|
|
ref_circuit.append(U3Gate(pi / 2, 0, pi), [qr[0]])
|
|
ref_circuit.id(qr[0])
|
|
ref_circuit.append(U3Gate(0.1, -pi / 2, pi / 2), [qr[0]])
|
|
ref_circuit.cx(qr[1], qr[0])
|
|
ref_circuit.append(U3Gate(0, 0, 0.6), [qr[0]])
|
|
ref_circuit.cx(qr[1], qr[0])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 2), [qr[0]])
|
|
ref_circuit.append(U3Gate(0, 0, -pi / 4), [qr[0]])
|
|
ref_circuit.append(U3Gate(pi / 2, 0.2, -0.1), [qr[0]])
|
|
ref_circuit.append(U3Gate(0, 0, pi), [qr[0]])
|
|
ref_circuit.append(U3Gate(0, 0, -pi / 2), [qr[1]])
|
|
ref_circuit.append(U3Gate(0, 0, 0.3), [qr[2]])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.cx(qr[2], qr[1])
|
|
ref_circuit.cx(qr[1], qr[2])
|
|
ref_circuit.append(U3Gate(0, 0, 0.1), [qr[1]])
|
|
ref_circuit.append(U3Gate(pi, pi / 2, pi / 2), [qr[1]])
|
|
ref_circuit.append(U3Gate(0, 0, pi / 4), [qr[2]])
|
|
ref_circuit.append(U3Gate(0.3, 0.0, -0.1), [qr[2]])
|
|
ref_circuit.append(U3Gate(pi, 0, pi), [qr[2]])
|
|
# ref_circuit.snapshot('0')
|
|
# ref_circuit.measure(qr, cr)
|
|
# ref_dag = circuit_to_dag(ref_circuit)
|
|
|
|
self.assertTrue(Operator(dag_to_circuit(unrolled_dag)).equiv(ref_circuit))
|
|
|
|
def test_simple_unroll_parameterized_without_expressions(self):
|
|
"""Verify unrolling parameterized gates without expressions."""
|
|
qr = QuantumRegister(1)
|
|
qc = QuantumCircuit(qr)
|
|
|
|
theta = Parameter("theta")
|
|
|
|
qc.rz(theta, qr[0])
|
|
dag = circuit_to_dag(qc)
|
|
|
|
pass_ = UnrollCustomDefinitions(std_eqlib, ["u1", "cx"])
|
|
dag = pass_.run(dag)
|
|
|
|
unrolled_dag = BasisTranslator(std_eqlib, ["u1", "cx"]).run(dag)
|
|
|
|
expected = QuantumCircuit(qr, global_phase=-theta / 2)
|
|
expected.append(U1Gate(theta), [qr[0]])
|
|
|
|
self.assertEqual(circuit_to_dag(expected), unrolled_dag)
|
|
|
|
def test_simple_unroll_parameterized_with_expressions(self):
|
|
"""Verify unrolling parameterized gates with expressions."""
|
|
qr = QuantumRegister(1)
|
|
qc = QuantumCircuit(qr)
|
|
|
|
theta = Parameter("theta")
|
|
phi = Parameter("phi")
|
|
sum_ = theta + phi
|
|
|
|
qc.rz(sum_, qr[0])
|
|
dag = circuit_to_dag(qc)
|
|
pass_ = UnrollCustomDefinitions(std_eqlib, ["p", "cx"])
|
|
dag = pass_.run(dag)
|
|
|
|
unrolled_dag = BasisTranslator(std_eqlib, ["p", "cx"]).run(dag)
|
|
|
|
expected = QuantumCircuit(qr, global_phase=-sum_ / 2)
|
|
expected.p(sum_, qr[0])
|
|
|
|
self.assertEqual(circuit_to_dag(expected), unrolled_dag)
|
|
|
|
def test_definition_unroll_parameterized(self):
|
|
"""Verify that unrolling complex gates with parameters does not raise."""
|
|
qr = QuantumRegister(2)
|
|
qc = QuantumCircuit(qr)
|
|
|
|
theta = Parameter("theta")
|
|
|
|
qc.cp(theta, qr[1], qr[0])
|
|
qc.cp(theta * theta, qr[0], qr[1])
|
|
dag = circuit_to_dag(qc)
|
|
pass_ = UnrollCustomDefinitions(std_eqlib, ["p", "cx"])
|
|
dag = pass_.run(dag)
|
|
|
|
out_dag = BasisTranslator(std_eqlib, ["p", "cx"]).run(dag)
|
|
|
|
self.assertEqual(out_dag.count_ops(), {"p": 6, "cx": 4})
|
|
|
|
def test_unrolling_parameterized_composite_gates(self):
|
|
"""Verify unrolling circuits with parameterized composite gates."""
|
|
mock_sel = EquivalenceLibrary(base=std_eqlib)
|
|
|
|
qr1 = QuantumRegister(2)
|
|
subqc = QuantumCircuit(qr1)
|
|
|
|
theta = Parameter("theta")
|
|
|
|
subqc.rz(theta, qr1[0])
|
|
subqc.cx(qr1[0], qr1[1])
|
|
subqc.rz(theta, qr1[1])
|
|
|
|
# Expanding across register with shared parameter
|
|
qr2 = QuantumRegister(4)
|
|
qc = QuantumCircuit(qr2)
|
|
|
|
sub_instr = circuit_to_instruction(subqc, equivalence_library=mock_sel)
|
|
qc.append(sub_instr, [qr2[0], qr2[1]])
|
|
qc.append(sub_instr, [qr2[2], qr2[3]])
|
|
|
|
dag = circuit_to_dag(qc)
|
|
pass_ = UnrollCustomDefinitions(mock_sel, ["p", "cx"])
|
|
dag = pass_.run(dag)
|
|
|
|
out_dag = BasisTranslator(mock_sel, ["p", "cx"]).run(dag)
|
|
|
|
# Pick up -1 * theta / 2 global phase four twice (once for each RZ -> P
|
|
# in each of the two sub_instr instructions).
|
|
expected = QuantumCircuit(qr2, global_phase=-1 * 4 * theta / 2.0)
|
|
expected.p(theta, qr2[0])
|
|
expected.p(theta, qr2[2])
|
|
expected.cx(qr2[0], qr2[1])
|
|
expected.cx(qr2[2], qr2[3])
|
|
expected.p(theta, qr2[1])
|
|
expected.p(theta, qr2[3])
|
|
|
|
self.assertEqual(circuit_to_dag(expected), out_dag)
|
|
|
|
# Expanding across register with shared parameter
|
|
qc = QuantumCircuit(qr2)
|
|
|
|
phi = Parameter("phi")
|
|
gamma = Parameter("gamma")
|
|
|
|
sub_instr = circuit_to_instruction(subqc, {theta: phi}, mock_sel)
|
|
qc.append(sub_instr, [qr2[0], qr2[1]])
|
|
sub_instr = circuit_to_instruction(subqc, {theta: gamma}, mock_sel)
|
|
qc.append(sub_instr, [qr2[2], qr2[3]])
|
|
|
|
dag = circuit_to_dag(qc)
|
|
pass_ = UnrollCustomDefinitions(mock_sel, ["p", "cx"])
|
|
dag = pass_.run(dag)
|
|
|
|
out_dag = BasisTranslator(mock_sel, ["p", "cx"]).run(dag)
|
|
|
|
expected = QuantumCircuit(qr2, global_phase=-1 * (2 * phi + 2 * gamma) / 2.0)
|
|
expected.p(phi, qr2[0])
|
|
expected.p(gamma, qr2[2])
|
|
expected.cx(qr2[0], qr2[1])
|
|
expected.cx(qr2[2], qr2[3])
|
|
expected.p(phi, qr2[1])
|
|
expected.p(gamma, qr2[3])
|
|
|
|
self.assertEqual(circuit_to_dag(expected), out_dag)
|
|
|
|
def test_treats_store_as_builtin(self):
|
|
"""Test that the `store` instruction is allowed as a builtin in all cases with no target."""
|
|
|
|
class MyHGate(Gate):
|
|
"""Hadamard, but it's _mine_."""
|
|
|
|
def __init__(self):
|
|
super().__init__("my_h", 1, [])
|
|
|
|
class MyCXGate(Gate):
|
|
"""CX, but it's _mine_."""
|
|
|
|
def __init__(self):
|
|
super().__init__("my_cx", 2, [])
|
|
|
|
h_to_my = QuantumCircuit(1)
|
|
h_to_my.append(MyHGate(), [0], [])
|
|
cx_to_my = QuantumCircuit(2)
|
|
cx_to_my.append(MyCXGate(), [0, 1], [])
|
|
eq_lib = EquivalenceLibrary()
|
|
eq_lib.add_equivalence(HGate(), h_to_my)
|
|
eq_lib.add_equivalence(CXGate(), cx_to_my)
|
|
|
|
a = expr.Var.new("a", types.Bool())
|
|
b = expr.Var.new("b", types.Uint(8))
|
|
|
|
qc = QuantumCircuit(2, 2, inputs=[a])
|
|
qc.add_var(b, 12)
|
|
qc.h(0)
|
|
qc.cx(0, 1)
|
|
qc.measure([0, 1], [0, 1])
|
|
qc.store(a, expr.bit_xor(qc.clbits[0], qc.clbits[1]))
|
|
|
|
expected = qc.copy_empty_like()
|
|
expected.store(b, 12)
|
|
expected.append(MyHGate(), [0], [])
|
|
expected.append(MyCXGate(), [0, 1], [])
|
|
expected.measure([0, 1], [0, 1])
|
|
expected.store(a, expr.bit_xor(expected.clbits[0], expected.clbits[1]))
|
|
|
|
# Note: store is present in the circuit but not in the basis set.
|
|
out = BasisTranslator(eq_lib, ["my_h", "my_cx"])(qc)
|
|
self.assertEqual(out, expected)
|
|
|
|
|
|
class TestBasisExamples(QiskitTestCase):
|
|
"""Test example circuits targeting example bases over the StandardEquivalenceLibrary."""
|
|
|
|
def test_cx_bell_to_cz(self):
|
|
"""Verify we can translate a CX bell circuit to CZ,RX,RZ."""
|
|
bell = QuantumCircuit(2)
|
|
bell.h(0)
|
|
bell.cx(0, 1)
|
|
|
|
in_dag = circuit_to_dag(bell)
|
|
out_dag = BasisTranslator(std_eqlib, ["cz", "rx", "rz"]).run(in_dag)
|
|
|
|
self.assertTrue(set(out_dag.count_ops()).issubset(["cz", "rx", "rz"]))
|
|
self.assertEqual(Operator(bell), Operator(dag_to_circuit(out_dag)))
|
|
|
|
def test_cx_bell_to_iswap(self):
|
|
"""Verify we can translate a CX bell to iSwap,U3."""
|
|
bell = QuantumCircuit(2)
|
|
bell.h(0)
|
|
bell.cx(0, 1)
|
|
|
|
in_dag = circuit_to_dag(bell)
|
|
out_dag = BasisTranslator(std_eqlib, ["iswap", "u"]).run(in_dag)
|
|
|
|
self.assertTrue(set(out_dag.count_ops()).issubset(["iswap", "u"]))
|
|
self.assertEqual(Operator(bell), Operator(dag_to_circuit(out_dag)))
|
|
|
|
def test_cx_bell_to_ecr(self):
|
|
"""Verify we can translate a CX bell to ECR,U."""
|
|
bell = QuantumCircuit(2)
|
|
bell.h(0)
|
|
bell.cx(0, 1)
|
|
|
|
in_dag = circuit_to_dag(bell)
|
|
out_dag = BasisTranslator(std_eqlib, ["ecr", "u"]).run(in_dag)
|
|
|
|
qr = QuantumRegister(2, "q")
|
|
expected = QuantumCircuit(2)
|
|
expected.u(pi / 2, 0, pi, qr[0])
|
|
expected.u(0, 0, -pi / 2, qr[0])
|
|
expected.u(pi, 0, 0, qr[0])
|
|
expected.u(pi / 2, -pi / 2, pi / 2, qr[1])
|
|
expected.ecr(0, 1)
|
|
expected_dag = circuit_to_dag(expected)
|
|
|
|
self.assertEqual(out_dag, expected_dag)
|
|
self.assertEqual(
|
|
Operator.from_circuit(bell), Operator.from_circuit(dag_to_circuit(out_dag))
|
|
)
|
|
|
|
def test_cx_bell_to_cp(self):
|
|
"""Verify we can translate a CX bell to CP,U."""
|
|
bell = QuantumCircuit(2)
|
|
bell.h(0)
|
|
bell.cx(0, 1)
|
|
|
|
in_dag = circuit_to_dag(bell)
|
|
out_dag = BasisTranslator(std_eqlib, ["cp", "u"]).run(in_dag)
|
|
|
|
self.assertTrue(set(out_dag.count_ops()).issubset(["cp", "u"]))
|
|
self.assertEqual(Operator(bell), Operator(dag_to_circuit(out_dag)))
|
|
|
|
qr = QuantumRegister(2, "q")
|
|
expected = QuantumCircuit(qr)
|
|
expected.u(pi / 2, 0, pi, 0)
|
|
expected.u(pi / 2, 0, pi, 1)
|
|
expected.cp(pi, 0, 1)
|
|
expected.u(pi / 2, 0, pi, 1)
|
|
expected_dag = circuit_to_dag(expected)
|
|
|
|
self.assertEqual(out_dag, expected_dag)
|
|
|
|
def test_cx_bell_to_crz(self):
|
|
"""Verify we can translate a CX bell to CRZ,U."""
|
|
bell = QuantumCircuit(2)
|
|
bell.h(0)
|
|
bell.cx(0, 1)
|
|
|
|
in_dag = circuit_to_dag(bell)
|
|
out_dag = BasisTranslator(std_eqlib, ["crz", "u"]).run(in_dag)
|
|
|
|
self.assertTrue(set(out_dag.count_ops()).issubset(["crz", "u"]))
|
|
self.assertEqual(Operator(bell), Operator(dag_to_circuit(out_dag)))
|
|
|
|
qr = QuantumRegister(2, "q")
|
|
expected = QuantumCircuit(qr)
|
|
expected.u(pi / 2, 0, pi, 0)
|
|
expected.u(0, 0, pi / 2, 0)
|
|
expected.u(pi / 2, 0, pi, 1)
|
|
expected.crz(pi, 0, 1)
|
|
expected.u(pi / 2, 0, pi, 1)
|
|
expected_dag = circuit_to_dag(expected)
|
|
|
|
self.assertEqual(out_dag, expected_dag)
|
|
|
|
def test_global_phase(self):
|
|
"""Verify global phase preserved in basis translation"""
|
|
circ = QuantumCircuit(1)
|
|
gate_angle = pi / 5
|
|
circ_angle = pi / 3
|
|
circ.rz(gate_angle, 0)
|
|
circ.global_phase = circ_angle
|
|
in_dag = circuit_to_dag(circ)
|
|
out_dag = BasisTranslator(std_eqlib, ["p"]).run(in_dag)
|
|
|
|
qr = QuantumRegister(1, "q")
|
|
expected = QuantumCircuit(qr)
|
|
expected.p(gate_angle, qr)
|
|
expected.global_phase = circ_angle - gate_angle / 2
|
|
expected_dag = circuit_to_dag(expected)
|
|
self.assertEqual(out_dag, expected_dag)
|
|
self.assertAlmostEqual(
|
|
float(out_dag.global_phase), float(expected_dag.global_phase), places=14
|
|
)
|
|
self.assertEqual(Operator(dag_to_circuit(out_dag)), Operator(expected))
|
|
|
|
def test_condition_set_substitute_node(self):
|
|
"""Verify condition is set in BasisTranslator on substitute_node"""
|
|
|
|
# ┌───┐ ┌───┐
|
|
# q_0: ┤ H ├──■──────┤ H ├─
|
|
# └───┘┌─┴─┐┌─┐ └─╥─┘
|
|
# q_1: ─────┤ X ├┤M├───╫───
|
|
# └───┘└╥┘┌──╨──┐
|
|
# c: 2/═══════════╩═╡ 0x1 ╞
|
|
# 1 └─────┘
|
|
qr = QuantumRegister(2, "q")
|
|
cr = ClassicalRegister(2, "c")
|
|
circ = QuantumCircuit(qr, cr)
|
|
circ.h(0)
|
|
circ.cx(0, 1)
|
|
circ.measure(1, 1)
|
|
with self.assertWarns(DeprecationWarning):
|
|
circ.h(0).c_if(cr, 1)
|
|
circ_transpiled = transpile(circ, optimization_level=3, basis_gates=["cx", "id", "u"])
|
|
|
|
# ┌────────────┐ ┌────────────┐
|
|
# q_0: ┤ U(π/2,0,π) ├──■─────┤ U(π/2,0,π) ├
|
|
# └────────────┘┌─┴─┐┌─┐└─────╥──────┘
|
|
# q_1: ──────────────┤ X ├┤M├──────╫───────
|
|
# └───┘└╥┘ ┌──╨──┐
|
|
# c: 2/════════════════════╩════╡ 0x1 ╞════
|
|
# 1 └─────┘
|
|
qr = QuantumRegister(2, "q")
|
|
cr = ClassicalRegister(2, "c")
|
|
expected = QuantumCircuit(qr, cr)
|
|
expected.u(pi / 2, 0, pi, 0)
|
|
expected.cx(0, 1)
|
|
expected.measure(1, 1)
|
|
with self.assertWarns(DeprecationWarning):
|
|
expected.u(pi / 2, 0, pi, 0).c_if(cr, 1)
|
|
|
|
self.assertEqual(circ_transpiled, expected)
|
|
|
|
def test_skip_target_basis_equivalences_1(self):
|
|
"""Test that BasisTranslator skips gates in the target_basis - #6085"""
|
|
circ = QuantumCircuit()
|
|
qasm_file = os.path.join(
|
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
|
"qasm",
|
|
"TestBasisTranslator_skip_target.qasm",
|
|
)
|
|
circ = circ.from_qasm_file(qasm_file)
|
|
circ_transpiled = transpile(
|
|
circ,
|
|
basis_gates=["id", "rz", "sx", "x", "cx"],
|
|
seed_transpiler=42,
|
|
optimization_level=1,
|
|
)
|
|
self.assertEqual(circ_transpiled.count_ops(), {"cx": 91, "rz": 66, "sx": 22})
|
|
|
|
|
|
class TestBasisTranslatorWithTarget(QiskitTestCase):
|
|
"""Test the basis translator when running with a Target."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.target = Target()
|
|
|
|
# U gate in qubit 0.
|
|
self.theta = Parameter("theta")
|
|
self.phi = Parameter("phi")
|
|
self.lam = Parameter("lambda")
|
|
u_props = {
|
|
(0,): InstructionProperties(duration=5.23e-8, error=0.00038115),
|
|
}
|
|
self.target.add_instruction(UGate(self.theta, self.phi, self.lam), u_props)
|
|
|
|
# Rz gate in qubit 1.
|
|
rz_props = {
|
|
(1,): InstructionProperties(duration=0.0, error=0),
|
|
}
|
|
self.target.add_instruction(RZGate(self.phi), rz_props)
|
|
|
|
# X gate in qubit 1.
|
|
x_props = {
|
|
(1,): InstructionProperties(
|
|
duration=3.5555555555555554e-08, error=0.00020056469709026198
|
|
),
|
|
}
|
|
self.target.add_instruction(XGate(), x_props)
|
|
|
|
# SX gate in qubit 1.
|
|
sx_props = {
|
|
(1,): InstructionProperties(
|
|
duration=3.5555555555555554e-08, error=0.00020056469709026198
|
|
),
|
|
}
|
|
self.target.add_instruction(SXGate(), sx_props)
|
|
|
|
cx_props = {
|
|
(0, 1): InstructionProperties(duration=5.23e-7, error=0.00098115),
|
|
(1, 0): InstructionProperties(duration=4.52e-7, error=0.00132115),
|
|
}
|
|
self.target.add_instruction(CXGate(), cx_props)
|
|
|
|
def test_2q_with_non_global_1q(self):
|
|
"""Test translation works with a 2q gate on a non-global 1q basis."""
|
|
qc = QuantumCircuit(2)
|
|
qc.cz(0, 1)
|
|
|
|
bt_pass = BasisTranslator(std_eqlib, target_basis=None, target=self.target)
|
|
output = bt_pass(qc)
|
|
# We need a second run of BasisTranslator to correct gates outside
|
|
# the target basis. This is a known issue, see:
|
|
# https://github.com/Qiskit/qiskit/issues/11339
|
|
# TODO: remove the second bt_pass call once fixed.
|
|
output = bt_pass(output)
|
|
expected = QuantumCircuit(2)
|
|
expected.rz(pi, 1)
|
|
expected.sx(1)
|
|
expected.rz(3 * pi / 2, 1)
|
|
expected.sx(1)
|
|
expected.rz(3 * pi, 1)
|
|
expected.cx(0, 1)
|
|
expected.rz(pi, 1)
|
|
expected.sx(1)
|
|
expected.rz(3 * pi / 2, 1)
|
|
expected.sx(1)
|
|
expected.rz(3 * pi, 1)
|
|
self.assertEqual(output, expected)
|
|
|
|
def test_treats_store_as_builtin(self):
|
|
"""Test that the `store` instruction is allowed as a builtin in all cases with a target."""
|
|
|
|
class MyHGate(Gate):
|
|
"""Hadamard, but it's _mine_."""
|
|
|
|
def __init__(self):
|
|
super().__init__("my_h", 1, [])
|
|
|
|
class MyCXGate(Gate):
|
|
"""CX, but it's _mine_."""
|
|
|
|
def __init__(self):
|
|
super().__init__("my_cx", 2, [])
|
|
|
|
h_to_my = QuantumCircuit(1)
|
|
h_to_my.append(MyHGate(), [0], [])
|
|
cx_to_my = QuantumCircuit(2)
|
|
cx_to_my.append(MyCXGate(), [0, 1], [])
|
|
eq_lib = EquivalenceLibrary()
|
|
eq_lib.add_equivalence(HGate(), h_to_my)
|
|
eq_lib.add_equivalence(CXGate(), cx_to_my)
|
|
|
|
a = expr.Var.new("a", types.Bool())
|
|
b = expr.Var.new("b", types.Uint(8))
|
|
|
|
qc = QuantumCircuit(2, 2, inputs=[a])
|
|
qc.add_var(b, 12)
|
|
qc.h(0)
|
|
qc.cx(0, 1)
|
|
qc.measure([0, 1], [0, 1])
|
|
qc.store(a, expr.bit_xor(qc.clbits[0], qc.clbits[1]))
|
|
|
|
expected = qc.copy_empty_like()
|
|
expected.store(b, 12)
|
|
expected.append(MyHGate(), [0], [])
|
|
expected.append(MyCXGate(), [0, 1], [])
|
|
expected.measure([0, 1], [0, 1])
|
|
expected.store(a, expr.bit_xor(expected.clbits[0], expected.clbits[1]))
|
|
|
|
# Note: store is present in the circuit but not in the target.
|
|
target = Target()
|
|
target.add_instruction(MyHGate(), {(i,): None for i in range(qc.num_qubits)})
|
|
target.add_instruction(Measure(), {(i,): None for i in range(qc.num_qubits)})
|
|
target.add_instruction(MyCXGate(), {(0, 1): None, (1, 0): None})
|
|
|
|
out = BasisTranslator(eq_lib, {"my_h", "my_cx"}, target)(qc)
|
|
self.assertEqual(out, expected)
|
|
|
|
def test_fractional_gate_in_basis_from_string(self):
|
|
"""Test transpiling with RZZ in basis with only basis_gates option."""
|
|
num_qubits = 2
|
|
seed = 9169
|
|
basis_gates = ["rz", "rx", "rzz"]
|
|
qc = QuantumCircuit(num_qubits)
|
|
mat = scipy.stats.unitary_group.rvs(2**num_qubits, random_state=seed)
|
|
qc.unitary(mat, range(num_qubits))
|
|
pm = generate_preset_pass_manager(
|
|
optimization_level=1, basis_gates=basis_gates, seed_transpiler=134
|
|
)
|
|
cqc = pm.run(qc)
|
|
self.assertEqual(Operator(qc), Operator(cqc))
|
|
|
|
def test_fractional_gate_in_basis_from_backendv2(self):
|
|
"""Test transpiling with RZZ in basis of backendv2."""
|
|
num_qubits = 2
|
|
seed = 9169
|
|
basis_gates = ["rz", "rx", "rzz"]
|
|
qc = QuantumCircuit(num_qubits)
|
|
mat = scipy.stats.unitary_group.rvs(2**num_qubits, random_state=seed)
|
|
qc.unitary(mat, range(num_qubits))
|
|
backend = GenericBackendV2(num_qubits, basis_gates=basis_gates)
|
|
target = backend.target
|
|
pm = generate_preset_pass_manager(optimization_level=1, target=target, seed_transpiler=134)
|
|
cqc = pm.run(qc)
|
|
self.assertEqual(Operator(qc), Operator.from_circuit(cqc))
|
|
|
|
def test_fractional_gate_in_basis_from_custom_target(self):
|
|
"""Test transpiling with RZZ in basis of custom target."""
|
|
num_qubits = 2
|
|
seed = 9169
|
|
qc = QuantumCircuit(num_qubits)
|
|
mat = scipy.stats.unitary_group.rvs(2**num_qubits, random_state=seed)
|
|
qc.unitary(mat, range(num_qubits))
|
|
target = Target()
|
|
target.add_instruction(RZGate(self.theta), {(i,): None for i in range(qc.num_qubits)})
|
|
target.add_instruction(RXGate(self.phi), {(i,): None for i in range(qc.num_qubits)})
|
|
target.add_instruction(
|
|
RZZGate(self.lam), {(i, i + 1): None for i in range(qc.num_qubits - 1)}
|
|
)
|
|
pm = generate_preset_pass_manager(optimization_level=1, target=target, seed_transpiler=134)
|
|
cqc = pm.run(qc)
|
|
self.assertEqual(Operator(qc), Operator.from_circuit(cqc))
|