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: multipliers #6470

Merged
merged 100 commits into from
Jun 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
09699ad
Implement initial draft of ripple-carry adder circuit
mantcep Mar 8, 2021
5c6d2a8
Switch to ancilla register for ancilla qubits
mantcep Mar 10, 2021
425a8ce
Remove padding for simpler implementation
mantcep Mar 10, 2021
e843300
Add small fixes from review
mantcep Mar 11, 2021
334bff1
Add initial unit tests
mantcep Mar 13, 2021
f62f6be
Switch carry out from ancilla to usual register
mantcep Mar 13, 2021
0bae830
Update __init__ docstring with raises
mantcep Mar 13, 2021
c774707
Add suggestions from review; Clean-up
mantcep Mar 20, 2021
beaaf9d
Add additional test for ripple-carry adder
mantcep Mar 20, 2021
3412e70
Merge branch 'master' into qft-adder
mantcep Mar 24, 2021
a193b95
Implement initial QFT adder circuit
mantcep Mar 28, 2021
f31a548
Fix QFT adder carry out; Switch to non-modular as default
mantcep Mar 28, 2021
88d1273
Expand adder tests with QFT adder
mantcep Mar 28, 2021
4359bc8
Rename test_adder.py -> test_adders.py
mantcep Mar 28, 2021
0cbe476
Include additional QFT adder documentation
mantcep Mar 28, 2021
7c1a750
Fix typos
mantcep Mar 28, 2021
be646c0
Implement ripple-carry adder circuit (#11)
mantcep Mar 28, 2021
e86916e
Merge branch 'master' into adders
Cryoris Mar 28, 2021
40e7e50
Merge with adders branch
mantcep Mar 29, 2021
6a7dd78
Fix documentation math
mantcep Mar 29, 2021
4d48def
Add non-modular test as default
mantcep Mar 29, 2021
8ba44cd
Remove duplicate file after merge
mantcep Mar 29, 2021
be569a3
Implement QFT adder (#22)
mantcep Mar 29, 2021
ea1d5f1
Merge branch 'adders' of https://github.com/Cryoris/qiskit-terra into…
mantcep Apr 5, 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
53ab793
Merge branch 'adders' of https://github.com/Cryoris/qiskit-terra into…
mantcep Apr 7, 2021
11da532
Merge branch 'master' into adders
Cryoris Apr 14, 2021
e62e233
Implement initial classical multiplier
mantcep Apr 18, 2021
a62efb9
Implement test for multipliers
mantcep Apr 18, 2021
89cc44f
Update docstring
mantcep Apr 18, 2021
599af6f
fix trailing whitespace in line beginning
Cryoris Apr 20, 2021
0abbaa4
rework docstrings for less duplication
Cryoris Apr 20, 2021
9147cbb
Change instructions to gates in adder circuits
mantcep Apr 24, 2021
2d71e34
Generalize multiplier for any adder
mantcep Apr 24, 2021
a29bf1f
Expand multiplier test with all adders
mantcep Apr 24, 2021
b3532b3
Merge branch 'adders' of https://github.com/Cryoris/qiskit-terra into…
mantcep Apr 24, 2021
10dbbdc
Fix pylint errors
mantcep Apr 24, 2021
e95d660
Merge branch 'main' into adders
Cryoris Apr 24, 2021
cff6fc9
Implement review suggestions
mantcep Apr 25, 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
a635799
Implement initial QFT multiplier draft
mantcep May 6, 2021
ed4fed6
Update QFT multiplier docstring
mantcep May 7, 2021
17db640
Merge with cryoris/adders branch
mantcep May 7, 2021
c5d224a
Create common multiplier class
mantcep May 7, 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
f43fdbd
Fix common multiplier errors
mantcep May 8, 2021
d052626
Merge branch 'adders' of https://github.com/Cryoris/qiskit-terra into…
mantcep May 9, 2021
95fd92c
Update classical multiplier default adder name
mantcep May 9, 2021
951605f
Fix QFT multiplier registers
mantcep May 9, 2021
53c5ac1
Implement tests for multipliers
mantcep May 9, 2021
6f99f35
Fix lint
mantcep May 9, 2021
7a41f92
Add black formatter changes
mantcep May 17, 2021
9a5dca5
Switch to adder object instead of type
mantcep May 17, 2021
5552354
Update classical multiplier drawing
mantcep May 18, 2021
0713ccd
Switch to PhaseGate in QFT multiplier
mantcep May 18, 2021
4a0c81f
Rename multipliers
mantcep May 18, 2021
0713eab
Implement modulo in QFT multiplier
mantcep May 19, 2021
ab3b8e7
Merge branch 'main' of https://github.com/Qiskit/qiskit-terra into mu…
mantcep May 21, 2021
3e72e12
Update to half adder in cumulative multiplier
mantcep May 21, 2021
8b57eab
Make number of output qubits optional
mantcep May 27, 2021
9c2cc7f
Implement modular cumulative multiplier
mantcep May 27, 2021
1c3aae8
Add test for modular multiplication
mantcep May 27, 2021
498255a
Fix wrong parameter name
mantcep May 27, 2021
73d9f42
Expand common multiplier class with checks; Add more tests for multip…
mantcep May 27, 2021
978e09f
Add black and lint changes
mantcep May 27, 2021
dd3a16d
Merge branch 'main' of https://github.com/Qiskit/qiskit-terra into mu…
mantcep May 27, 2021
acb3dd6
Add releasenote
mantcep May 27, 2021
08e4d22
Merge branch 'main' into multipliers
mantcep May 27, 2021
b811887
Attempt cyclic import fix
mantcep May 27, 2021
8fbda52
Attempt to fix cyclic import once more
mantcep May 27, 2021
833c12e
Update qiskit/circuit/library/arithmetic/multipliers/hrs_cumulative_m…
mantcep May 29, 2021
190233a
Update qiskit/circuit/library/arithmetic/multipliers/hrs_cumulative_m…
mantcep May 29, 2021
a45f2f2
Update qiskit/circuit/library/arithmetic/multipliers/hrs_cumulative_m…
mantcep May 29, 2021
a304715
Update qiskit/circuit/library/arithmetic/multipliers/multiplier.py
mantcep May 29, 2021
388afbd
Update qiskit/circuit/library/arithmetic/multipliers/hrs_cumulative_m…
mantcep May 29, 2021
06fb379
Add additional test cases
mantcep May 29, 2021
e135f6b
Include circuit diagram for QFT multiplier
mantcep May 29, 2021
2fb0889
Update docstring as suggested
mantcep May 29, 2021
e9fd830
Move num_result_qubits logic to parent class
mantcep May 29, 2021
4b499ea
Switch to NotImplementedError for cumulative multiplier
mantcep May 29, 2021
e9fe77e
Add black changes
mantcep May 29, 2021
0c602ae
Update Multiplier class docstring
mantcep May 29, 2021
6ce3dd8
Add lint changes
mantcep May 29, 2021
ddbf4b5
Update qiskit/circuit/library/arithmetic/multipliers/multiplier.py
mantcep May 31, 2021
3b4624f
Update qiskit/circuit/library/arithmetic/multipliers/multiplier.py
mantcep May 31, 2021
07e7edf
Add black and pylint fixes
mantcep May 31, 2021
f97706e
Merge branch 'main' of https://github.com/Qiskit/qiskit-terra into mu…
mantcep May 31, 2021
1324858
Merge branch 'main' into multipliers
mergify[bot] Jun 1, 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
11 changes: 11 additions & 0 deletions qiskit/circuit/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@
VBERippleCarryAdder
WeightedAdder

Multipliers
+++++++++++

.. autosummary::
:toctree: ../stubs/

HRSCumulativeMultiplier
RGQFTMultiplier

Comparators
+++++++++++

Expand Down Expand Up @@ -362,6 +371,8 @@
CDKMRippleCarryAdder,
DraperQFTAdder,
PiecewiseChebyshev,
HRSCumulativeMultiplier,
RGQFTMultiplier,
)

from .n_local import (
Expand Down
1 change: 1 addition & 0 deletions qiskit/circuit/library/arithmetic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@
from .linear_amplitude_function import LinearAmplitudeFunction
from .adders import VBERippleCarryAdder, CDKMRippleCarryAdder, DraperQFTAdder
from .piecewise_chebyshev import PiecewiseChebyshev
from .multipliers import HRSCumulativeMultiplier, RGQFTMultiplier
16 changes: 16 additions & 0 deletions qiskit/circuit/library/arithmetic/multipliers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# 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 multiplier circuit library."""

from .hrs_cumulative_multiplier import HRSCumulativeMultiplier
from .rg_qft_multiplier import RGQFTMultiplier
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# 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 product of two qubit registers using classical multiplication approach."""

from typing import Optional
from qiskit.circuit import QuantumRegister, AncillaRegister, QuantumCircuit

from .multiplier import Multiplier


class HRSCumulativeMultiplier(Multiplier):
r"""A multiplication circuit to store product of two input registers out-of-place.

Circuit uses the approach from [1]. As an example, a multiplier circuit that
performs a non-modular multiplication on two 3-qubit sized registers with
the default adder is as follows (where ``Adder`` denotes the
``CDKMRippleCarryAdder``):

.. parsed-literal::

a_0: ────■─────────────────────────
a_1: ────┼─────────■───────────────
│ │
a_2: ────┼─────────┼─────────■─────
┌───┴────┐┌───┴────┐┌───┴────┐
b_0: ┤0 ├┤0 ├┤0 ├
│ ││ ││ │
b_1: ┤1 ├┤1 ├┤1 ├
│ ││ ││ │
b_2: ┤2 ├┤2 ├┤2 ├
│ ││ ││ │
out_0: ┤3 ├┤ ├┤ ├
│ ││ ││ │
out_1: ┤4 ├┤3 ├┤ ├
│ Adder ││ Adder ││ Adder │
out_2: ┤5 ├┤4 ├┤3 ├
│ ││ ││ │
out_3: ┤6 ├┤5 ├┤4 ├
│ ││ ││ │
out_4: ┤ ├┤6 ├┤5 ├
│ ││ ││ │
out_5: ┤ ├┤ ├┤6 ├
│ ││ ││ │
aux_0: ┤7 ├┤7 ├┤7 ├
└────────┘└────────┘└────────┘

Multiplication in this circuit is implemented in a classical approach by performing
a series of shifted additions using one of the input registers while the qubits
from the other input register act as control qubits for the adders.

**References:**

[1] Häner et al., Optimizing Quantum Circuits for Arithmetic, 2018.
`arXiv:1805.12445 <https://arxiv.org/pdf/1805.12445.pdf>`_

"""

def __init__(
self,
num_state_qubits: int,
num_result_qubits: Optional[int] = None,
adder: Optional[QuantumCircuit] = None,
name: str = "HRSCumulativeMultiplier",
) -> 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.
num_result_qubits: The number of result qubits to limit the output to.
If number of result qubits is :math:`n`, multiplication modulo :math:`2^n` is performed
to limit the output to the specified number of qubits. Default
value is ``2 * num_state_qubits`` to represent any possible
result from the multiplication of the two inputs.
adder: Half adder circuit to be used for performing multiplication. The
CDKMRippleCarryAdder is used as default if no adder is provided.
name: The name of the circuit object.
Raises:
NotImplementedError: If ``num_result_qubits`` is not default and a custom adder is provided.
"""
super().__init__(num_state_qubits, num_result_qubits, name=name)

if self.num_result_qubits != 2 * num_state_qubits and adder is not None:
raise NotImplementedError("Only default adder is supported for modular multiplication.")

# define the registers
qr_a = QuantumRegister(num_state_qubits, name="a")
qr_b = QuantumRegister(num_state_qubits, name="b")
qr_out = QuantumRegister(self.num_result_qubits, name="out")
self.add_register(qr_a, qr_b, qr_out)

# prepare adder as controlled gate
if adder is None:
from qiskit.circuit.library.arithmetic.adders import CDKMRippleCarryAdder

adder = CDKMRippleCarryAdder(num_state_qubits, kind="half")

# get the number of helper qubits needed
num_helper_qubits = adder.num_ancillas

# add helper qubits if required
if num_helper_qubits > 0:
qr_h = AncillaRegister(num_helper_qubits, name="helper") # helper/ancilla qubits
self.add_register(qr_h)

# build multiplication circuit
for i in range(num_state_qubits):
excess_qubits = max(0, num_state_qubits + i + 1 - self.num_result_qubits)
if excess_qubits == 0:
num_adder_qubits = num_state_qubits
adder_for_current_step = adder
else:
num_adder_qubits = num_state_qubits - excess_qubits + 1
adder_for_current_step = CDKMRippleCarryAdder(num_adder_qubits, kind="fixed")
Cryoris marked this conversation as resolved.
Show resolved Hide resolved
controlled_adder = adder_for_current_step.to_gate().control(1)
qr_list = (
[qr_a[i]]
+ qr_b[:num_adder_qubits]
+ qr_out[i : num_state_qubits + i + 1 - excess_qubits]
)
if num_helper_qubits > 0:
qr_list.extend(qr_h[:])
self.append(controlled_adder, qr_list)
101 changes: 101 additions & 0 deletions qiskit/circuit/library/arithmetic/multipliers/multiplier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# 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 product of two equally sized qubit registers."""

from typing import Optional

from qiskit.circuit import QuantumCircuit


class Multiplier(QuantumCircuit):
r"""Compute the product of two equally sized qubit registers into a new register.

For two input registers :math:`|a\rangle_n`, :math:`|b\rangle_n` with :math:`n` qubits each
and an output register with :math:`2n` qubits, a multiplier performs the following operation

.. math::

|a\rangle_n |b\rangle_n |0\rangle_{t} \mapsto |a\rangle_n |b\rangle_n |a \cdot b\rangle_t

where :math:`t` is the number of bits used to represent the result. To completely store the result
of the multiplication without overflow we need :math:`t = 2n` bits.

The quantum register :math:`|a\rangle_n` (analogously :math:`|b\rangle_n` and
output register)

.. 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,
num_result_qubits: Optional[int] = None,
name: str = "Multiplier",
) -> None:
"""
Args:
num_state_qubits: The number of qubits in each of the input registers.
num_result_qubits: The number of result qubits to limit the output to.
Default value is ``2 * num_state_qubits`` to represent any possible
result from the multiplication of the two inputs.
name: The name of the circuit.
Raises:
ValueError: If ``num_state_qubits`` is smaller than 1.
ValueError: If ``num_result_qubits`` is smaller than ``num_state_qubits``.
ValueError: If ``num_result_qubits`` is larger than ``2 * num_state_qubits``.
"""
if num_state_qubits < 1:
raise ValueError("The number of qubits must be at least 1.")

if num_result_qubits is None:
num_result_qubits = 2 * num_state_qubits

if num_result_qubits < num_state_qubits:
raise ValueError(
"Number of result qubits is smaller than number of input state qubits."
)
if num_result_qubits > 2 * num_state_qubits:
raise ValueError(
"Number of result qubits is larger than twice the number of input state qubits."
)

super().__init__(name=name)
self._num_state_qubits = num_state_qubits
self._num_result_qubits = num_result_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

@property
def num_result_qubits(self) -> int:
"""The number of result qubits to limit the output to.

Returns:
The number of result qubits.
"""
return self._num_result_qubits
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# 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 product of two qubit registers using QFT."""

from typing import Optional
import numpy as np

from qiskit.circuit import QuantumRegister
from qiskit.circuit.library.standard_gates import PhaseGate
from qiskit.circuit.library.basis_change import QFT

from .multiplier import Multiplier


class RGQFTMultiplier(Multiplier):
r"""A QFT multiplication circuit to store product of two input registers out-of-place.

Multiplication in this circuit is implemented using the procedure of Fig. 3 in [1], where
weighted sum rotations are implemented as given in Fig. 5 in [1]. QFT is used on the output
register and is followed by rotations controlled by input registers. The rotations
transform the state into the product of two input registers in QFT base, which is
reverted from QFT base using inverse QFT.
Cryoris marked this conversation as resolved.
Show resolved Hide resolved
As an example, a circuit that performs a modular QFT multiplication on two 2-qubit
sized input registers with an output register of 2 qubits, is as follows:

.. parsed-literal::

a_0: ────────────────────────────────────────■───────■──────■──────■────────────────
│ │ │ │
a_1: ─────────■───────■───────■───────■──────┼───────┼──────┼──────┼────────────────
│ │ │ │ │ │ │ │
b_0: ─────────┼───────┼───────■───────■──────┼───────┼──────■──────■────────────────
│ │ │ │ │ │ │ │
b_1: ─────────■───────■───────┼───────┼──────■───────■──────┼──────┼────────────────
┌──────┐ │P(4π) │ │P(2π) │ │P(2π) │ │P(π) │ ┌───────┐
out_0: ┤0 ├─■───────┼───────■───────┼──────■───────┼──────■──────┼───────┤0 ├
│ qft │ │P(2π) │P(π) │P(π) │P(π/2) │ iqft │
out_1: ┤1 ├─────────■───────────────■──────────────■─────────────■───────┤1 ├
└──────┘ └───────┘

**References:**

[1] Ruiz-Perez et al., Quantum arithmetic with the Quantum Fourier Transform, 2017.
`arXiv:1411.5949 <https://arxiv.org/pdf/1411.5949.pdf>`_

"""

def __init__(
self,
num_state_qubits: int,
num_result_qubits: Optional[int] = None,
name: str = "RGQFTMultiplier",
) -> 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.
num_result_qubits: The number of result qubits to limit the output to.
If number of result qubits is :math:`n`, multiplication modulo :math:`2^n` is performed
to limit the output to the specified number of qubits. Default
value is ``2 * num_state_qubits`` to represent any possible
result from the multiplication of the two inputs.
name: The name of the circuit object.

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

# define the registers
qr_a = QuantumRegister(num_state_qubits, name="a")
qr_b = QuantumRegister(num_state_qubits, name="b")
qr_out = QuantumRegister(self.num_result_qubits, name="out")
self.add_register(qr_a, qr_b, qr_out)

# build multiplication circuit
self.append(QFT(self.num_result_qubits, do_swaps=False).to_gate(), qr_out[:])

for j in range(1, num_state_qubits + 1):
for i in range(1, num_state_qubits + 1):
for k in range(1, self.num_result_qubits + 1):
lam = (2 * np.pi) / (2 ** (i + j + k - 2 * num_state_qubits))
self.append(
PhaseGate(lam).control(2),
[qr_a[num_state_qubits - j], qr_b[num_state_qubits - i], qr_out[k - 1]],
)

self.append(QFT(self.num_result_qubits, do_swaps=False).inverse().to_gate(), qr_out[:])
Loading