From 5c78bc866d3834c145767d7476bd3a896eea04a4 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Wed, 18 Sep 2024 13:23:04 +0200 Subject: [PATCH 1/7] rename internal add_control to _add_control --- qiskit/circuit/add_control.py | 2 +- qiskit/circuit/gate.py | 4 ++-- .../notes/private-add_control-2367872c0f7136c4.yaml | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/private-add_control-2367872c0f7136c4.yaml diff --git a/qiskit/circuit/add_control.py b/qiskit/circuit/add_control.py index 04bb934a0211..11a1965d1b4c 100644 --- a/qiskit/circuit/add_control.py +++ b/qiskit/circuit/add_control.py @@ -23,7 +23,7 @@ from ._utils import _ctrl_state_to_int -def add_control( +def _add_control( operation: Gate | ControlledGate, num_ctrl_qubits: int, label: str | None, diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 2f1c09668252..0afe089a2ed7 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -135,9 +135,9 @@ def control( """ if not annotated: # captures both None and False # pylint: disable=cyclic-import - from .add_control import add_control + from .add_control import _add_control - return add_control(self, num_ctrl_qubits, label, ctrl_state) + return _add_control(self, num_ctrl_qubits, label, ctrl_state) else: return AnnotatedOperation( diff --git a/releasenotes/notes/private-add_control-2367872c0f7136c4.yaml b/releasenotes/notes/private-add_control-2367872c0f7136c4.yaml new file mode 100644 index 000000000000..898aba310c29 --- /dev/null +++ b/releasenotes/notes/private-add_control-2367872c0f7136c4.yaml @@ -0,0 +1,6 @@ +--- +upgrade_circuits: + - | + The internal function ``add_control`` was renamed to ``_add_control`` to emphasise + that is not part of the public API. If you need a controlled gate do ``SomeGate(...).control(...)`` instead of + ``add_control(SomeGate(...), ...)``. From 0b3266dbc70173e9c5fdcac6a466b75b24e32a6a Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Thu, 26 Sep 2024 18:09:39 +0200 Subject: [PATCH 2/7] move qiskit.circuit.add_control._add_control --- qiskit/circuit/add_control.py | 43 ----------------------------------- qiskit/circuit/gate.py | 11 ++++++--- 2 files changed, 8 insertions(+), 46 deletions(-) diff --git a/qiskit/circuit/add_control.py b/qiskit/circuit/add_control.py index 11a1965d1b4c..227bc905a87b 100644 --- a/qiskit/circuit/add_control.py +++ b/qiskit/circuit/add_control.py @@ -14,7 +14,6 @@ from __future__ import annotations from qiskit.circuit.exceptions import CircuitError -from qiskit.circuit.library import UnitaryGate from qiskit.transpiler import PassManager from qiskit.transpiler.passes.basis import BasisTranslator, UnrollCustomDefinitions from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel @@ -23,48 +22,6 @@ from ._utils import _ctrl_state_to_int -def _add_control( - operation: Gate | ControlledGate, - num_ctrl_qubits: int, - label: str | None, - ctrl_state: str | int | None, -) -> ControlledGate: - """For standard gates, if the controlled version already exists in the - library, it will be returned (e.g. XGate.control() = CnotGate(). - - For more generic gates, this method implements the controlled - version by first decomposing into the ['u1', 'u3', 'cx'] basis, then - controlling each gate in the decomposition. - - Open controls are implemented by conjugating the control line with - X gates. Adds num_ctrl_qubits controls to operation. - - This function is meant to be called from the - :method:`qiskit.circuit.gate.Gate.control()` method. - - Args: - operation: The operation to be controlled. - num_ctrl_qubits: The number of controls to add to gate. - label: An 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: - Controlled version of gate. - - """ - if isinstance(operation, UnitaryGate): - # attempt decomposition - operation._define() - cgate = control(operation, num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) - if operation.label is not None: - cgate.base_gate = cgate.base_gate.to_mutable() - cgate.base_gate.label = operation.label - return cgate - - def control( operation: Gate | ControlledGate, num_ctrl_qubits: int | None = 1, diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 0afe089a2ed7..8f03e28fcad3 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -135,10 +135,15 @@ def control( """ if not annotated: # captures both None and False # pylint: disable=cyclic-import - from .add_control import _add_control - - return _add_control(self, num_ctrl_qubits, label, ctrl_state) + from .add_control import control + cgate = control( + self, num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state + ) + if self.label is not None: + cgate.base_gate = cgate.base_gate.to_mutable() + cgate.base_gate.label = self.label + return cgate else: return AnnotatedOperation( self, ControlModifier(num_ctrl_qubits=num_ctrl_qubits, ctrl_state=ctrl_state) From 51c9a8b9c6af8907c85bb6036a50e82c9a94e320 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Thu, 26 Sep 2024 19:24:10 +0200 Subject: [PATCH 3/7] reno --- .../notes/private-add_control-2367872c0f7136c4.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/releasenotes/notes/private-add_control-2367872c0f7136c4.yaml b/releasenotes/notes/private-add_control-2367872c0f7136c4.yaml index 898aba310c29..862ccaa3af7b 100644 --- a/releasenotes/notes/private-add_control-2367872c0f7136c4.yaml +++ b/releasenotes/notes/private-add_control-2367872c0f7136c4.yaml @@ -1,6 +1,6 @@ --- upgrade_circuits: - | - The internal function ``add_control`` was renamed to ``_add_control`` to emphasise - that is not part of the public API. If you need a controlled gate do ``SomeGate(...).control(...)`` instead of - ``add_control(SomeGate(...), ...)``. + The internal function ``qiskit.circuit.add_control.add_control`` was removed and inlined, as it is not part of the + public API. User that had been wrongly using it as ``add_control(SomeGate(...), ...)`` should change their code to + ``SomeGate(...).control(...)`` instead. From a711f5f965029163f65d74ec99aef965f5b986ff Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 27 Sep 2024 18:29:07 +0200 Subject: [PATCH 4/7] move for defs --- qiskit/circuit/add_control.py | 201 +------------------ qiskit/circuit/gate.py | 211 +++++++++++++++++++- test/python/circuit/test_controlled_gate.py | 9 +- 3 files changed, 213 insertions(+), 208 deletions(-) diff --git a/qiskit/circuit/add_control.py b/qiskit/circuit/add_control.py index 227bc905a87b..fbb5ec62ce33 100644 --- a/qiskit/circuit/add_control.py +++ b/qiskit/circuit/add_control.py @@ -18,193 +18,7 @@ from qiskit.transpiler.passes.basis import BasisTranslator, UnrollCustomDefinitions from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel -from . import ControlledGate, Gate, QuantumRegister, QuantumCircuit -from ._utils import _ctrl_state_to_int - - -def control( - operation: Gate | ControlledGate, - num_ctrl_qubits: int | None = 1, - label: str | None = None, - ctrl_state: str | int | None = None, -) -> ControlledGate: - """Return controlled version of gate using controlled rotations. This function - first checks the name of the operation to see if it knows of a method from which - to generate a controlled version. Currently, these are ``x``, ``rx``, ``ry``, and ``rz``. - If a method is not directly known, it calls the unroller to convert to `u1`, `u3`, - and `cx` gates. - - Args: - operation: The gate used to create the ControlledGate. - num_ctrl_qubits: The number of controls to add to gate (default=1). - label: An 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: - Controlled version of gate. - - Raises: - CircuitError: gate contains non-gate in definition - """ - from math import pi - - # pylint: disable=cyclic-import - from qiskit.circuit import controlledgate - - ctrl_state = _ctrl_state_to_int(ctrl_state, num_ctrl_qubits) - - q_control = QuantumRegister(num_ctrl_qubits, name="control") - q_target = QuantumRegister(operation.num_qubits, name="target") - q_ancillae = None # TODO: add - controlled_circ = QuantumCircuit(q_control, q_target, name=f"c_{operation.name}") - if isinstance(operation, controlledgate.ControlledGate): - original_ctrl_state = operation.ctrl_state - global_phase = 0 - if operation.name == "x" or ( - isinstance(operation, controlledgate.ControlledGate) and operation.base_gate.name == "x" - ): - controlled_circ.mcx(q_control[:] + q_target[:-1], q_target[-1], q_ancillae) - if operation.definition is not None and operation.definition.global_phase: - global_phase += operation.definition.global_phase - else: - basis = ["p", "u", "x", "z", "rx", "ry", "rz", "cx"] - if isinstance(operation, controlledgate.ControlledGate): - operation = operation.to_mutable() - operation.ctrl_state = None - unrolled_gate = _unroll_gate(operation, basis_gates=basis) - if unrolled_gate.definition.global_phase: - global_phase += unrolled_gate.definition.global_phase - - definition = unrolled_gate.definition - bit_indices = { - bit: index - for bits in [definition.qubits, definition.clbits] - for index, bit in enumerate(bits) - } - - for instruction in definition.data: - gate, qargs = instruction.operation, instruction.qubits - if gate.name == "x": - controlled_circ.mcx(q_control, q_target[bit_indices[qargs[0]]], q_ancillae) - elif gate.name == "rx": - controlled_circ.mcrx( - gate.definition.data[0].operation.params[0], - q_control, - q_target[bit_indices[qargs[0]]], - use_basis_gates=False, - ) - elif gate.name == "ry": - controlled_circ.mcry( - gate.definition.data[0].operation.params[0], - q_control, - q_target[bit_indices[qargs[0]]], - q_ancillae, - mode="noancilla", - use_basis_gates=False, - ) - elif gate.name == "rz": - controlled_circ.mcrz( - gate.definition.data[0].operation.params[0], - q_control, - q_target[bit_indices[qargs[0]]], - use_basis_gates=False, - ) - continue - elif gate.name == "p": - from qiskit.circuit.library import MCPhaseGate - - controlled_circ.append( - MCPhaseGate(gate.params[0], num_ctrl_qubits), - q_control[:] + [q_target[bit_indices[qargs[0]]]], - ) - elif gate.name == "cx": - controlled_circ.mcx( - q_control[:] + [q_target[bit_indices[qargs[0]]]], - q_target[bit_indices[qargs[1]]], - q_ancillae, - ) - elif gate.name == "u": - theta, phi, lamb = gate.params - if num_ctrl_qubits == 1: - if theta == 0 and phi == 0: - controlled_circ.cp(lamb, q_control[0], q_target[bit_indices[qargs[0]]]) - else: - controlled_circ.cu( - theta, phi, lamb, 0, q_control[0], q_target[bit_indices[qargs[0]]] - ) - else: - if phi == -pi / 2 and lamb == pi / 2: - controlled_circ.mcrx( - theta, q_control, q_target[bit_indices[qargs[0]]], use_basis_gates=True - ) - elif phi == 0 and lamb == 0: - controlled_circ.mcry( - theta, - q_control, - q_target[bit_indices[qargs[0]]], - q_ancillae, - use_basis_gates=True, - ) - elif theta == 0 and phi == 0: - controlled_circ.mcp(lamb, q_control, q_target[bit_indices[qargs[0]]]) - else: - controlled_circ.mcp(lamb, q_control, q_target[bit_indices[qargs[0]]]) - controlled_circ.mcry( - theta, - q_control, - q_target[bit_indices[qargs[0]]], - q_ancillae, - use_basis_gates=True, - ) - controlled_circ.mcp(phi, q_control, q_target[bit_indices[qargs[0]]]) - elif gate.name == "z": - controlled_circ.h(q_target[bit_indices[qargs[0]]]) - controlled_circ.mcx(q_control, q_target[bit_indices[qargs[0]]], q_ancillae) - controlled_circ.h(q_target[bit_indices[qargs[0]]]) - else: - raise CircuitError(f"gate contains non-controllable instructions: {gate.name}") - if gate.definition is not None and gate.definition.global_phase: - global_phase += gate.definition.global_phase - # apply controlled global phase - if global_phase: - if len(q_control) < 2: - controlled_circ.p(global_phase, q_control) - else: - controlled_circ.mcp(global_phase, q_control[:-1], q_control[-1]) - if isinstance(operation, controlledgate.ControlledGate): - operation.ctrl_state = original_ctrl_state - new_num_ctrl_qubits = num_ctrl_qubits + operation.num_ctrl_qubits - new_ctrl_state = operation.ctrl_state << num_ctrl_qubits | ctrl_state - base_name = operation.base_gate.name - base_gate = operation.base_gate - else: - new_num_ctrl_qubits = num_ctrl_qubits - new_ctrl_state = ctrl_state - base_name = operation.name - base_gate = operation - # In order to maintain some backward compatibility with gate names this - # uses a naming convention where if the number of controls is <=2 the gate - # is named like "cc", else it is named like - # "c". - if new_num_ctrl_qubits > 2: - ctrl_substr = f"c{new_num_ctrl_qubits:d}" - else: - ctrl_substr = ("{0}" * new_num_ctrl_qubits).format("c") - new_name = f"{ctrl_substr}{base_name}" - cgate = controlledgate.ControlledGate( - new_name, - controlled_circ.num_qubits, - operation.params, - label=label, - num_ctrl_qubits=new_num_ctrl_qubits, - definition=controlled_circ, - ctrl_state=new_ctrl_state, - base_gate=base_gate, - ) - return cgate +from . import QuantumRegister, QuantumCircuit def _gate_to_circuit(operation): @@ -216,16 +30,3 @@ def _gate_to_circuit(operation): qc = QuantumCircuit(qr, name=operation.name) qc.append(operation, qr) return qc - - -def _unroll_gate(operation, basis_gates): - """Unrolls a gate, possibly composite, to the target basis""" - circ = _gate_to_circuit(operation) - pm = PassManager( - [ - UnrollCustomDefinitions(sel, basis_gates=basis_gates), - BasisTranslator(sel, target_basis=basis_gates), - ] - ) - opqc = pm.run(circ) - return opqc.to_gate() diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 8f03e28fcad3..9b2027372ae4 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -135,10 +135,9 @@ def control( """ if not annotated: # captures both None and False # pylint: disable=cyclic-import - from .add_control import control - cgate = control( - self, num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state + cgate = self._control( + num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state ) if self.label is not None: cgate.base_gate = cgate.base_gate.to_mutable() @@ -149,6 +148,212 @@ def control( self, ControlModifier(num_ctrl_qubits=num_ctrl_qubits, ctrl_state=ctrl_state) ) + def _control( + self: Gate, + num_ctrl_qubits: int | None = 1, + label: str | None = None, + ctrl_state: str | int | None = None, + ): + """Return controlled version of gate using controlled rotations. This function + first checks the name of the operation to see if it knows of a method from which + to generate a controlled version. Currently, these are ``x``, ``rx``, ``ry``, and ``rz``. + If a method is not directly known, it calls the unroller to convert to `u1`, `u3`, + and `cx` gates. + + Args: + self: The gate used to create the ControlledGate. + num_ctrl_qubits: The number of controls to add to gate (default=1). + label: An 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: + Controlled version of gate. + + Raises: + CircuitError: gate contains non-gate in definition + """ + from math import pi + + # pylint: disable=cyclic-import + from qiskit.circuit import controlledgate + from qiskit import QuantumCircuit, QuantumRegister + from ._utils import _ctrl_state_to_int + + ctrl_state = _ctrl_state_to_int(ctrl_state, num_ctrl_qubits) + + q_control = QuantumRegister(num_ctrl_qubits, name="control") + q_target = QuantumRegister(self.num_qubits, name="target") + q_ancillae = None # TODO: add + controlled_circ = QuantumCircuit(q_control, q_target, name=f"c_{self.name}") + if isinstance(self, controlledgate.ControlledGate): + original_ctrl_state = self.ctrl_state + global_phase = 0 + if self.name == "x" or ( + isinstance(self, controlledgate.ControlledGate) and self.base_gate.name == "x" + ): + controlled_circ.mcx(q_control[:] + q_target[:-1], q_target[-1], q_ancillae) + if self.definition is not None and self.definition.global_phase: + global_phase += self.definition.global_phase + else: + basis = ["p", "u", "x", "z", "rx", "ry", "rz", "cx"] + if isinstance(self, controlledgate.ControlledGate): + self = self.to_mutable() + self.ctrl_state = None + unrolled_gate = self._unroll_gate(basis_gates=basis) + if unrolled_gate.definition.global_phase: + global_phase += unrolled_gate.definition.global_phase + + definition = unrolled_gate.definition + bit_indices = { + bit: index + for bits in [definition.qubits, definition.clbits] + for index, bit in enumerate(bits) + } + + for instruction in definition.data: + gate, qargs = instruction.operation, instruction.qubits + if gate.name == "x": + controlled_circ.mcx(q_control, q_target[bit_indices[qargs[0]]], q_ancillae) + elif gate.name == "rx": + controlled_circ.mcrx( + gate.definition.data[0].operation.params[0], + q_control, + q_target[bit_indices[qargs[0]]], + use_basis_gates=False, + ) + elif gate.name == "ry": + controlled_circ.mcry( + gate.definition.data[0].operation.params[0], + q_control, + q_target[bit_indices[qargs[0]]], + q_ancillae, + mode="noancilla", + use_basis_gates=False, + ) + elif gate.name == "rz": + controlled_circ.mcrz( + gate.definition.data[0].operation.params[0], + q_control, + q_target[bit_indices[qargs[0]]], + use_basis_gates=False, + ) + continue + elif gate.name == "p": + from qiskit.circuit.library import MCPhaseGate + + controlled_circ.append( + MCPhaseGate(gate.params[0], num_ctrl_qubits), + q_control[:] + [q_target[bit_indices[qargs[0]]]], + ) + elif gate.name == "cx": + controlled_circ.mcx( + q_control[:] + [q_target[bit_indices[qargs[0]]]], + q_target[bit_indices[qargs[1]]], + q_ancillae, + ) + elif gate.name == "u": + theta, phi, lamb = gate.params + if num_ctrl_qubits == 1: + if theta == 0 and phi == 0: + controlled_circ.cp(lamb, q_control[0], q_target[bit_indices[qargs[0]]]) + else: + controlled_circ.cu( + theta, phi, lamb, 0, q_control[0], q_target[bit_indices[qargs[0]]] + ) + else: + if phi == -pi / 2 and lamb == pi / 2: + controlled_circ.mcrx( + theta, + q_control, + q_target[bit_indices[qargs[0]]], + use_basis_gates=True, + ) + elif phi == 0 and lamb == 0: + controlled_circ.mcry( + theta, + q_control, + q_target[bit_indices[qargs[0]]], + q_ancillae, + use_basis_gates=True, + ) + elif theta == 0 and phi == 0: + controlled_circ.mcp(lamb, q_control, q_target[bit_indices[qargs[0]]]) + else: + controlled_circ.mcp(lamb, q_control, q_target[bit_indices[qargs[0]]]) + controlled_circ.mcry( + theta, + q_control, + q_target[bit_indices[qargs[0]]], + q_ancillae, + use_basis_gates=True, + ) + controlled_circ.mcp(phi, q_control, q_target[bit_indices[qargs[0]]]) + elif gate.name == "z": + controlled_circ.h(q_target[bit_indices[qargs[0]]]) + controlled_circ.mcx(q_control, q_target[bit_indices[qargs[0]]], q_ancillae) + controlled_circ.h(q_target[bit_indices[qargs[0]]]) + else: + raise CircuitError(f"gate contains non-controllable instructions: {gate.name}") + if gate.definition is not None and gate.definition.global_phase: + global_phase += gate.definition.global_phase + # apply controlled global phase + if global_phase: + if len(q_control) < 2: + controlled_circ.p(global_phase, q_control) + else: + controlled_circ.mcp(global_phase, q_control[:-1], q_control[-1]) + if isinstance(self, controlledgate.ControlledGate): + self.ctrl_state = original_ctrl_state + new_num_ctrl_qubits = num_ctrl_qubits + self.num_ctrl_qubits + new_ctrl_state = self.ctrl_state << num_ctrl_qubits | ctrl_state + base_name = self.base_gate.name + base_gate = self.base_gate + else: + new_num_ctrl_qubits = num_ctrl_qubits + new_ctrl_state = ctrl_state + base_name = self.name + base_gate = self + # In order to maintain some backward compatibility with gate names this + # uses a naming convention where if the number of controls is <=2 the gate + # is named like "cc", else it is named like + # "c". + if new_num_ctrl_qubits > 2: + ctrl_substr = f"c{new_num_ctrl_qubits:d}" + else: + ctrl_substr = ("{0}" * new_num_ctrl_qubits).format("c") + new_name = f"{ctrl_substr}{base_name}" + cgate = controlledgate.ControlledGate( + new_name, + controlled_circ.num_qubits, + self.params, + label=label, + num_ctrl_qubits=new_num_ctrl_qubits, + definition=controlled_circ, + ctrl_state=new_ctrl_state, + base_gate=base_gate, + ) + return cgate + + def _unroll_gate(self, basis_gates): + """Unrolls a gate, possibly composite, to the target basis""" + from qiskit.transpiler import PassManager + from qiskit.transpiler.passes.basis import BasisTranslator, UnrollCustomDefinitions + from .add_control import _gate_to_circuit + from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel + + circ = _gate_to_circuit(self) + pm = PassManager( + [ + UnrollCustomDefinitions(sel, basis_gates=basis_gates), + BasisTranslator(sel, target_basis=basis_gates), + ] + ) + opqc = pm.run(circ) + return opqc.to_gate() + @staticmethod def _broadcast_single_argument(qarg: list) -> Iterator[tuple[list, list]]: """Expands a single argument. diff --git a/test/python/circuit/test_controlled_gate.py b/test/python/circuit/test_controlled_gate.py index 03a041355b87..e754add35f46 100644 --- a/test/python/circuit/test_controlled_gate.py +++ b/test/python/circuit/test_controlled_gate.py @@ -27,7 +27,6 @@ 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 @@ -1527,10 +1526,10 @@ class TestSingleControlledRotationGates(QiskitTestCase): 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"]) + ugu1 = gu1._unroll_gate(["p", "u", "cx"]) + ugrx = grx._unroll_gate(["p", "u", "cx"]) + ugry = gry._unroll_gate(["p", "u", "cx"]) + ugrz = grz._unroll_gate(["p", "u", "cx"]) ugrz.params = grz.params cgu1 = ugu1.control(num_ctrl) From e441ebbd8091b9ed41d7ecce40551062cf75ce7f Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Sun, 29 Sep 2024 10:51:18 +0200 Subject: [PATCH 5/7] _gate_to_circuit --- qiskit/circuit/add_control.py | 32 ------------------ qiskit/circuit/gate.py | 61 +++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 59 deletions(-) delete mode 100644 qiskit/circuit/add_control.py diff --git a/qiskit/circuit/add_control.py b/qiskit/circuit/add_control.py deleted file mode 100644 index fbb5ec62ce33..000000000000 --- a/qiskit/circuit/add_control.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# 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. - -"""Add control to operation if supported.""" -from __future__ import annotations - -from qiskit.circuit.exceptions import CircuitError -from qiskit.transpiler import PassManager -from qiskit.transpiler.passes.basis import BasisTranslator, UnrollCustomDefinitions -from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel - -from . import QuantumRegister, QuantumCircuit - - -def _gate_to_circuit(operation): - """Converts a gate instance to a QuantumCircuit""" - if hasattr(operation, "definition") and operation.definition is not None: - return operation.definition - else: - qr = QuantumRegister(operation.num_qubits) - qc = QuantumCircuit(qr, name=operation.name) - qc.append(operation, qr) - return qc diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 9b2027372ae4..6223e30efc09 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -136,8 +136,8 @@ def control( if not annotated: # captures both None and False # pylint: disable=cyclic-import - cgate = self._control( - num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state + cgate = Gate._control( + self, num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state ) if self.label is not None: cgate.base_gate = cgate.base_gate.to_mutable() @@ -148,8 +148,9 @@ def control( self, ControlModifier(num_ctrl_qubits=num_ctrl_qubits, ctrl_state=ctrl_state) ) + @staticmethod def _control( - self: Gate, + operation: Gate, num_ctrl_qubits: int | None = 1, label: str | None = None, ctrl_state: str | int | None = None, @@ -161,7 +162,7 @@ def _control( and `cx` gates. Args: - self: The gate used to create the ControlledGate. + operation: The gate used to create the ControlledGate. num_ctrl_qubits: The number of controls to add to gate (default=1). label: An optional gate label. ctrl_state: The control state in decimal or as @@ -185,24 +186,24 @@ def _control( ctrl_state = _ctrl_state_to_int(ctrl_state, num_ctrl_qubits) q_control = QuantumRegister(num_ctrl_qubits, name="control") - q_target = QuantumRegister(self.num_qubits, name="target") + q_target = QuantumRegister(operation.num_qubits, name="target") q_ancillae = None # TODO: add - controlled_circ = QuantumCircuit(q_control, q_target, name=f"c_{self.name}") - if isinstance(self, controlledgate.ControlledGate): - original_ctrl_state = self.ctrl_state + controlled_circ = QuantumCircuit(q_control, q_target, name=f"c_{operation.name}") + if isinstance(operation, controlledgate.ControlledGate): + original_ctrl_state = operation.ctrl_state global_phase = 0 - if self.name == "x" or ( - isinstance(self, controlledgate.ControlledGate) and self.base_gate.name == "x" + if operation.name == "x" or ( + isinstance(operation, controlledgate.ControlledGate) and operation.base_gate.name == "x" ): controlled_circ.mcx(q_control[:] + q_target[:-1], q_target[-1], q_ancillae) - if self.definition is not None and self.definition.global_phase: - global_phase += self.definition.global_phase + if operation.definition is not None and operation.definition.global_phase: + global_phase += operation.definition.global_phase else: basis = ["p", "u", "x", "z", "rx", "ry", "rz", "cx"] - if isinstance(self, controlledgate.ControlledGate): - self = self.to_mutable() - self.ctrl_state = None - unrolled_gate = self._unroll_gate(basis_gates=basis) + if isinstance(operation, controlledgate.ControlledGate): + operation = operation.to_mutable() + operation.ctrl_state = None + unrolled_gate = operation._unroll_gate(basis_gates=basis) if unrolled_gate.definition.global_phase: global_phase += unrolled_gate.definition.global_phase @@ -305,17 +306,17 @@ def _control( controlled_circ.p(global_phase, q_control) else: controlled_circ.mcp(global_phase, q_control[:-1], q_control[-1]) - if isinstance(self, controlledgate.ControlledGate): - self.ctrl_state = original_ctrl_state - new_num_ctrl_qubits = num_ctrl_qubits + self.num_ctrl_qubits - new_ctrl_state = self.ctrl_state << num_ctrl_qubits | ctrl_state - base_name = self.base_gate.name - base_gate = self.base_gate + if isinstance(operation, controlledgate.ControlledGate): + operation.ctrl_state = original_ctrl_state + new_num_ctrl_qubits = num_ctrl_qubits + operation.num_ctrl_qubits + new_ctrl_state = operation.ctrl_state << num_ctrl_qubits | ctrl_state + base_name = operation.base_gate.name + base_gate = operation.base_gate else: new_num_ctrl_qubits = num_ctrl_qubits new_ctrl_state = ctrl_state - base_name = self.name - base_gate = self + base_name = operation.name + base_gate = operation # In order to maintain some backward compatibility with gate names this # uses a naming convention where if the number of controls is <=2 the gate # is named like "cc", else it is named like @@ -328,7 +329,7 @@ def _control( cgate = controlledgate.ControlledGate( new_name, controlled_circ.num_qubits, - self.params, + operation.params, label=label, num_ctrl_qubits=new_num_ctrl_qubits, definition=controlled_circ, @@ -341,10 +342,16 @@ def _unroll_gate(self, basis_gates): """Unrolls a gate, possibly composite, to the target basis""" from qiskit.transpiler import PassManager from qiskit.transpiler.passes.basis import BasisTranslator, UnrollCustomDefinitions - from .add_control import _gate_to_circuit + from qiskit import QuantumCircuit, QuantumRegister from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel - circ = _gate_to_circuit(self) + if hasattr(self, "definition") and self.definition is not None: + circ = self.definition + else: + qr = QuantumRegister(self.num_qubits) + circ = QuantumCircuit(qr, name=self.name) + circ.append(self, qr) + pm = PassManager( [ UnrollCustomDefinitions(sel, basis_gates=basis_gates), From d255d094052299db886a9416ce41ff12242fad21 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Sun, 29 Sep 2024 14:06:38 +0200 Subject: [PATCH 6/7] qiskit/circuit/gate.py --- qiskit/circuit/gate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 6223e30efc09..09856449d0f5 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -340,6 +340,7 @@ def _control( def _unroll_gate(self, basis_gates): """Unrolls a gate, possibly composite, to the target basis""" + # pylint: disable=cyclic-import from qiskit.transpiler import PassManager from qiskit.transpiler.passes.basis import BasisTranslator, UnrollCustomDefinitions from qiskit import QuantumCircuit, QuantumRegister From d889189c62e21886fe8a30c93a6efc1cf4c7cfed Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Sun, 29 Sep 2024 14:12:39 +0200 Subject: [PATCH 7/7] no lint exception --- qiskit/circuit/gate.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 09856449d0f5..48870b0073de 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -134,8 +134,6 @@ def control( QiskitError: unrecognized mode or invalid ctrl_state """ if not annotated: # captures both None and False - # pylint: disable=cyclic-import - cgate = Gate._control( self, num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state )