Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1ff2894
port mcu1 from function to gate
Cryoris Feb 24, 2020
b58ad75
update init, remove old implementation
Cryoris Feb 24, 2020
72ae9c9
fix tests
Cryoris Feb 24, 2020
fdb71fd
fix cyclic import by full path
Cryoris Feb 25, 2020
d321650
Merge branch 'master' of github.com:Qiskit/qiskit-terra into implemen…
Cryoris Feb 25, 2020
79571db
make `control` use MCU1
Cryoris Feb 25, 2020
5fdba01
Merge branch 'master' into implement-mcu1-gate
Cryoris Feb 26, 2020
eb81bb2
mcu1 already importet via u1.py
Cryoris Feb 26, 2020
9222c8d
Merge branch 'master' of github.com:Qiskit/qiskit-terra into implemen…
Cryoris Feb 27, 2020
b0ee263
implement review changes and fix tests
Cryoris Feb 27, 2020
0813029
Merge branch 'master' of github.com:Qiskit/qiskit-terra into implemen…
Cryoris Mar 4, 2020
91b6aac
apply changes of the review
Cryoris Mar 4, 2020
9c6cec2
Merge branch 'master' into implement-mcu1-gate
Cryoris Mar 24, 2020
1dfccda
move MCU1 into CU1
Cryoris Mar 24, 2020
7bb7d12
try to fix docstring error?
Cryoris Mar 24, 2020
9194785
move gray code logic to external function
Cryoris Mar 25, 2020
e6a5c5e
circumvent special case distinction for CU1Gate
Cryoris Mar 25, 2020
1e691ee
Merge branch 'master' into implement-mcu1-gate
Cryoris Mar 25, 2020
f78df2f
keep mode 'noancilla'
Cryoris Mar 25, 2020
d07531d
move generate gray code to u3
Cryoris Mar 25, 2020
6191586
move determination of num free params to utils
Cryoris Mar 25, 2020
ce58bcd
Merge branch 'master' into implement-mcu1-gate
Cryoris Mar 26, 2020
bf0035c
Merge branch 'master' into implement-mcu1-gate
Cryoris Mar 26, 2020
d4457d9
Merge branch 'master' into implement-mcu1-gate
Cryoris Apr 2, 2020
60440eb
revert to MCU1 as own class
Cryoris Apr 2, 2020
5991ba8
Merge branch 'implement-mcu1-gate' of github.com:Cryoris/qiskit-terra…
Cryoris Apr 2, 2020
3fb1091
Merge branch 'master' into implement-mcu1-gate
Cryoris Apr 2, 2020
7278b59
Merge branch 'master' into implement-mcu1-gate
mergify[bot] Apr 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 5 additions & 11 deletions qiskit/circuit/add_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ def control(operation: Union[Gate, ControlledGate],
# pylint: disable=unused-import
import qiskit.extensions.standard.multi_control_rotation_gates
import qiskit.extensions.standard.multi_control_toffoli_gate
import qiskit.extensions.standard.multi_control_u1_gate

q_control = QuantumRegister(num_ctrl_qubits, name='control')
q_target = QuantumRegister(operation.num_qubits, name='target')
Expand All @@ -100,16 +99,13 @@ def control(operation: Union[Gate, ControlledGate],
if operation.name == 'x' or (
isinstance(operation, controlledgate.ControlledGate) and
operation.base_gate.name == 'x'):
qc.mct(q_control[:] + q_target[:-1],
q_target[-1],
None,
mode='noancilla')
qc.mct(q_control[:] + q_target[:-1], q_target[-1], q_ancillae)
elif operation.name == 'rx':
qc.mcrx(operation.definition[0][0].params[0], q_control, q_target[0],
use_basis_gates=True)
elif operation.name == 'ry':
qc.mcry(operation.definition[0][0].params[0], q_control, q_target[0],
q_ancillae, use_basis_gates=True)
q_ancillae, mode='noancilla', use_basis_gates=True)
elif operation.name == 'rz':
qc.mcrz(operation.definition[0][0].params[0], q_control, q_target[0],
use_basis_gates=True)
Expand All @@ -124,7 +120,7 @@ def control(operation: Union[Gate, ControlledGate],
use_basis_gates=True)
elif phi == 0 and lamb == 0:
qc.mcry(theta, q_control, q_target[rule[1][0].index],
q_ancillae, mode='noancilla', use_basis_gates=True)
q_ancillae, use_basis_gates=True)
Comment thread
Cryoris marked this conversation as resolved.
elif theta == 0 and phi == 0:
qc.mcrz(lamb, q_control, q_target[rule[1][0].index],
use_basis_gates=True)
Expand All @@ -138,10 +134,8 @@ def control(operation: Union[Gate, ControlledGate],
elif rule[0].name == 'u1':
qc.mcu1(rule[0].params[0], q_control, q_target[rule[1][0].index])
elif rule[0].name == 'cx':
qc.mct(q_control[:] + [q_target[rule[1][0].index]],
q_target[rule[1][1].index],
None,
mode='noancilla')
qc.mct(q_control[:] + [q_target[rule[1][0].index]], q_target[rule[1][1].index],
q_ancillae)
else:
raise CircuitError('gate contains non-controllable instructions')
instr = qc.to_instruction()
Expand Down
3 changes: 1 addition & 2 deletions qiskit/extensions/standard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,14 @@
from .iswap import iSwapGate
from .dcx import DCXGate
from .t import TGate, TdgGate
from .u1 import U1Gate, CU1Gate
from .u1 import U1Gate, CU1Gate, MCU1Gate
from .u2 import U2Gate
from .u3 import U3Gate, CU3Gate
from .x import XGate, CXGate, CCXGate
from .y import YGate, CYGate
from .z import ZGate, CZGate

# to be converted to gates
from .multi_control_u1_gate import mcu1
from .multi_control_toffoli_gate import mct
from .multi_control_rotation_gates import mcrx, mcry, mcrz

Expand Down
10 changes: 1 addition & 9 deletions qiskit/extensions/standard/multi_control_rotation_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from math import pi
from qiskit.circuit import QuantumCircuit, QuantumRegister, Qubit
from qiskit.exceptions import QiskitError
from qiskit.extensions.standard.u3 import _generate_gray_code

logger = logging.getLogger(__name__)

Expand All @@ -35,15 +36,6 @@ def _apply_cu3(circuit, theta, phi, lam, control, target, use_basis_gates=True):
circuit.cu3(theta, phi, lam, control, target)


def _generate_gray_code(num_bits):
if num_bits <= 0:
raise QiskitError("Must have at least 1 control in a controlled gate")
result = [0]
for i in range(num_bits):
result += [x + 2**i for x in reversed(result)]
return [format(x, "0%sb" % num_bits) for x in result]


def _apply_mcu3_graycode(circuit, theta, phi, lam, ctls, tgt, use_basis_gates):
"""Apply multi-controlled u3 gate from ctls to tgt using graycode
pattern with single-step angles theta, phi, lam."""
Expand Down
111 changes: 0 additions & 111 deletions qiskit/extensions/standard/multi_control_u1_gate.py

This file was deleted.

99 changes: 97 additions & 2 deletions qiskit/extensions/standard/u1.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
"""
if ctrl_state is None:
if num_ctrl_qubits == 1:
return CU1Gate(*self.params)
return CU1Gate(self.params[0])
return MCU1Gate(self.params[0], num_ctrl_qubits)
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label,
ctrl_state=ctrl_state)

Expand Down Expand Up @@ -192,20 +193,38 @@ def _define(self):
u1(lambda/2) b;
}
"""
from qiskit.extensions.standard.x import CXGate
definition = []
q = QuantumRegister(2, 'q')
from qiskit.extensions.standard.x import CXGate
rule = [
(U1Gate(self.params[0] / 2), [q[0]], []),
(CXGate(), [q[0], q[1]], []),
(U1Gate(-self.params[0] / 2), [q[1]], []),
(CXGate(), [q[0], q[1]], []),
(U1Gate(self.params[0] / 2), [q[1]], [])
]

for inst in rule:
definition.append(inst)
self.definition = definition

def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
"""Controlled version of this gate.

Args:
num_ctrl_qubits (int): number of control qubits.
label (str or None): An optional label for the gate [Default: None]
ctrl_state (int or str or None): control state expressed as integer,
string (e.g. '110'), or None. If None, use all 1s.

Returns:
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)

def inverse(self):
r"""Return inverted CU1 gate (:math:`CU1(\lambda){\dagger} = CU1(-\lambda)`)"""
return CU1Gate(-self.params[0])
Expand All @@ -232,3 +251,79 @@ def cu1(self, theta, control_qubit, target_qubit,


QuantumCircuit.cu1 = cu1


class MCU1Gate(ControlledGate):
r"""Multi-controlled-U1 gate.

This is a diagonal and symmetric gate that induces a
phase on the state of the target qubit, depending on the state of the control qubits.

**Circuit symbol:**

.. parsed-literal::

q_0: ────■────
.
q_(n-1): ────■────
┌───┴───┐
q_n: ┤ U1(λ) ├
└───────┘
Comment on lines +266 to +273
Copy link
Copy Markdown
Member

@ajavadia ajavadia Apr 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this how this actually gets rendered currently? It should be something like:

            q_0: ────■────
                     │ 
                     .
                     │         λ
        q_(n-1): ────■────
                     .
                     │
        q_(n):   ────■────

Because in this gate changing control and target will not make a difference. I know the drawer is currently correct with one control, but maybe not for more controls. If not, then can you open an issue? (It should b e fixed for all 3 types of drawer: latex, text, mpl)


.. seealso::

:class:`~qiskit.extensions.standard.CU1Gate`:
The singly-controlled-version of this gate.
"""

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

def _define(self):
q = QuantumRegister(self.num_qubits, 'q')

if self.num_ctrl_qubits == 0:
definition = U1Gate(self.params[0]).definition
if self.num_ctrl_qubits == 1:
definition = CU1Gate(self.params[0]).definition
else:
from qiskit.extensions.standard.u3 import _gray_code_chain
scaled_lam = self.params[0] / (2 ** (self.num_ctrl_qubits - 1))
bottom_gate = CU1Gate(scaled_lam)
definition = _gray_code_chain(q, self.num_ctrl_qubits, bottom_gate)

self.definition = definition

def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
"""Controlled version of this gate.

Args:
num_ctrl_qubits (int): number of control qubits.
label (str or None): An optional label for the gate [Default: None]
ctrl_state (int or str or None): control state expressed as integer,
string (e.g. '110'), or None. If None, use all 1s.

Returns:
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)

