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

Single-register arithmetic gates #13371

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ 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"
"IntComp.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:IntComparatorSynthesisDefault"
"IntComp.noaux" = "qiskit.transpiler.passes.synthesis.hls_plugins:IntComparatorSynthesisNoAux"
"IntComp.twos" = "qiskit.transpiler.passes.synthesis.hls_plugins:IntComparatorSynthesis2s"

[project.entry-points."qiskit.transpiler.init"]
default = "qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager"
Expand Down
18 changes: 18 additions & 0 deletions qiskit/circuit/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@
:template: autosummary/class_no_inherited_members.rst

LinearAmplitudeFunction
LinearAmplitudeFunctionGate

Functional Pauli Rotations
--------------------------
Expand All @@ -253,10 +254,15 @@

FunctionalPauliRotations
LinearPauliRotations
LinearPauliRotationsGate
PolynomialPauliRotations
PolynomialPauliRotationsGate
PiecewiseLinearPauliRotations
PiecewiseLinearPauliRotationsGate
PiecewisePolynomialPauliRotations
PiecewisePolynomialPauliRotationsGate
PiecewiseChebyshev
PiecewiseChebyshevGate

Adders
------
Expand Down Expand Up @@ -288,6 +294,7 @@
:template: autosummary/class_no_inherited_members.rst

IntegerComparator
IntegerComparatorGate

Functions on binary variables
-----------------------------
Expand All @@ -297,6 +304,7 @@
:template: autosummary/class_no_inherited_members.rst

QuadraticForm
QuadraticFormGate

Other arithmetic functions
--------------------------
Expand All @@ -306,6 +314,7 @@
:template: autosummary/class_no_inherited_members.rst

ExactReciprocal
ExactReciprocalGate

Particular Quantum Circuits
===========================
Expand Down Expand Up @@ -531,20 +540,29 @@
from .arithmetic import (
FunctionalPauliRotations,
LinearPauliRotations,
LinearPauliRotationsGate,
PiecewiseLinearPauliRotations,
PiecewiseLinearPauliRotationsGate,
PiecewisePolynomialPauliRotations,
PiecewisePolynomialPauliRotationsGate,
PolynomialPauliRotations,
PolynomialPauliRotationsGate,
IntegerComparator,
IntegerComparatorGate,
WeightedAdder,
QuadraticForm,
QuadraticFormGate,
LinearAmplitudeFunction,
LinearAmplitudeFunctionGate,
VBERippleCarryAdder,
CDKMRippleCarryAdder,
DraperQFTAdder,
PiecewiseChebyshev,
PiecewiseChebyshevGate,
HRSCumulativeMultiplier,
RGQFTMultiplier,
ExactReciprocal,
ExactReciprocalGate,
)

