-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Implement multi-controlled U1 as gate #3883
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 17 commits
1ff2894
b58ad75
72ae9c9
fdb71fd
d321650
79571db
5fdba01
eb81bb2
9222c8d
b0ee263
0813029
91b6aac
9c6cec2
1dfccda
7bb7d12
9194785
e6a5c5e
1e691ee
f78df2f
d07531d
6191586
ce58bcd
bf0035c
d4457d9
60440eb
5991ba8
3fb1091
7278b59
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,8 +55,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): | |
| ControlledGate: controlled version of this gate. | ||
| """ | ||
| if ctrl_state is None: | ||
| if num_ctrl_qubits == 1: | ||
| return CU1Gate(*self.params) | ||
| return CU1Gate(*self.params, num_ctrl_qubits) | ||
| return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, | ||
| ctrl_state=ctrl_state) | ||
|
|
||
|
|
@@ -119,9 +118,9 @@ def __instancecheck__(mcs, inst): | |
| class CU1Gate(ControlledGate, metaclass=CU1Meta): | ||
| """The controlled-u1 gate.""" | ||
|
|
||
| def __init__(self, theta): | ||
| def __init__(self, theta, num_ctrl_qubits=1): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hm, a CU1Gate should really just mean 1 control. I think a MCU1 is a more appropriate name for more controls (similar to mcrx, mcry, etc. that we have). Separating them gives you the flexibility to write algorithms for synthesis of multi-control gates (which are non-trivial), whereas for CU1 it's already a 2-qubit gate and is easier to decompose (requires no ancilla, routing, etc.). I know that ultimately one is the generalization of the other, but it's more about separating out common cases. By this logic we could lump together X, CX, CCX, and MCT (which I think should be called MCX btw). Each has the same base gate, with 0, 1, 2, 3+ controls respectively. But we don't do that since X, CX and CCX are extensively studied and are common.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, agreed, also if we find different ways to synthesize the MCU1 it makes sense to separate this logic from CU1 and have different types derived from MCU1. |
||
| """Create new cu1 gate.""" | ||
| super().__init__('cu1', 2, [theta], num_ctrl_qubits=1) | ||
| super().__init__('cu1', num_ctrl_qubits + 1, [theta], num_ctrl_qubits=num_ctrl_qubits) | ||
| self.base_gate = U1Gate(theta) | ||
|
|
||
| def _define(self): | ||
|
|
@@ -132,23 +131,57 @@ def _define(self): | |
| u1(lambda/2) b; | ||
| } | ||
| """ | ||
| from qiskit.extensions.standard.x import CXGate | ||
| definition = [] | ||
| q = QuantumRegister(2, 'q') | ||
| 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]], []) | ||
| ] | ||
| q = QuantumRegister(self.num_qubits, 'q') | ||
| if self.num_ctrl_qubits == 1: | ||
| 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]], []) | ||
| ] | ||
| 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) | ||
| rule = _gray_code_chain(q, self.num_ctrl_qubits, bottom_gate) | ||
|
|
||
| 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 CU1Gate(*self.params, num_ctrl_qubits=self.num_ctrl_qubits + num_ctrl_qubits) | ||
| return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, | ||
| ctrl_state=ctrl_state) | ||
|
|
||
| def inverse(self): | ||
| """Invert this gate.""" | ||
| return CU1Gate(-self.params[0]) | ||
| return CU1Gate(-self.params[0], num_ctrl_qubits=self.num_ctrl_qubits) | ||
|
|
||
| def to_matrix(self): | ||
| """Return a numpy.array for the multi-controlled U1 gate.""" | ||
| lam = self.params[0] | ||
| if self.num_ctrl_qubits == 0: | ||
| return U1Gate(lam).to_matrix() | ||
|
|
||
| from qiskit.extensions.unitary import _compute_control_matrix | ||
| base_mat = U1Gate(lam).to_matrix() | ||
| return _compute_control_matrix(base_mat, self.num_ctrl_qubits, ctrl_state=self.ctrl_state) | ||
|
Cryoris marked this conversation as resolved.
Outdated
|
||
|
|
||
|
|
||
| class Cu1Gate(CU1Gate, metaclass=CU1Meta): | ||
|
|
@@ -191,3 +224,31 @@ def cu1(self, theta, control_qubit, target_qubit, | |
|
|
||
|
|
||
| QuantumCircuit.cu1 = cu1 | ||
|
|
||
|
|
||
| def mcu1(self, lam, control_qubits, target_qubit): | ||
|
Cryoris marked this conversation as resolved.
Outdated
|
||
| r"""Apply multi-cU1 gate. | ||
|
Cryoris marked this conversation as resolved.
Outdated
|
||
|
|
||
| Applied from a specified controls ``control_qubits`` to target | ||
| ``target_qubit`` qubit with angle ``lam``. A multi-cU1 gate implements a | ||
| :math:`\lambda` radian rotation of the qubit state vector about the z axis | ||
| of the Bloch sphere when all control qubits are in state :math:`|1\rangle`. | ||
|
|
||
| Examples: | ||
|
|
||
| Circuit Representation: | ||
|
|
||
| .. jupyter-execute:: | ||
|
|
||
| from qiskit.circuit import QuantumCircuit, Parameter | ||
|
|
||
| lam = Parameter('λ') | ||
| circuit = QuantumCircuit(4) | ||
| circuit.mcu1(lam, [0, 1, 2], 3) | ||
| circuit.draw() | ||
| """ | ||
| num_ctrl_qubits = len(control_qubits) | ||
| return self.append(CU1Gate(lam, num_ctrl_qubits), control_qubits[:] + [target_qubit], []) | ||
|
|
||
|
|
||
| QuantumCircuit.mcu1 = mcu1 | ||
Uh oh!
There was an error while loading. Please reload this page.