def inverse(self):
r"""Return inverted MCU1 gate (:math:`MCU1(\lambda){\dagger} = MCU1(-\lambda)`)"""
return MCU1Gate(-self.params[0], self.num_ctrl_qubits)


def mcu1(self, lam, control_qubits, target_qubit):
"""Apply :class:`~qiskit.extensions.standard.CU1Gate`."""
num_ctrl_qubits = len(control_qubits)
return self.append(MCU1Gate(lam, num_ctrl_qubits), control_qubits[:] + [target_qubit], [])


QuantumCircuit.mcu1 = mcu1
64 changes: 64 additions & 0 deletions qiskit/extensions/standard/u3.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,67 @@ def cu3(self, theta, phi, lam, control_qubit, target_qubit,


QuantumCircuit.cu3 = cu3


def _generate_gray_code(num_bits):
"""Generate the gray code for ``num_bits`` bits."""
if num_bits <= 0:
raise ValueError('Cannot generate the gray code for less than 1 bit.')
result = [0]
for i in range(num_bits):
result += [x + 2**i for x in reversed(result)]
return [format(x, '0%sb' % num_bits) for x in result]


def _gray_code_chain(q, num_ctrl_qubits, gate):
"""Apply the gate to the the last qubit in the register ``q``, controlled on all
preceding qubits. This function uses the gray code to propagate down to the last qubit.

Ported and adapted from Aqua (github.com/Qiskit/qiskit-aqua),
commit 769ca8d, file qiskit/aqua/circuits/gates/multi_control_u1_gate.py.
"""
from qiskit.extensions.standard.x import CXGate

rule = []
q_controls, q_target = q[:num_ctrl_qubits], q[num_ctrl_qubits]
gray_code = _generate_gray_code(num_ctrl_qubits)
last_pattern = None

for pattern in gray_code:
if '1' not in pattern:
continue
if last_pattern is None:
last_pattern = pattern
# find left most set bit
lm_pos = list(pattern).index('1')

# find changed bit
comp = [i != j for i, j in zip(pattern, last_pattern)]
if True in comp:
pos = comp.index(True)
else:
pos = None
if pos is not None:
if pos != lm_pos:
rule.append(
(CXGate(), [q_controls[pos], q_controls[lm_pos]], [])
)
else:
indices = [i for i, x in enumerate(pattern) if x == '1']
for idx in indices[1:]:
rule.append(
(CXGate(), [q_controls[idx], q_controls[lm_pos]], [])
)
# check parity
if pattern.count('1') % 2 == 0:
# inverse
rule.append(
(gate.inverse(), [q_controls[lm_pos], q_target], [])
)
else:
rule.append(
(gate, [q_controls[lm_pos], q_target], [])
)
last_pattern = pattern

return rule
Loading