Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arithmetic circuit library: Adders #6164

Merged
merged 29 commits into from
May 21, 2021
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
be646c0
Implement ripple-carry adder circuit (#11)
mantcep Mar 28, 2021
e86916e
Merge branch 'master' into adders
Cryoris Mar 28, 2021
be569a3
Implement QFT adder (#22)
mantcep Mar 29, 2021
b2dab3f
Implemented classical adder in QFT paper (#13)
ManjulaGandhi Apr 6, 2021
38a759f
unify tests
Cryoris Apr 6, 2021
8768780
inject base class ``Adder``
Cryoris Apr 6, 2021
ccee887
add reno
Cryoris Apr 6, 2021
d74b1d6
fix lint
Cryoris Apr 6, 2021
a544a2e
Merge branch 'master' into adders
Cryoris Apr 6, 2021
11da532
Merge branch 'master' into adders
Cryoris Apr 14, 2021
599af6f
fix trailing whitespace in line beginning
Cryoris Apr 20, 2021
0abbaa4
rework docstrings for less duplication
Cryoris Apr 20, 2021
e95d660
Merge branch 'main' into adders
Cryoris Apr 24, 2021
f0bdb97
several updates
Cryoris Apr 27, 2021
335b2ce
lint
Cryoris Apr 27, 2021
a0cc65c
rename classical to plain, make plain modular
Cryoris May 2, 2021
89535de
Renamed PlainAdder to VBERippleCarryAdder (#29)
ManjulaGandhi May 7, 2021
ac5463e
rename modular->fixed point
Cryoris May 7, 2021
5e93a37
add author names to adders
Cryoris May 7, 2021
1b096ec
add comment on how the test works
Cryoris May 7, 2021
ba9e100
Merge branch 'main' into adders
Cryoris May 7, 2021
b5079b8
black
Cryoris May 7, 2021
6196144
add fixed/half/full, misses test for full
Cryoris May 19, 2021
d6a72df
add test for "full"
Cryoris May 20, 2021
b488399
black
Cryoris May 20, 2021
b5887b0
update docstrings
Cryoris May 20, 2021
ceedc70
fix sphinx
Cryoris May 20, 2021
456020a
rename fixed-point to fixed-sized
Cryoris May 20, 2021
9061abd
Merge branch 'main' into adders
mergify[bot] May 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions qiskit/circuit/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@
.. autosummary::
:toctree: ../stubs/

ClassicalAdder
QFTAdder
RippleCarryAdder
WeightedAdder

Comparators
Expand Down Expand Up @@ -354,8 +357,12 @@
WeightedAdder,
QuadraticForm,
LinearAmplitudeFunction,
ClassicalAdder,
RippleCarryAdder,
QFTAdder,
PiecewiseChebyshev,
)

from .n_local import (
NLocal,
TwoLocal,
Expand Down
3 changes: 2 additions & 1 deletion qiskit/circuit/library/arithmetic/__init__.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -21,4 +21,5 @@
from .weighted_adder import WeightedAdder
from .quadratic_form import QuadraticForm
from .linear_amplitude_function import LinearAmplitudeFunction
from .adders import ClassicalAdder, RippleCarryAdder, QFTAdder
from .piecewise_chebyshev import PiecewiseChebyshev
17 changes: 17 additions & 0 deletions qiskit/circuit/library/arithmetic/adders/__init__.py
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 .ripple_carry_adder import RippleCarryAdder
from .qft_adder import QFTAdder
from .classical_adder import ClassicalAdder
58 changes: 58 additions & 0 deletions qiskit/circuit/library/arithmetic/adders/adder.py
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
118 changes: 118 additions & 0 deletions qiskit/circuit/library/arithmetic/adders/classical_adder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# 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

from .adder import Adder


class ClassicalAdder(Adder):
r"""A circuit that uses Classical Addition to perform in-place addition on two qubit registers.

As an example, a classical adder circuit that performs addition on two 3-qubit sized
registers is as follows:

.. parsed-literal::

┌────────┐ ┌────────┐┌──────┐
a_0: ┤1 ├───────────────────────────────────────────────────┤1 ├┤1 ├
│ │┌────────┐ ┌────────┐┌──────┐│ ││ │
a_1: ┤ ├┤1 ├───────────────────────┤1 ├┤1 ├┤ ├┤ ├
│ ││ │┌────────┐ ┌──────┐│ ││ ││ ││ │
a_2: ┤ ├┤ ├┤1 ├──■──┤1 ├┤ ├┤ ├┤ ├┤ ├
│ ││ ││ │ │ │ ││ ││ ││ ││ │
b_0: ┤2 ├┤ ├┤ ├──┼──┤ ├┤ ├┤ ├┤2 ├┤2 ├
│ ││ ││ │ │ │ ││ ││ ││ ││ Sum │
b_1: ┤ Carry ├┤2 ├┤ ├──┼──┤ ├┤2 ├┤2 ├┤ Carry ├┤ ├
│ ││ ││ │┌─┴─┐│ ││ ││ Sum ││ ││ │
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] T. G. Draper, Addition on a Quantum Computer, 2000.
`arXiv:quant-ph/0008033 <https://arxiv.org/pdf/quant-ph/0008033.pdf>`_
Cryoris marked this conversation as resolved.
Show resolved Hide resolved

"""

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.')

super().__init__(num_state_qubits, name=name)

# define the registers
qr_a = QuantumRegister(num_state_qubits, name='a')
qr_b = QuantumRegister(num_state_qubits, name='b')
qr_cin = AncillaRegister(num_state_qubits, name='cin')
qr_cout = QuantumRegister(1, name='cout')

# initialize the registers
self.add_register(qr_a, qr_b, qr_cout, qr_cin)

# 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_gate()

# 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_gate()

# 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.inverse(), [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]])
109 changes: 109 additions & 0 deletions qiskit/circuit/library/arithmetic/adders/qft_adder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# 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 QFTAdder(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 `modular=True`) or ordinary addition by adding a carry qubits (with
`modular=False`).

As an example, a non-modular 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 ├
└──────┘ └───────┘

Cryoris marked this conversation as resolved.
Show resolved Hide resolved
**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,
modular: bool = False,
name: str = 'QFTAdder'
) -> 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.
modular: Whether addition is modular with mod :math:`2^n`.
Additional qubit is attached in case of non-modular addition
to carry the most significant qubit of the sum.
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)

qr_a = QuantumRegister(num_state_qubits, name='a')
qr_b = QuantumRegister(num_state_qubits, name='b')
qr_list = [qr_a, qr_b]

if not modular:
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 modular else qr_b[:] + qr_z[:]
num_qubits_qft = num_state_qubits if modular 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 not modular:
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[:])
Loading