diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index eefa33d48632..bc7d11ba2f34 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -142,6 +142,7 @@ .. autosummary:: :toctree: ../stubs/ + ClassicalAdder WeightedAdder Comparators @@ -311,9 +312,11 @@ WeightedAdder, QuadraticForm, LinearAmplitudeFunction, + ClassicalAdder, RippleCarryAdder, QFTAdder ) + from .n_local import ( NLocal, TwoLocal, diff --git a/qiskit/circuit/library/arithmetic/__init__.py b/qiskit/circuit/library/arithmetic/__init__.py index c59065c6c4d9..44769fe16063 100644 --- a/qiskit/circuit/library/arithmetic/__init__.py +++ b/qiskit/circuit/library/arithmetic/__init__.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2020. +# (C) Copyright IBM 2021. # # 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 @@ -21,4 +21,6 @@ from .weighted_adder import WeightedAdder from .quadratic_form import QuadraticForm from .linear_amplitude_function import LinearAmplitudeFunction -from .adders import RippleCarryAdder, QFTAdder +from .adders import ClassicalAdder, RippleCarryAdder, QFTAdder + + diff --git a/qiskit/circuit/library/arithmetic/adders/__init__.py b/qiskit/circuit/library/arithmetic/adders/__init__.py index dd384b022167..43c1ca96074a 100644 --- a/qiskit/circuit/library/arithmetic/adders/__init__.py +++ b/qiskit/circuit/library/arithmetic/adders/__init__.py @@ -14,3 +14,4 @@ from .ripple_carry_adder import RippleCarryAdder from .qft_adder import QFTAdder +from .classical_adder import ClassicalAdder \ No newline at end of file diff --git a/qiskit/circuit/library/arithmetic/adders/classical_adder.py b/qiskit/circuit/library/arithmetic/adders/classical_adder.py new file mode 100644 index 000000000000..bdf6154ae2c1 --- /dev/null +++ b/qiskit/circuit/library/arithmetic/adders/classical_adder.py @@ -0,0 +1,128 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2021. +# +# 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 + +"""Compute the sum of two qubit registers using Classical Addition.""" + +from qiskit.circuit import QuantumCircuit, QuantumRegister, AncillaRegister + + +class ClassicalAdder(QuantumCircuit): + r"""A circuit that uses Classical Addition to perform in-place addition on two qubit registers. + + Circuit to compute the sum of two qubit registers using the Classical Addition Part from [1]. + Given two equally sized input registers that store quantum states + :math:`|a\rangle` and :math:`|b\rangle`, performs addition of numbers that + can be represented by the states, storing the resulting state in-place in the second register: + + .. math:: + + |a\rangle |b\rangle \mapsto |a\rangle |a+b\rangle + + Here :math:`|a\rangle` (and correspondingly :math:`|b\rangle`) stands for the direct product + :math:`|a_n\rangle \otimes |a_{n-1}\rangle \ldots |a_{1}\rangle \otimes |a_{0}\rangle` + which denotes a quantum register prepared with the value :math:`a = 2^{0}a_{0} + 2^{1}a_{1} + + \ldots 2^{n}a_{n}`[2]. + As an example, a classical adder circuit that performs addition on two 3-qubit sized + registers is as follows: + + .. parsed-literal:: + + ┌────────┐ ┌────────┐┌──────┐ + input_a_0: ┤1 ├───────────────────────────────────────────────────┤1 ├┤1 ├ + │ │┌────────┐ ┌────────┐┌──────┐│ ││ │ + input_a_1: ┤ ├┤1 ├───────────────────────┤1 ├┤1 ├┤ ├┤ ├ + │ ││ │┌────────┐ ┌──────┐│ ││ ││ ││ │ + input_a_2: ┤ ├┤ ├┤1 ├──■──┤1 ├┤ ├┤ ├┤ ├┤ ├ + │ ││ ││ │ │ │ ││ ││ ││ ││ │ + input_b_0: ┤2 ├┤ ├┤ ├──┼──┤ ├┤ ├┤ ├┤2 ├┤2 ├ + │ ││ ││ │ │ │ ││ ││ ││ ││ Sum │ + input_b_1: ┤ Carry ├┤2 ├┤ ├──┼──┤ ├┤2 ├┤2 ├┤ Carry ├┤ ├ + │ ││ ││ │┌─┴─┐│ ││ ││ Sum ││ ││ │ + input_b_2: ┤ ├┤ Carry ├┤2 ├┤ X ├┤2 ├┤ Carry ├┤ ├┤ ├┤ ├ + │ ││ ││ Carry │└───┘│ Sum ││ ││ ││ ││ │ + cout_0: ┤ ├┤ ├┤3 ├─────┤ ├┤ ├┤ ├┤ ├┤ ├ + │ ││ ││ │ │ ││ ││ ││ ││ │ + cin_0: ┤0 ├┤ ├┤ ├─────┤ ├┤ ├┤ ├┤0 ├┤0 ├ + │ ││ ││ │ │ ││ ││ ││ │└──────┘ + cin_1: ┤3 ├┤0 ├┤ ├─────┤ ├┤0 ├┤0 ├┤3 ├──────── + └────────┘│ ││ │ │ ││ │└──────┘└────────┘ + cin_2: ──────────┤3 ├┤0 ├─────┤0 ├┤3 ├────────────────────────── + └────────┘└────────┘ └──────┘└────────┘ + + + Here *Carry* and *Sum* gates correspond to the gates introduced in [1]. Note that + in this implementation the input register qubits are ordered as all qubits from + the first input register, followed by all qubits from the second input register. + This is different ordering as compared to Figure 2 in [1], which leads to a different + drawing of the circuit. + + **References** + + [1] Thomas G.Draper, 2000. "Addition on a Quantum Computer" + `Journal https://arxiv.org/pdf/quant-ph/0008033.pdf`_ + + [2] Vedral et al., Quantum Networks for Elementary Arithmetic Operations, 1995. + `arXiv:quant-ph/9511018 `_ + """ + + def __init__(self, + num_state_qubits: int, + name: str = 'ClassicalAdder' + ) -> None: + """ + Args: + num_state_qubits: The size of the register. + name: The name of the circuit. + Raises: + ValueError: If ``num_state_qubits`` is lower than 1. + """ + if num_state_qubits < 1: + raise ValueError('The number of qubits must be at least 1.') + # define the registers + qr_a = QuantumRegister(num_state_qubits, name='input_a') + qr_b = QuantumRegister(num_state_qubits, name='input_b') + qr_cin = AncillaRegister(num_state_qubits, name='cin') + qr_cout = QuantumRegister(1, name='cout') + + # initialize the circuit + super().__init__(qr_a, qr_b, qr_cout, qr_cin, name=name) + + #corresponds to Carry gate in [1] + qc_carry = QuantumCircuit(4, name='Carry') + qc_carry.ccx(1, 2, 3) + qc_carry.cx(1, 2) + qc_carry.ccx(0, 2, 3) + qc_instruction_carry = qc_carry.to_instruction() + + #corresponds to Sum gate in [1] + qc_sum = QuantumCircuit(3, name='Sum') + qc_sum.cx(1, 2) + qc_sum.cx(0, 2) + qc_instruction_sum = qc_sum.to_instruction() + + # Build a temporary subcircuit that adds a to b, + # storing the result in b + + for j in range(num_state_qubits - 1): + self.append(qc_instruction_carry, [qr_cin[j], qr_a[j], qr_b[j], qr_cin[j+1]]) + + self.append(qc_instruction_carry, [qr_cin[num_state_qubits - 1], + qr_a[num_state_qubits - 1], qr_b[num_state_qubits - 1], + qr_cout]) + self.cx(qr_a[num_state_qubits - 1], qr_b[num_state_qubits - 1]) + self.append(qc_instruction_sum, [qr_cin[num_state_qubits - 1], + qr_a[num_state_qubits - 1], qr_b[num_state_qubits - 1]]) + + for j in reversed(range(num_state_qubits - 1)): + self.append(qc_instruction_carry, [qr_cin[j], qr_a[j], qr_b[j], qr_cin[j+1]]) + self.append(qc_instruction_sum, [qr_cin[j], qr_a[j], qr_b[j]]) + diff --git a/test/python/circuit/library/adders/test_classicaladder.py b/test/python/circuit/library/adders/test_classicaladder.py new file mode 100644 index 000000000000..ee4f60431f62 --- /dev/null +++ b/test/python/circuit/library/adders/test_classicaladder.py @@ -0,0 +1,84 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2021. +# +# 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. + +"""Test adder circuits.""" + +import unittest +import numpy as np +from ddt import ddt, data, unpack + +from qiskit.test.base import QiskitTestCase +from qiskit.circuit import QuantumCircuit +from qiskit.quantum_info import Statevector, partial_trace +from qiskit.circuit.library import ClassicalAdder + + +@ddt +class TestAdder(QiskitTestCase): + """Test the adder circuits.""" + + def assertAdditionIsCorrect(self, num_state_qubits, adder, inplace): + """Assert that adder correctly implements the summation w.r.t. its set weights.""" + circuit = QuantumCircuit(*adder.qregs) + # create equal superposition + circuit.h(range(2 * num_state_qubits)) + # apply adder circuit + circuit.compose(adder, inplace=True) + # obtain the statevector + statevector = Statevector(circuit) + # trace out the ancillas if necessary + if circuit.num_ancillas > 0: + ancillas = list(range(circuit.num_qubits - circuit.num_ancillas, circuit.num_qubits)) + probabilities = np.diagonal(partial_trace(statevector, ancillas)) + else: + probabilities = np.abs(statevector) ** 2 + # compute the expected results + expectations = np.zeros_like(probabilities) + num_bits_sum = num_state_qubits + 1 + # iterate over all possible inputs + for x in range(2 ** num_state_qubits): + for y in range(2 ** num_state_qubits): + # compute the sum + addition = x + y + # compute correct index in statevector + bin_x = bin(x)[2:].zfill(num_state_qubits) + bin_y = bin(y)[2:].zfill(num_state_qubits) + bin_res = bin(addition)[2:].zfill(num_bits_sum) + bin_index = bin_res + bin_x if inplace else bin_res + bin_y + bin_x + index = int(bin_index, 2) + expectations[index] += 1 / 2 ** (2 * num_state_qubits) + np.testing.assert_array_almost_equal(expectations, probabilities) + + @data( + (1, ClassicalAdder, True), + (2, ClassicalAdder, True), + (5, ClassicalAdder, True) + # other adders to be added here + ) + @unpack + def test_summation(self, num_state_qubits, adder, inplace): + """Test summation for all implemented adders.""" + adder = adder(num_state_qubits) + self.assertAdditionIsCorrect(num_state_qubits, adder, inplace) + + @data( + ClassicalAdder + # other adders to be added here + ) + def test_raises_on_wrong_num_bits(self, adder): + """Test an error is raised for a bad number of qubits.""" + with self.assertRaises(ValueError): + _ = adder(-1) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file