mirror of https://github.com/Qiskit/qiskit.git
432 lines
16 KiB
Python
432 lines
16 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 decompose pass"""
|
|
|
|
from numpy import pi
|
|
|
|
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
|
|
from qiskit.transpiler.passes import Decompose
|
|
from qiskit.converters import circuit_to_dag
|
|
from qiskit.circuit.library import HGate, CCXGate, U2Gate
|
|
from qiskit.quantum_info.operators import Operator, Clifford
|
|
|
|
from test import QiskitTestCase # pylint: disable=wrong-import-order
|
|
|
|
|
|
class TestDecompose(QiskitTestCase):
|
|
"""Tests the decompose pass."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
# example complex circuit
|
|
# ┌────────┐ ┌───┐┌─────────────┐
|
|
# q2_0: ┤0 ├────────────■──┤ H ├┤0 ├
|
|
# │ │ │ └───┘│ circuit-57 │
|
|
# q2_1: ┤1 gate1 ├────────────■───────┤1 ├
|
|
# │ │┌────────┐ │ └─────────────┘
|
|
# q2_2: ┤2 ├┤0 ├──■──────────────────────
|
|
# └────────┘│ │ │
|
|
# q2_3: ──────────┤1 gate2 ├──■──────────────────────
|
|
# │ │┌─┴─┐
|
|
# q2_4: ──────────┤2 ├┤ X ├────────────────────
|
|
# └────────┘└───┘
|
|
circ1 = QuantumCircuit(3)
|
|
circ1.h(0)
|
|
circ1.t(1)
|
|
circ1.x(2)
|
|
my_gate = circ1.to_gate(label="gate1")
|
|
circ2 = QuantumCircuit(3)
|
|
circ2.h(0)
|
|
circ2.cx(0, 1)
|
|
circ2.x(2)
|
|
my_gate2 = circ2.to_gate(label="gate2")
|
|
circ3 = QuantumCircuit(2)
|
|
circ3.x(0)
|
|
q_bits = QuantumRegister(5)
|
|
qc = QuantumCircuit(q_bits)
|
|
qc.append(my_gate, q_bits[:3])
|
|
qc.append(my_gate2, q_bits[2:])
|
|
qc.mcx(q_bits[:4], q_bits[4])
|
|
qc.h(0)
|
|
qc.append(circ3, [0, 1])
|
|
self.complex_circuit = qc
|
|
|
|
def test_basic(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_ = Decompose(HGate)
|
|
after_dag = pass_.run(dag)
|
|
op_nodes = after_dag.op_nodes()
|
|
self.assertEqual(len(op_nodes), 1)
|
|
self.assertEqual(op_nodes[0].name, "u2")
|
|
|
|
def test_decompose_none(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_ = Decompose()
|
|
after_dag = pass_.run(dag)
|
|
op_nodes = after_dag.op_nodes()
|
|
self.assertEqual(len(op_nodes), 1)
|
|
self.assertEqual(op_nodes[0].name, "u2")
|
|
|
|
def test_decompose_only_h(self):
|
|
"""Test to decompose a single H, without the rest"""
|
|
qr = QuantumRegister(2, "qr")
|
|
circuit = QuantumCircuit(qr)
|
|
circuit.h(qr[0])
|
|
circuit.cx(qr[0], qr[1])
|
|
dag = circuit_to_dag(circuit)
|
|
pass_ = Decompose(HGate)
|
|
after_dag = pass_.run(dag)
|
|
op_nodes = after_dag.op_nodes()
|
|
self.assertEqual(len(op_nodes), 2)
|
|
for node in op_nodes:
|
|
self.assertIn(node.name, ["cx", "u2"])
|
|
|
|
def test_decompose_toffoli(self):
|
|
"""Test decompose CCX."""
|
|
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_ = Decompose(CCXGate)
|
|
after_dag = pass_.run(dag)
|
|
op_nodes = after_dag.op_nodes()
|
|
self.assertEqual(len(op_nodes), 15)
|
|
for node in op_nodes:
|
|
self.assertIn(node.name, ["h", "t", "tdg", "cx"])
|
|
|
|
def test_decompose_conditional(self):
|
|
"""Test decompose a 1-qubit gates with a conditional."""
|
|
qr = QuantumRegister(1, "qr")
|
|
cr = ClassicalRegister(1, "cr")
|
|
circuit = QuantumCircuit(qr, cr)
|
|
with self.assertWarns(DeprecationWarning):
|
|
circuit.h(qr).c_if(cr, 1)
|
|
with self.assertWarns(DeprecationWarning):
|
|
circuit.x(qr).c_if(cr, 1)
|
|
dag = circuit_to_dag(circuit)
|
|
pass_ = Decompose(HGate)
|
|
after_dag = pass_.run(dag)
|
|
|
|
ref_circuit = QuantumCircuit(qr, cr)
|
|
with self.assertWarns(DeprecationWarning):
|
|
ref_circuit.append(U2Gate(0, pi), [qr[0]]).c_if(cr, 1)
|
|
with self.assertWarns(DeprecationWarning):
|
|
ref_circuit.x(qr).c_if(cr, 1)
|
|
ref_dag = circuit_to_dag(ref_circuit)
|
|
|
|
self.assertEqual(after_dag, ref_dag)
|
|
|
|
def test_decompose_oversized_instruction(self):
|
|
"""Test decompose on a single-op gate that doesn't use all qubits."""
|
|
# ref: https://github.com/Qiskit/qiskit-terra/issues/3440
|
|
qc1 = QuantumCircuit(2)
|
|
qc1.x(0)
|
|
gate = qc1.to_gate()
|
|
|
|
qc2 = QuantumCircuit(2)
|
|
qc2.append(gate, [0, 1])
|
|
|
|
output = qc2.decompose()
|
|
|
|
self.assertEqual(qc1, output)
|
|
|
|
def test_decomposition_preserves_qregs_order(self):
|
|
"""Test decomposing a gate preserves the order of registers in its definition"""
|
|
qr = QuantumRegister(2, "qr1")
|
|
qc1 = QuantumCircuit(qr)
|
|
qc1.cx(1, 0)
|
|
gate = qc1.to_gate()
|
|
|
|
qr2 = QuantumRegister(2, "qr2")
|
|
qc2 = QuantumCircuit(qr2)
|
|
qc2.append(gate, qr2)
|
|
|
|
expected = QuantumCircuit(qr2)
|
|
expected.cx(1, 0)
|
|
|
|
self.assertEqual(qc2.decompose(), expected)
|
|
|
|
def test_decompose_global_phase_1q(self):
|
|
"""Test decomposition of circuit with global phase"""
|
|
qc1 = QuantumCircuit(1)
|
|
qc1.rz(0.1, 0)
|
|
qc1.ry(0.5, 0)
|
|
qc1.global_phase += pi / 4
|
|
qcd = qc1.decompose()
|
|
self.assertEqual(Operator(qc1), Operator(qcd))
|
|
|
|
def test_decompose_global_phase_2q(self):
|
|
"""Test decomposition of circuit with global phase"""
|
|
qc1 = QuantumCircuit(2, global_phase=pi / 4)
|
|
qc1.rz(0.1, 0)
|
|
qc1.rxx(0.2, 0, 1)
|
|
qcd = qc1.decompose()
|
|
self.assertEqual(Operator(qc1), Operator(qcd))
|
|
|
|
def test_decompose_global_phase_1q_composite(self):
|
|
"""Test decomposition of circuit with global phase in a composite gate."""
|
|
circ = QuantumCircuit(1, global_phase=pi / 2)
|
|
circ.x(0)
|
|
circ.h(0)
|
|
v = circ.to_gate()
|
|
|
|
qc1 = QuantumCircuit(1)
|
|
qc1.append(v, [0])
|
|
qcd = qc1.decompose()
|
|
self.assertEqual(Operator(qc1), Operator(qcd))
|
|
|
|
def test_decompose_only_h_gate(self):
|
|
"""Test decomposition parameters so that only a certain gate is decomposed."""
|
|
circ = QuantumCircuit(2, 1)
|
|
circ.h(0)
|
|
circ.cz(0, 1)
|
|
decom_circ = circ.decompose(["h"])
|
|
dag = circuit_to_dag(decom_circ)
|
|
self.assertEqual(len(dag.op_nodes()), 2)
|
|
self.assertEqual(dag.op_nodes()[0].name, "u2")
|
|
self.assertEqual(dag.op_nodes()[1].name, "cz")
|
|
|
|
def test_decompose_only_given_label(self):
|
|
"""Test decomposition parameters so that only a given label is decomposed."""
|
|
decom_circ = self.complex_circuit.decompose(["gate2"])
|
|
dag = circuit_to_dag(decom_circ)
|
|
|
|
self.assertEqual(len(dag.op_nodes()), 7)
|
|
self.assertEqual(dag.op_nodes()[0].op.label, "gate1")
|
|
self.assertEqual(dag.op_nodes()[1].name, "h")
|
|
self.assertEqual(dag.op_nodes()[2].name, "cx")
|
|
self.assertEqual(dag.op_nodes()[3].name, "x")
|
|
self.assertEqual(dag.op_nodes()[4].name, "mcx")
|
|
self.assertEqual(dag.op_nodes()[5].name, "h")
|
|
self.assertRegex(dag.op_nodes()[6].name, "circuit-")
|
|
|
|
def test_decompose_only_given_name(self):
|
|
"""Test decomposition parameters so that only given name is decomposed."""
|
|
decom_circ = self.complex_circuit.decompose(["mcx"], reps=2)
|
|
dag = circuit_to_dag(decom_circ)
|
|
|
|
self.assertEqual(len(dag.op_nodes()), 13)
|
|
self.assertEqual(dag.op_nodes()[0].op.label, "gate1")
|
|
self.assertEqual(dag.op_nodes()[1].op.label, "gate2")
|
|
self.assertEqual(dag.op_nodes()[2].name, "h")
|
|
self.assertEqual(dag.op_nodes()[3].name, "cu1")
|
|
self.assertEqual(dag.op_nodes()[4].name, "rcccx")
|
|
self.assertEqual(dag.op_nodes()[5].name, "h")
|
|
self.assertEqual(dag.op_nodes()[6].name, "h")
|
|
self.assertEqual(dag.op_nodes()[7].name, "cu1")
|
|
self.assertEqual(dag.op_nodes()[8].name, "rcccx_dg")
|
|
self.assertEqual(dag.op_nodes()[9].name, "h")
|
|
self.assertEqual(dag.op_nodes()[10].name, "c3sx")
|
|
self.assertEqual(dag.op_nodes()[11].name, "h")
|
|
self.assertRegex(dag.op_nodes()[12].name, "circuit-")
|
|
|
|
def test_decompose_mixture_of_names_and_labels(self):
|
|
"""Test decomposition parameters so that mixture of names and labels is decomposed"""
|
|
decom_circ = self.complex_circuit.decompose(["mcx", "gate2"], reps=2)
|
|
dag = circuit_to_dag(decom_circ)
|
|
|
|
self.assertEqual(len(dag.op_nodes()), 15)
|
|
self.assertEqual(dag.op_nodes()[0].op.label, "gate1")
|
|
self.assertEqual(dag.op_nodes()[1].name, "h")
|
|
self.assertEqual(dag.op_nodes()[2].name, "cx")
|
|
self.assertEqual(dag.op_nodes()[3].name, "x")
|
|
self.assertEqual(dag.op_nodes()[4].name, "h")
|
|
self.assertEqual(dag.op_nodes()[5].name, "cu1")
|
|
self.assertEqual(dag.op_nodes()[6].name, "rcccx")
|
|
self.assertEqual(dag.op_nodes()[7].name, "h")
|
|
self.assertEqual(dag.op_nodes()[8].name, "h")
|
|
self.assertEqual(dag.op_nodes()[9].name, "cu1")
|
|
self.assertEqual(dag.op_nodes()[10].name, "rcccx_dg")
|
|
self.assertEqual(dag.op_nodes()[11].name, "h")
|
|
self.assertEqual(dag.op_nodes()[12].name, "c3sx")
|
|
self.assertEqual(dag.op_nodes()[13].name, "h")
|
|
self.assertRegex(dag.op_nodes()[14].name, "circuit-")
|
|
|
|
def test_decompose_name_wildcards(self):
|
|
"""Test decomposition parameters so that name wildcards is decomposed"""
|
|
decom_circ = self.complex_circuit.decompose(["circuit-*"])
|
|
dag = circuit_to_dag(decom_circ)
|
|
|
|
self.assertEqual(len(dag.op_nodes()), 9)
|
|
self.assertEqual(dag.op_nodes()[0].name, "h")
|
|
self.assertEqual(dag.op_nodes()[1].name, "t")
|
|
self.assertEqual(dag.op_nodes()[2].name, "x")
|
|
self.assertEqual(dag.op_nodes()[3].name, "h")
|
|
self.assertRegex(dag.op_nodes()[4].name, "cx")
|
|
self.assertEqual(dag.op_nodes()[5].name, "x")
|
|
self.assertEqual(dag.op_nodes()[6].name, "mcx")
|
|
self.assertEqual(dag.op_nodes()[7].name, "h")
|
|
self.assertEqual(dag.op_nodes()[8].name, "x")
|
|
|
|
def test_decompose_label_wildcards(self):
|
|
"""Test decomposition parameters so that label wildcards is decomposed"""
|
|
decom_circ = self.complex_circuit.decompose(["gate*"])
|
|
dag = circuit_to_dag(decom_circ)
|
|
|
|
self.assertEqual(len(dag.op_nodes()), 9)
|
|
self.assertEqual(dag.op_nodes()[0].name, "h")
|
|
self.assertEqual(dag.op_nodes()[1].name, "t")
|
|
self.assertEqual(dag.op_nodes()[2].name, "x")
|
|
self.assertEqual(dag.op_nodes()[3].name, "h")
|
|
self.assertEqual(dag.op_nodes()[4].name, "cx")
|
|
self.assertEqual(dag.op_nodes()[5].name, "x")
|
|
self.assertEqual(dag.op_nodes()[6].name, "mcx")
|
|
self.assertEqual(dag.op_nodes()[7].name, "h")
|
|
self.assertRegex(dag.op_nodes()[8].name, "circuit-")
|
|
|
|
def test_decompose_empty_gate(self):
|
|
"""Test a gate where the definition is an empty circuit is decomposed."""
|
|
empty = QuantumCircuit(1)
|
|
circuit = QuantumCircuit(1)
|
|
circuit.append(empty.to_gate(), [0])
|
|
|
|
decomposed = circuit.decompose()
|
|
self.assertEqual(len(decomposed.data), 0)
|
|
|
|
def test_decompose_reps(self):
|
|
"""Test decompose reps function is decomposed correctly"""
|
|
decom_circ = self.complex_circuit.decompose(reps=2)
|
|
decomposed = self.complex_circuit.decompose().decompose()
|
|
self.assertEqual(decom_circ, decomposed)
|
|
|
|
def test_decompose_single_qubit_clbit(self):
|
|
"""Test the decomposition of a block with a single qubit and clbit works.
|
|
|
|
Regression test of Qiskit/qiskit-terra#8591.
|
|
"""
|
|
block = QuantumCircuit(1, 1)
|
|
block.h(0)
|
|
|
|
circuit = QuantumCircuit(1, 1)
|
|
circuit.append(block, [0], [0])
|
|
|
|
decomposed = circuit.decompose()
|
|
|
|
self.assertEqual(decomposed, block)
|
|
|
|
def test_decompose_synthesis(self):
|
|
"""Test a high-level object with only a synthesis and no definition is correctly decomposed."""
|
|
qc = QuantumCircuit(1)
|
|
qc.h(0)
|
|
cliff = Clifford(qc)
|
|
|
|
bigger = QuantumCircuit(1)
|
|
bigger.append(cliff, [0])
|
|
|
|
decomposed = bigger.decompose()
|
|
|
|
self.assertEqual(qc, decomposed)
|
|
|
|
def test_specify_hls_object(self):
|
|
"""Test specifying an HLS object by name works."""
|
|
qc = QuantumCircuit(1)
|
|
qc.h(0)
|
|
cliff = Clifford(qc)
|
|
|
|
bigger = QuantumCircuit(1)
|
|
bigger.append(cliff, [0])
|
|
bigger.h(0) # add another gate that should remain unaffected, but has a definition
|
|
|
|
decomposed = bigger.decompose(gates_to_decompose=["clifford"])
|
|
|
|
expected = QuantumCircuit(1)
|
|
expected.h(0)
|
|
expected.h(0)
|
|
|
|
self.assertEqual(expected, decomposed)
|
|
|
|
def test_cif(self):
|
|
"""Test decomposition with c_if."""
|
|
circuit = QuantumCircuit(1, 1)
|
|
with self.assertWarns(DeprecationWarning):
|
|
circuit.x(0).c_if(0, 0)
|
|
|
|
ops = circuit.decompose().count_ops()
|
|
self.assertEqual(ops.get("u3", 0), 1)
|
|
|
|
def test_cif_no_definition(self):
|
|
"""Test decomposition with c_if when the gate has no definition.
|
|
|
|
Regression test of #13493.
|
|
"""
|
|
circuit = QuantumCircuit(1, 1)
|
|
with self.assertWarns(DeprecationWarning):
|
|
circuit.u(1, 2, 3, 0).c_if(0, 0)
|
|
|
|
ops = circuit.decompose().count_ops()
|
|
self.assertEqual(ops.get("u", 0), 1)
|
|
|
|
def test_control_flow_if(self):
|
|
"""Test decompose with control flow."""
|
|
qr = QuantumRegister(2)
|
|
cr = ClassicalRegister(1)
|
|
qc = QuantumCircuit(qr, cr)
|
|
|
|
qc.p(0.2, 0)
|
|
qc.measure(0, 0)
|
|
|
|
with qc.if_test((cr[0], 0)) as else_:
|
|
qc.cry(0.5, 0, 1)
|
|
with else_:
|
|
qc.crz(0.5, 0, 1)
|
|
|
|
expect = qc.copy_empty_like()
|
|
expect.u(0, 0, 0.2, 0)
|
|
expect.measure(0, 0)
|
|
|
|
with expect.if_test((cr[0], 0)) as else_:
|
|
expect.ry(0.25, 1)
|
|
expect.cx(0, 1)
|
|
expect.ry(-0.25, 1)
|
|
expect.cx(0, 1)
|
|
with else_:
|
|
expect.rz(0.25, 1)
|
|
expect.cx(0, 1)
|
|
expect.rz(-0.25, 1)
|
|
expect.cx(0, 1)
|
|
|
|
self.assertEqual(expect, qc.decompose())
|
|
|
|
def test_control_flow_for(self):
|
|
"""Test decompose with control flow."""
|
|
qr = QuantumRegister(2)
|
|
cr = ClassicalRegister(1)
|
|
qc = QuantumCircuit(qr, cr)
|
|
|
|
qc.p(0.2, 0)
|
|
qc.measure(0, 0)
|
|
|
|
with qc.for_loop(range(3)):
|
|
qc.cry(0.5, 0, 1)
|
|
|
|
expect = qc.copy_empty_like()
|
|
expect.u(0, 0, 0.2, 0)
|
|
expect.measure(0, 0)
|
|
|
|
with expect.for_loop(range(3)):
|
|
expect.ry(0.25, 1)
|
|
expect.cx(0, 1)
|
|
expect.ry(-0.25, 1)
|
|
expect.cx(0, 1)
|
|
|
|
self.assertEqual(expect, qc.decompose())
|