From 6da44bd75f2b883d4dd98ba7928ea9bd79250d14 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 16 Oct 2024 10:40:55 +0300 Subject: [PATCH 01/14] AND -> AndGate --- qiskit/circuit/library/__init__.py | 2 + .../circuit/library/boolean_logic/__init__.py | 2 +- .../library/boolean_logic/quantum_and.py | 109 +++++++++++++++++- .../circuit/library/test_boolean_logic.py | 49 ++++++-- test/python/circuit/test_gate_definitions.py | 1 + 5 files changed, 149 insertions(+), 14 deletions(-) diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index a4203f63b738..00bed00f0d7f 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -211,6 +211,7 @@ :template: autosummary/class_no_inherited_members.rst AND + AndGate OR XOR InnerProduct @@ -523,6 +524,7 @@ from .hamiltonian_gate import HamiltonianGate from .boolean_logic import ( AND, + AndGate, OR, XOR, InnerProduct, diff --git a/qiskit/circuit/library/boolean_logic/__init__.py b/qiskit/circuit/library/boolean_logic/__init__.py index b273f0d6a245..0540f5acabbd 100644 --- a/qiskit/circuit/library/boolean_logic/__init__.py +++ b/qiskit/circuit/library/boolean_logic/__init__.py @@ -12,7 +12,7 @@ """The Boolean logic circuit library.""" -from .quantum_and import AND +from .quantum_and import AND, AndGate from .quantum_or import OR from .quantum_xor import XOR from .inner_product import InnerProduct diff --git a/qiskit/circuit/library/boolean_logic/quantum_and.py b/qiskit/circuit/library/boolean_logic/quantum_and.py index ce2d253ca85a..ba4401a38c82 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_and.py +++ b/qiskit/circuit/library/boolean_logic/quantum_and.py @@ -14,8 +14,9 @@ """Implementations of boolean logic quantum circuits.""" from __future__ import annotations -from qiskit.circuit import QuantumRegister, QuantumCircuit, AncillaRegister +from qiskit.circuit import QuantumRegister, QuantumCircuit, AncillaRegister, Gate from qiskit.circuit.library.standard_gates import MCXGate +from qiskit.utils.deprecation import deprecate_func class AND(QuantumCircuit): @@ -49,6 +50,11 @@ class AND(QuantumCircuit): """ + @deprecate_func( + since="1.3", + additional_msg="Use qiskit.circuit.library.AndGate instead.", + pending=True, + ) def __init__( self, num_variable_qubits: int, @@ -58,7 +64,7 @@ def __init__( """Create a new logical AND circuit. Args: - num_variable_qubits: The qubits of which the OR is computed. The result will be written + num_variable_qubits: The qubits of which the AND is computed. The result will be written into an additional result qubit. flags: A list of +1/0/-1 marking negations or omissions of qubits. mcx_mode: The mode to be used to implement the multi-controlled X gate. @@ -95,3 +101,102 @@ def __init__( super().__init__(*circuit.qregs, name="and") self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) + + +class AndGate(Gate): + r"""A gate representing the logical AND operation on a number of qubits. + + For the AND operation the state :math:`|1\rangle` is interpreted as ``True``. The result + qubit is flipped, if the state of all variable qubits is ``True``. In this format, the AND + operation equals a multi-controlled X gate, which is controlled on all variable qubits. + Using a list of flags however, qubits can be skipped or negated. Practically, the flags + allow to skip controls or to apply pre- and post-X gates to the negated qubits. + + The AndGate gate without special flags equals the multi-controlled-X gate: + + .. plot:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import AndGate + from qiskit.visualization.library import _generate_circuit_library_visualization + circuit = QuantumCircuit(6) + circuit.append(AndGate(5), [0, 1, 2, 3, 4, 5]) + _generate_circuit_library_visualization(circuit) + + Using flags we can negate qubits or skip them. For instance, if we have 5 qubits and want to + return ``True`` if the first qubit is ``False`` and the last two are ``True`` we use the flags + ``[-1, 0, 0, 1, 1]``. + + .. plot:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import AND + from qiskit.visualization.library import _generate_circuit_library_visualization + circuit = QuantumCircuit(6) + circuit.append(AndGate(5, flags=[-1, 0, 0, 1, 1]), [0, 1, 2, 3, 4, 5]) + _generate_circuit_library_visualization(circuit) + + """ + + def __init__( + self, + num_variable_qubits: int, + flags: list[int] | None = None, + ) -> None: + """Create a new AndGate. + + Args: + num_variable_qubits: The qubits of which the AND is computed. The result will be written + into an additional result qubit. + flags: A list of +1/0/-1 marking negations or omissions of qubits. + """ + super().__init__("and", num_variable_qubits + 1, []) + self.num_variable_qubits = num_variable_qubits + self.flags = flags + + def _define(self): + """Create definition circuit.""" + + # add registers + qr_variable = QuantumRegister(self.num_variable_qubits, name="variable") + qr_result = QuantumRegister(1, name="result") + + circuit = QuantumCircuit(qr_variable, qr_result, name="and") + + # determine the control qubits: all that have a nonzero flag + flags = self.flags or [1] * self.num_variable_qubits + control_qubits = [q for q, flag in zip(qr_variable, flags) if flag != 0] + + # determine the qubits that need to be flipped (if a flag is < 0) + flip_qubits = [q for q, flag in zip(qr_variable, flags) if flag < 0] + + # determine the number of ancillas + if len(flip_qubits) > 0: + circuit.x(flip_qubits) + circuit.mcx(control_qubits, qr_result[:]) + if len(flip_qubits) > 0: + circuit.x(flip_qubits) + + self.definition = circuit + + # pylint: disable=unused-argument + def _inverse(self, annotated: bool = False): + r"""Return inverted AND gate (itself). + + Args: + annotated: when set to ``True``, this is typically used to return an + :class:`.AnnotatedOperation` with an inverse modifier set instead of a concrete + :class:`.Gate`. However, for this class this argument is ignored as this gate + is self-inverse. + + Returns: + AndGate: inverse gate (self-inverse). + """ + return AndGate(self.num_variable_qubits, self.flags) + + def __eq__(self, other): + return ( + isinstance(other, AndGate) + and self.num_variable_qubits == other.num_variable_qubits + and self.flags == other.flags + ) diff --git a/test/python/circuit/library/test_boolean_logic.py b/test/python/circuit/library/test_boolean_logic.py index cdadc9142a97..ac10e4ead80c 100644 --- a/test/python/circuit/library/test_boolean_logic.py +++ b/test/python/circuit/library/test_boolean_logic.py @@ -17,7 +17,7 @@ import numpy as np from qiskit.circuit import QuantumCircuit -from qiskit.circuit.library import XOR, InnerProduct, AND, OR +from qiskit.circuit.library import XOR, InnerProduct, AND, OR, AndGate from qiskit.quantum_info import Statevector from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -26,11 +26,15 @@ class TestBooleanLogicLibrary(QiskitTestCase): """Test library of boolean logic quantum circuits.""" - def assertBooleanFunctionIsCorrect(self, boolean_circuit, reference): - """Assert that ``boolean_circuit`` implements the reference boolean function correctly.""" - circuit = QuantumCircuit(boolean_circuit.num_qubits) - circuit.h(list(range(boolean_circuit.num_variable_qubits))) - circuit.append(boolean_circuit.to_instruction(), list(range(boolean_circuit.num_qubits))) + def assertBooleanFunctionIsCorrect(self, boolean_object, reference): + """Assert that ``boolean_object`` implements the reference boolean function correctly.""" + circuit = QuantumCircuit(boolean_object.num_qubits) + circuit.h(list(range(boolean_object.num_variable_qubits))) + + if isinstance(boolean_object, AndGate): + circuit.append(boolean_object, list(range(boolean_object.num_qubits))) + else: + circuit.append(boolean_object.to_instruction(), list(range(boolean_object.num_qubits))) # compute the statevector of the circuit statevector = Statevector.from_label("0" * circuit.num_qubits) @@ -38,18 +42,18 @@ def assertBooleanFunctionIsCorrect(self, boolean_circuit, reference): # trace out ancillas probabilities = statevector.probabilities( - qargs=list(range(boolean_circuit.num_variable_qubits + 1)) + qargs=list(range(boolean_object.num_variable_qubits + 1)) ) # compute the expected outcome by computing the entries of the statevector that should # have a 1 / sqrt(2**n) factor expectations = np.zeros_like(probabilities) - for x in range(2**boolean_circuit.num_variable_qubits): - bits = np.array(list(bin(x)[2:].zfill(boolean_circuit.num_variable_qubits)), dtype=int) + for x in range(2**boolean_object.num_variable_qubits): + bits = np.array(list(bin(x)[2:].zfill(boolean_object.num_variable_qubits)), dtype=int) result = reference(bits[::-1]) - entry = int(str(int(result)) + bin(x)[2:].zfill(boolean_circuit.num_variable_qubits), 2) - expectations[entry] = 1 / 2**boolean_circuit.num_variable_qubits + entry = int(str(int(result)) + bin(x)[2:].zfill(boolean_object.num_variable_qubits), 2) + expectations[entry] = 1 / 2**boolean_object.num_variable_qubits np.testing.assert_array_almost_equal(probabilities, expectations) @@ -123,6 +127,29 @@ def reference(bits): self.assertBooleanFunctionIsCorrect(and_circuit, reference) + @data( + (2, None), + (2, [-1, 1]), + (5, [0, 0, -1, 1, -1]), + (5, [-1, 0, 0, 1, 1]), + ) + @unpack + def test_and_gate(self, num_variables, flags): + """Test the and circuit.""" + and_gate = AndGate(num_variables, flags) + flags = flags or [1] * num_variables + + def reference(bits): + flagged = [] + for flag, bit in zip(flags, bits): + if flag < 0: + flagged += [1 - bit] + elif flag > 0: + flagged += [bit] + return np.all(flagged) + + self.assertBooleanFunctionIsCorrect(and_gate, reference) + if __name__ == "__main__": unittest.main() diff --git a/test/python/circuit/test_gate_definitions.py b/test/python/circuit/test_gate_definitions.py index 220478159ee3..9fe6d2277efd 100644 --- a/test/python/circuit/test_gate_definitions.py +++ b/test/python/circuit/test_gate_definitions.py @@ -311,6 +311,7 @@ class TestGateEquivalenceEqual(QiskitTestCase): "_SingletonGateOverrides", "_SingletonControlledGateOverrides", "QFTGate", + "AndGate", } # Amazingly, Python's scoping rules for class bodies means that this is the closest we can get From 447d7c3fbc24862cd97a48c88b4e3b3aa64ea0f5 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 16 Oct 2024 10:51:58 +0300 Subject: [PATCH 02/14] more AND tests --- .../library/boolean_logic/quantum_and.py | 2 +- .../circuit/library/test_boolean_logic.py | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/qiskit/circuit/library/boolean_logic/quantum_and.py b/qiskit/circuit/library/boolean_logic/quantum_and.py index ba4401a38c82..593639513e8a 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_and.py +++ b/qiskit/circuit/library/boolean_logic/quantum_and.py @@ -130,7 +130,7 @@ class AndGate(Gate): .. plot:: from qiskit.circuit import QuantumCircuit - from qiskit.circuit.library import AND + from qiskit.circuit.library import AndGate from qiskit.visualization.library import _generate_circuit_library_visualization circuit = QuantumCircuit(6) circuit.append(AndGate(5, flags=[-1, 0, 0, 1, 1]), [0, 1, 2, 3, 4, 5]) diff --git a/test/python/circuit/library/test_boolean_logic.py b/test/python/circuit/library/test_boolean_logic.py index ac10e4ead80c..e3837515cf9f 100644 --- a/test/python/circuit/library/test_boolean_logic.py +++ b/test/python/circuit/library/test_boolean_logic.py @@ -18,7 +18,7 @@ from qiskit.circuit import QuantumCircuit from qiskit.circuit.library import XOR, InnerProduct, AND, OR, AndGate -from qiskit.quantum_info import Statevector +from qiskit.quantum_info import Statevector, Operator from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -150,6 +150,21 @@ def reference(bits): self.assertBooleanFunctionIsCorrect(and_gate, reference) + @data( + (2, None), + (2, [-1, 1]), + (5, [0, 0, -1, 1, -1]), + (5, [-1, 0, 0, 1, 1]), + ) + @unpack + def test_and_equivalence(self, num_variables, flags): + """Test that AND-circuit and AND-gate yield equal operators + (when not using ancilla qubits). + """ + and_gate = AndGate(num_variables, flags) + and_circuit = AND(num_variables, flags) + self.assertEqual(Operator(and_gate), Operator(and_circuit)) + if __name__ == "__main__": unittest.main() From 3dee90e607280f78524af12e37a6f7a90473fc28 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 16 Oct 2024 11:04:34 +0300 Subject: [PATCH 03/14] more AND tests --- .../circuit/library/boolean_logic/quantum_and.py | 6 +++--- test/python/circuit/library/test_boolean_logic.py | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/qiskit/circuit/library/boolean_logic/quantum_and.py b/qiskit/circuit/library/boolean_logic/quantum_and.py index 593639513e8a..5d080d47e2e5 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_and.py +++ b/qiskit/circuit/library/boolean_logic/quantum_and.py @@ -161,8 +161,6 @@ def _define(self): qr_variable = QuantumRegister(self.num_variable_qubits, name="variable") qr_result = QuantumRegister(1, name="result") - circuit = QuantumCircuit(qr_variable, qr_result, name="and") - # determine the control qubits: all that have a nonzero flag flags = self.flags or [1] * self.num_variable_qubits control_qubits = [q for q, flag in zip(qr_variable, flags) if flag != 0] @@ -170,7 +168,9 @@ def _define(self): # determine the qubits that need to be flipped (if a flag is < 0) flip_qubits = [q for q, flag in zip(qr_variable, flags) if flag < 0] - # determine the number of ancillas + # create the definition circuit + circuit = QuantumCircuit(qr_variable, qr_result, name="and") + if len(flip_qubits) > 0: circuit.x(flip_qubits) circuit.mcx(control_qubits, qr_result[:]) diff --git a/test/python/circuit/library/test_boolean_logic.py b/test/python/circuit/library/test_boolean_logic.py index e3837515cf9f..1c941febb288 100644 --- a/test/python/circuit/library/test_boolean_logic.py +++ b/test/python/circuit/library/test_boolean_logic.py @@ -135,7 +135,7 @@ def reference(bits): ) @unpack def test_and_gate(self, num_variables, flags): - """Test the and circuit.""" + """Test correctness of the AND gate.""" and_gate = AndGate(num_variables, flags) flags = flags or [1] * num_variables @@ -150,6 +150,19 @@ def reference(bits): self.assertBooleanFunctionIsCorrect(and_gate, reference) + @data( + (2, None), + (2, [-1, 1]), + (5, [0, 0, -1, 1, -1]), + (5, [-1, 0, 0, 1, 1]), + ) + @unpack + def test_and_gate_inverse(self, num_variables, flags): + """Test correctness of the AND-gate inverse.""" + and_gate = AndGate(num_variables, flags) + and_gate_inverse = and_gate.inverse() + self.assertEqual(Operator(and_gate), Operator(and_gate_inverse).adjoint()) + @data( (2, None), (2, [-1, 1]), From 4131d3a5f33f440ace8601cd8917ab8f0b3e5a67 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 16 Oct 2024 11:37:06 +0300 Subject: [PATCH 04/14] OR -> OrGate --- qiskit/circuit/library/__init__.py | 2 + .../circuit/library/boolean_logic/__init__.py | 2 +- .../library/boolean_logic/quantum_or.py | 108 +++++++++++++++++- .../circuit/library/test_boolean_logic.py | 61 +++++++++- test/python/circuit/test_gate_definitions.py | 1 + 5 files changed, 167 insertions(+), 7 deletions(-) diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index 00bed00f0d7f..046296a9d29f 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -213,6 +213,7 @@ AND AndGate OR + OrGate XOR InnerProduct @@ -526,6 +527,7 @@ AND, AndGate, OR, + OrGate, XOR, InnerProduct, ) diff --git a/qiskit/circuit/library/boolean_logic/__init__.py b/qiskit/circuit/library/boolean_logic/__init__.py index 0540f5acabbd..9b4d3e601e29 100644 --- a/qiskit/circuit/library/boolean_logic/__init__.py +++ b/qiskit/circuit/library/boolean_logic/__init__.py @@ -13,6 +13,6 @@ """The Boolean logic circuit library.""" from .quantum_and import AND, AndGate -from .quantum_or import OR +from .quantum_or import OR, OrGate from .quantum_xor import XOR from .inner_product import InnerProduct diff --git a/qiskit/circuit/library/boolean_logic/quantum_or.py b/qiskit/circuit/library/boolean_logic/quantum_or.py index 91d6c4fbdd52..88955eeb99ab 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_or.py +++ b/qiskit/circuit/library/boolean_logic/quantum_or.py @@ -15,8 +15,9 @@ from __future__ import annotations from typing import List, Optional -from qiskit.circuit import QuantumRegister, QuantumCircuit, AncillaRegister +from qiskit.circuit import QuantumRegister, QuantumCircuit, AncillaRegister, Gate from qiskit.circuit.library.standard_gates import MCXGate +from qiskit.utils.deprecation import deprecate_func class OR(QuantumCircuit): @@ -50,6 +51,11 @@ class OR(QuantumCircuit): """ + @deprecate_func( + since="1.3", + additional_msg="Use qiskit.circuit.library.AndGate instead.", + pending=True, + ) def __init__( self, num_variable_qubits: int, @@ -96,3 +102,103 @@ def __init__( super().__init__(*circuit.qregs, name="or") self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) + + +class OrGate(Gate): + r"""A gate representing the logical OR operation on a number of qubits. + + For the OR operation the state :math:`|1\rangle` is interpreted as ``True``. The result + qubit is flipped, if the state of any variable qubit is ``True``. The OR is implemented using + a multi-open-controlled X gate (i.e. flips if the state is :math:`|0\rangle`) and + applying an X gate on the result qubit. + Using a list of flags, qubits can be skipped or negated. + + The OrGate gate without special flags: + + .. plot:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import OrGate + from qiskit.visualization.library import _generate_circuit_library_visualization + circuit = QuantumCircuit(6) + circuit.append(OrGate(5), [0, 1, 2, 3, 4, 5]) + _generate_circuit_library_visualization(circuit) + + Using flags we can negate qubits or skip them. For instance, if we have 5 qubits and want to + return ``True`` if the first qubit is ``False`` or one of the last two are ``True`` we use the + flags ``[-1, 0, 0, 1, 1]``. + + .. plot:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import OrGate + from qiskit.visualization.library import _generate_circuit_library_visualization + circuit = QuantumCircuit(6) + circuit.append(OrGate(5, flags=[-1, 0, 0, 1, 1]), [0, 1, 2, 3, 4, 5]) + _generate_circuit_library_visualization(circuit) + + """ + + def __init__( + self, + num_variable_qubits: int, + flags: list[int] | None = None, + ) -> None: + """Create a new AndGate. + + Args: + num_variable_qubits: The qubits of which the AND is computed. The result will be written + into an additional result qubit. + flags: A list of +1/0/-1 marking negations or omissions of qubits. + """ + super().__init__("and", num_variable_qubits + 1, []) + self.num_variable_qubits = num_variable_qubits + self.flags = flags + + def _define(self): + """Create definition circuit.""" + + # add registers + qr_variable = QuantumRegister(self.num_variable_qubits, name="variable") + qr_result = QuantumRegister(1, name="result") + + # determine the control qubits: all that have a nonzero flag + flags = self.flags or [1] * self.num_variable_qubits + control_qubits = [q for q, flag in zip(qr_variable, flags) if flag != 0] + + # determine the qubits that need to be flipped (if a flag is > 0) + flip_qubits = [q for q, flag in zip(qr_variable, flags) if flag > 0] + + # create the definition circuit + circuit = QuantumCircuit(qr_variable, qr_result, name="or") + + circuit.x(qr_result) + if len(flip_qubits) > 0: + circuit.x(flip_qubits) + circuit.mcx(control_qubits, qr_result[:]) + if len(flip_qubits) > 0: + circuit.x(flip_qubits) + + self.definition = circuit + + # pylint: disable=unused-argument + def _inverse(self, annotated: bool = False): + r"""Return inverted OR gate (itself). + + Args: + annotated: when set to ``True``, this is typically used to return an + :class:`.AnnotatedOperation` with an inverse modifier set instead of a concrete + :class:`.Gate`. However, for this class this argument is ignored as this gate + is self-inverse. + + Returns: + AndGate: inverse gate (self-inverse). + """ + return OrGate(self.num_variable_qubits, self.flags) + + def __eq__(self, other): + return ( + isinstance(other, OrGate) + and self.num_variable_qubits == other.num_variable_qubits + and self.flags == other.flags + ) diff --git a/test/python/circuit/library/test_boolean_logic.py b/test/python/circuit/library/test_boolean_logic.py index 1c941febb288..eb801e6267f6 100644 --- a/test/python/circuit/library/test_boolean_logic.py +++ b/test/python/circuit/library/test_boolean_logic.py @@ -16,8 +16,8 @@ from ddt import ddt, data, unpack import numpy as np -from qiskit.circuit import QuantumCircuit -from qiskit.circuit.library import XOR, InnerProduct, AND, OR, AndGate +from qiskit.circuit import QuantumCircuit, Gate +from qiskit.circuit.library import XOR, InnerProduct, AND, OR, AndGate, OrGate from qiskit.quantum_info import Statevector, Operator from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -31,7 +31,7 @@ def assertBooleanFunctionIsCorrect(self, boolean_object, reference): circuit = QuantumCircuit(boolean_object.num_qubits) circuit.h(list(range(boolean_object.num_variable_qubits))) - if isinstance(boolean_object, AndGate): + if isinstance(boolean_object, Gate): circuit.append(boolean_object, list(range(boolean_object.num_qubits))) else: circuit.append(boolean_object.to_instruction(), list(range(boolean_object.num_qubits))) @@ -104,6 +104,57 @@ def reference(bits): self.assertBooleanFunctionIsCorrect(or_circuit, reference) + @data( + (2, None), + (2, [-1, 1]), + (5, [0, 0, -1, 1, -1]), + (5, [-1, 0, 0, 1, 1]), + ) + @unpack + def test_or_gate(self, num_variables, flags): + """Test correctness of the OrGate.""" + or_gate = OrGate(num_variables, flags) + flags = flags or [1] * num_variables + + def reference(bits): + flagged = [] + for flag, bit in zip(flags, bits): + if flag < 0: + flagged += [1 - bit] + elif flag > 0: + flagged += [bit] + return np.any(flagged) + + self.assertBooleanFunctionIsCorrect(or_gate, reference) + + @data( + (2, None), + (2, [-1, 1]), + (5, [0, 0, -1, 1, -1]), + (5, [-1, 0, 0, 1, 1]), + ) + @unpack + def test_or_gate_inverse(self, num_variables, flags): + """Test correctness of the OrGate's inverse.""" + or_gate = OrGate(num_variables, flags) + or_gate_inverse = or_gate.inverse() + self.assertEqual(Operator(or_gate), Operator(or_gate_inverse).adjoint()) + + @data( + (2, None), + (2, [-1, 1]), + (5, [0, 0, -1, 1, -1]), + (5, [-1, 0, 0, 1, 1]), + ) + @unpack + def test_or_equivalence(self, num_variables, flags): + """Test that OR-circuit and OrGate yield equal operators + (when not using ancilla qubits). + """ + or_gate = OrGate(num_variables, flags) + or_circuit = OR(num_variables, flags) + self.assertEqual(Operator(or_gate), Operator(or_circuit)) + @data( (2, None, "noancilla"), (2, [-1, 1], "v-chain"), @@ -112,7 +163,7 @@ def reference(bits): ) @unpack def test_and(self, num_variables, flags, mcx_mode): - """Test the and circuit.""" + """Test the AND-circuit.""" and_circuit = AND(num_variables, flags, mcx_mode=mcx_mode) flags = flags or [1] * num_variables @@ -135,7 +186,7 @@ def reference(bits): ) @unpack def test_and_gate(self, num_variables, flags): - """Test correctness of the AND gate.""" + """Test correctness of the AndGate.""" and_gate = AndGate(num_variables, flags) flags = flags or [1] * num_variables diff --git a/test/python/circuit/test_gate_definitions.py b/test/python/circuit/test_gate_definitions.py index 9fe6d2277efd..0ed4897ae331 100644 --- a/test/python/circuit/test_gate_definitions.py +++ b/test/python/circuit/test_gate_definitions.py @@ -312,6 +312,7 @@ class TestGateEquivalenceEqual(QiskitTestCase): "_SingletonControlledGateOverrides", "QFTGate", "AndGate", + "OrGate", } # Amazingly, Python's scoping rules for class bodies means that this is the closest we can get From 58d665f7e4d32d135d3de660531eb464aa3d726b Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 16 Oct 2024 11:38:12 +0300 Subject: [PATCH 05/14] fix --- qiskit/circuit/library/boolean_logic/quantum_or.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/circuit/library/boolean_logic/quantum_or.py b/qiskit/circuit/library/boolean_logic/quantum_or.py index 88955eeb99ab..d8102608eb8a 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_or.py +++ b/qiskit/circuit/library/boolean_logic/quantum_or.py @@ -53,7 +53,7 @@ class OR(QuantumCircuit): @deprecate_func( since="1.3", - additional_msg="Use qiskit.circuit.library.AndGate instead.", + additional_msg="Use qiskit.circuit.library.OrGate instead.", pending=True, ) def __init__( From 85d4fa2a83e9dc1530f851cb2fe2e2c1521c6464 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 16 Oct 2024 12:46:15 +0300 Subject: [PATCH 06/14] XOR -> BitwiseXorGate --- qiskit/circuit/library/__init__.py | 2 + .../circuit/library/boolean_logic/__init__.py | 2 +- .../library/boolean_logic/quantum_xor.py | 83 ++++++++++++++++++- .../circuit/library/test_boolean_logic.py | 20 ++++- test/python/circuit/test_gate_definitions.py | 1 + 5 files changed, 105 insertions(+), 3 deletions(-) diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index 046296a9d29f..4a24bd7ef7cc 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -215,6 +215,7 @@ OR OrGate XOR + BitwiseXorGate InnerProduct Basis Change Circuits @@ -529,6 +530,7 @@ OR, OrGate, XOR, + BitwiseXorGate, InnerProduct, ) from .basis_change import QFT, QFTGate diff --git a/qiskit/circuit/library/boolean_logic/__init__.py b/qiskit/circuit/library/boolean_logic/__init__.py index 9b4d3e601e29..e3df281c0d2e 100644 --- a/qiskit/circuit/library/boolean_logic/__init__.py +++ b/qiskit/circuit/library/boolean_logic/__init__.py @@ -14,5 +14,5 @@ from .quantum_and import AND, AndGate from .quantum_or import OR, OrGate -from .quantum_xor import XOR +from .quantum_xor import XOR, BitwiseXorGate, random_bitwise_xor from .inner_product import InnerProduct diff --git a/qiskit/circuit/library/boolean_logic/quantum_xor.py b/qiskit/circuit/library/boolean_logic/quantum_xor.py index 5b230345137d..5bb21fb49919 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_xor.py +++ b/qiskit/circuit/library/boolean_logic/quantum_xor.py @@ -16,8 +16,9 @@ from typing import Optional import numpy as np -from qiskit.circuit import QuantumCircuit +from qiskit.circuit import QuantumCircuit, Gate from qiskit.circuit.exceptions import CircuitError +from qiskit.utils.deprecation import deprecate_func class XOR(QuantumCircuit): @@ -28,6 +29,11 @@ class XOR(QuantumCircuit): This circuit can also represent addition by ``amount`` over the finite field GF(2). """ + @deprecate_func( + since="1.3", + additional_msg="Use qiskit.circuit.library.BitwiseXorGate instead.", + pending=True, + ) def __init__( self, num_qubits: int, @@ -69,3 +75,78 @@ def __init__( super().__init__(*circuit.qregs, name="xor") self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) + + +class BitwiseXorGate(Gate): + """An n-qubit gate for bitwise xor-ing the input with some integer ``amount``. + + The ``amount`` is xor-ed in bitstring form with the input. + + This gate can also represent addition by ``amount`` over the finite field GF(2). + + Reference Circuit: + + .. plot:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import BitwiseXorGate + from qiskit.visualization.library import _generate_circuit_library_visualization + circuit = QuantumCircuit(5) + circuit.append(BitwiseXorGate(5, amount=12), [0, 1, 2, 3, 4]) + _generate_circuit_library_visualization(circuit) + + """ + + def __init__( + self, + num_qubits: int, + amount: int, + ) -> None: + """Return a circuit implementing bitwise xor. + + Args: + num_qubits: the width of circuit. + amount: the xor amount in decimal form. + + Raises: + CircuitError: if the xor bitstring exceeds available qubits. + + """ + if len(bin(amount)[2:]) > num_qubits: + raise CircuitError("Bits in 'amount' exceed circuit width") + + super().__init__("xor", num_qubits, []) + self.amount = amount + + def _define(self): + """Create definition circuit.""" + circuit = QuantumCircuit(self.num_qubits, name="xor") + amount = self.amount + for i in range(self.num_qubits): + bit = amount & 1 + amount = amount >> 1 + if bit == 1: + circuit.x(i) + + self.definition = circuit + + def __eq__(self, other): + return ( + isinstance(other, BitwiseXorGate) + and self.num_qubits == other.num_qubits + and self.amount == other.amount + ) + + +def random_bitwise_xor(num_qubits: int, seed: int) -> BitwiseXorGate: + """ + Create a random BitwiseXorGate. + + Args: + num_qubits: the width of circuit. + seed: random seed in case a random xor is requested. + """ + + rng = np.random.default_rng(seed) + amount = rng.integers(0, 2**num_qubits) + return BitwiseXorGate(num_qubits, amount) diff --git a/test/python/circuit/library/test_boolean_logic.py b/test/python/circuit/library/test_boolean_logic.py index eb801e6267f6..535e909dc894 100644 --- a/test/python/circuit/library/test_boolean_logic.py +++ b/test/python/circuit/library/test_boolean_logic.py @@ -17,7 +17,7 @@ import numpy as np from qiskit.circuit import QuantumCircuit, Gate -from qiskit.circuit.library import XOR, InnerProduct, AND, OR, AndGate, OrGate +from qiskit.circuit.library import XOR, InnerProduct, AND, OR, AndGate, OrGate, BitwiseXorGate from qiskit.quantum_info import Statevector, Operator from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -67,6 +67,24 @@ def test_xor(self): expected.x(2) self.assertEqual(circuit.decompose(), expected) + def test_xor_gate(self): + """Test XOR-gate.""" + xor_gate = BitwiseXorGate(num_qubits=3, amount=4) + expected = QuantumCircuit(3) + expected.x(2) + self.assertEqual(Operator(xor_gate), Operator(expected)) + + @data( + (5, 12), + (6, 21), + ) + @unpack + def test_xor_equivalence(self, num_qubits, amount): + """Test that XR-circuit and BitwiseXorGate yield equal operators.""" + xor_gate = BitwiseXorGate(num_qubits, amount) + xor_circuit = XOR(num_qubits, amount) + self.assertEqual(Operator(xor_gate), Operator(xor_circuit)) + def test_inner_product(self): """Test inner product circuit. diff --git a/test/python/circuit/test_gate_definitions.py b/test/python/circuit/test_gate_definitions.py index 0ed4897ae331..1377cf78066e 100644 --- a/test/python/circuit/test_gate_definitions.py +++ b/test/python/circuit/test_gate_definitions.py @@ -313,6 +313,7 @@ class TestGateEquivalenceEqual(QiskitTestCase): "QFTGate", "AndGate", "OrGate", + "BitwiseXorGate", } # Amazingly, Python's scoping rules for class bodies means that this is the closest we can get From 9f4468c652084a7fa1fc0e849f22525d41920fcd Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 16 Oct 2024 13:33:51 +0300 Subject: [PATCH 07/14] InnerProduct -> InnerProductGate --- qiskit/circuit/library/__init__.py | 2 + .../circuit/library/boolean_logic/__init__.py | 2 +- .../library/boolean_logic/inner_product.py | 82 ++++++++++++++++++- .../library/boolean_logic/quantum_and.py | 5 +- .../library/boolean_logic/quantum_or.py | 5 +- .../library/boolean_logic/quantum_xor.py | 4 +- .../circuit/library/test_boolean_logic.py | 29 ++++++- test/python/circuit/test_gate_definitions.py | 1 + 8 files changed, 118 insertions(+), 12 deletions(-) diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index 4a24bd7ef7cc..f73849e699d9 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -217,6 +217,7 @@ XOR BitwiseXorGate InnerProduct + InnerProductGate Basis Change Circuits ===================== @@ -532,6 +533,7 @@ XOR, BitwiseXorGate, InnerProduct, + InnerProductGate, ) from .basis_change import QFT, QFTGate from .arithmetic import ( diff --git a/qiskit/circuit/library/boolean_logic/__init__.py b/qiskit/circuit/library/boolean_logic/__init__.py index e3df281c0d2e..736f983b05f9 100644 --- a/qiskit/circuit/library/boolean_logic/__init__.py +++ b/qiskit/circuit/library/boolean_logic/__init__.py @@ -15,4 +15,4 @@ from .quantum_and import AND, AndGate from .quantum_or import OR, OrGate from .quantum_xor import XOR, BitwiseXorGate, random_bitwise_xor -from .inner_product import InnerProduct +from .inner_product import InnerProduct, InnerProductGate diff --git a/qiskit/circuit/library/boolean_logic/inner_product.py b/qiskit/circuit/library/boolean_logic/inner_product.py index 6efbc8a02913..b899f8e6e1d3 100644 --- a/qiskit/circuit/library/boolean_logic/inner_product.py +++ b/qiskit/circuit/library/boolean_logic/inner_product.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2020. +# (C) Copyright IBM 2020, 2024. # # 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 @@ -11,10 +11,11 @@ # that they have been altered from the originals. -"""InnerProduct circuit.""" +"""InnerProduct circuit and gate.""" -from qiskit.circuit import QuantumRegister, QuantumCircuit +from qiskit.circuit import QuantumRegister, QuantumCircuit, Gate +from qiskit.utils.deprecation import deprecate_func class InnerProduct(QuantumCircuit): @@ -61,6 +62,11 @@ class InnerProduct(QuantumCircuit): _generate_circuit_library_visualization(circuit) """ + @deprecate_func( + since="1.3", + additional_msg="Use qiskit.circuit.library.InnerProductGate instead.", + pending=True, + ) def __init__(self, num_qubits: int) -> None: """Return a circuit to compute the inner product of 2 n-qubit registers. @@ -76,3 +82,73 @@ def __init__(self, num_qubits: int) -> None: super().__init__(*inner.qregs, name="inner_product") self.compose(inner.to_gate(), qubits=self.qubits, inplace=True) + + +class InnerProductGate(Gate): + r"""A 2n-qubit Boolean function that computes the inner product of + two n-qubit vectors over :math:`F_2`. + + This implementation is a phase oracle which computes the following transform. + + .. math:: + + \mathcal{IP}_{2n} : F_2^{2n} \rightarrow {-1, 1} + \mathcal{IP}_{2n}(x_1, \cdots, x_n, y_1, \cdots, y_n) = (-1)^{x.y} + + The corresponding unitary is a diagonal, which induces a -1 phase on any inputs + where the inner product of the top and bottom registers is 1. Otherwise, it keeps + the input intact. + + .. parsed-literal:: + + + q0_0: ─■────────── + │ + q0_1: ─┼──■─────── + │ │ + q0_2: ─┼──┼──■──── + │ │ │ + q0_3: ─┼──┼──┼──■─ + │ │ │ │ + q1_0: ─■──┼──┼──┼─ + │ │ │ + q1_1: ────■──┼──┼─ + │ │ + q1_2: ───────■──┼─ + │ + q1_3: ──────────■─ + + + Reference Circuit: + .. plot:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import InnerProductGate + from qiskit.visualization.library import _generate_circuit_library_visualization + circuit = QuantumCircuit(8) + circuit.append(InnerProductGate(4), [0, 1, 2, 3, 4, 5, 6, 7]) + _generate_circuit_library_visualization(circuit) + """ + + def __init__( + self, + num_qubits: int, + ) -> None: + """Return a 2n-qubit gate representing the inner product of 2 n-qubit registers. + + Args: + num_qubits: width of top and bottom registers (half total number of qubits). + """ + super().__init__("inner_product", 2 * num_qubits, []) + + def _define(self): + """Create definition circuit.""" + num_qubits = self.num_qubits // 2 + qr_a = QuantumRegister(num_qubits) + qr_b = QuantumRegister(num_qubits) + + circuit = QuantumCircuit(qr_a, qr_b, name="inner_product") + for i in range(num_qubits): + circuit.cz(qr_a[i], qr_b[i]) + + self.definition = circuit diff --git a/qiskit/circuit/library/boolean_logic/quantum_and.py b/qiskit/circuit/library/boolean_logic/quantum_and.py index 5d080d47e2e5..768fcb5f5d30 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_and.py +++ b/qiskit/circuit/library/boolean_logic/quantum_and.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2020. +# (C) Copyright IBM 2020, 2024. # # 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 @@ -11,7 +11,8 @@ # that they have been altered from the originals. -"""Implementations of boolean logic quantum circuits.""" +"""Boolean AND circuit and gate.""" + from __future__ import annotations from qiskit.circuit import QuantumRegister, QuantumCircuit, AncillaRegister, Gate diff --git a/qiskit/circuit/library/boolean_logic/quantum_or.py b/qiskit/circuit/library/boolean_logic/quantum_or.py index d8102608eb8a..b4e2c5450c22 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_or.py +++ b/qiskit/circuit/library/boolean_logic/quantum_or.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2020. +# (C) Copyright IBM 2020, 2024. # # 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 @@ -11,7 +11,8 @@ # that they have been altered from the originals. -"""Implementations of boolean logic quantum circuits.""" +"""Boolean OR circuit and gate.""" + from __future__ import annotations from typing import List, Optional diff --git a/qiskit/circuit/library/boolean_logic/quantum_xor.py b/qiskit/circuit/library/boolean_logic/quantum_xor.py index 5bb21fb49919..07de37186c69 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_xor.py +++ b/qiskit/circuit/library/boolean_logic/quantum_xor.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2020. +# (C) Copyright IBM 2020, 2024. # # 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 @@ -11,7 +11,7 @@ # that they have been altered from the originals. -"""XOR circuit.""" +"""Bitwise XOR circuit and gate.""" from typing import Optional diff --git a/test/python/circuit/library/test_boolean_logic.py b/test/python/circuit/library/test_boolean_logic.py index 535e909dc894..21faef401172 100644 --- a/test/python/circuit/library/test_boolean_logic.py +++ b/test/python/circuit/library/test_boolean_logic.py @@ -17,7 +17,16 @@ import numpy as np from qiskit.circuit import QuantumCircuit, Gate -from qiskit.circuit.library import XOR, InnerProduct, AND, OR, AndGate, OrGate, BitwiseXorGate +from qiskit.circuit.library import ( + XOR, + InnerProduct, + AND, + OR, + AndGate, + OrGate, + BitwiseXorGate, + InnerProductGate, +) from qiskit.quantum_info import Statevector, Operator from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -80,7 +89,7 @@ def test_xor_gate(self): ) @unpack def test_xor_equivalence(self, num_qubits, amount): - """Test that XR-circuit and BitwiseXorGate yield equal operators.""" + """Test that XOR-circuit and BitwiseXorGate yield equal operators.""" xor_gate = BitwiseXorGate(num_qubits, amount) xor_circuit = XOR(num_qubits, amount) self.assertEqual(Operator(xor_gate), Operator(xor_circuit)) @@ -97,6 +106,22 @@ def test_inner_product(self): expected.cz(2, 5) self.assertEqual(circuit.decompose(), expected) + def test_inner_product_gate(self): + """Test inner product gate.""" + inner_product = InnerProductGate(num_qubits=3) + expected = QuantumCircuit(6) + expected.cz(0, 3) + expected.cz(1, 4) + expected.cz(2, 5) + self.assertEqual(Operator(inner_product), Operator(expected)) + + @data(4, 5, 6) + def test_inner_product_equivalence(self, num_qubits): + """Test that XOR-circuit and BitwiseXorGate yield equal operators.""" + inner_product_gate = InnerProductGate(num_qubits) + inner_product_circuit = InnerProduct(num_qubits) + self.assertEqual(Operator(inner_product_gate), Operator(inner_product_circuit)) + @data( (2, None, "noancilla"), (5, None, "noancilla"), diff --git a/test/python/circuit/test_gate_definitions.py b/test/python/circuit/test_gate_definitions.py index 1377cf78066e..ba2546b2e8a5 100644 --- a/test/python/circuit/test_gate_definitions.py +++ b/test/python/circuit/test_gate_definitions.py @@ -314,6 +314,7 @@ class TestGateEquivalenceEqual(QiskitTestCase): "AndGate", "OrGate", "BitwiseXorGate", + "InnerProductGate", } # Amazingly, Python's scoping rules for class bodies means that this is the closest we can get From b679fc452802da64c47ae72b5845ae900c53af41 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 16 Oct 2024 13:46:08 +0300 Subject: [PATCH 08/14] reno --- .../boolean-logic-gates-40add5cf0b20b5e9.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml diff --git a/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml b/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml new file mode 100644 index 000000000000..d4d8c3119fe7 --- /dev/null +++ b/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml @@ -0,0 +1,14 @@ +--- +features_circuits: + - | + Quantum circuits in :mod:`qiskit.circuit.library.boolean_logic` now have equivalent + representations as Gates: + + * :class:`~qiskit.circuit.library.AndGate`, + representing :class:`~qiskit.circuit.library.AND`, + * :class:`~qiskit.circuit.library.OrGate`, + representing :class:`~qiskit.circuit.library.OR`, + * :class:`~qiskit.circuit.library.BitwiseXorGate`, + representing :class:`~qiskit.circuit.library.XOR`, + * :class:`~qiskit.circuit.library.InnerProductGate`, + representing :class:`~qiskit.circuit.library.InnerProduct`. From 63c5951293ffe0f99f1b49b8af2eef8ffd167773 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 30 Oct 2024 12:16:20 +0200 Subject: [PATCH 09/14] applying review comments --- qiskit/circuit/library/boolean_logic/inner_product.py | 8 +++----- qiskit/circuit/library/boolean_logic/quantum_and.py | 7 ++----- qiskit/circuit/library/boolean_logic/quantum_or.py | 7 ++----- qiskit/circuit/library/boolean_logic/quantum_xor.py | 5 +---- .../notes/boolean-logic-gates-40add5cf0b20b5e9.yaml | 2 +- 5 files changed, 9 insertions(+), 20 deletions(-) diff --git a/qiskit/circuit/library/boolean_logic/inner_product.py b/qiskit/circuit/library/boolean_logic/inner_product.py index b899f8e6e1d3..e6bd749d3aaa 100644 --- a/qiskit/circuit/library/boolean_logic/inner_product.py +++ b/qiskit/circuit/library/boolean_logic/inner_product.py @@ -134,18 +134,16 @@ def __init__( self, num_qubits: int, ) -> None: - """Return a 2n-qubit gate representing the inner product of 2 n-qubit registers. - + """ Args: num_qubits: width of top and bottom registers (half total number of qubits). """ super().__init__("inner_product", 2 * num_qubits, []) def _define(self): - """Create definition circuit.""" num_qubits = self.num_qubits // 2 - qr_a = QuantumRegister(num_qubits) - qr_b = QuantumRegister(num_qubits) + qr_a = QuantumRegister(num_qubits, name="x") + qr_b = QuantumRegister(num_qubits, name="y") circuit = QuantumCircuit(qr_a, qr_b, name="inner_product") for i in range(num_qubits): diff --git a/qiskit/circuit/library/boolean_logic/quantum_and.py b/qiskit/circuit/library/boolean_logic/quantum_and.py index 768fcb5f5d30..53099aa7be84 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_and.py +++ b/qiskit/circuit/library/boolean_logic/quantum_and.py @@ -144,8 +144,7 @@ def __init__( num_variable_qubits: int, flags: list[int] | None = None, ) -> None: - """Create a new AndGate. - + """ Args: num_variable_qubits: The qubits of which the AND is computed. The result will be written into an additional result qubit. @@ -156,8 +155,6 @@ def __init__( self.flags = flags def _define(self): - """Create definition circuit.""" - # add registers qr_variable = QuantumRegister(self.num_variable_qubits, name="variable") qr_result = QuantumRegister(1, name="result") @@ -181,7 +178,7 @@ def _define(self): self.definition = circuit # pylint: disable=unused-argument - def _inverse(self, annotated: bool = False): + def inverse(self, annotated: bool = False): r"""Return inverted AND gate (itself). Args: diff --git a/qiskit/circuit/library/boolean_logic/quantum_or.py b/qiskit/circuit/library/boolean_logic/quantum_or.py index b4e2c5450c22..fe879b2dce63 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_or.py +++ b/qiskit/circuit/library/boolean_logic/quantum_or.py @@ -145,8 +145,7 @@ def __init__( num_variable_qubits: int, flags: list[int] | None = None, ) -> None: - """Create a new AndGate. - + """ Args: num_variable_qubits: The qubits of which the AND is computed. The result will be written into an additional result qubit. @@ -157,8 +156,6 @@ def __init__( self.flags = flags def _define(self): - """Create definition circuit.""" - # add registers qr_variable = QuantumRegister(self.num_variable_qubits, name="variable") qr_result = QuantumRegister(1, name="result") @@ -183,7 +180,7 @@ def _define(self): self.definition = circuit # pylint: disable=unused-argument - def _inverse(self, annotated: bool = False): + def inverse(self, annotated: bool = False): r"""Return inverted OR gate (itself). Args: diff --git a/qiskit/circuit/library/boolean_logic/quantum_xor.py b/qiskit/circuit/library/boolean_logic/quantum_xor.py index 07de37186c69..8d2db0576c8b 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_xor.py +++ b/qiskit/circuit/library/boolean_logic/quantum_xor.py @@ -102,15 +102,13 @@ def __init__( num_qubits: int, amount: int, ) -> None: - """Return a circuit implementing bitwise xor. - + """ Args: num_qubits: the width of circuit. amount: the xor amount in decimal form. Raises: CircuitError: if the xor bitstring exceeds available qubits. - """ if len(bin(amount)[2:]) > num_qubits: raise CircuitError("Bits in 'amount' exceed circuit width") @@ -119,7 +117,6 @@ def __init__( self.amount = amount def _define(self): - """Create definition circuit.""" circuit = QuantumCircuit(self.num_qubits, name="xor") amount = self.amount for i in range(self.num_qubits): diff --git a/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml b/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml index d4d8c3119fe7..6d8329ee12f4 100644 --- a/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml +++ b/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml @@ -2,7 +2,7 @@ features_circuits: - | Quantum circuits in :mod:`qiskit.circuit.library.boolean_logic` now have equivalent - representations as Gates: + representations as :class:`.Gate` objects:: * :class:`~qiskit.circuit.library.AndGate`, representing :class:`~qiskit.circuit.library.AND`, From e8f4423c08f3ded23fbd83abbb8b6c93b4c07bb2 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 30 Oct 2024 13:01:48 +0200 Subject: [PATCH 10/14] adding missing eq and inverse methods + tests --- .../library/boolean_logic/inner_product.py | 3 +++ .../circuit/library/boolean_logic/quantum_xor.py | 15 +++++++++++++++ test/python/circuit/library/test_boolean_logic.py | 15 +++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/qiskit/circuit/library/boolean_logic/inner_product.py b/qiskit/circuit/library/boolean_logic/inner_product.py index e27f2ff04ac7..84b3807fb56c 100644 --- a/qiskit/circuit/library/boolean_logic/inner_product.py +++ b/qiskit/circuit/library/boolean_logic/inner_product.py @@ -150,3 +150,6 @@ def _define(self): circuit.cz(qr_a[i], qr_b[i]) self.definition = circuit + + def __eq__(self, other): + return isinstance(other, InnerProductGate) and self.num_qubits == other.num_qubits diff --git a/qiskit/circuit/library/boolean_logic/quantum_xor.py b/qiskit/circuit/library/boolean_logic/quantum_xor.py index 8d2db0576c8b..3c7535460725 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_xor.py +++ b/qiskit/circuit/library/boolean_logic/quantum_xor.py @@ -134,6 +134,21 @@ def __eq__(self, other): and self.amount == other.amount ) + # pylint: disable=unused-argument + def inverse(self, annotated: bool = False): + r"""Return inverted BitwiseXorGate gate (itself). + + Args: + annotated: when set to ``True``, this is typically used to return an + :class:`.AnnotatedOperation` with an inverse modifier set instead of a concrete + :class:`.Gate`. However, for this class this argument is ignored as this gate + is self-inverse. + + Returns: + BitwiseXorGate: inverse gate (self-inverse). + """ + return BitwiseXorGate(self.num_qubits, self.amount) + def random_bitwise_xor(num_qubits: int, seed: int) -> BitwiseXorGate: """ diff --git a/test/python/circuit/library/test_boolean_logic.py b/test/python/circuit/library/test_boolean_logic.py index 21faef401172..265f494f4c0c 100644 --- a/test/python/circuit/library/test_boolean_logic.py +++ b/test/python/circuit/library/test_boolean_logic.py @@ -94,6 +94,21 @@ def test_xor_equivalence(self, num_qubits, amount): xor_circuit = XOR(num_qubits, amount) self.assertEqual(Operator(xor_gate), Operator(xor_circuit)) + def test_xor_eq(self): + """Test BitwiseXorGate's equality method.""" + xor1 = BitwiseXorGate(num_qubits=5, amount=10) + xor2 = BitwiseXorGate(num_qubits=5, amount=10) + xor3 = BitwiseXorGate(num_qubits=5, amount=11) + self.assertEqual(xor1, xor2) + self.assertNotEqual(xor1, xor3) + + def test_xor_inverse(self): + """Test correctness of the BitwiseXorGate's inverse.""" + xor_gate = BitwiseXorGate(num_qubits=5, amount=10) + xor_gate_inverse = xor_gate.inverse() + self.assertEqual(xor_gate, xor_gate_inverse) + self.assertEqual(Operator(xor_gate), Operator(xor_gate_inverse).adjoint()) + def test_inner_product(self): """Test inner product circuit. From 97bdf6ff2a2a43cedd760008ead65c997377b4a3 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 30 Oct 2024 13:48:14 +0200 Subject: [PATCH 11/14] docstring fix --- qiskit/circuit/library/boolean_logic/quantum_or.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/circuit/library/boolean_logic/quantum_or.py b/qiskit/circuit/library/boolean_logic/quantum_or.py index fe879b2dce63..95b346b34846 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_or.py +++ b/qiskit/circuit/library/boolean_logic/quantum_or.py @@ -190,7 +190,7 @@ def inverse(self, annotated: bool = False): is self-inverse. Returns: - AndGate: inverse gate (self-inverse). + OrGate: inverse gate (self-inverse). """ return OrGate(self.num_variable_qubits, self.flags) From 9f9053ebbf136093435163949e895e4aa464a18c Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 30 Oct 2024 13:59:57 +0200 Subject: [PATCH 12/14] docstring and API ref --- qiskit/circuit/library/__init__.py | 2 ++ qiskit/circuit/library/boolean_logic/quantum_xor.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index b90459f72c20..f6e737cba4e8 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -216,6 +216,7 @@ OrGate XOR BitwiseXorGate + random_bitwise_xor InnerProduct InnerProductGate @@ -532,6 +533,7 @@ OrGate, XOR, BitwiseXorGate, + random_bitwise_xor, InnerProduct, InnerProductGate, ) diff --git a/qiskit/circuit/library/boolean_logic/quantum_xor.py b/qiskit/circuit/library/boolean_logic/quantum_xor.py index 3c7535460725..73a2178830bc 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_xor.py +++ b/qiskit/circuit/library/boolean_logic/quantum_xor.py @@ -31,7 +31,8 @@ class XOR(QuantumCircuit): @deprecate_func( since="1.3", - additional_msg="Use qiskit.circuit.library.BitwiseXorGate instead.", + additional_msg="Instead, for xor-ing with a specified amount, use BitwiseXorGate," + "and for xor-ing with a random amount, use random_bitwise_xor.", pending=True, ) def __init__( From 1d9a608120409687b3e6eccad214f12b656fb642 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 30 Oct 2024 14:59:59 +0200 Subject: [PATCH 13/14] fix reno --- releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml b/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml index 6d8329ee12f4..d1de403ab4f7 100644 --- a/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml +++ b/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml @@ -2,7 +2,7 @@ features_circuits: - | Quantum circuits in :mod:`qiskit.circuit.library.boolean_logic` now have equivalent - representations as :class:`.Gate` objects:: + representations as :class:`.Gate` objects: * :class:`~qiskit.circuit.library.AndGate`, representing :class:`~qiskit.circuit.library.AND`, From e0720c11acfaece240fc5e397868e8c33064663f Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 30 Oct 2024 16:59:05 +0200 Subject: [PATCH 14/14] docstring fix --- qiskit/circuit/library/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index f6e737cba4e8..4a763856c82d 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -216,10 +216,11 @@ OrGate XOR BitwiseXorGate - random_bitwise_xor InnerProduct InnerProductGate +.. autofunction:: random_bitwise_xor + Basis Change Circuits =====================