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

Binary arithmetic gates #13354

Merged
merged 17 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,20 @@ sk = "qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis:SolovayKitaevS
"qft.line" = "qiskit.transpiler.passes.synthesis.hls_plugins:QFTSynthesisLine"
"qft.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:QFTSynthesisFull"
"permutation.token_swapper" = "qiskit.transpiler.passes.synthesis.hls_plugins:TokenSwapperSynthesisPermutation"
"ModularAdder.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:ModularAdderSynthesisDefault"
"ModularAdder.ripple_c04" = "qiskit.transpiler.passes.synthesis.hls_plugins:ModularAdderSynthesisC04"
"ModularAdder.ripple_v95" = "qiskit.transpiler.passes.synthesis.hls_plugins:ModularAdderSynthesisV95"
"ModularAdder.qft_d00" = "qiskit.transpiler.passes.synthesis.hls_plugins:ModularAdderSynthesisD00"
"HalfAdder.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:HalfAdderSynthesisDefault"
"HalfAdder.ripple_c04" = "qiskit.transpiler.passes.synthesis.hls_plugins:HalfAdderSynthesisC04"
"HalfAdder.ripple_v95" = "qiskit.transpiler.passes.synthesis.hls_plugins:HalfAdderSynthesisV95"
"HalfAdder.qft_d00" = "qiskit.transpiler.passes.synthesis.hls_plugins:HalfAdderSynthesisD00"
"FullAdder.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:FullAdderSynthesisC04"
"FullAdder.ripple_c04" = "qiskit.transpiler.passes.synthesis.hls_plugins:FullAdderSynthesisC04"
"FullAdder.ripple_v95" = "qiskit.transpiler.passes.synthesis.hls_plugins:FullAdderSynthesisV95"
"Multiplier.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:MultiplierSynthesisR17"
"Multiplier.qft_r17" = "qiskit.transpiler.passes.synthesis.hls_plugins:MultiplierSynthesisR17"
"Multiplier.cumulative_h18" = "qiskit.transpiler.passes.synthesis.hls_plugins:MultiplierSynthesisH18"
"PauliEvolution.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:PauliEvolutionSynthesisDefault"

[project.entry-points."qiskit.transpiler.init"]
Expand Down
4 changes: 4 additions & 0 deletions qiskit/circuit/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,10 @@
)
from .basis_change import QFT, QFTGate
from .arithmetic import (
ModularAdderGate,
HalfAdderGate,
FullAdderGate,
MultiplierGate,
FunctionalPauliRotations,
LinearPauliRotations,
PiecewiseLinearPauliRotations,
Expand Down
11 changes: 9 additions & 2 deletions qiskit/circuit/library/arithmetic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@
from .weighted_adder import WeightedAdder
from .quadratic_form import QuadraticForm
from .linear_amplitude_function import LinearAmplitudeFunction
from .adders import VBERippleCarryAdder, CDKMRippleCarryAdder, DraperQFTAdder
from .adders import (
VBERippleCarryAdder,
CDKMRippleCarryAdder,
DraperQFTAdder,
ModularAdderGate,
HalfAdderGate,
FullAdderGate,
)
from .piecewise_chebyshev import PiecewiseChebyshev
from .multipliers import HRSCumulativeMultiplier, RGQFTMultiplier
from .multipliers import HRSCumulativeMultiplier, RGQFTMultiplier, MultiplierGate
from .exact_reciprocal import ExactReciprocal
1 change: 1 addition & 0 deletions qiskit/circuit/library/arithmetic/adders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
from .cdkm_ripple_carry_adder import CDKMRippleCarryAdder
from .draper_qft_adder import DraperQFTAdder
from .vbe_ripple_carry_adder import VBERippleCarryAdder
from .adder import ModularAdderGate, HalfAdderGate, FullAdderGate
154 changes: 153 additions & 1 deletion qiskit/circuit/library/arithmetic/adders/adder.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@

"""Compute the sum of two equally sized qubit registers."""

from qiskit.circuit import QuantumCircuit
from __future__ import annotations

from qiskit.circuit import QuantumCircuit, Gate
from qiskit.utils.deprecation import deprecate_func


class Adder(QuantumCircuit):
Expand All @@ -39,6 +42,16 @@ class Adder(QuantumCircuit):

"""

@deprecate_func(
since="1.3",
additional_msg=(
"Use the adder gates provided in qiskit.circuit.library.arithmetic instead. "
"The gate type depends on the adder kind: fixed, half, full are represented by "
"ModularAdderGate, HalfAdderGate, FullAdderGate, respectively. For different adder "
"implementations, see https://docs.quantum.ibm.com/api/qiskit/synthesis.",
),
pending=True,
)
def __init__(self, num_state_qubits: int, name: str = "Adder") -> None:
"""
Args:
Expand All @@ -56,3 +69,142 @@ def num_state_qubits(self) -> int:
The number of state qubits.
"""
return self._num_state_qubits


class HalfAdderGate(Gate):
r"""Compute the sum of two equally-sized qubit registers, including a carry-out bit.

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, label: str | None = None) -> None:
"""
Args:
num_state_qubits: The number of qubits in each of the registers.
name: The name of the circuit.
"""
if num_state_qubits < 1:
raise ValueError("Need at least 1 state qubit.")

