-
Notifications
You must be signed in to change notification settings - Fork 3k
add ability to specify open controls. #3739
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 13 commits
fdf4dad
95583d5
61517ef
2209614
7359fff
5f067c5
3a233d7
8a79b0b
997aeb2
60e43ce
5eb6e51
d9ddcd3
f4b4d98
0d7dc82
be9e962
af623f2
72663c9
c48d4f4
81d4157
8083507
0e5c635
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,22 +14,31 @@ | |
| """ | ||
| Add control to operation if supported. | ||
| """ | ||
| from qiskit import QiskitError | ||
| from typing import Union, Optional | ||
|
|
||
| from qiskit.circuit.exceptions import CircuitError | ||
| from qiskit.extensions import UnitaryGate | ||
| from . import ControlledGate, Gate, QuantumRegister, QuantumCircuit | ||
|
|
||
|
|
||
| def add_control(operation, num_ctrl_qubits, label): | ||
| """Add num_ctrl_qubits controls to operation | ||
| def add_control(operation: Union[Gate, ControlledGate], | ||
| num_ctrl_qubits: int, | ||
| label: Union[str, None], | ||
| ctrl_state: Union[int, str, None]) -> ControlledGate: | ||
| """Adds num_ctrl_qubits controls to operation. | ||
|
|
||
| Args: | ||
| operation (Gate or ControlledGate): operation to add control to. | ||
| num_ctrl_qubits (int): number of controls to add to gate (default=1) | ||
| label (str): optional gate label | ||
| operation: Operation for which control will be added. | ||
| num_ctrl_qubits: The number of controls to add to gate (default=1). | ||
| label: Optional gate label. | ||
| ctrl_state (int or str or None): The control state in decimal or as | ||
| a bitstring (e.g. '111'). If specified as a bitstring the length | ||
| must equal num_ctrl_qubits, MSB on left. If None, use | ||
| 2**num_ctrl_qubits-1. | ||
|
|
||
| Returns: | ||
| ControlledGate: controlled version of gate. This default algorithm | ||
| uses num_ctrl_qubits-1 ancillae qubits so returns a gate of size | ||
| num_qubits + 2*num_ctrl_qubits - 1. | ||
| Controlled version of gate. | ||
|
|
||
| """ | ||
| import qiskit.extensions.standard as standard | ||
| if isinstance(operation, standard.RZGate) or operation.name == 'rz': | ||
|
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. This is from before, but why is Rz a special case here, and all other gates are further down?
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. This is due to the global phase issue. When num_ctrl_qubits=1 CrzGate is properly retrieved. However when num_ctrl_qubits > 1, the algorithm computes the control gate from the definition of the base gate. Since RzGate is defined by U1Gate, this leads to the wrong phase. By reducing the number of controls by 1 and using instead the definition from CrzGate, which is phase correct, the proper controlled version is generated.
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. Ok, so maybe a comment here so someone reading the code would know why this is the way it is |
||
|
|
@@ -42,29 +51,34 @@ def add_control(operation, num_ctrl_qubits, label): | |
| if isinstance(operation, UnitaryGate): | ||
| # attempt decomposition | ||
| operation._define() | ||
| return control(operation, num_ctrl_qubits=num_ctrl_qubits, label=label) | ||
| return control(operation, num_ctrl_qubits=num_ctrl_qubits, label=label, | ||
| ctrl_state=ctrl_state) | ||
|
|
||
|
|
||
| def control(operation, num_ctrl_qubits=1, label=None): | ||
| def control(operation: Union[Gate, ControlledGate], | ||
| num_ctrl_qubits: Optional[int] = 1, | ||
| label: Optional[Union[None, str]] = None, | ||
| ctrl_state: Optional[Union[None, int, str]] = None) -> ControlledGate: | ||
| """Return controlled version of gate using controlled rotations | ||
|
|
||
| Args: | ||
| operation (Gate or Controlledgate): gate to create ControlledGate from | ||
| num_ctrl_qubits (int): number of controls to add to gate (default=1) | ||
| label (str): optional gate label | ||
| operation: gate to create ControlledGate from | ||
| num_ctrl_qubits: number of controls to add to gate (default=1) | ||
| label: optional gate label | ||
| ctrl_state: The control state in decimal or as | ||
| a bitstring (e.g. '111'). If specified as a bitstring the length | ||
| must equal num_ctrl_qubits, MSB on left. If None, use | ||
| 2**num_ctrl_qubits-1. | ||
|
|
||
| Returns: | ||
| ControlledGate: controlled version of gate. This default algorithm | ||
| uses num_ctrl_qubits-1 ancillae qubits so returns a gate of size | ||
| num_qubits + 2*num_ctrl_qubits - 1. | ||
| Controlled version of gate. | ||
|
|
||
| Raises: | ||
| QiskitError: gate contains non-gate in definitionl | ||
| CircuitError: gate contains non-gate in definition | ||
| """ | ||
| from math import pi | ||
| # pylint: disable=cyclic-import | ||
| import qiskit.circuit.controlledgate as controlledgate | ||
| from qiskit.circuit.quantumregister import QuantumRegister | ||
| from qiskit.circuit.quantumcircuit import QuantumCircuit | ||
| # pylint: disable=unused-import | ||
| import qiskit.extensions.standard.multi_control_rotation_gates | ||
| import qiskit.extensions.standard.multi_control_toffoli_gate | ||
|
|
@@ -73,9 +87,19 @@ def control(operation, num_ctrl_qubits=1, label=None): | |
| q_control = QuantumRegister(num_ctrl_qubits, name='control') | ||
| q_target = QuantumRegister(operation.num_qubits, name='target') | ||
| q_ancillae = None # TODO: add | ||
|
|
||
| qc = QuantumCircuit(q_control, q_target) | ||
|
|
||
| if ctrl_state is not None: | ||
| if isinstance(ctrl_state, str): | ||
| try: | ||
| assert len(ctrl_state) == num_ctrl_qubits | ||
| ctrl_state = int(ctrl_state, 2) | ||
| except ValueError: | ||
| raise CircuitError('invalid control bit string: ' + ctrl_state) | ||
| except AssertionError: | ||
| raise CircuitError('invalid control bit string: length != ' | ||
| 'num_ctrl_qubits') | ||
| _toggle_ctrl_state(qc, num_ctrl_qubits, ctrl_state) | ||
| if operation.name == 'x' or ( | ||
| isinstance(operation, controlledgate.ControlledGate) and | ||
| operation.base_gate.name == 'x'): | ||
|
|
@@ -122,7 +146,9 @@ def control(operation, num_ctrl_qubits=1, label=None): | |
| None, | ||
| mode='noancilla') | ||
| else: | ||
| raise QiskitError('gate contains non-controllable instructions') | ||
| raise CircuitError('gate contains non-controllable instructions') | ||
| if ctrl_state is not None: | ||
| _toggle_ctrl_state(qc, num_ctrl_qubits, ctrl_state) | ||
| instr = qc.to_instruction() | ||
| if isinstance(operation, controlledgate.ControlledGate): | ||
| new_num_ctrl_qubits = num_ctrl_qubits + operation.num_ctrl_qubits | ||
|
|
@@ -151,15 +177,25 @@ def control(operation, num_ctrl_qubits=1, label=None): | |
| return cgate | ||
|
|
||
|
|
||
| def _toggle_ctrl_state(qc: QuantumCircuit, | ||
| num_qubits: int, | ||
| ctrl_state: int) -> Gate: | ||
| if isinstance(ctrl_state, int) and 0 <= ctrl_state < 2**num_qubits: | ||
|
ewinston marked this conversation as resolved.
Outdated
|
||
| bit_ctrl_state = bin(ctrl_state)[2:].zfill(num_qubits) | ||
| else: | ||
| raise CircuitError('invalid control state specified') | ||
| for ind, val in enumerate(bit_ctrl_state): | ||
| if val == '0': | ||
| qc.x(num_qubits - ind - 1) | ||
|
|
||
|
|
||
| def _gate_to_circuit(operation): | ||
| from qiskit.circuit.quantumcircuit import QuantumCircuit | ||
| from qiskit.circuit.quantumregister import QuantumRegister | ||
| qr = QuantumRegister(operation.num_qubits) | ||
| qc = QuantumCircuit(qr, name=operation.name) | ||
| if hasattr(operation, 'definition') and operation.definition: | ||
| for rule in operation.definition: | ||
| if rule[0].name in {'id', 'barrier', 'measure', 'snapshot'}: | ||
| raise QiskitError('Cannot make controlled gate with {} instruction'.format( | ||
| raise CircuitError('Cannot make controlled gate with {} instruction'.format( | ||
| rule[0].name)) | ||
| qc.append(rule[0], qargs=[qr[bit.index] for bit in rule[1]], cargs=[]) | ||
| else: | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.