Skip to content

Commit

Permalink
controlled gates label awareness
Browse files Browse the repository at this point in the history
  • Loading branch information
Luciano Bello committed Apr 25, 2020
1 parent 93c27fa commit 7f3db35
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 55 deletions.
5 changes: 3 additions & 2 deletions qiskit/circuit/add_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ def add_control(operation: Union[Gate, ControlledGate],
if isinstance(operation, UnitaryGate):
# attempt decomposition
operation._define()
return control(operation, num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
cgate = control(operation, num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
cgate.base_gate.label = operation.label
return cgate


def control(operation: Union[Gate, ControlledGate],
Expand Down
4 changes: 3 additions & 1 deletion qiskit/extensions/standard/h.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
return CHGate(label=label, ctrl_state=ctrl_state)
gate = CHGate(label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)

Expand Down
5 changes: 3 additions & 2 deletions qiskit/extensions/standard/ms.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ class MSGate(Gate):
"""

@deprecate_arguments({'n_qubits': 'num_qubits'})
def __init__(self, num_qubits, theta, *, n_qubits=None): # pylint:disable=unused-argument
def __init__(self, num_qubits, theta, *, n_qubits=None, # pylint:disable=unused-argument
label=None):
"""Create new MS gate."""
super().__init__('ms', num_qubits, [theta])
super().__init__('ms', num_qubits, [theta], label=label)

def _define(self):
from qiskit.extensions.standard.rxx import RXXGate
Expand Down
7 changes: 4 additions & 3 deletions qiskit/extensions/standard/rx.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
return CRXGate(self.params[0], label=label, ctrl_state=ctrl_state)
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
gate = CRXGate(self.params[0], label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)

def inverse(self):
r"""Return inverted RX gate.
Expand Down
7 changes: 4 additions & 3 deletions qiskit/extensions/standard/ry.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
return CRYGate(self.params[0], label=label, ctrl_state=ctrl_state)
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
gate = CRYGate(self.params[0], label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)

def inverse(self):
r"""Return inverted RY gate.
Expand Down
11 changes: 6 additions & 5 deletions qiskit/extensions/standard/rz.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ class RZGate(Gate):
Reference for virtual Z gate implementation:
`1612.00858 <https://arxiv.org/abs/1612.00858>`_
"""
def __init__(self, phi):
def __init__(self, phi, label=None):
"""Create new RZ gate."""
super().__init__('rz', 1, [phi])
super().__init__('rz', 1, [phi], label=label)

def _define(self):
"""
Expand Down Expand Up @@ -89,9 +89,10 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
return CRZGate(self.params[0], label=label, ctrl_state=ctrl_state)
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
gate = CRZGate(self.params[0], label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)

def inverse(self):
r"""Return inverted RZ gate
Expand Down
11 changes: 6 additions & 5 deletions qiskit/extensions/standard/swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ class SwapGate(Gate):
|a, b\rangle \rightarrow |b, a\rangle
"""

def __init__(self):
def __init__(self, label=None):
"""Create new SWAP gate."""
super().__init__('swap', 2, [])
super().__init__('swap', 2, [], label=label)

def _define(self):
"""
Expand Down Expand Up @@ -91,9 +91,10 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
return CSwapGate(label=None, ctrl_state=ctrl_state)
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
gate = CSwapGate(label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)

def inverse(self):
"""Return inverse Swap gate (itself)."""
Expand Down
33 changes: 20 additions & 13 deletions qiskit/extensions/standard/u1.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,14 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
return CU1Gate(self.params[0], label=label, ctrl_state=ctrl_state)
if ctrl_state is None and num_ctrl_qubits > 1:
return MCU1Gate(self.params[0], num_ctrl_qubits)
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
gate = CU1Gate(self.params[0], label=label, ctrl_state=ctrl_state)
elif ctrl_state is None and num_ctrl_qubits > 1:
gate = MCU1Gate(self.params[0], num_ctrl_qubits, label=label)
else:
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
gate.base_gate.label = self.label
return gate

def inverse(self):
r"""Return inverted U1 gate (:math:`U1(\lambda){\dagger} = U1(-\lambda)`)"""
Expand Down Expand Up @@ -222,9 +225,10 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
ControlledGate: controlled version of this gate.
"""
if ctrl_state is None:
return MCU1Gate(self.params[0], num_ctrl_qubits=num_ctrl_qubits + 1)
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
gate = MCU1Gate(self.params[0], num_ctrl_qubits=num_ctrl_qubits + 1, label=label)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)

def inverse(self):
r"""Return inverted CU1 gate (:math:`CU1(\lambda){\dagger} = CU1(-\lambda)`)"""
Expand Down Expand Up @@ -281,9 +285,10 @@ class MCU1Gate(ControlledGate):
The singly-controlled-version of this gate.
"""

def __init__(self, lam, num_ctrl_qubits):
def __init__(self, lam, num_ctrl_qubits, label=None):
"""Create new MCU1 gate."""
super().__init__('mcu1', num_ctrl_qubits + 1, [lam], num_ctrl_qubits=num_ctrl_qubits)
super().__init__('mcu1', num_ctrl_qubits + 1, [lam], num_ctrl_qubits=num_ctrl_qubits,
label=label)
self.base_gate = U1Gate(lam)

def _define(self):
Expand Down Expand Up @@ -314,9 +319,11 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
ControlledGate: controlled version of this gate.
"""
if ctrl_state is None:
return MCU1Gate(self.params[0], num_ctrl_qubits=num_ctrl_qubits + self.num_ctrl_qubits)
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
gate = MCU1Gate(self.params[0], num_ctrl_qubits=num_ctrl_qubits + self.num_ctrl_qubits,
label=label)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)

def inverse(self):
r"""Return inverted MCU1 gate (:math:`MCU1(\lambda){\dagger} = MCU1(-\lambda)`)"""
Expand Down
7 changes: 4 additions & 3 deletions qiskit/extensions/standard/u3.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
return CU3Gate(*self.params, label=label, ctrl_state=ctrl_state)
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
gate = CU3Gate(*self.params, label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)

def to_matrix(self):
"""Return a Numpy.array for the U3 gate."""
Expand Down
33 changes: 23 additions & 10 deletions qiskit/extensions/standard/x.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
Returns:
ControlledGate: controlled version of this gate.
"""
return MCXGate(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
return gate

def inverse(self):
r"""Return inverted X gate (itself)"""
Expand Down Expand Up @@ -216,7 +218,9 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
if ctrl_state is None:
ctrl_state = 2**num_ctrl_qubits - 1
new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state
return MCXGate(num_ctrl_qubits=num_ctrl_qubits + 1, label=label, ctrl_state=new_ctrl_state)
gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 1, label=label, ctrl_state=new_ctrl_state)
gate.base_gate.label = self.label
return gate

def inverse(self):
"""Return inverted CX gate (itself)."""
Expand Down Expand Up @@ -389,7 +393,9 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
if ctrl_state is None:
ctrl_state = 2**num_ctrl_qubits - 1
new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state
return MCXGate(num_ctrl_qubits=num_ctrl_qubits + 2, label=label, ctrl_state=new_ctrl_state)
gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 2, label=label, ctrl_state=new_ctrl_state)
gate.base_gate.label = self.label
return gate

def inverse(self):
"""Return an inverted CCX gate (also a CCX)."""
Expand Down Expand Up @@ -441,9 +447,9 @@ class RCCXGate(Gate):
of Fig. 3.
"""

def __init__(self):
def __init__(self, label=None):
"""Create a new simplified CCX gate."""
super().__init__('rccx', 3, [])
super().__init__('rccx', 3, [], label=label)

def _define(self):
"""
Expand Down Expand Up @@ -584,7 +590,9 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
if ctrl_state is None:
ctrl_state = 2**num_ctrl_qubits - 1
new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state
return MCXGate(num_ctrl_qubits=num_ctrl_qubits + 3, label=label, ctrl_state=new_ctrl_state)
gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 3, label=label, ctrl_state=new_ctrl_state)
gate.base_gate.label = self.label
return gate

def inverse(self):
"""Invert this gate. The C3X is its own inverse."""
Expand Down Expand Up @@ -622,9 +630,9 @@ class RC3XGate(Gate):
of Fig. 4.
"""

def __init__(self):
def __init__(self, label=None):
"""Create a new RC3X gate."""
super().__init__('rcccx', 4, [])
super().__init__('rcccx', 4, [], label=label)

def _define(self):
"""
Expand Down Expand Up @@ -773,7 +781,9 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
if ctrl_state is None:
ctrl_state = 2**num_ctrl_qubits - 1
new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state
return MCXGate(num_ctrl_qubits=num_ctrl_qubits + 4, label=label, ctrl_state=new_ctrl_state)
gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 4, label=label, ctrl_state=new_ctrl_state)
gate.base_gate.label = self.label
return gate

def inverse(self):
"""Invert this gate. The C4X is its own inverse."""
Expand Down Expand Up @@ -853,7 +863,10 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
"""
if ctrl_state is None:
# use __class__ so this works for derived classes
return self.__class__(self.num_ctrl_qubits + num_ctrl_qubits)
gate = self.__class__(self.num_ctrl_qubits + num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits, label=label, ctrl_state=ctrl_state)


Expand Down
7 changes: 4 additions & 3 deletions qiskit/extensions/standard/y.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,10 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
return CYGate(label=label, ctrl_state=ctrl_state)
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
gate = CYGate(label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)

def inverse(self):
r"""Return inverted Y gate (:math:`Y{\dagger} = Y`)"""
Expand Down
7 changes: 4 additions & 3 deletions qiskit/extensions/standard/z.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,10 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
return CZGate(label=label, ctrl_state=ctrl_state)
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)
gate = CZGate(label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)

def inverse(self):
r"""Return inverted Z gate (itself)."""
Expand Down
56 changes: 54 additions & 2 deletions test/python/circuit/test_controlled_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
U3Gate, CHGate, CRZGate, CU3Gate,
MSGate, Barrier, RCCXGate, RC3XGate,
MCU1Gate, MCXGate, MCXGrayCode, MCXRecursive,
MCXVChain)
MCXVChain, C3XGate, C4XGate)
from qiskit.circuit._utils import _compute_control_matrix
import qiskit.extensions.standard as allGates

Expand Down Expand Up @@ -756,7 +756,7 @@ def test_open_controlled_gate(self):
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)
np.set_printoptions(linewidth=200,)
np.set_printoptions(linewidth=200, )
self.assertEqual(Operator(cgate), Operator(target_mat))

ctrl_state = 7
Expand Down Expand Up @@ -978,5 +978,57 @@ def test_ctrl_state_one(self, gate, controlled_gate):
# Operator(controlled_gate.to_matrix()))


@ddt
class TestControlledGateLabel(QiskitTestCase):
"""Tests for controlled gate labels."""
gates_and_args = [(XGate, []),
(YGate, []),
(ZGate, []),
(HGate, []),
(CXGate, []),
(CCXGate, []),
(C3XGate, []),
(C4XGate, []),
(MCXGate, [5]),
(U1Gate, [0.1]),
(CYGate, []),
(CZGate, []),
(CU1Gate, [0.1]),
(SwapGate, []),
(CCXGate, []),
(RZGate, [0.1]),
(RXGate, [0.1]),
(RYGate, [0.1]),
(CRYGate, [0.1]),
(CRXGate, [0.1]),
(CSwapGate, []),
(U3Gate, [0.1, 0.2, 0.3]),
(CHGate, []),
(CRZGate, [0.1]),
(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')


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

0 comments on commit 7f3db35

Please sign in to comment.