from .n_local import (
Expand Down
24 changes: 15 additions & 9 deletions qiskit/circuit/library/arithmetic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,21 @@
"""The arithmetic circuit library."""

from .functional_pauli_rotations import FunctionalPauliRotations
from .integer_comparator import IntegerComparator
from .linear_pauli_rotations import LinearPauliRotations
from .piecewise_linear_pauli_rotations import PiecewiseLinearPauliRotations
from .piecewise_polynomial_pauli_rotations import PiecewisePolynomialPauliRotations
from .polynomial_pauli_rotations import PolynomialPauliRotations
from .integer_comparator import IntegerComparator, IntegerComparatorGate
from .linear_pauli_rotations import LinearPauliRotations, LinearPauliRotationsGate
from .piecewise_linear_pauli_rotations import (
PiecewiseLinearPauliRotations,
PiecewiseLinearPauliRotationsGate,
)
from .piecewise_polynomial_pauli_rotations import (
PiecewisePolynomialPauliRotations,
PiecewisePolynomialPauliRotationsGate,
)
from .polynomial_pauli_rotations import PolynomialPauliRotations, PolynomialPauliRotationsGate
from .weighted_adder import WeightedAdder
from .quadratic_form import QuadraticForm
from .linear_amplitude_function import LinearAmplitudeFunction
from .quadratic_form import QuadraticForm, QuadraticFormGate
from .linear_amplitude_function import LinearAmplitudeFunction, LinearAmplitudeFunctionGate
from .adders import VBERippleCarryAdder, CDKMRippleCarryAdder, DraperQFTAdder
from .piecewise_chebyshev import PiecewiseChebyshev
from .piecewise_chebyshev import PiecewiseChebyshev, PiecewiseChebyshevGate
from .multipliers import HRSCumulativeMultiplier, RGQFTMultiplier
from .exact_reciprocal import ExactReciprocal
from .exact_reciprocal import ExactReciprocal, ExactReciprocalGate
78 changes: 57 additions & 21 deletions qiskit/circuit/library/arithmetic/exact_reciprocal.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from math import isclose
import numpy as np
from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.circuit import QuantumCircuit, QuantumRegister, Gate
from qiskit.circuit.library.generalized_gates import UCRYGate


Expand Down Expand Up @@ -46,43 +46,79 @@ def __init__(
"""
qr_state = QuantumRegister(num_state_qubits, "state")
qr_flag = QuantumRegister(1, "flag")
circuit = QuantumCircuit(qr_state, qr_flag, name=name)
super().__init__(qr_state, qr_flag, name=name)

reciprocal = ExactReciprocalGate(num_state_qubits, scaling, neg_vals, label=name)
self.append(reciprocal, self.qubits)


class ExactReciprocalGate(Gate):
r"""Exact reciprocal

.. math::

|x\rangle |0\rangle \mapsto \cos(1/x)|x\rangle|0\rangle + \sin(1/x)|x\rangle |1\rangle
"""

def __init__(
self, num_state_qubits: int, scaling: float, neg_vals: bool = False, label: str = "1/x"
) -> None:
r"""
Args:
num_state_qubits: The number of qubits representing the value to invert.
scaling: Scaling factor :math:`s` of the reciprocal function, i.e. to compute
:math:`s / x`.
neg_vals: Whether :math:`x` might represent negative values. In this case the first
qubit is the sign, with :math:`|1\rangle` for negative and :math:`|0\rangle` for
positive. For the negative case it is assumed that the remaining string represents
:math:`1 - x`. This is because :math:`e^{-2 \pi i x} = e^{2 \pi i (1 - x)}` for
:math:`x \in [0,1)`.
name: The name of the object.

.. note::

It is assumed that the binary string :math:`x` represents a number < 1.
"""
super().__init__("ExactReciprocal", num_state_qubits + 1, [], label=label)

self.scaling = scaling
self.neg_vals = neg_vals

def _define(self):
num_state_qubits = self.num_qubits - 1
qr_state = QuantumRegister(num_state_qubits, "state")
qr_flag = QuantumRegister(1, "flag")
circuit = QuantumCircuit(qr_state, qr_flag)

angles = [0.0]
nl = 2 ** (num_state_qubits - 1) if neg_vals else 2**num_state_qubits
nl = 2 ** (num_state_qubits - 1) if self.neg_vals else 2**num_state_qubits

# Angles to rotate by scaling / x, where x = i / nl
for i in range(1, nl):
if isclose(scaling * nl / i, 1, abs_tol=1e-5):
if isclose(self.scaling * nl / i, 1, abs_tol=1e-5):
angles.append(np.pi)
elif scaling * nl / i < 1:
angles.append(2 * np.arcsin(scaling * nl / i))
elif self.scaling * nl / i < 1:
angles.append(2 * np.arcsin(self.scaling * nl / i))
else:
angles.append(0.0)

circuit.compose(
UCRYGate(angles), [qr_flag[0]] + qr_state[: len(qr_state) - neg_vals], inplace=True
)
circuit.append(UCRYGate(angles), [qr_flag[0]] + qr_state[: len(qr_state) - self.neg_vals])

if neg_vals:
circuit.compose(
if self.neg_vals:
circuit.append(
UCRYGate([-theta for theta in angles]).control(),
[qr_state[-1]] + [qr_flag[0]] + qr_state[:-1],
inplace=True,
)
angles_neg = [0.0]
for i in range(1, nl):
if isclose(scaling * (-1) / (1 - i / nl), -1, abs_tol=1e-5):
if isclose(self.scaling * (-1) / (1 - i / nl), -1, abs_tol=1e-5):
angles_neg.append(-np.pi)
elif np.abs(scaling * (-1) / (1 - i / nl)) < 1:
angles_neg.append(2 * np.arcsin(scaling * (-1) / (1 - i / nl)))
elif np.abs(self.scaling * (-1) / (1 - i / nl)) < 1:
angles_neg.append(2 * np.arcsin(self.scaling * (-1) / (1 - i / nl)))
else:
angles_neg.append(0.0)
circuit.compose(
UCRYGate(angles_neg).control(),
[qr_state[-1]] + [qr_flag[0]] + qr_state[:-1],
inplace=True,
circuit.append(
UCRYGate(angles_neg).control(), [qr_state[-1]] + [qr_flag[0]] + qr_state[:-1]
)

super().__init__(*circuit.qregs, name=name)
self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True)
self.definition = circuit
109 changes: 29 additions & 80 deletions qiskit/circuit/library/arithmetic/integer_comparator.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@
"""Integer Comparator."""

from __future__ import annotations
import math

from qiskit.circuit import QuantumCircuit, QuantumRegister, AncillaRegister
from qiskit.circuit import QuantumRegister, AncillaRegister, Gate
from qiskit.circuit.exceptions import CircuitError
from ..boolean_logic import OR
from qiskit.synthesis.arithmetic.comparators import synth_integer_comparator_2s
from ..blueprintcircuit import BlueprintCircuit


Expand Down Expand Up @@ -134,19 +133,6 @@ def num_state_qubits(self, num_state_qubits: int | None) -> None:
qr_ancilla = AncillaRegister(num_ancillas)
self.add_register(qr_ancilla)

def _get_twos_complement(self) -> list[int]:
"""Returns the 2's complement of ``self.value`` as array.

Returns:
The 2's complement of ``self.value``.
"""
twos_complement = pow(2, self.num_state_qubits) - math.ceil(self.value)
twos_complement = f"{twos_complement:b}".rjust(self.num_state_qubits, "0")
twos_complement = [
1 if twos_complement[i] == "1" else 0 for i in reversed(range(len(twos_complement)))
]
return twos_complement

def _check_configuration(self, raise_on_failure: bool = True) -> bool:
"""Check if the current configuration is valid."""
valid = True
Expand Down Expand Up @@ -176,68 +162,31 @@ def _build(self) -> None:

super()._build()

qr_state = self.qubits[: self.num_state_qubits]
q_compare = self.qubits[self.num_state_qubits]
qr_ancilla = self.qubits[self.num_state_qubits + 1 :]

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

if self.value <= 0: # condition always satisfied for non-positive values
if self._geq: # otherwise the condition is never satisfied
circuit.x(q_compare)
# condition never satisfied for values larger than or equal to 2^n
elif self.value < pow(2, self.num_state_qubits):

if self.num_state_qubits > 1:
twos = self._get_twos_complement()
for i in range(self.num_state_qubits):
if i == 0:
if twos[i] == 1:
circuit.cx(qr_state[i], qr_ancilla[i])
elif i < self.num_state_qubits - 1:
if twos[i] == 1:
circuit.compose(
OR(2), [qr_state[i], qr_ancilla[i - 1], qr_ancilla[i]], inplace=True
)
else:
circuit.ccx(qr_state[i], qr_ancilla[i - 1], qr_ancilla[i])
else:
if twos[i] == 1:
# OR needs the result argument as qubit not register, thus
# access the index [0]
circuit.compose(
OR(2), [qr_state[i], qr_ancilla[i - 1], q_compare], inplace=True
)
else:
circuit.ccx(qr_state[i], qr_ancilla[i - 1], q_compare)

# flip result bit if geq flag is false
if not self._geq:
circuit.x(q_compare)

# uncompute ancillas state
for i in reversed(range(self.num_state_qubits - 1)):
if i == 0:
if twos[i] == 1:
circuit.cx(qr_state[i], qr_ancilla[i])
else:
if twos[i] == 1:
circuit.compose(
OR(2), [qr_state[i], qr_ancilla[i - 1], qr_ancilla[i]], inplace=True
)
else:
circuit.ccx(qr_state[i], qr_ancilla[i - 1], qr_ancilla[i])
else:

# num_state_qubits == 1 and value == 1:
circuit.cx(qr_state[0], q_compare)

# flip result bit if geq flag is false
if not self._geq:
circuit.x(q_compare)

else:
if not self._geq: # otherwise the condition is never satisfied
circuit.x(q_compare)

circuit = synth_integer_comparator_2s(self.num_state_qubits, self.value, self.geq)
self.append(circuit.to_gate(), self.qubits)


class IntegerComparatorGate(Gate):
r"""Perform a :math:`\geq` (or :math:`<`) on a qubit register against a classical integer.

This operator compares basis states :math:`|i\rangle_n` against a classically given integer
:math:`L` of fixed value and flips a target qubit if :math:`i \geq L`
(or :math:`<` depending on the parameter ``geq``):

.. math::

|i\rangle_n |0\rangle \mapsto |i\rangle_n |i \geq L\rangle

"""

def __init__(
self, num_state_qubits: int, value: int, geq: bool = True, label: str | None = None
):
r"""
Args:
num_state_qubits: The number of qubits in the registers.
geq: If ``True`` compute :math:`i \geq L`, otherwise compute :math:`i < L`.
"""
super().__init__("IntComp", num_state_qubits + 1, [], label=label)
self.value = value
self.geq = geq
Loading
Loading