super().__init__("HalfAdder", 2 * num_state_qubits + 1, [], label=label)
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


class ModularAdderGate(Gate):
r"""Compute the sum modulo :math:`2^n` of two :math:`n`-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 \text{ mod } 2^n\rangle_n.

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, label: str | None = None) -> None:
"""
Args:
num_state_qubits: The number of qubits in each of the registers.
name: The name of the circuit.
"""
if num_state_qubits < 1:
raise ValueError("Need at least 1 state qubit.")

super().__init__("ModularAdder", 2 * num_state_qubits, [], label=label)
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


class FullAdderGate(Gate):
r"""Compute the sum of two :math:`n`-sized qubit registers, including carry-in and -out bits.

For two registers :math:`|a\rangle_n` and :math:|b\rangle_n` with :math:`n` qubits each, an
adder performs the following operation

.. math::

|c_{\text{in}\rangle_1 |a\rangle_n |b\rangle_n
\mapsto |a\rangle_n |c_{\text{in}} + 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, label: str | None = None) -> None:
"""
Args:
num_state_qubits: The number of qubits in each of the registers.
name: The name of the circuit.
"""
if num_state_qubits < 1:
raise ValueError("Need at least 1 state qubit.")

super().__init__("FullAdder", 2 * num_state_qubits + 2, [], label=label)
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@

"""Compute the sum of two qubit registers using ripple-carry approach."""

from qiskit.circuit import QuantumCircuit, QuantumRegister, AncillaRegister

from qiskit.synthesis.arithmetic import adder_ripple_c04
from .adder import Adder


Expand Down Expand Up @@ -75,6 +74,21 @@ class CDKMRippleCarryAdder(Adder):
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.

.. seealso::

Cryoris marked this conversation as resolved.
Show resolved Hide resolved
The following generic gate objects perform additions, like this circuit class,
but allow the compiler to select the optimal decomposition based on the context.
Specific implementations can be set via the :class:`.HLSConfig`, e.g. this circuit
can be chosen via ``Adder=["ripple_c04"]``.

:class:`.ModularAdderGate`: A generic inplace adder, modulo :math:`2^n`. This
is functionally equivalent to ``kind="fixed"``.

:class:`.AdderGate`: A generic inplace adder. This
is functionally equivalent to ``kind="half"``.

:class:`.FullAdderGate`: A generic inplace adder, with a carry-in bit. This
is functionally equivalent to ``kind="full"``.

**References:**

Expand Down Expand Up @@ -102,58 +116,8 @@ def __init__(
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)
circuit = adder_ripple_c04(num_state_qubits, kind)

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

circuit = QuantumCircuit(*self.qregs, name=name)

# build ripple-carry adder circuit
circuit.append(maj_gate, [qr_a[0], qr_b[0], qr_c[0]])

for i in range(num_state_qubits - 1):
circuit.append(maj_gate, [qr_a[i + 1], qr_b[i + 1], qr_a[i]])

if kind in ["full", "half"]:
circuit.cx(qr_a[-1], qr_z[0])

for i in reversed(range(num_state_qubits - 1)):
circuit.append(uma_gate, [qr_a[i + 1], qr_b[i + 1], qr_a[i]])

circuit.append(uma_gate, [qr_a[0], qr_b[0], qr_c[0]])

self.add_register(*circuit.qregs)
self.append(circuit.to_gate(), self.qubits)
13 changes: 13 additions & 0 deletions qiskit/circuit/library/arithmetic/adders/draper_qft_adder.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ class DraperQFTAdder(Adder):
cout_0: ┤2 ├────────────────────────■────────■───────┤2 ├
└──────┘ └───────┘

.. seealso::

The following generic gate objects perform additions, like this circuit class,
but allow the compiler to select the optimal decomposition based on the context.
Specific implementations can be set via the :class:`.HLSConfig`, e.g. this
circuit can be chosen via ``Adder=["qft_d00"]``.

:class:`.ModularAdderGate`: A generic inplace adder, modulo :math:`2^n`. This
is functionally equivalent to ``kind="fixed"``.

:class:`.AdderGate`: A generic inplace adder. This
is functionally equivalent to ``kind="half"``.

**References:**

[1] T. G. Draper, Addition on a Quantum Computer, 2000.
Expand Down
Loading