forked from Qiskit/qiskit
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Arithmetic circuit library: Adders (Qiskit#6164)
* Implement ripple-carry adder circuit (#11) * Implement initial draft of ripple-carry adder circuit * Switch to ancilla register for ancilla qubits * Remove padding for simpler implementation * Add small fixes from review * Add initial unit tests Co-authored-by: Julien Gacon <jules.gacon@googlemail.com> * Switch carry out from ancilla to usual register * Update __init__ docstring with raises * Add suggestions from review; Clean-up Co-authored-by: Julien Gacon <jules.gacon@googlemail.com> * Add additional test for ripple-carry adder Co-authored-by: Julien Gacon <jules.gacon@googlemail.com> * Implement QFT adder (#22) * Implement initial draft of ripple-carry adder circuit * Switch to ancilla register for ancilla qubits * Remove padding for simpler implementation * Add small fixes from review * Add initial unit tests Co-authored-by: Julien Gacon <jules.gacon@googlemail.com> * Switch carry out from ancilla to usual register * Update __init__ docstring with raises * Add suggestions from review; Clean-up Co-authored-by: Julien Gacon <jules.gacon@googlemail.com> * Add additional test for ripple-carry adder * Implement initial QFT adder circuit Co-authored-by: Julien Gacon <jules.gacon@googlemail.com> * Fix QFT adder carry out; Switch to non-modular as default * Expand adder tests with QFT adder * Rename test_adder.py -> test_adders.py * Include additional QFT adder documentation * Fix typos * Fix documentation math Co-authored-by: Julien Gacon <jules.gacon@googlemail.com> * Add non-modular test as default Co-authored-by: Julien Gacon <jules.gacon@googlemail.com> * Remove duplicate file after merge Co-authored-by: Julien Gacon <jules.gacon@googlemail.com> * Implemented classical adder in QFT paper (#13) * empty * empty * Implemented classical adder in QFT paper * Implemented classical adder in QFT paper * fix classicaladd imports * fix missing imports * Added the tests part for ClassicalAdd * move cin to bottom * Updated the documentation and test parts for ClassicalAdder * Updated init files * Deleted adder folder from the git repository * Deleted test_classicaladd.py * Deleted old classicaladder file * Deleted __init__.py file from repository * Updated classical_adder file Co-authored-by: Cryoris <jules.gacon@googlemail.com> Co-authored-by: Julien Gacon <gaconju@gmail.com> * unify tests * inject base class ``Adder`` * add reno * fix lint * fix trailing whitespace in line beginning * rework docstrings for less duplication * several updates * modular RippleCarryAdder * test that ancillas are uncomputed * fix ClassicalAdder ancilla uncompute * replace all to_instruction by to_gate * lint * rename classical to plain, make plain modular * Renamed PlainAdder to VBERippleCarryAdder (#29) Co-authored-by: Cryoris <jules.gacon@googlemail.com> * rename modular->fixed point and fix lint * add author names to adders * add comment on how the test works * black * add fixed/half/full, misses test for full * add test for "full" * black * update docstrings * fix sphinx * rename fixed-point to fixed-sized Co-authored-by: Mantas Čepulkovskis <60826723+mantcep@users.noreply.github.com> Co-authored-by: Manjula <64902594+ManjulaGandhi@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
- Loading branch information
1 parent
c795068
commit 9efd63d
Showing
9 changed files
with
693 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# 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. | ||
|
||
"""The adder circuit library.""" | ||
|
||
from .cdkm_ripple_carry_adder import CDKMRippleCarryAdder | ||
from .draper_qft_adder import DraperQFTAdder | ||
from .vbe_ripple_carry_adder import VBERippleCarryAdder |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# 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 equally sized qubit registers.""" | ||
|
||
from qiskit.circuit import QuantumCircuit | ||
|
||
|
||
class Adder(QuantumCircuit): | ||
r"""Compute the sum of two equally sized qubit registers. | ||
For two registers :math:`|a\rangle_n` and :math:|b\rangle_n` with :math:`n` qubits each, an | ||
adder performs the following operation | ||
.. math:: | ||
|a\rangle_n |b\rangle_n \mapsto |a\rangle_n |a + b\rangle_{n + 1}. | ||
The quantum register :math:`|a\rangle_n` (and analogously :math:`|b\rangle_n`) | ||
.. math:: | ||
|a\rangle_n = |a_0\rangle \otimes \cdots \otimes |a_{n - 1}\rangle, | ||
for :math:`a_i \in \{0, 1\}`, is associated with the integer value | ||
.. math:: | ||
a = 2^{0}a_{0} + 2^{1}a_{1} + \cdots + 2^{n - 1}a_{n - 1}. | ||
""" | ||
|
||
def __init__(self, num_state_qubits: int, name: str = "Adder") -> None: | ||
""" | ||
Args: | ||
num_state_qubits: The number of qubits in each of the registers. | ||
name: The name of the circuit. | ||
""" | ||
super().__init__(name=name) | ||
self._num_state_qubits = num_state_qubits | ||
|
||
@property | ||
def num_state_qubits(self) -> int: | ||
"""The number of state qubits, i.e. the number of bits in each input register. | ||
Returns: | ||
The number of state qubits. | ||
""" | ||
return self._num_state_qubits |
155 changes: 155 additions & 0 deletions
155
qiskit/circuit/library/arithmetic/adders/cdkm_ripple_carry_adder.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
# 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 ripple-carry approach.""" | ||
|
||
from qiskit.circuit import QuantumCircuit, QuantumRegister, AncillaRegister | ||
|
||
from .adder import Adder | ||
|
||
|
||
class CDKMRippleCarryAdder(Adder): | ||
r"""A ripple-carry circuit to perform in-place addition on two qubit registers. | ||
As an example, a ripple-carry adder circuit that performs addition on two 3-qubit sized | ||
registers with a carry-in bit (``kind="full"``) is as follows: | ||
.. parsed-literal:: | ||
┌──────┐ ┌──────┐ | ||
cin_0: ┤2 ├─────────────────────────────────────┤2 ├ | ||
│ │┌──────┐ ┌──────┐│ │ | ||
a_0: ┤0 ├┤2 ├─────────────────────┤2 ├┤0 ├ | ||
│ ││ │┌──────┐ ┌──────┐│ ││ │ | ||
a_1: ┤ MAJ ├┤0 ├┤2 ├─────┤2 ├┤0 ├┤ UMA ├ | ||
│ ││ ││ │ │ ││ ││ │ | ||
a_2: ┤ ├┤ MAJ ├┤0 ├──■──┤0 ├┤ UMA ├┤ ├ | ||
│ ││ ││ │ │ │ ││ ││ │ | ||
b_0: ┤1 ├┤ ├┤ MAJ ├──┼──┤ UMA ├┤ ├┤1 ├ | ||
└──────┘│ ││ │ │ │ ││ │└──────┘ | ||
b_1: ────────┤1 ├┤ ├──┼──┤ ├┤1 ├──────── | ||
└──────┘│ │ │ │ │└──────┘ | ||
b_2: ────────────────┤1 ├──┼──┤1 ├──────────────── | ||
└──────┘┌─┴─┐└──────┘ | ||
cout_0: ────────────────────────┤ X ├──────────────────────── | ||
└───┘ | ||
Here *MAJ* and *UMA* 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. | ||
Two different kinds of adders are supported. By setting the ``kind`` argument, you can also | ||
choose a half-adder, which doesn't have a carry-in, and a fixed-sized-adder, which has neither | ||
carry-in nor carry-out, and thus acts on fixed register sizes. Unlike the full-adder, | ||
these circuits need one additional helper qubit. | ||
The circuit diagram for the fixed-point adder (``kind="fixed"``) on 3-qubit sized inputs is | ||
.. parsed-literal:: | ||
┌──────┐┌──────┐ ┌──────┐┌──────┐ | ||
a_0: ┤0 ├┤2 ├────────────────┤2 ├┤0 ├ | ||
│ ││ │┌──────┐┌──────┐│ ││ │ | ||
a_1: ┤ ├┤0 ├┤2 ├┤2 ├┤0 ├┤ ├ | ||
│ ││ ││ ││ ││ ││ │ | ||
a_2: ┤ ├┤ MAJ ├┤0 ├┤0 ├┤ UMA ├┤ ├ | ||
│ ││ ││ ││ ││ ││ │ | ||
b_0: ┤1 MAJ ├┤ ├┤ MAJ ├┤ UMA ├┤ ├┤1 UMA ├ | ||
│ ││ ││ ││ ││ ││ │ | ||
b_1: ┤ ├┤1 ├┤ ├┤ ├┤1 ├┤ ├ | ||
│ │└──────┘│ ││ │└──────┘│ │ | ||
b_2: ┤ ├────────┤1 ├┤1 ├────────┤ ├ | ||
│ │ └──────┘└──────┘ │ │ | ||
help_0: ┤2 ├────────────────────────────────┤2 ├ | ||
└──────┘ └──────┘ | ||
It has one less qubit than the full-adder since it doesn't have the carry-out, but uses | ||
a helper qubit instead of the carry-in, so it only has one less qubit, not two. | ||
**References:** | ||
[1] Cuccaro et al., A new quantum ripple-carry addition circuit, 2004. | ||
`arXiv:quant-ph/0410184 <https://arxiv.org/pdf/quant-ph/0410184.pdf>`_ | ||
[2] Vedral et al., Quantum Networks for Elementary Arithmetic Operations, 1995. | ||
`arXiv:quant-ph/9511018 <https://arxiv.org/pdf/quant-ph/9511018.pdf>`_ | ||
""" | ||
|
||
def __init__( | ||
self, num_state_qubits: int, kind: str = "full", name: str = "CDKMRippleCarryAdder" | ||
) -> None: | ||
r""" | ||
Args: | ||
num_state_qubits: The number of qubits in either input register for | ||
state :math:`|a\rangle` or :math:`|b\rangle`. The two input | ||
registers must have the same number of qubits. | ||
kind: The kind of adder, can be ``'full'`` for a full adder, ``'half'`` for a half | ||
adder, or ``'fixed'`` for a fixed-sized adder. A full adder includes both carry-in | ||
and carry-out, a half only carry-out, and a fixed-sized adder neither carry-in | ||
nor carry-out. | ||
name: The name of the circuit object. | ||
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.") | ||
|
||
super().__init__(num_state_qubits, name=name) | ||
|
||
if kind == "full": | ||
qr_c = QuantumRegister(1, name="cin") | ||
self.add_register(qr_c) | ||
else: | ||
qr_c = AncillaRegister(1, name="help") | ||
|
||
qr_a = QuantumRegister(num_state_qubits, name="a") | ||
qr_b = QuantumRegister(num_state_qubits, name="b") | ||
self.add_register(qr_a, qr_b) | ||
|
||
if kind in ["full", "half"]: | ||
qr_z = QuantumRegister(1, name="cout") | ||
self.add_register(qr_z) | ||
|
||
if kind != "full": | ||
self.add_register(qr_c) | ||
|
||
# build carry circuit for majority of 3 bits in-place | ||
# corresponds to MAJ gate in [1] | ||
qc_maj = QuantumCircuit(3, name="MAJ") | ||
qc_maj.cx(0, 1) | ||
qc_maj.cx(0, 2) | ||
qc_maj.ccx(2, 1, 0) | ||
maj_gate = qc_maj.to_gate() | ||
|
||
# build circuit for reversing carry operation | ||
# corresponds to UMA gate in [1] | ||
qc_uma = QuantumCircuit(3, name="UMA") | ||
qc_uma.ccx(2, 1, 0) | ||
qc_uma.cx(0, 2) | ||
qc_uma.cx(2, 1) | ||
uma_gate = qc_uma.to_gate() | ||
|
||
# build ripple-carry adder circuit | ||
self.append(maj_gate, [qr_a[0], qr_b[0], qr_c[0]]) | ||
|
||
for i in range(num_state_qubits - 1): | ||
self.append(maj_gate, [qr_a[i + 1], qr_b[i + 1], qr_a[i]]) | ||
|
||
if kind in ["full", "half"]: | ||
self.cx(qr_a[-1], qr_z[0]) | ||
|
||
for i in reversed(range(num_state_qubits - 1)): | ||
self.append(uma_gate, [qr_a[i + 1], qr_b[i + 1], qr_a[i]]) | ||
|
||
self.append(uma_gate, [qr_a[0], qr_b[0], qr_c[0]]) |
111 changes: 111 additions & 0 deletions
111
qiskit/circuit/library/arithmetic/adders/draper_qft_adder.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
# 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 QFT.""" | ||
|
||
import numpy as np | ||
|
||
from qiskit.circuit.quantumregister import QuantumRegister | ||
from qiskit.circuit.library.basis_change import QFT | ||
|
||
from .adder import Adder | ||
|
||
|
||
class DraperQFTAdder(Adder): | ||
r"""A circuit that uses QFT to perform in-place addition on two qubit registers. | ||
For registers with :math:`n` qubits, the QFT adder can perform addition modulo | ||
:math:`2^n` (with ``kind="fixed"``) or ordinary addition by adding a carry qubits (with | ||
``kind="half"``). | ||
As an example, a non-fixed_point QFT adder circuit that performs addition on two 2-qubit sized | ||
registers is as follows: | ||
.. parsed-literal:: | ||
a_0: ─────────■──────■────────────────────────■──────────────── | ||
│ │ │ | ||
a_1: ─────────┼──────┼────────■──────■────────┼──────────────── | ||
┌──────┐ │P(π) │ │ │ │ ┌───────┐ | ||
b_0: ┤0 ├─■──────┼────────┼──────┼────────┼───────┤0 ├ | ||
│ │ │P(π/2) │P(π) │ │ │ │ | ||
b_1: ┤1 qft ├────────■────────■──────┼────────┼───────┤1 iqft ├ | ||
│ │ │P(π/2) │P(π/4) │ │ | ||
cout_0: ┤2 ├────────────────────────■────────■───────┤2 ├ | ||
└──────┘ └───────┘ | ||
**References:** | ||
[1] T. G. Draper, Addition on a Quantum Computer, 2000. | ||
`arXiv:quant-ph/0008033 <https://arxiv.org/pdf/quant-ph/0008033.pdf>`_ | ||
[2] Ruiz-Perez et al., Quantum arithmetic with the Quantum Fourier Transform, 2017. | ||
`arXiv:1411.5949 <https://arxiv.org/pdf/1411.5949.pdf>`_ | ||
[3] Vedral et al., Quantum Networks for Elementary Arithmetic Operations, 1995. | ||
`arXiv:quant-ph/9511018 <https://arxiv.org/pdf/quant-ph/9511018.pdf>`_ | ||
""" | ||
|
||
def __init__( | ||
self, num_state_qubits: int, kind: str = "fixed", name: str = "DraperQFTAdder" | ||
) -> None: | ||
r""" | ||
Args: | ||
num_state_qubits: The number of qubits in either input register for | ||
state :math:`|a\rangle` or :math:`|b\rangle`. The two input | ||
registers must have the same number of qubits. | ||
kind: The kind of adder, can be ``'half'`` for a half adder or | ||
``'fixed'`` for a fixed-sized adder. A half adder contains a carry-out to represent | ||
the most-significant bit, but the fixed-sized adder doesn't and hence performs | ||
addition modulo ``2 ** num_state_qubits``. | ||
name: The name of the circuit object. | ||
Raises: | ||
ValueError: If ``num_state_qubits`` is lower than 1. | ||
""" | ||
if kind == "full": | ||
raise ValueError("The DraperQFTAdder only supports 'half' and 'fixed' as ``kind``.") | ||
|
||
if num_state_qubits < 1: | ||
raise ValueError("The number of qubits must be at least 1.") | ||
|
||
super().__init__(num_state_qubits, name=name) | ||
|
||
qr_a = QuantumRegister(num_state_qubits, name="a") | ||
qr_b = QuantumRegister(num_state_qubits, name="b") | ||
qr_list = [qr_a, qr_b] | ||
|
||
if kind == "half": | ||
qr_z = QuantumRegister(1, name="cout") | ||
qr_list.append(qr_z) | ||
|
||
# add registers | ||
self.add_register(*qr_list) | ||
|
||
# define register containing the sum and number of qubits for QFT circuit | ||
qr_sum = qr_b[:] if kind == "fixed" else qr_b[:] + qr_z[:] | ||
num_qubits_qft = num_state_qubits if kind == "fixed" else num_state_qubits + 1 | ||
|
||
# build QFT adder circuit | ||
self.append(QFT(num_qubits_qft, do_swaps=False).to_gate(), qr_sum[:]) | ||
|
||
for j in range(num_state_qubits): | ||
for k in range(num_state_qubits - j): | ||
lam = np.pi / (2 ** k) | ||
self.cp(lam, qr_a[j], qr_b[j + k]) | ||
|
||
if kind == "half": | ||
for j in range(num_state_qubits): | ||
lam = np.pi / (2 ** (j + 1)) | ||
self.cp(lam, qr_a[num_state_qubits - j - 1], qr_z[0]) | ||
|
||
self.append(QFT(num_qubits_qft, do_swaps=False).inverse().to_gate(), qr_sum[:]) |
Oops, something went wrong.