mirror of https://github.com/Qiskit/qiskit.git
1865 lines
74 KiB
Python
1865 lines
74 KiB
Python
# This code is part of Qiskit.
|
|
#
|
|
# (C) Copyright IBM 2019, 2023.
|
|
#
|
|
# 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 Qiskit's controlled gate operation."""
|
|
|
|
import unittest
|
|
|
|
import numpy as np
|
|
from numpy import pi
|
|
from ddt import ddt, data, unpack
|
|
|
|
from qiskit import QuantumRegister, QuantumCircuit, QiskitError, transpile
|
|
from qiskit.circuit import ControlledGate, Parameter, Gate
|
|
from qiskit.circuit.annotated_operation import AnnotatedOperation
|
|
from qiskit.circuit.singleton import SingletonControlledGate, _SingletonControlledGateOverrides
|
|
from qiskit.circuit.exceptions import CircuitError
|
|
from qiskit.quantum_info.operators.predicates import matrix_equal, is_unitary_matrix
|
|
from qiskit.quantum_info.random import random_unitary
|
|
from qiskit.quantum_info.states import Statevector
|
|
import qiskit.circuit.add_control as ac
|
|
from qiskit.transpiler.passes import UnrollCustomDefinitions, BasisTranslator
|
|
from qiskit.converters.circuit_to_dag import circuit_to_dag
|
|
from qiskit.converters.dag_to_circuit import dag_to_circuit
|
|
from qiskit.quantum_info import Operator
|
|
from qiskit.circuit.library import (
|
|
CXGate,
|
|
XGate,
|
|
YGate,
|
|
ZGate,
|
|
U1Gate,
|
|
CYGate,
|
|
CZGate,
|
|
CU1Gate,
|
|
SwapGate,
|
|
PhaseGate,
|
|
CCXGate,
|
|
HGate,
|
|
RZGate,
|
|
RYGate,
|
|
RXGate,
|
|
RZZGate,
|
|
RZXGate,
|
|
RYYGate,
|
|
RXXGate,
|
|
CPhaseGate,
|
|
CRYGate,
|
|
CRXGate,
|
|
CSwapGate,
|
|
UGate,
|
|
U3Gate,
|
|
CHGate,
|
|
CRZGate,
|
|
CU3Gate,
|
|
CUGate,
|
|
SXGate,
|
|
CSXGate,
|
|
MSGate,
|
|
Barrier,
|
|
RCCXGate,
|
|
RC3XGate,
|
|
MCU1Gate,
|
|
MCXGate,
|
|
MCXGrayCode,
|
|
MCXRecursive,
|
|
MCXVChain,
|
|
C3XGate,
|
|
C3SXGate,
|
|
C4XGate,
|
|
MCPhaseGate,
|
|
XXMinusYYGate,
|
|
XXPlusYYGate,
|
|
GlobalPhaseGate,
|
|
UnitaryGate,
|
|
MCMTGate,
|
|
)
|
|
from qiskit.circuit._utils import _compute_control_matrix
|
|
import qiskit.circuit.library.standard_gates as allGates
|
|
from qiskit.circuit.library.standard_gates.multi_control_rotation_gates import _mcsu2_real_diagonal
|
|
from qiskit.circuit.library.standard_gates.equivalence_library import (
|
|
StandardEquivalenceLibrary as std_eqlib,
|
|
)
|
|
from test import combine # pylint: disable=wrong-import-order
|
|
from test import QiskitTestCase # pylint: disable=wrong-import-order
|
|
|
|
from .gate_utils import _get_free_params
|
|
|
|
|
|
@ddt
|
|
class TestControlledGate(QiskitTestCase):
|
|
"""Tests for controlled gates and the ControlledGate class."""
|
|
|
|
def test_controlled_x(self):
|
|
"""Test creation of controlled x gate"""
|
|
self.assertEqual(XGate().control(), CXGate())
|
|
|
|
def test_controlled_y(self):
|
|
"""Test creation of controlled y gate"""
|
|
self.assertEqual(YGate().control(), CYGate())
|
|
|
|
def test_controlled_z(self):
|
|
"""Test creation of controlled z gate"""
|
|
self.assertEqual(ZGate().control(), CZGate())
|
|
|
|
def test_controlled_h(self):
|
|
"""Test the creation of a controlled H gate."""
|
|
self.assertEqual(HGate().control(), CHGate())
|
|
|
|
def test_controlled_phase(self):
|
|
"""Test the creation of a controlled U1 gate."""
|
|
theta = 0.5
|
|
self.assertEqual(PhaseGate(theta).control(), CPhaseGate(theta))
|
|
|
|
def test_double_controlled_phase(self):
|
|
"""Test the creation of a controlled phase gate."""
|
|
theta = 0.5
|
|
self.assertEqual(PhaseGate(theta).control(2), MCPhaseGate(theta, 2))
|
|
|
|
def test_controlled_u1(self):
|
|
"""Test the creation of a controlled U1 gate."""
|
|
theta = 0.5
|
|
self.assertEqual(U1Gate(theta).control(), CU1Gate(theta))
|
|
|
|
circ = QuantumCircuit(1)
|
|
circ.append(U1Gate(theta), circ.qregs[0])
|
|
unroller = UnrollCustomDefinitions(std_eqlib, ["cx", "u", "p"])
|
|
basis_translator = BasisTranslator(std_eqlib, ["cx", "u", "p"])
|
|
ctrl_circ_gate = dag_to_circuit(
|
|
basis_translator.run(unroller.run(circuit_to_dag(circ)))
|
|
).control()
|
|
ctrl_circ = QuantumCircuit(2)
|
|
ctrl_circ.append(ctrl_circ_gate, ctrl_circ.qregs[0])
|
|
ctrl_circ = ctrl_circ.decompose().decompose()
|
|
self.assertEqual(ctrl_circ.size(), 1)
|
|
|
|
def test_controlled_rz(self):
|
|
"""Test the creation of a controlled RZ gate."""
|
|
theta = 0.5
|
|
self.assertEqual(RZGate(theta).control(), CRZGate(theta))
|
|
|
|
def test_control_parameters(self):
|
|
"""Test different ctrl_state formats for control function."""
|
|
theta = 0.5
|
|
|
|
self.assertEqual(
|
|
CRYGate(theta).control(2, ctrl_state="01"), CRYGate(theta).control(2, ctrl_state=1)
|
|
)
|
|
self.assertEqual(
|
|
CRYGate(theta).control(2, ctrl_state=None), CRYGate(theta).control(2, ctrl_state=3)
|
|
)
|
|
|
|
self.assertEqual(CCXGate().control(2, ctrl_state="01"), CCXGate().control(2, ctrl_state=1))
|
|
self.assertEqual(CCXGate().control(2, ctrl_state=None), CCXGate().control(2, ctrl_state=3))
|
|
|
|
def test_controlled_ry(self):
|
|
"""Test the creation of a controlled RY gate."""
|
|
theta = 0.5
|
|
self.assertEqual(RYGate(theta).control(), CRYGate(theta))
|
|
|
|
def test_controlled_rx(self):
|
|
"""Test the creation of a controlled RX gate."""
|
|
theta = 0.5
|
|
self.assertEqual(RXGate(theta).control(), CRXGate(theta))
|
|
|
|
def test_controlled_u(self):
|
|
"""Test the creation of a controlled U gate."""
|
|
theta, phi, lamb = 0.1, 0.2, 0.3
|
|
self.assertEqual(UGate(theta, phi, lamb).control(), CUGate(theta, phi, lamb, 0))
|
|
|
|
def test_controlled_u3(self):
|
|
"""Test the creation of a controlled U3 gate."""
|
|
theta, phi, lamb = 0.1, 0.2, 0.3
|
|
self.assertEqual(U3Gate(theta, phi, lamb).control(), CU3Gate(theta, phi, lamb))
|
|
|
|
circ = QuantumCircuit(1)
|
|
circ.append(U3Gate(theta, phi, lamb), circ.qregs[0])
|
|
unroller = UnrollCustomDefinitions(std_eqlib, ["cx", "u", "p"])
|
|
basis_translator = BasisTranslator(std_eqlib, ["cx", "u", "p"])
|
|
ctrl_circ_gate = dag_to_circuit(
|
|
basis_translator.run(unroller.run(circuit_to_dag(circ)))
|
|
).control()
|
|
ctrl_circ = QuantumCircuit(2)
|
|
ctrl_circ.append(ctrl_circ_gate, ctrl_circ.qregs[0])
|
|
ctrl_circ = ctrl_circ.decompose().decompose()
|
|
|
|
self.assertEqual(ctrl_circ.size(), 1)
|
|
|
|
def test_controlled_cx(self):
|
|
"""Test creation of controlled cx gate"""
|
|
self.assertEqual(CXGate().control(), CCXGate())
|
|
|
|
def test_controlled_swap(self):
|
|
"""Test creation of controlled swap gate"""
|
|
self.assertEqual(SwapGate().control(), CSwapGate())
|
|
|
|
def test_special_cases_equivalent_to_controlled_base_gate(self):
|
|
"""Test that ``ControlledGate`` subclasses for more efficient representations give
|
|
equivalent matrices and definitions to the naive ``base_gate.control(n)``."""
|
|
# Angles used here are not important, we just pick slightly strange values to ensure that
|
|
# there are no coincidental equivalences.
|
|
tests = [
|
|
(CXGate(), 1),
|
|
(CCXGate(), 2),
|
|
(C3XGate(), 3),
|
|
(C4XGate(), 4),
|
|
(MCXGate(5), 5),
|
|
(CYGate(), 1),
|
|
(CZGate(), 1),
|
|
(CPhaseGate(np.pi / 7), 1),
|
|
(MCPhaseGate(np.pi / 7, 2), 2),
|
|
(CSwapGate(), 1),
|
|
(CSXGate(), 1),
|
|
(C3SXGate(), 3),
|
|
(CHGate(), 1),
|
|
(CU1Gate(np.pi / 7), 1),
|
|
(MCU1Gate(np.pi / 7, 2), 2),
|
|
# `CUGate` takes an extra "global" phase parameter compared to `UGate`, and consequently
|
|
# is only equal to `base_gate.control()` when this extra phase is 0.
|
|
(CUGate(np.pi / 7, np.pi / 5, np.pi / 3, 0), 1),
|
|
(CU3Gate(np.pi / 7, np.pi / 5, np.pi / 3), 1),
|
|
(CRXGate(np.pi / 7), 1),
|
|
(CRYGate(np.pi / 7), 1),
|
|
(CRZGate(np.pi / 7), 1),
|
|
]
|
|
for special_case_gate, n_controls in tests:
|
|
with self.subTest(gate=special_case_gate.name):
|
|
naive_operator = Operator(special_case_gate.base_gate.control(n_controls))
|
|
# Ensure that both the array form (if the gate overrides `__array__`) and the
|
|
# circuit-definition form are tested.
|
|
self.assertTrue(Operator(special_case_gate).equiv(naive_operator))
|
|
if not isinstance(special_case_gate, (MCXGate, MCPhaseGate, MCU1Gate)):
|
|
# Ensure that the to_matrix method yields the same result
|
|
np.testing.assert_allclose(
|
|
special_case_gate.to_matrix(), naive_operator.to_matrix(), atol=1e-8
|
|
)
|
|
|
|
if not isinstance(special_case_gate, CXGate):
|
|
# CX is treated like a primitive within Terra, and doesn't have a definition.
|
|
self.assertTrue(Operator(special_case_gate.definition).equiv(naive_operator))
|
|
|
|
def test_global_phase_control(self):
|
|
"""Test creation of a GlobalPhaseGate."""
|
|
base = GlobalPhaseGate(np.pi / 7)
|
|
expected_1q = PhaseGate(np.pi / 7)
|
|
self.assertEqual(Operator(base.control()), Operator(expected_1q))
|
|
|
|
expected_2q = PhaseGate(np.pi / 7).control()
|
|
self.assertEqual(Operator(base.control(2)), Operator(expected_2q))
|
|
|
|
expected_open = QuantumCircuit(1)
|
|
expected_open.x(0)
|
|
expected_open.p(np.pi / 7, 0)
|
|
expected_open.x(0)
|
|
self.assertEqual(Operator(base.control(ctrl_state=0)), Operator(expected_open))
|
|
|
|
def test_circuit_append(self):
|
|
"""Test appending a controlled gate to a quantum circuit."""
|
|
circ = QuantumCircuit(5)
|
|
inst = CXGate()
|
|
circ.append(inst.control(), qargs=[0, 2, 1])
|
|
circ.append(inst.control(2), qargs=[0, 3, 1, 2])
|
|
circ.append(inst.control().control(), qargs=[0, 3, 1, 2]) # should be same as above
|
|
self.assertEqual(circ[1].operation, circ[2].operation)
|
|
self.assertEqual(circ.depth(), 3)
|
|
self.assertEqual(circ[0].operation.num_ctrl_qubits, 2)
|
|
self.assertEqual(circ[1].operation.num_ctrl_qubits, 3)
|
|
self.assertEqual(circ[2].operation.num_ctrl_qubits, 3)
|
|
self.assertEqual(circ[0].operation.num_qubits, 3)
|
|
self.assertEqual(circ[1].operation.num_qubits, 4)
|
|
self.assertEqual(circ[2].operation.num_qubits, 4)
|
|
for instr in circ:
|
|
self.assertTrue(isinstance(instr.operation, ControlledGate))
|
|
|
|
def test_swap_definition_specification(self):
|
|
"""Test the instantiation of a controlled swap gate with explicit definition."""
|
|
swap = SwapGate()
|
|
cswap = ControlledGate(
|
|
"cswap", 3, [], num_ctrl_qubits=1, definition=swap.definition, base_gate=swap
|
|
)
|
|
self.assertEqual(swap.definition, cswap.definition)
|
|
|
|
def test_multi_controlled_composite_gate(self):
|
|
"""Test a multi controlled composite gate."""
|
|
num_ctrl = 3
|
|
# create composite gate
|
|
sub_q = QuantumRegister(2)
|
|
cgate = QuantumCircuit(sub_q, name="cgate")
|
|
cgate.h(sub_q[0])
|
|
cgate.crz(pi / 2, sub_q[0], sub_q[1])
|
|
cgate.swap(sub_q[0], sub_q[1])
|
|
cgate.u(0.1, 0.2, 0.3, sub_q[1])
|
|
cgate.t(sub_q[0])
|
|
num_target = cgate.width()
|
|
gate = cgate.to_gate()
|
|
cont_gate = gate.control(num_ctrl_qubits=num_ctrl)
|
|
control = QuantumRegister(num_ctrl)
|
|
target = QuantumRegister(num_target)
|
|
qc = QuantumCircuit(control, target)
|
|
qc.append(cont_gate, control[:] + target[:])
|
|
op_mat = Operator(cgate).data
|
|
cop_mat = _compute_control_matrix(op_mat, num_ctrl)
|
|
ref_mat = Operator(qc).data
|
|
self.assertTrue(matrix_equal(cop_mat, ref_mat))
|
|
|
|
def test_single_controlled_composite_gate(self):
|
|
"""Test a singly controlled composite gate."""
|
|
num_ctrl = 1
|
|
# create composite gate
|
|
sub_q = QuantumRegister(2)
|
|
cgate = QuantumCircuit(sub_q, name="cgate")
|
|
cgate.h(sub_q[0])
|
|
cgate.cx(sub_q[0], sub_q[1])
|
|
num_target = cgate.width()
|
|
gate = cgate.to_gate()
|
|
cont_gate = gate.control(num_ctrl_qubits=num_ctrl)
|
|
control = QuantumRegister(num_ctrl, "control")
|
|
target = QuantumRegister(num_target, "target")
|
|
qc = QuantumCircuit(control, target)
|
|
qc.append(cont_gate, control[:] + target[:])
|
|
op_mat = Operator(cgate).data
|
|
cop_mat = _compute_control_matrix(op_mat, num_ctrl)
|
|
ref_mat = Operator(qc).data
|
|
self.assertTrue(matrix_equal(cop_mat, ref_mat))
|
|
|
|
def test_control_open_controlled_gate(self):
|
|
"""Test control(2) vs control.control where inner gate has open controls."""
|
|
gate1pre = ZGate().control(1, ctrl_state=0)
|
|
gate1 = gate1pre.control(1, ctrl_state=1)
|
|
gate2 = ZGate().control(2, ctrl_state=1)
|
|
expected = Operator(_compute_control_matrix(ZGate().to_matrix(), 2, ctrl_state=1))
|
|
self.assertEqual(expected, Operator(gate1))
|
|
self.assertEqual(expected, Operator(gate2))
|
|
|
|
def test_multi_control_z(self):
|
|
"""Test a multi controlled Z gate."""
|
|
qc = QuantumCircuit(1)
|
|
qc.z(0)
|
|
ctr_gate = qc.to_gate().control(2)
|
|
|
|
ctr_circ = QuantumCircuit(3)
|
|
ctr_circ.append(ctr_gate, range(3))
|
|
|
|
ref_circ = QuantumCircuit(3)
|
|
ref_circ.h(2)
|
|
ref_circ.ccx(0, 1, 2)
|
|
ref_circ.h(2)
|
|
|
|
self.assertEqual(ctr_circ.decompose(), ref_circ)
|
|
|
|
def test_multi_control_u3(self):
|
|
"""Test the matrix representation of the controlled and controlled-controlled U3 gate."""
|
|
from qiskit.circuit.library.standard_gates import u3
|
|
|
|
num_ctrl = 3
|
|
# U3 gate params
|
|
alpha, beta, gamma = 0.2, 0.3, 0.4
|
|
u3gate = u3.U3Gate(alpha, beta, gamma)
|
|
cu3gate = u3.CU3Gate(alpha, beta, gamma)
|
|
|
|
# cnu3 gate
|
|
cnu3 = u3gate.control(num_ctrl)
|
|
width = cnu3.num_qubits
|
|
qr = QuantumRegister(width)
|
|
qcnu3 = QuantumCircuit(qr)
|
|
qcnu3.append(cnu3, qr, [])
|
|
|
|
# U3 gate
|
|
qu3 = QuantumCircuit(1)
|
|
qu3.append(u3gate, [0])
|
|
|
|
# CU3 gate
|
|
qcu3 = QuantumCircuit(2)
|
|
qcu3.append(cu3gate, [0, 1])
|
|
|
|
# c-cu3 gate
|
|
width = 3
|
|
qr = QuantumRegister(width)
|
|
qc_cu3 = QuantumCircuit(qr)
|
|
|
|
c_cu3 = cu3gate.control(1)
|
|
qc_cu3.append(c_cu3, qr, [])
|
|
|
|
# Circuit unitaries
|
|
mat_cnu3 = Operator(qcnu3).data
|
|
mat_u3 = Operator(qu3).data
|
|
mat_cu3 = Operator(qcu3).data
|
|
mat_c_cu3 = Operator(qc_cu3).data
|
|
|
|
# Target Controlled-U3 unitary
|
|
target_cnu3 = _compute_control_matrix(mat_u3, num_ctrl)
|
|
target_cu3 = np.kron(mat_u3, np.diag([0, 1])) + np.kron(np.eye(2), np.diag([1, 0]))
|
|
target_c_cu3 = np.kron(mat_cu3, np.diag([0, 1])) + np.kron(np.eye(4), np.diag([1, 0]))
|
|
|
|
tests = [
|
|
("check unitary of u3.control against tensored unitary of u3", target_cu3, mat_cu3),
|
|
(
|
|
"check unitary of cu3.control against tensored unitary of cu3",
|
|
target_c_cu3,
|
|
mat_c_cu3,
|
|
),
|
|
("check unitary of cnu3 against tensored unitary of u3", target_cnu3, mat_cnu3),
|
|
]
|
|
for itest in tests:
|
|
info, target, decomp = itest[0], itest[1], itest[2]
|
|
with self.subTest(i=info):
|
|
self.assertTrue(matrix_equal(target, decomp, atol=1e-8, rtol=1e-5))
|
|
|
|
def test_multi_control_u1(self):
|
|
"""Test the matrix representation of the controlled and controlled-controlled U1 gate."""
|
|
from qiskit.circuit.library.standard_gates import u1
|
|
|
|
num_ctrl = 3
|
|
# U1 gate params
|
|
theta = 0.2
|
|
u1gate = u1.U1Gate(theta)
|
|
cu1gate = u1.CU1Gate(theta)
|
|
|
|
# cnu1 gate
|
|
cnu1 = u1gate.control(num_ctrl)
|
|
width = cnu1.num_qubits
|
|
qr = QuantumRegister(width)
|
|
qcnu1 = QuantumCircuit(qr)
|
|
qcnu1.append(cnu1, qr, [])
|
|
|
|
# U1 gate
|
|
qu1 = QuantumCircuit(1)
|
|
qu1.append(u1gate, [0])
|
|
|
|
# CU1 gate
|
|
qcu1 = QuantumCircuit(2)
|
|
qcu1.append(cu1gate, [0, 1])
|
|
|
|
# c-cu1 gate
|
|
width = 3
|
|
qr = QuantumRegister(width)
|
|
qc_cu1 = QuantumCircuit(qr)
|
|
c_cu1 = cu1gate.control(1)
|
|
qc_cu1.append(c_cu1, qr, [])
|
|
|
|
# Circuit unitaries
|
|
mat_cnu1 = Operator(qcnu1).data
|
|
# trace out ancillae
|
|
|
|
mat_u1 = Operator(qu1).data
|
|
mat_cu1 = Operator(qcu1).data
|
|
mat_c_cu1 = Operator(qc_cu1).data
|
|
|
|
# Target Controlled-U1 unitary
|
|
target_cnu1 = _compute_control_matrix(mat_u1, num_ctrl)
|
|
target_cu1 = np.kron(mat_u1, np.diag([0, 1])) + np.kron(np.eye(2), np.diag([1, 0]))
|
|
target_c_cu1 = np.kron(mat_cu1, np.diag([0, 1])) + np.kron(np.eye(4), np.diag([1, 0]))
|
|
|
|
tests = [
|
|
("check unitary of u1.control against tensored unitary of u1", target_cu1, mat_cu1),
|
|
(
|
|
"check unitary of cu1.control against tensored unitary of cu1",
|
|
target_c_cu1,
|
|
mat_c_cu1,
|
|
),
|
|
("check unitary of cnu1 against tensored unitary of u1", target_cnu1, mat_cnu1),
|
|
]
|
|
for itest in tests:
|
|
info, target, decomp = itest[0], itest[1], itest[2]
|
|
with self.subTest(i=info):
|
|
self.log.info(info)
|
|
self.assertTrue(matrix_equal(target, decomp))
|
|
|
|
@data(1, 2, 3, 4)
|
|
def test_multi_controlled_u1_matrix(self, num_controls):
|
|
"""Test the matrix representation of the multi-controlled CU1 gate.
|
|
|
|
Based on the test moved here from Aqua:
|
|
https://github.com/Qiskit/qiskit-aqua/blob/769ca8f/test/aqua/test_mcu1.py
|
|
"""
|
|
|
|
# registers for the circuit
|
|
q_controls = QuantumRegister(num_controls)
|
|
q_target = QuantumRegister(1)
|
|
|
|
# iterate over all possible combinations of control qubits
|
|
for ctrl_state in range(2**num_controls):
|
|
bitstr = bin(ctrl_state)[2:].zfill(num_controls)[::-1]
|
|
lam = 0.3165354 * pi
|
|
qc = QuantumCircuit(q_controls, q_target)
|
|
for idx, bit in enumerate(bitstr):
|
|
if bit == "0":
|
|
qc.x(q_controls[idx])
|
|
|
|
qc.mcp(lam, q_controls, q_target[0])
|
|
|
|
# for idx in subset:
|
|
for idx, bit in enumerate(bitstr):
|
|
if bit == "0":
|
|
qc.x(q_controls[idx])
|
|
|
|
simulated = Operator(qc)
|
|
base = PhaseGate(lam).to_matrix()
|
|
expected = _compute_control_matrix(base, num_controls, ctrl_state=ctrl_state)
|
|
with self.subTest(msg=f"control state = {ctrl_state}"):
|
|
self.assertTrue(matrix_equal(simulated, expected))
|
|
|
|
@combine(
|
|
num_controls=[2, 3, 4, 5, 6],
|
|
mode=[
|
|
"noancilla",
|
|
"recursion",
|
|
"v-chain",
|
|
"v-chain-dirty",
|
|
"advanced",
|
|
"basic",
|
|
"basic-dirty-ancilla",
|
|
],
|
|
)
|
|
def test_multi_control_toffoli_matrix_advanced_num_ancillas(self, num_controls, mode):
|
|
"""Test the multi-control Toffoli gate methods with and w/o ancillas."""
|
|
q_controls = QuantumRegister(num_controls)
|
|
q_target = QuantumRegister(1)
|
|
qc = QuantumCircuit(q_controls, q_target)
|
|
|
|
q_ancillas = None
|
|
if mode == "noancilla":
|
|
num_ancillas = 0
|
|
if mode in ["recursion", "advanced"]:
|
|
num_ancillas = int(num_controls > 4)
|
|
q_ancillas = QuantumRegister(num_ancillas)
|
|
qc.add_register(q_ancillas)
|
|
if mode[:7] == "v-chain" or mode[:5] == "basic":
|
|
num_ancillas = max(0, num_controls - 2)
|
|
q_ancillas = QuantumRegister(num_ancillas)
|
|
qc.add_register(q_ancillas)
|
|
|
|
qc.mcx(q_controls, q_target[0], q_ancillas, mode=mode)
|
|
|
|
simulated = Operator(qc).data
|
|
if num_ancillas > 0:
|
|
simulated = simulated[: 2 ** (num_controls + 1), : 2 ** (num_controls + 1)]
|
|
|
|
base = XGate().to_matrix()
|
|
expected = _compute_control_matrix(base, num_controls)
|
|
self.assertTrue(matrix_equal(simulated, expected, atol=1e-8))
|
|
|
|
def test_mcsu2_real_diagonal(self):
|
|
"""Test mcsu2_real_diagonal"""
|
|
num_ctrls = 6
|
|
theta = 0.3
|
|
ry_matrix = RYGate(theta).to_matrix()
|
|
qc = _mcsu2_real_diagonal(ry_matrix, num_ctrls)
|
|
|
|
mcry_matrix = _compute_control_matrix(ry_matrix, 6)
|
|
self.assertTrue(np.allclose(mcry_matrix, Operator(qc).to_matrix()))
|
|
|
|
@combine(num_controls=[1, 2, 4], base_gate_name=["x", "y", "z"], use_basis_gates=[True, False])
|
|
def test_multi_controlled_rotation_gate_matrices(
|
|
self, num_controls, base_gate_name, use_basis_gates
|
|
):
|
|
"""Test the multi controlled rotation gates without ancillas.
|
|
|
|
Based on the test moved here from Aqua:
|
|
https://github.com/Qiskit/qiskit-aqua/blob/769ca8f/test/aqua/test_mcr.py
|
|
"""
|
|
q_controls = QuantumRegister(num_controls)
|
|
q_target = QuantumRegister(1)
|
|
|
|
# iterate over all possible combinations of control qubits
|
|
for ctrl_state in range(2**num_controls):
|
|
bitstr = bin(ctrl_state)[2:].zfill(num_controls)[::-1]
|
|
theta = 0.871236 * pi
|
|
qc = QuantumCircuit(q_controls, q_target)
|
|
for idx, bit in enumerate(bitstr):
|
|
if bit == "0":
|
|
qc.x(q_controls[idx])
|
|
|
|
# call mcrx/mcry/mcrz
|
|
if base_gate_name == "y":
|
|
qc.mcry(
|
|
theta,
|
|
q_controls,
|
|
q_target[0],
|
|
None,
|
|
mode="noancilla",
|
|
use_basis_gates=use_basis_gates,
|
|
)
|
|
else: # case 'x' or 'z' only support the noancilla mode and do not have this keyword
|
|
getattr(qc, "mcr" + base_gate_name)(
|
|
theta, q_controls, q_target[0], use_basis_gates=use_basis_gates
|
|
)
|
|
|
|
for idx, bit in enumerate(bitstr):
|
|
if bit == "0":
|
|
qc.x(q_controls[idx])
|
|
|
|
if use_basis_gates:
|
|
with self.subTest(msg="check only basis gates used"):
|
|
gates_used = set(qc.count_ops().keys())
|
|
self.assertTrue(gates_used.issubset({"x", "u", "p", "cx"}))
|
|
|
|
simulated = Operator(qc)
|
|
|
|
if base_gate_name == "x":
|
|
rot_mat = RXGate(theta).to_matrix()
|
|
elif base_gate_name == "y":
|
|
rot_mat = RYGate(theta).to_matrix()
|
|
else: # case 'z'
|
|
rot_mat = RZGate(theta).to_matrix()
|
|
|
|
expected = _compute_control_matrix(rot_mat, num_controls, ctrl_state=ctrl_state)
|
|
with self.subTest(msg=f"control state = {ctrl_state}"):
|
|
self.assertTrue(matrix_equal(simulated, expected))
|
|
|
|
@combine(num_controls=[1, 2, 4], use_basis_gates=[True, False])
|
|
def test_multi_controlled_y_rotation_matrix_basic_mode(self, num_controls, use_basis_gates):
|
|
"""Test the multi controlled Y rotation using the mode 'basic'.
|
|
|
|
Based on the test moved here from Aqua:
|
|
https://github.com/Qiskit/qiskit-aqua/blob/769ca8f/test/aqua/test_mcr.py
|
|
"""
|
|
|
|
# get the number of required ancilla qubits
|
|
if num_controls <= 2:
|
|
num_ancillas = 0
|
|
else:
|
|
num_ancillas = num_controls - 2
|
|
|
|
q_controls = QuantumRegister(num_controls)
|
|
q_target = QuantumRegister(1)
|
|
|
|
for ctrl_state in range(2**num_controls):
|
|
bitstr = bin(ctrl_state)[2:].zfill(num_controls)[::-1]
|
|
theta = 0.871236 * pi
|
|
if num_ancillas > 0:
|
|
q_ancillas = QuantumRegister(num_ancillas)
|
|
qc = QuantumCircuit(q_controls, q_target, q_ancillas)
|
|
else:
|
|
qc = QuantumCircuit(q_controls, q_target)
|
|
q_ancillas = None
|
|
|
|
for idx, bit in enumerate(bitstr):
|
|
if bit == "0":
|
|
qc.x(q_controls[idx])
|
|
|
|
qc.mcry(
|
|
theta,
|
|
q_controls,
|
|
q_target[0],
|
|
q_ancillas,
|
|
mode="basic",
|
|
use_basis_gates=use_basis_gates,
|
|
)
|
|
|
|
for idx, bit in enumerate(bitstr):
|
|
if bit == "0":
|
|
qc.x(q_controls[idx])
|
|
|
|
rot_mat = RYGate(theta).to_matrix()
|
|
simulated = Operator(qc).data
|
|
|
|
if num_ancillas > 0:
|
|
simulated = simulated[: 2 ** (num_controls + 1), : 2 ** (num_controls + 1)]
|
|
|
|
expected = _compute_control_matrix(rot_mat, num_controls, ctrl_state=ctrl_state)
|
|
|
|
with self.subTest(msg=f"control state = {ctrl_state}"):
|
|
self.assertTrue(matrix_equal(simulated, expected))
|
|
|
|
def test_mcry_defaults_to_vchain(self):
|
|
"""Test mcry defaults to the v-chain mode if sufficient work qubits are provided."""
|
|
circuit = QuantumCircuit(5)
|
|
control_qubits = circuit.qubits[:3]
|
|
target_qubit = circuit.qubits[3]
|
|
additional_qubits = circuit.qubits[4:]
|
|
circuit.mcry(0.2, control_qubits, target_qubit, additional_qubits)
|
|
|
|
# If the v-chain mode is selected, all qubits are used. If the noancilla mode would be
|
|
# selected, the bottom qubit would remain unused.
|
|
dag = circuit_to_dag(circuit)
|
|
self.assertEqual(len(list(dag.idle_wires())), 0)
|
|
|
|
@data(1, 2)
|
|
def test_mcx_gates_yield_explicit_gates(self, num_ctrl_qubits):
|
|
"""Test the creating a MCX gate yields the explicit definition if we know it."""
|
|
cls = MCXGate(num_ctrl_qubits).__class__
|
|
explicit = {1: CXGate, 2: CCXGate}
|
|
self.assertEqual(cls, explicit[num_ctrl_qubits])
|
|
|
|
@data(1, 2, 3, 4)
|
|
def test_small_mcx_gates_yield_cx_count(self, num_ctrl_qubits):
|
|
"""Test the creating a MCX gate with small number of controls (with no ancillas)
|
|
yields the expected number of cx gates."""
|
|
qc = QuantumCircuit(num_ctrl_qubits + 1)
|
|
qc.append(MCXGate(num_ctrl_qubits), range(num_ctrl_qubits + 1))
|
|
|
|
cqc = transpile(qc, basis_gates=["u", "cx"])
|
|
cx_count = cqc.count_ops()["cx"]
|
|
expected = {1: 1, 2: 6, 3: 14, 4: 36}
|
|
self.assertEqual(cx_count, expected[num_ctrl_qubits])
|
|
|
|
@data(1, 2, 3, 4)
|
|
def test_mcxgraycode_gates_yield_explicit_gates(self, num_ctrl_qubits):
|
|
"""Test an MCXGrayCode yields explicit definition."""
|
|
qc = QuantumCircuit(num_ctrl_qubits + 1)
|
|
qc.append(MCXGrayCode(num_ctrl_qubits), list(range(qc.num_qubits)), [])
|
|
explicit = {1: CXGate, 2: CCXGate, 3: C3XGate, 4: C4XGate}
|
|
self.assertEqual(qc[0].operation.base_class, explicit[num_ctrl_qubits])
|
|
|
|
@data(3, 4, 5, 8)
|
|
def test_mcx_gates(self, num_ctrl_qubits):
|
|
"""Test the mcx gates."""
|
|
reference = np.zeros(2 ** (num_ctrl_qubits + 1))
|
|
reference[-1] = 1
|
|
|
|
for gate in [
|
|
MCXGrayCode(num_ctrl_qubits),
|
|
MCXRecursive(num_ctrl_qubits),
|
|
MCXVChain(num_ctrl_qubits, False),
|
|
MCXVChain(num_ctrl_qubits, True),
|
|
]:
|
|
with self.subTest(gate=gate):
|
|
circuit = QuantumCircuit(gate.num_qubits)
|
|
if num_ctrl_qubits > 0:
|
|
circuit.x(list(range(num_ctrl_qubits)))
|
|
circuit.append(gate, list(range(gate.num_qubits)), [])
|
|
statevector = Statevector(circuit).data
|
|
|
|
# account for ancillas
|
|
if hasattr(gate, "num_ancilla_qubits") and gate.num_ancilla_qubits > 0:
|
|
corrected = np.zeros(2 ** (num_ctrl_qubits + 1), dtype=complex)
|
|
for i, statevector_amplitude in enumerate(statevector):
|
|
i = int(bin(i)[2:].zfill(circuit.num_qubits)[gate.num_ancilla_qubits :], 2)
|
|
corrected[i] += statevector_amplitude
|
|
statevector = corrected
|
|
np.testing.assert_array_almost_equal(statevector.real, reference)
|
|
|
|
@data(5, 10, 15)
|
|
def test_mcxvchain_dirty_ancilla_cx_count(self, num_ctrl_qubits):
|
|
"""Test if cx count of the v-chain mcx with dirty ancilla
|
|
is less than upper bound."""
|
|
mcx_vchain = MCXVChain(num_ctrl_qubits, dirty_ancillas=True)
|
|
qc = QuantumCircuit(mcx_vchain.num_qubits)
|
|
|
|
qc.append(mcx_vchain, list(range(mcx_vchain.num_qubits)))
|
|
|
|
tr_mcx_vchain = transpile(qc, basis_gates=["u", "cx"])
|
|
cx_count = tr_mcx_vchain.count_ops()["cx"]
|
|
|
|
self.assertLessEqual(cx_count, 8 * num_ctrl_qubits - 6)
|
|
|
|
@data(5, 10, 15)
|
|
def test_mcxvchain_clean_ancilla_cx_count(self, num_ctrl_qubits):
|
|
"""Test if cx count of the v-chain mcx with clean ancilla
|
|
is less than upper bound."""
|
|
mcx_vchain = MCXVChain(num_ctrl_qubits, dirty_ancillas=False)
|
|
qc = QuantumCircuit(mcx_vchain.num_qubits)
|
|
|
|
qc.append(mcx_vchain, list(range(mcx_vchain.num_qubits)))
|
|
|
|
tr_mcx_vchain = transpile(qc, basis_gates=["u", "cx"])
|
|
cx_count = tr_mcx_vchain.count_ops()["cx"]
|
|
|
|
self.assertLessEqual(cx_count, 6 * num_ctrl_qubits - 6)
|
|
|
|
@data(7, 10, 15)
|
|
def test_mcxrecursive_clean_ancilla_cx_count(self, num_ctrl_qubits):
|
|
"""Test if cx count of the mcx with one clean ancilla
|
|
is less than upper bound."""
|
|
mcx_recursive = MCXRecursive(num_ctrl_qubits)
|
|
qc = QuantumCircuit(mcx_recursive.num_qubits)
|
|
|
|
qc.append(mcx_recursive, list(range(mcx_recursive.num_qubits)))
|
|
|
|
tr_mcx_rec = transpile(qc, basis_gates=["u", "cx"])
|
|
cx_count = tr_mcx_rec.count_ops()["cx"]
|
|
|
|
self.assertLessEqual(cx_count, 16 * num_ctrl_qubits - 8)
|
|
|
|
def test_mcxvchain_dirty_ancilla_action_only(self):
|
|
"""Test the v-chain mcx with dirty auxiliary qubits
|
|
with gate cancelling with mirrored circuit."""
|
|
|
|
num_ctrl_qubits = 5
|
|
|
|
gate = MCXVChain(num_ctrl_qubits, dirty_ancillas=True)
|
|
gate_with_cancelling = MCXVChain(num_ctrl_qubits, dirty_ancillas=True, action_only=True)
|
|
|
|
num_qubits = gate.num_qubits
|
|
ref_circuit = QuantumCircuit(num_qubits)
|
|
circuit = QuantumCircuit(num_qubits)
|
|
|
|
ref_circuit.append(gate, list(range(num_qubits)), [])
|
|
ref_circuit.h(num_ctrl_qubits)
|
|
ref_circuit.append(gate, list(range(num_qubits)), [])
|
|
|
|
circuit.append(gate_with_cancelling, list(range(num_qubits)), [])
|
|
circuit.h(num_ctrl_qubits)
|
|
circuit.append(gate_with_cancelling.inverse(), list(range(num_qubits)), [])
|
|
|
|
self.assertTrue(matrix_equal(Operator(circuit).data, Operator(ref_circuit).data))
|
|
|
|
def test_mcxvchain_dirty_ancilla_relative_phase(self):
|
|
"""Test the v-chain mcx with dirty auxiliary qubits
|
|
with only relative phase Toffoli gates."""
|
|
num_ctrl_qubits = 5
|
|
|
|
gate = MCXVChain(num_ctrl_qubits, dirty_ancillas=True)
|
|
gate_relative_phase = MCXVChain(num_ctrl_qubits, dirty_ancillas=True, relative_phase=True)
|
|
|
|
num_qubits = gate.num_qubits + 1
|
|
ref_circuit = QuantumCircuit(num_qubits)
|
|
circuit = QuantumCircuit(num_qubits)
|
|
|
|
ref_circuit.append(gate, list(range(num_qubits - 1)), [])
|
|
ref_circuit.h(num_qubits - 1)
|
|
ref_circuit.append(gate, list(range(num_qubits - 1)), [])
|
|
|
|
circuit.append(gate_relative_phase, list(range(num_qubits - 1)), [])
|
|
circuit.h(num_qubits - 1)
|
|
circuit.append(gate_relative_phase.inverse(), list(range(num_qubits - 1)), [])
|
|
|
|
self.assertTrue(matrix_equal(Operator(circuit).data, Operator(ref_circuit).data))
|
|
|
|
@data(1, 2, 3, 4)
|
|
def test_inverse_x(self, num_ctrl_qubits):
|
|
"""Test inverting the controlled X gate."""
|
|
cnx = XGate().control(num_ctrl_qubits)
|
|
inv_cnx = cnx.inverse()
|
|
result = Operator(cnx).compose(Operator(inv_cnx))
|
|
np.testing.assert_array_almost_equal(result.data, np.identity(result.dim[0]))
|
|
|
|
@data(1, 2, 3)
|
|
def test_inverse_gate(self, num_ctrl_qubits):
|
|
"""Test inverting a controlled gate based on a circuit definition."""
|
|
qc = QuantumCircuit(3)
|
|
qc.h(0)
|
|
qc.cx(0, 1)
|
|
qc.cx(1, 2)
|
|
qc.rx(np.pi / 4, [0, 1, 2])
|
|
gate = qc.to_gate()
|
|
cgate = gate.control(num_ctrl_qubits)
|
|
inv_cgate = cgate.inverse()
|
|
result = Operator(cgate).compose(Operator(inv_cgate))
|
|
np.testing.assert_array_almost_equal(result.data, np.identity(result.dim[0]))
|
|
|
|
@data(1, 2, 3)
|
|
def test_inverse_circuit(self, num_ctrl_qubits):
|
|
"""Test inverting a controlled gate based on a circuit definition."""
|
|
qc = QuantumCircuit(3)
|
|
qc.h(0)
|
|
qc.cx(0, 1)
|
|
qc.cx(1, 2)
|
|
qc.rx(np.pi / 4, [0, 1, 2])
|
|
cqc = qc.control(num_ctrl_qubits)
|
|
cqc_inv = cqc.inverse()
|
|
result = Operator(cqc).compose(Operator(cqc_inv))
|
|
np.testing.assert_array_almost_equal(result.data, np.identity(result.dim[0]))
|
|
|
|
@data(1, 2, 3, 4, 5)
|
|
def test_controlled_unitary(self, num_ctrl_qubits):
|
|
"""Test the matrix data of an Operator, which is based on a controlled gate."""
|
|
num_target = 1
|
|
q_target = QuantumRegister(num_target)
|
|
qc1 = QuantumCircuit(q_target)
|
|
# for h-rx(pi/2)
|
|
theta, phi, lamb = 1.57079632679490, 0.0, 4.71238898038469
|
|
qc1.u(theta, phi, lamb, q_target[0])
|
|
base_gate = qc1.to_gate()
|
|
# get UnitaryGate version of circuit
|
|
base_op = Operator(qc1)
|
|
base_mat = base_op.data
|
|
cgate = base_gate.control(num_ctrl_qubits)
|
|
test_op = Operator(cgate)
|
|
cop_mat = _compute_control_matrix(base_mat, num_ctrl_qubits)
|
|
self.assertTrue(is_unitary_matrix(base_mat))
|
|
self.assertTrue(matrix_equal(cop_mat, test_op.data))
|
|
|
|
@combine(num_ctrl_qubits=(1, 2, 3, 4, 5), num_target=(2, 3))
|
|
def test_controlled_random_unitary(self, num_ctrl_qubits, num_target):
|
|
"""Test the matrix data of an Operator based on a random UnitaryGate."""
|
|
base_gate = random_unitary(2**num_target).to_instruction()
|
|
base_mat = base_gate.to_matrix()
|
|
cgate = base_gate.control(num_ctrl_qubits)
|
|
test_op = Operator(cgate)
|
|
cop_mat = _compute_control_matrix(base_mat, num_ctrl_qubits)
|
|
self.assertTrue(matrix_equal(cop_mat, test_op.data))
|
|
|
|
@combine(num_ctrl_qubits=[1, 2, 3], ctrl_state=[0, None])
|
|
def test_open_controlled_unitary_z(self, num_ctrl_qubits, ctrl_state):
|
|
"""Test that UnitaryGate with control returns params."""
|
|
umat = np.array([[1, 0], [0, -1]])
|
|
ugate = UnitaryGate(umat)
|
|
cugate = ugate.control(num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
ref_mat = _compute_control_matrix(umat, num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
self.assertEqual(Operator(cugate), Operator(ref_mat))
|
|
|
|
def test_controlled_controlled_rz(self):
|
|
"""Test that UnitaryGate with control returns params."""
|
|
qc = QuantumCircuit(1)
|
|
qc.rz(0.2, 0)
|
|
controlled = QuantumCircuit(2)
|
|
controlled.compose(qc.control(), inplace=True)
|
|
self.assertEqual(Operator(controlled), Operator(CRZGate(0.2)))
|
|
self.assertEqual(Operator(controlled), Operator(RZGate(0.2).control()))
|
|
|
|
def test_controlled_controlled_unitary(self):
|
|
"""Test that global phase in iso decomposition of unitary is handled."""
|
|
umat = np.array([[1, 0], [0, -1]])
|
|
ugate = UnitaryGate(umat)
|
|
cugate = ugate.control()
|
|
ccugate = cugate.control()
|
|
ccugate2 = ugate.control(2)
|
|
ref_mat = _compute_control_matrix(umat, 2)
|
|
self.assertTrue(Operator(ccugate2).equiv(Operator(ref_mat)))
|
|
self.assertTrue(Operator(ccugate).equiv(Operator(ccugate2)))
|
|
|
|
@data(1, 2, 3)
|
|
def test_open_controlled_unitary_matrix(self, num_ctrl_qubits):
|
|
"""test open controlled unitary matrix"""
|
|
# verify truth table
|
|
num_target_qubits = 2
|
|
num_qubits = num_ctrl_qubits + num_target_qubits
|
|
target_op = Operator(XGate())
|
|
for i in range(num_target_qubits - 1):
|
|
target_op = target_op.tensor(XGate())
|
|
|
|
for i in range(2**num_qubits):
|
|
input_bitstring = bin(i)[2:].zfill(num_qubits)
|
|
input_target = input_bitstring[0:num_target_qubits]
|
|
input_ctrl = input_bitstring[-num_ctrl_qubits:]
|
|
phi = Statevector.from_label(input_bitstring)
|
|
cop = Operator(
|
|
_compute_control_matrix(target_op.data, num_ctrl_qubits, ctrl_state=input_ctrl)
|
|
)
|
|
for j in range(2**num_qubits):
|
|
output_bitstring = bin(j)[2:].zfill(num_qubits)
|
|
output_target = output_bitstring[0:num_target_qubits]
|
|
output_ctrl = output_bitstring[-num_ctrl_qubits:]
|
|
psi = Statevector.from_label(output_bitstring)
|
|
cxout = np.dot(phi.data, psi.evolve(cop).data)
|
|
if input_ctrl == output_ctrl:
|
|
# flip the target bits
|
|
cond_output = "".join([str(int(not int(a))) for a in input_target])
|
|
else:
|
|
cond_output = input_target
|
|
if cxout == 1:
|
|
self.assertTrue((output_ctrl == input_ctrl) and (output_target == cond_output))
|
|
else:
|
|
self.assertTrue(
|
|
((output_ctrl == input_ctrl) and (output_target != cond_output))
|
|
or output_ctrl != input_ctrl
|
|
)
|
|
|
|
def test_open_control_cx_unrolling(self):
|
|
"""test unrolling of open control gates when gate is in basis"""
|
|
qc = QuantumCircuit(2)
|
|
qc.cx(0, 1, ctrl_state=0)
|
|
ref_circuit = QuantumCircuit(2)
|
|
ref_circuit.append(U3Gate(np.pi, 0, np.pi), [0])
|
|
ref_circuit.cx(0, 1)
|
|
ref_circuit.append(U3Gate(np.pi, 0, np.pi), [0])
|
|
self.assertEqualTranslated(qc, ref_circuit, ["u3", "cx"])
|
|
|
|
def test_open_control_cy_unrolling(self):
|
|
"""test unrolling of open control gates when gate is in basis"""
|
|
qc = QuantumCircuit(2)
|
|
qc.cy(0, 1, ctrl_state=0)
|
|
ref_circuit = QuantumCircuit(2)
|
|
ref_circuit.append(U3Gate(np.pi, 0, np.pi), [0])
|
|
ref_circuit.cy(0, 1)
|
|
ref_circuit.append(U3Gate(np.pi, 0, np.pi), [0])
|
|
self.assertEqualTranslated(qc, ref_circuit, ["u3", "cy"])
|
|
|
|
def test_open_control_ccx_unrolling(self):
|
|
"""test unrolling of open control gates when gate is in basis"""
|
|
qreg = QuantumRegister(3)
|
|
qc = QuantumCircuit(qreg)
|
|
ccx = CCXGate(ctrl_state=0)
|
|
qc.append(ccx, [0, 1, 2])
|
|
# ┌───┐ ┌───┐
|
|
# q0_0: ┤ X ├──■──┤ X ├
|
|
# ├───┤ │ ├───┤
|
|
# q0_1: ┤ X ├──■──┤ X ├
|
|
# └───┘┌─┴─┐└───┘
|
|
# q0_2: ─────┤ X ├─────
|
|
# └───┘
|
|
ref_circuit = QuantumCircuit(qreg)
|
|
ref_circuit.x(qreg[0])
|
|
ref_circuit.x(qreg[1])
|
|
ref_circuit.ccx(qreg[0], qreg[1], qreg[2])
|
|
ref_circuit.x(qreg[0])
|
|
ref_circuit.x(qreg[1])
|
|
self.assertEqualTranslated(qc, ref_circuit, ["x", "ccx"])
|
|
|
|
def test_ccx_ctrl_state_consistency(self):
|
|
"""Test the consistency of parameters ctrl_state in CCX
|
|
See issue: https://github.com/Qiskit/qiskit-terra/issues/6465
|
|
"""
|
|
qreg = QuantumRegister(3)
|
|
qc = QuantumCircuit(qreg)
|
|
qc.ccx(qreg[0], qreg[1], qreg[2], ctrl_state=0)
|
|
|
|
ref_circuit = QuantumCircuit(qreg)
|
|
ccx = CCXGate(ctrl_state=0)
|
|
ref_circuit.append(ccx, [qreg[0], qreg[1], qreg[2]])
|
|
self.assertEqual(qc, ref_circuit)
|
|
|
|
@data((4, [0, 1, 2], 3, "010"), (4, [2, 1, 3], 0, 2))
|
|
@unpack
|
|
def test_multi_control_x_ctrl_state_parameter(
|
|
self, num_qubits, ctrl_qubits, target_qubit, ctrl_state
|
|
):
|
|
"""To check the consistency of parameters ctrl_state in MCX"""
|
|
qc = QuantumCircuit(num_qubits)
|
|
qc.mcx(ctrl_qubits, target_qubit, ctrl_state=ctrl_state)
|
|
operator_qc = Operator(qc)
|
|
|
|
qc1 = QuantumCircuit(num_qubits)
|
|
gate = MCXGate(num_ctrl_qubits=len(ctrl_qubits), ctrl_state=ctrl_state)
|
|
qc1.append(gate, ctrl_qubits + [target_qubit])
|
|
operator_qc1 = Operator(qc1)
|
|
|
|
self.assertEqual(operator_qc, operator_qc1)
|
|
|
|
@data((4, 0.2, [0, 1, 2], 3, "010"), (4, 0.6, [2, 1, 3], 0, 0))
|
|
@unpack
|
|
def test_multi_control_p_ctrl_state_parameter(
|
|
self, num_qubits, lam, ctrl_qubits, target_qubit, ctrl_state
|
|
):
|
|
"""To check the consistency of parameters ctrl_state in MCP"""
|
|
qc = QuantumCircuit(num_qubits)
|
|
qc.mcp(lam, ctrl_qubits, target_qubit, ctrl_state=ctrl_state)
|
|
operator_qc = Operator(qc)
|
|
|
|
qc1 = QuantumCircuit(num_qubits)
|
|
gate = MCPhaseGate(lam, num_ctrl_qubits=len(ctrl_qubits), ctrl_state=ctrl_state)
|
|
qc1.append(gate, ctrl_qubits + [target_qubit])
|
|
operator_qc1 = Operator(qc1)
|
|
|
|
self.assertEqual(operator_qc, operator_qc1)
|
|
|
|
@data((4, 0.2, [0, 1, 2], 3, "000"), (3, 0.6, [0, 1], 2, 1))
|
|
@unpack
|
|
def test_open_control_mcphase_ctrl_state_parameter(
|
|
self, num_qubits, lam, ctrl_qubits, target_qubit, ctrl_state
|
|
):
|
|
"""To check the consistency of parameters ctrl_state in MCPhaseGate"""
|
|
qc = QuantumCircuit(num_qubits)
|
|
num_controls = len(ctrl_qubits)
|
|
qc.mcp(lam, ctrl_qubits, target_qubit, ctrl_state=ctrl_state)
|
|
|
|
# obtain unitary for circuit
|
|
simulated = Operator(qc).data
|
|
simulated = simulated[: 2 ** (num_controls + 1), : 2 ** (num_controls + 1)]
|
|
base = PhaseGate(lam).to_matrix()
|
|
expected = _compute_control_matrix(base, num_controls, ctrl_state=ctrl_state)
|
|
|
|
self.assertTrue(matrix_equal(simulated, expected))
|
|
|
|
def test_open_control_composite_unrolling(self):
|
|
"""test unrolling of open control gates when gate is in basis"""
|
|
# create composite gate
|
|
qreg = QuantumRegister(2)
|
|
qcomp = QuantumCircuit(qreg, name="bell")
|
|
qcomp.h(qreg[0])
|
|
qcomp.cx(qreg[0], qreg[1])
|
|
bell = qcomp.to_gate()
|
|
# create controlled composite gate
|
|
cqreg = QuantumRegister(3)
|
|
qc = QuantumCircuit(cqreg)
|
|
qc.append(bell.control(ctrl_state=0), qc.qregs[0][:])
|
|
# create reference circuit
|
|
ref_circuit = QuantumCircuit(cqreg)
|
|
ref_circuit.x(cqreg[0])
|
|
ref_circuit.append(bell.control(), [cqreg[0], cqreg[1], cqreg[2]])
|
|
ref_circuit.x(cqreg[0])
|
|
self.assertEqualTranslated(qc, ref_circuit, ["x", "u1", "cbell"])
|
|
|
|
@data(*ControlledGate.__subclasses__())
|
|
def test_standard_base_gate_setting(self, gate_class):
|
|
"""Test all standard gates which are of type ControlledGate
|
|
and have a base gate setting.
|
|
"""
|
|
if gate_class in {SingletonControlledGate, _SingletonControlledGateOverrides}:
|
|
self.skipTest("SingletonControlledGate isn't directly instantiated.")
|
|
|
|
if gate_class is MCMTGate:
|
|
# parameters are (base_gate, num_controls, num_targets)
|
|
free_params = [HGate(), 3, 2]
|
|
else:
|
|
gate_params = _get_free_params(gate_class.__init__, ignore=["self"])
|
|
num_free_params = len(gate_params)
|
|
free_params = [0.1 * i for i in range(num_free_params)]
|
|
# set number of control qubits
|
|
for i in range(num_free_params):
|
|
if gate_params[i] == "num_ctrl_qubits":
|
|
free_params[i] = 3
|
|
|
|
base_gate = gate_class(*free_params)
|
|
cgate = base_gate.control()
|
|
|
|
# the base gate of CU is U (3 params), the base gate of CCU is CU (4 params)
|
|
if gate_class == CUGate:
|
|
self.assertListEqual(cgate.base_gate.params[:3], base_gate.base_gate.params[:3])
|
|
else:
|
|
self.assertEqual(base_gate.base_gate, cgate.base_gate)
|
|
|
|
@combine(
|
|
gate=[cls for cls in allGates.__dict__.values() if isinstance(cls, type)],
|
|
num_ctrl_qubits=[1, 2],
|
|
ctrl_state=[None, 0, 1],
|
|
)
|
|
def test_all_inverses(self, gate, num_ctrl_qubits, ctrl_state):
|
|
"""Test all standard gates except those that cannot be controlled."""
|
|
if not (issubclass(gate, ControlledGate) or issubclass(gate, allGates.IGate)):
|
|
# only verify basic gates right now, as already controlled ones
|
|
# will generate differing definitions
|
|
try:
|
|
numargs = len(_get_free_params(gate))
|
|
args = [2] * numargs
|
|
gate = gate(*args)
|
|
|
|
self.assertEqual(
|
|
gate.inverse().control(num_ctrl_qubits, ctrl_state=ctrl_state),
|
|
gate.control(num_ctrl_qubits, ctrl_state=ctrl_state).inverse(),
|
|
)
|
|
|
|
except AttributeError:
|
|
# skip gates that do not have a control attribute (e.g. barrier)
|
|
pass
|
|
|
|
@data(2, 3)
|
|
def test_relative_phase_toffoli_gates(self, num_ctrl_qubits):
|
|
"""Test the relative phase Toffoli gates.
|
|
|
|
This test compares the matrix representation of the relative phase gate classes
|
|
(i.e. RCCXGate().to_matrix()), the matrix obtained from the unitary simulator,
|
|
and the exact version of the gate as obtained through `_compute_control_matrix`.
|
|
"""
|
|
# get target matrix (w/o relative phase)
|
|
base_mat = XGate().to_matrix()
|
|
target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits)
|
|
|
|
# build the matrix for the relative phase toffoli using the unitary simulator
|
|
circuit = QuantumCircuit(num_ctrl_qubits + 1)
|
|
if num_ctrl_qubits == 2:
|
|
circuit.rccx(0, 1, 2)
|
|
else: # num_ctrl_qubits == 3:
|
|
circuit.rcccx(0, 1, 2, 3)
|
|
|
|
simulated_mat = Operator(circuit)
|
|
|
|
# get the matrix representation from the class itself
|
|
if num_ctrl_qubits == 2:
|
|
repr_mat = RCCXGate().to_matrix()
|
|
else: # num_ctrl_qubits == 3:
|
|
repr_mat = RC3XGate().to_matrix()
|
|
|
|
# test up to phase
|
|
# note, that all entries may have an individual phase! (as opposed to a global phase)
|
|
self.assertTrue(matrix_equal(np.abs(simulated_mat), target_mat))
|
|
|
|
# compare simulated matrix with the matrix representation provided by the class
|
|
self.assertTrue(matrix_equal(simulated_mat, repr_mat))
|
|
|
|
def test_open_controlled_gate(self):
|
|
"""
|
|
Test controlled gates with control on '0'
|
|
"""
|
|
base_gate = XGate()
|
|
base_mat = base_gate.to_matrix()
|
|
num_ctrl_qubits = 3
|
|
|
|
ctrl_state = 5
|
|
cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
self.assertEqual(Operator(cgate), Operator(target_mat))
|
|
|
|
ctrl_state = None
|
|
cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
self.assertEqual(Operator(cgate), Operator(target_mat))
|
|
|
|
ctrl_state = 0
|
|
cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
self.assertEqual(Operator(cgate), Operator(target_mat))
|
|
|
|
ctrl_state = 7
|
|
cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
self.assertEqual(Operator(cgate), Operator(target_mat))
|
|
|
|
ctrl_state = "110"
|
|
cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
self.assertEqual(Operator(cgate), Operator(target_mat))
|
|
|
|
def test_open_controlled_gate_raises(self):
|
|
"""
|
|
Test controlled gates with open controls raises if ctrl_state isn't allowed.
|
|
"""
|
|
base_gate = XGate()
|
|
num_ctrl_qubits = 3
|
|
with self.assertRaises(CircuitError):
|
|
base_gate.control(num_ctrl_qubits, ctrl_state=-1)
|
|
with self.assertRaises(CircuitError):
|
|
base_gate.control(num_ctrl_qubits, ctrl_state=2**num_ctrl_qubits)
|
|
with self.assertRaises(CircuitError):
|
|
base_gate.control(num_ctrl_qubits, ctrl_state="201")
|
|
|
|
def test_base_gate_params_reference(self):
|
|
"""
|
|
Test all standard gates which are of type ControlledGate and have a base gate
|
|
setting have params which reference the one in their base gate.
|
|
"""
|
|
num_ctrl_qubits = 1
|
|
for gate_class in ControlledGate.__subclasses__():
|
|
with self.subTest(i=repr(gate_class)):
|
|
if gate_class in {SingletonControlledGate, _SingletonControlledGateOverrides}:
|
|
self.skipTest("Singleton class isn't intended to be created directly.")
|
|
gate_params = _get_free_params(gate_class.__init__, ignore=["self"])
|
|
num_free_params = len(gate_params)
|
|
free_params = [0.1 * i for i in range(num_free_params)]
|
|
# set number of control qubits
|
|
for i in range(num_free_params):
|
|
if gate_params[i] == "num_ctrl_qubits":
|
|
free_params[i] = 3
|
|
|
|
base_gate = gate_class(*free_params)
|
|
if base_gate.params:
|
|
cgate = base_gate.control(num_ctrl_qubits)
|
|
self.assertIs(cgate.base_gate.params, cgate.params)
|
|
|
|
def test_assign_parameters(self):
|
|
"""Test assigning parameters to quantum circuit with controlled gate."""
|
|
qc = QuantumCircuit(2, name="assign")
|
|
ptest = Parameter("p")
|
|
gate = CRYGate(ptest)
|
|
qc.append(gate, [0, 1])
|
|
|
|
subs1, subs2 = {ptest: Parameter("a")}, {ptest: Parameter("b")}
|
|
bound1 = qc.assign_parameters(subs1, inplace=False)
|
|
bound2 = qc.assign_parameters(subs2, inplace=False)
|
|
|
|
self.assertEqual(qc.parameters, {ptest})
|
|
self.assertEqual(bound1.parameters, {subs1[ptest]})
|
|
self.assertEqual(bound2.parameters, {subs2[ptest]})
|
|
|
|
def test_assign_cugate(self):
|
|
"""Test assignment of CUGate, which breaks the `ControlledGate` requirements by not being
|
|
equivalent to a direct control of its base gate."""
|
|
|
|
parameters = [Parameter("t"), Parameter("p"), Parameter("l"), Parameter("g")]
|
|
values = [0.1, 0.2, 0.3, 0.4]
|
|
|
|
qc = QuantumCircuit(2)
|
|
qc.cu(*parameters, 0, 1)
|
|
assigned = qc.assign_parameters(dict(zip(parameters, values)), inplace=False)
|
|
|
|
expected = QuantumCircuit(2)
|
|
expected.cu(*values, 0, 1)
|
|
|
|
self.assertEqual(assigned.data[0].operation.base_gate, expected.data[0].operation.base_gate)
|
|
self.assertEqual(assigned, expected)
|
|
|
|
def test_modify_cugate_params_slice(self):
|
|
"""Test that CUGate.params can be modified by a standard slice (without changing the number
|
|
of elements) and changes propagate to the base gate. This is only needed for as long as
|
|
CUGate's `base_gate` is `UGate`, which has the "wrong" number of parameters."""
|
|
cu = CUGate(0.1, 0.2, 0.3, 0.4)
|
|
self.assertEqual(cu.params, [0.1, 0.2, 0.3, 0.4])
|
|
self.assertEqual(cu.base_gate.params, [0.1, 0.2, 0.3])
|
|
|
|
cu.params[0:4] = [0.5, 0.4, 0.3, 0.2]
|
|
self.assertEqual(cu.params, [0.5, 0.4, 0.3, 0.2])
|
|
self.assertEqual(cu.base_gate.params, [0.5, 0.4, 0.3])
|
|
|
|
cu.params[:] = [0.1, 0.2, 0.3, 0.4]
|
|
self.assertEqual(cu.params, [0.1, 0.2, 0.3, 0.4])
|
|
self.assertEqual(cu.base_gate.params, [0.1, 0.2, 0.3])
|
|
|
|
cu.params[:3] = [0.5, 0.4, 0.3]
|
|
self.assertEqual(cu.params, [0.5, 0.4, 0.3, 0.4])
|
|
self.assertEqual(cu.base_gate.params, [0.5, 0.4, 0.3])
|
|
|
|
# indices (3, 2, 1, 0), note that the assignment is in reverse.
|
|
cu.params[-1::-1] = [0.1, 0.2, 0.3, 0.4]
|
|
self.assertEqual(cu.params, [0.4, 0.3, 0.2, 0.1])
|
|
self.assertEqual(cu.base_gate.params, [0.4, 0.3, 0.2])
|
|
|
|
def test_assign_nested_controlled_cu(self):
|
|
"""Test assignment of an arbitrary controlled parametrized gate that appears through the
|
|
`Gate.control()` method on an already-controlled gate."""
|
|
theta = Parameter("t")
|
|
qc_c = QuantumCircuit(2)
|
|
qc_c.crx(theta, 1, 0)
|
|
custom_gate = qc_c.to_gate().control()
|
|
qc = QuantumCircuit(3)
|
|
qc.append(custom_gate, [0, 1, 2])
|
|
assigned = qc.assign_parameters({theta: 0.5})
|
|
|
|
# We're testing here that everything's been propagated through to the base gates; the `reps`
|
|
# is just some high number to make sure we unwrap any controlled and custom gates.
|
|
self.assertEqual(set(assigned.decompose(reps=3).parameters), set())
|
|
|
|
@data(-1, 0, 1.4, "1", 4, 10)
|
|
def test_improper_num_ctrl_qubits(self, num_ctrl_qubits):
|
|
"""
|
|
Test improperly specified num_ctrl_qubits.
|
|
"""
|
|
num_qubits = 4
|
|
with self.assertRaises(CircuitError):
|
|
ControlledGate(
|
|
name="cgate", num_qubits=num_qubits, params=[], num_ctrl_qubits=num_ctrl_qubits
|
|
)
|
|
|
|
def test_improper_num_ctrl_qubits_base_gate(self):
|
|
"""Test that the allowed number of control qubits takes the base gate into account."""
|
|
with self.assertRaises(CircuitError):
|
|
ControlledGate(
|
|
name="cx?", num_qubits=2, params=[], num_ctrl_qubits=2, base_gate=XGate()
|
|
)
|
|
self.assertIsInstance(
|
|
ControlledGate(
|
|
name="cx?", num_qubits=2, params=[], num_ctrl_qubits=1, base_gate=XGate()
|
|
),
|
|
ControlledGate,
|
|
)
|
|
self.assertIsInstance(
|
|
ControlledGate(
|
|
name="p",
|
|
num_qubits=1,
|
|
params=[np.pi],
|
|
num_ctrl_qubits=1,
|
|
base_gate=Gate("gphase", 0, [np.pi]),
|
|
),
|
|
ControlledGate,
|
|
)
|
|
|
|
def test_open_controlled_equality(self):
|
|
"""
|
|
Test open controlled gates are equal if their base gates and control states are equal.
|
|
"""
|
|
|
|
self.assertEqual(XGate().control(1), XGate().control(1))
|
|
|
|
self.assertNotEqual(XGate().control(1), YGate().control(1))
|
|
|
|
self.assertNotEqual(XGate().control(1), XGate().control(2))
|
|
|
|
self.assertEqual(XGate().control(1, ctrl_state="0"), XGate().control(1, ctrl_state="0"))
|
|
|
|
self.assertNotEqual(XGate().control(1, ctrl_state="0"), XGate().control(1, ctrl_state="1"))
|
|
|
|
def test_cx_global_phase(self):
|
|
"""
|
|
Test controlling CX with global phase
|
|
"""
|
|
theta = pi / 2
|
|
circ = QuantumCircuit(2, global_phase=theta)
|
|
circ.cx(0, 1)
|
|
cx = circ.to_gate()
|
|
self.assertNotEqual(Operator(CXGate()), Operator(cx))
|
|
|
|
ccx = cx.control(1)
|
|
base_mat = Operator(cx).data
|
|
target = _compute_control_matrix(base_mat, 1)
|
|
self.assertEqual(Operator(ccx), Operator(target))
|
|
|
|
expected = QuantumCircuit(*ccx.definition.qregs)
|
|
expected.ccx(0, 1, 2)
|
|
expected.p(theta, 0)
|
|
self.assertEqual(ccx.definition, expected)
|
|
|
|
@data(1, 2)
|
|
def test_controlled_global_phase(self, num_ctrl_qubits):
|
|
"""
|
|
Test controlled global phase on base gate.
|
|
"""
|
|
theta = pi / 4
|
|
circ = QuantumCircuit(2, global_phase=theta)
|
|
base_gate = circ.to_gate()
|
|
base_mat = Operator(base_gate).data
|
|
target = _compute_control_matrix(base_mat, num_ctrl_qubits)
|
|
cgate = base_gate.control(num_ctrl_qubits)
|
|
ccirc = circ.control(num_ctrl_qubits)
|
|
self.assertEqual(Operator(cgate), Operator(target))
|
|
self.assertEqual(Operator(ccirc), Operator(target))
|
|
|
|
@data(1, 2)
|
|
def test_rz_composite_global_phase(self, num_ctrl_qubits):
|
|
"""
|
|
Test controlling CX with global phase
|
|
"""
|
|
theta = pi / 4
|
|
circ = QuantumCircuit(2, global_phase=theta)
|
|
circ.rz(0.1, 0)
|
|
circ.rz(0.2, 1)
|
|
ccirc = circ.control(num_ctrl_qubits)
|
|
base_gate = circ.to_gate()
|
|
cgate = base_gate.control(num_ctrl_qubits)
|
|
base_mat = Operator(base_gate).data
|
|
target = _compute_control_matrix(base_mat, num_ctrl_qubits)
|
|
self.assertEqual(Operator(cgate), Operator(target))
|
|
self.assertEqual(Operator(ccirc), Operator(target))
|
|
|
|
@data(1, 2)
|
|
def test_nested_global_phase(self, num_ctrl_qubits):
|
|
"""
|
|
Test controlling a gate with nested global phase.
|
|
"""
|
|
theta = pi / 4
|
|
circ = QuantumCircuit(1, global_phase=theta)
|
|
circ.z(0)
|
|
v = circ.to_gate()
|
|
|
|
qc = QuantumCircuit(1)
|
|
qc.append(v, [0])
|
|
ctrl_qc = qc.control(num_ctrl_qubits)
|
|
|
|
base_mat = Operator(qc).data
|
|
target = _compute_control_matrix(base_mat, num_ctrl_qubits)
|
|
self.assertEqual(Operator(ctrl_qc), Operator(target))
|
|
|
|
@data(1, 2)
|
|
def test_control_zero_operand_gate(self, num_ctrl_qubits):
|
|
"""Test that a zero-operand gate (such as a make-shift global-phase gate) can be
|
|
controlled."""
|
|
gate = QuantumCircuit(global_phase=np.pi).to_gate()
|
|
controlled = gate.control(num_ctrl_qubits)
|
|
self.assertIsInstance(controlled, ControlledGate)
|
|
self.assertEqual(controlled.num_ctrl_qubits, num_ctrl_qubits)
|
|
self.assertEqual(controlled.num_qubits, num_ctrl_qubits)
|
|
target = np.eye(2**num_ctrl_qubits, dtype=np.complex128)
|
|
target.flat[-1] = -1
|
|
self.assertEqual(Operator(controlled), Operator(target))
|
|
|
|
@data(
|
|
RXGate,
|
|
RYGate,
|
|
RXXGate,
|
|
RYYGate,
|
|
RZXGate,
|
|
RZZGate,
|
|
UGate,
|
|
U3Gate,
|
|
XXMinusYYGate,
|
|
XXPlusYYGate,
|
|
)
|
|
def test_mc_failure_without_annotation(self, gate_cls):
|
|
"""Test error for gates that cannot be multi-controlled without annotation."""
|
|
theta = Parameter("theta")
|
|
num_params = len(_get_free_params(gate_cls.__init__, ignore=["self"]))
|
|
params = [theta] + (num_params - 1) * [1.234]
|
|
|
|
for annotated in [False, None]:
|
|
with self.subTest(annotated=annotated):
|
|
# if annotated is False, check that a sensible error is raised
|
|
if annotated is False:
|
|
with self.assertRaisesRegex(QiskitError, "unbound parameter"):
|
|
_ = gate_cls(*params).control(5, annotated=False)
|
|
|
|
# else, check that the gate can be synthesized after all parameters
|
|
# have been bound
|
|
else:
|
|
mc_gate = gate_cls(*params).control(5)
|
|
|
|
circuit = QuantumCircuit(mc_gate.num_qubits)
|
|
circuit.append(mc_gate, circuit.qubits)
|
|
|
|
bound = circuit.assign_parameters([0.5123])
|
|
unrolled = transpile(bound, basis_gates=["u", "cx"], optimization_level=0)
|
|
|
|
self.assertEqual(unrolled.num_parameters, 0)
|
|
|
|
def assertEqualTranslated(self, circuit, unrolled_reference, basis):
|
|
"""Assert that the circuit is equal to the unrolled reference circuit."""
|
|
unroller = UnrollCustomDefinitions(std_eqlib, basis)
|
|
basis_translator = BasisTranslator(std_eqlib, basis)
|
|
unrolled = basis_translator(unroller(circuit))
|
|
self.assertEqual(unrolled, unrolled_reference)
|
|
|
|
|
|
@ddt
|
|
class TestOpenControlledToMatrix(QiskitTestCase):
|
|
"""Test controlled_gates implementing to_matrix work with ctrl_state"""
|
|
|
|
@combine(gate_class=ControlledGate.__subclasses__(), ctrl_state=[0, None])
|
|
def test_open_controlled_to_matrix(self, gate_class, ctrl_state):
|
|
"""Test open controlled to_matrix."""
|
|
if gate_class in {SingletonControlledGate, _SingletonControlledGateOverrides}:
|
|
self.skipTest("SingletonGateClass isn't intended for direct initalization")
|
|
|
|
if gate_class is MCMTGate:
|
|
# parameters are (base_gate, num_controls, num_targets)
|
|
free_params = [HGate(), 3, 2]
|
|
else:
|
|
gate_params = _get_free_params(gate_class.__init__, ignore=["self"])
|
|
num_free_params = len(gate_params)
|
|
free_params = [0.1 * i for i in range(1, num_free_params + 1)]
|
|
# set number of control qubits
|
|
for i in range(num_free_params):
|
|
if gate_params[i] == "num_ctrl_qubits":
|
|
free_params[i] = 3
|
|
|
|
cgate = gate_class(*free_params)
|
|
cgate.ctrl_state = ctrl_state
|
|
|
|
base_mat = Operator(cgate.base_gate).data
|
|
if gate_class == CUGate: # account for global phase
|
|
base_mat = np.array(base_mat) * np.exp(1j * cgate.params[3])
|
|
|
|
target = _compute_control_matrix(base_mat, cgate.num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
try:
|
|
actual = cgate.to_matrix()
|
|
except CircuitError as cerr:
|
|
self.skipTest(str(cerr))
|
|
self.assertTrue(np.allclose(actual, target))
|
|
|
|
|
|
@ddt
|
|
class TestSingleControlledRotationGates(QiskitTestCase):
|
|
"""Test the controlled rotation gates controlled on one qubit."""
|
|
|
|
from qiskit.circuit.library.standard_gates import u1, rx, ry, rz
|
|
|
|
num_ctrl = 2
|
|
num_target = 1
|
|
|
|
theta = pi / 2
|
|
gu1 = u1.U1Gate(theta)
|
|
grx = rx.RXGate(theta)
|
|
gry = ry.RYGate(theta)
|
|
grz = rz.RZGate(theta)
|
|
|
|
ugu1 = ac._unroll_gate(gu1, ["p", "u", "cx"])
|
|
ugrx = ac._unroll_gate(grx, ["p", "u", "cx"])
|
|
ugry = ac._unroll_gate(gry, ["p", "u", "cx"])
|
|
ugrz = ac._unroll_gate(grz, ["p", "u", "cx"])
|
|
ugrz.params = grz.params
|
|
|
|
cgu1 = ugu1.control(num_ctrl)
|
|
cgrx = ugrx.control(num_ctrl)
|
|
cgry = ugry.control(num_ctrl)
|
|
cgrz = ugrz.control(num_ctrl)
|
|
|
|
@data((gu1, cgu1), (grx, cgrx), (gry, cgry), (grz, cgrz))
|
|
@unpack
|
|
def test_single_controlled_rotation_gates(self, gate, cgate):
|
|
"""Test the controlled rotation gates controlled on one qubit."""
|
|
if gate.name == "rz":
|
|
iden = Operator.from_label("I")
|
|
zgen = Operator.from_label("Z")
|
|
op_mat = (np.cos(0.5 * self.theta) * iden - 1j * np.sin(0.5 * self.theta) * zgen).data
|
|
else:
|
|
op_mat = Operator(gate).data
|
|
ref_mat = Operator(cgate).data
|
|
cop_mat = _compute_control_matrix(op_mat, self.num_ctrl)
|
|
self.assertTrue(matrix_equal(cop_mat, ref_mat))
|
|
cqc = QuantumCircuit(self.num_ctrl + self.num_target)
|
|
cqc.append(cgate, cqc.qregs[0])
|
|
dag = circuit_to_dag(cqc)
|
|
unroller = UnrollCustomDefinitions(std_eqlib, ["u", "cx"])
|
|
basis_translator = BasisTranslator(std_eqlib, ["u", "cx"])
|
|
uqc = dag_to_circuit(basis_translator.run(unroller.run(dag)))
|
|
self.log.info("%s gate count: %d", cgate.name, uqc.size())
|
|
self.log.info("\n%s", str(uqc))
|
|
# these limits could be changed
|
|
if gate.name == "ry":
|
|
self.assertLessEqual(uqc.size(), 32, f"\n{uqc}")
|
|
elif gate.name == "rz":
|
|
self.assertLessEqual(uqc.size(), 43, f"\n{uqc}")
|
|
else:
|
|
self.assertLessEqual(uqc.size(), 20, f"\n{uqc}")
|
|
|
|
def test_composite(self):
|
|
"""Test composite gate count."""
|
|
qreg = QuantumRegister(self.num_ctrl + self.num_target)
|
|
qc = QuantumCircuit(qreg, name="composite")
|
|
qc.append(self.grx.control(self.num_ctrl), qreg)
|
|
qc.append(self.gry.control(self.num_ctrl), qreg)
|
|
qc.append(self.gry, qreg[0 : self.gry.num_qubits])
|
|
qc.append(self.grz.control(self.num_ctrl), qreg)
|
|
|
|
dag = circuit_to_dag(qc)
|
|
unroller = UnrollCustomDefinitions(std_eqlib, ["u", "cx"])
|
|
basis_translator = BasisTranslator(std_eqlib, ["u", "cx"])
|
|
uqc = dag_to_circuit(basis_translator.run(unroller.run(dag)))
|
|
self.log.info("%s gate count: %d", uqc.name, uqc.size())
|
|
self.assertLessEqual(uqc.size(), 96, f"\n{uqc}") # this limit could be changed
|
|
|
|
|
|
@ddt
|
|
class TestControlledStandardGates(QiskitTestCase):
|
|
"""Tests for control standard gates."""
|
|
|
|
@combine(
|
|
num_ctrl_qubits=[1, 2, 3],
|
|
gate_class=[cls for cls in allGates.__dict__.values() if isinstance(cls, type)],
|
|
)
|
|
def test_controlled_standard_gates(self, num_ctrl_qubits, gate_class):
|
|
"""Test controlled versions of all standard gates."""
|
|
theta = pi / 2
|
|
ctrl_state_ones = 2**num_ctrl_qubits - 1
|
|
ctrl_state_zeros = 0
|
|
ctrl_state_mixed = ctrl_state_ones >> int(num_ctrl_qubits / 2)
|
|
|
|
gate_params = _get_free_params(gate_class)
|
|
numargs = len(gate_params)
|
|
args = [theta] * numargs
|
|
if gate_class in [MSGate, Barrier]:
|
|
args[0] = 2
|
|
elif gate_class in [MCU1Gate, MCPhaseGate]:
|
|
args[1] = 2
|
|
elif issubclass(gate_class, MCXGate):
|
|
args = [5]
|
|
else:
|
|
# set number of control qubits
|
|
for i in range(numargs):
|
|
if gate_params[i] == "num_ctrl_qubits":
|
|
args[i] = 2
|
|
|
|
gate = gate_class(*args)
|
|
|
|
for ctrl_state in (ctrl_state_ones, ctrl_state_zeros, ctrl_state_mixed):
|
|
with self.subTest(i=f"{gate_class.__name__}, ctrl_state={ctrl_state}"):
|
|
if hasattr(gate, "num_ancilla_qubits") and gate.num_ancilla_qubits > 0:
|
|
# skip matrices that include ancilla qubits
|
|
continue
|
|
try:
|
|
cgate = gate.control(num_ctrl_qubits, ctrl_state=ctrl_state)
|
|
except (AttributeError, QiskitError):
|
|
# 'object has no attribute "control"'
|
|
# skipping Id and Barrier
|
|
continue
|
|
base_mat = Operator(gate).data
|
|
|
|
target_mat = _compute_control_matrix(
|
|
base_mat, num_ctrl_qubits, ctrl_state=ctrl_state
|
|
)
|
|
self.assertEqual(Operator(cgate), Operator(target_mat))
|
|
|
|
|
|
@ddt
|
|
class TestParameterCtrlState(QiskitTestCase):
|
|
"""Test gate equality with ctrl_state parameter."""
|
|
|
|
@data(
|
|
(RXGate(0.5), CRXGate(0.5)),
|
|
(RYGate(0.5), CRYGate(0.5)),
|
|
(RZGate(0.5), CRZGate(0.5)),
|
|
(XGate(), CXGate()),
|
|
(YGate(), CYGate()),
|
|
(ZGate(), CZGate()),
|
|
(U1Gate(0.5), CU1Gate(0.5)),
|
|
(PhaseGate(0.5), CPhaseGate(0.5)),
|
|
(SwapGate(), CSwapGate()),
|
|
(HGate(), CHGate()),
|
|
(U3Gate(0.1, 0.2, 0.3), CU3Gate(0.1, 0.2, 0.3)),
|
|
(UGate(0.1, 0.2, 0.3), CUGate(0.1, 0.2, 0.3, 0)),
|
|
)
|
|
@unpack
|
|
def test_ctrl_state_one(self, gate, controlled_gate):
|
|
"""Test controlled gates with ctrl_state
|
|
See https://github.com/Qiskit/qiskit-terra/pull/4025
|
|
"""
|
|
self.assertEqual(
|
|
Operator(gate.control(1, ctrl_state="1")), Operator(controlled_gate.to_matrix())
|
|
)
|
|
|
|
|
|
@ddt
|
|
class TestControlledGateLabel(QiskitTestCase):
|
|
"""Tests for controlled gate labels."""
|
|
|
|
gates_and_args = [
|
|
(XGate, []),
|
|
(YGate, []),
|
|
(ZGate, []),
|
|
(HGate, []),
|
|
(CXGate, []),
|
|
(CCXGate, []),
|
|
(C3XGate, []),
|
|
(C3SXGate, []),
|
|
(C4XGate, []),
|
|
(MCXGate, [5]),
|
|
(PhaseGate, [0.1]),
|
|
(U1Gate, [0.1]),
|
|
(CYGate, []),
|
|
(CZGate, []),
|
|
(CPhaseGate, [0.1]),
|
|
(CU1Gate, [0.1]),
|
|
(SwapGate, []),
|
|
(SXGate, []),
|
|
(CSXGate, []),
|
|
(CCXGate, []),
|
|
(RZGate, [0.1]),
|
|
(RXGate, [0.1]),
|
|
(RYGate, [0.1]),
|
|
(CRYGate, [0.1]),
|
|
(CRXGate, [0.1]),
|
|
(CSwapGate, []),
|
|
(UGate, [0.1, 0.2, 0.3]),
|
|
(U3Gate, [0.1, 0.2, 0.3]),
|
|
(CHGate, []),
|
|
(CRZGate, [0.1]),
|
|
(CUGate, [0.1, 0.2, 0.3, 0.4]),
|
|
(CU3Gate, [0.1, 0.2, 0.3]),
|
|
(MSGate, [5, 0.1]),
|
|
(RCCXGate, []),
|
|
(RC3XGate, []),
|
|
(MCU1Gate, [0.1, 1]),
|
|
(MCXGate, [5]),
|
|
]
|
|
|
|
@data(*gates_and_args)
|
|
@unpack
|
|
def test_control_label(self, gate, args):
|
|
"""Test gate(label=...).control(label=...)"""
|
|
cgate = gate(*args, label="a gate").control(label="a controlled gate")
|
|
self.assertEqual(cgate.label, "a controlled gate")
|
|
self.assertEqual(cgate.base_gate.label, "a gate")
|
|
|
|
@data(*gates_and_args)
|
|
@unpack
|
|
def test_control_label_1(self, gate, args):
|
|
"""Test gate(label=...).control(1, label=...)"""
|
|
cgate = gate(*args, label="a gate").control(1, label="a controlled gate")
|
|
self.assertEqual(cgate.label, "a controlled gate")
|
|
self.assertEqual(cgate.base_gate.label, "a gate")
|
|
|
|
|
|
@ddt
|
|
class TestControlledAnnotatedGate(QiskitTestCase):
|
|
"""Tests for controlled gates and the AnnotatedOperation class."""
|
|
|
|
def test_controlled_x(self):
|
|
"""Test creation of controlled x gate"""
|
|
controlled = XGate().control(annotated=False)
|
|
annotated = XGate().control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_y(self):
|
|
"""Test creation of controlled y gate"""
|
|
controlled = YGate().control(annotated=False)
|
|
annotated = YGate().control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_z(self):
|
|
"""Test creation of controlled z gate"""
|
|
controlled = ZGate().control(annotated=False)
|
|
annotated = ZGate().control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_h(self):
|
|
"""Test the creation of a controlled H gate."""
|
|
controlled = HGate().control(annotated=False)
|
|
annotated = HGate().control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_phase(self):
|
|
"""Test the creation of a controlled U1 gate."""
|
|
theta = 0.5
|
|
controlled = PhaseGate(theta).control(annotated=False)
|
|
annotated = PhaseGate(theta).control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_u1(self):
|
|
"""Test the creation of a controlled U1 gate."""
|
|
theta = 0.5
|
|
controlled = U1Gate(theta).control(annotated=False)
|
|
annotated = U1Gate(theta).control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_rz(self):
|
|
"""Test the creation of a controlled RZ gate."""
|
|
theta = 0.5
|
|
controlled = RZGate(theta).control(annotated=False)
|
|
annotated = RZGate(theta).control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_ry(self):
|
|
"""Test the creation of a controlled RY gate."""
|
|
theta = 0.5
|
|
controlled = RYGate(theta).control(annotated=False)
|
|
annotated = RYGate(theta).control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_rx(self):
|
|
"""Test the creation of a controlled RX gate."""
|
|
theta = 0.5
|
|
controlled = RXGate(theta).control(annotated=False)
|
|
annotated = RXGate(theta).control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_u(self):
|
|
"""Test the creation of a controlled U gate."""
|
|
theta, phi, lamb = 0.1, 0.2, 0.3
|
|
controlled = UGate(theta, phi, lamb).control(annotated=False)
|
|
annotated = UGate(theta, phi, lamb).control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_u3(self):
|
|
"""Test the creation of a controlled U3 gate."""
|
|
theta, phi, lamb = 0.1, 0.2, 0.3
|
|
controlled = U3Gate(theta, phi, lamb).control(annotated=False)
|
|
annotated = U3Gate(theta, phi, lamb).control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_cx(self):
|
|
"""Test creation of controlled cx gate"""
|
|
controlled = CXGate().control(annotated=False)
|
|
annotated = CXGate().control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_swap(self):
|
|
"""Test creation of controlled swap gate"""
|
|
controlled = SwapGate().control(annotated=False)
|
|
annotated = SwapGate().control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
def test_controlled_sx(self):
|
|
"""Test creation of controlled SX gate"""
|
|
controlled = SXGate().control(annotated=False)
|
|
annotated = SXGate().control(annotated=True)
|
|
self.assertNotIsInstance(controlled, AnnotatedOperation)
|
|
self.assertIsInstance(annotated, AnnotatedOperation)
|
|
self.assertEqual(Operator(controlled), Operator(annotated))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|