# The original version of the following code is sourced from Qiskit circuit
# library and involve the following files for dependency decoupling:
# https://github.com/Qiskit/qiskit/blob/stable/2.3/qiskit/circuit/library/arithmetic/multipliers/multiplier.py#L114-L201
# https://github.com/Qiskit/qiskit/blob/main/qiskit/synthesis/arithmetic/multipliers/rg_qft_multiplier.py#L25
#
# This program is adapted for use as a benchmark in controlled software testing experiments.
# Modifications made to the original code include (for Apache License 2.0):
# - Dependency decoupling: attach `add_ripple_c04` function to this file directly.
# - Refactored to expose unified program interfaces:
# - Unify the inherited class name from `Gate` to `QuantumCircuit` for consistency across benchmarks.
# - __init__ method updated to match QuantumCircuit signature.
# - _define() -> _build()
# - self.definition is replaced with self.compose()
#
# Original repository link:
# https://github.com/Qiskit/qiskit/tree/f14e0b29a484795034447ea5bfb637fe845c194f
#
# 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 __future__ import annotations
import numpy as np
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.circuit import QuantumRegister
from qiskit.circuit.library.standard_gates import PhaseGate
from ..quantum_fourier_transform import QFT
# ---------- benchmark registration ----------
from ..benchmark_registry import register_benchmark
from pathlib import Path
@register_benchmark(
Path(__file__).stem,
family=Path(__file__).resolve().parent.name,
description="Compute the product of two equally sized qubit registers using QFT",
class_name="MultiplierQFT",
source={
"repo": "https://github.com/Qiskit/qiskit/tree/f14e0b29a484795034447ea5bfb637fe845c194f",
"file": "qiskit/circuit/library/arithmetic/multipliers/multiplier.py",
"sdk": "Qiskit",
"available_doc": True
},
testability_refactoring=[
"Dependency decoupling",
"Structure reorganization"
]
)
def create_multiplier_qft(
num_state_qubits: int,
num_result_qubits: int | None = None,
) -> QuantumCircuit:
return MultiplierQFT(
num_state_qubits=num_state_qubits,
num_result_qubits=num_result_qubits,
)
[docs]
class MultiplierQFT(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: int | None = None,
label: str | None = None,
) -> None:
"""
Args:
num_state_qubits: The number of qubits (:math:`n`) in each of the input registers.
num_result_qubits: The number of result qubits (:math:`t`) to limit the output to.
The default value is `None`. If `None`, defaults to `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 state qubits must be at least 1.")
if num_result_qubits is None:
num_result_qubits = 2 * num_state_qubits
elif num_result_qubits < num_state_qubits or num_result_qubits > 2 * num_state_qubits:
raise ValueError(
f"num_result_qubits ({num_result_qubits}) must be in between num_state_qubits "
f"({num_state_qubits}) and 2 * num_state_qubits ({2 * num_state_qubits})"
)
# super().__init__("Multiplier", 2 * num_state_qubits + num_result_qubits, [], label=label)
super().__init__(
2 * num_state_qubits + num_result_qubits,
name="Multiplier" or label
) # Modified for QuantumCircuit
self._num_state_qubits = num_state_qubits
self._num_result_qubits = num_result_qubits
# Build the circuit
self._build()
@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
def _multiplier_qft_r17(
self, num_state_qubits: int, num_result_qubits: int | None = None
) -> QuantumCircuit:
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.
For example, on 3 state qubits, a full multiplier is given by:
.. plot::
:alt: Circuit diagram output by the previous code.
:include-source:
from qiskit.synthesis.arithmetic import multiplier_qft_r17
num_state_qubits = 3
circuit = multiplier_qft_r17(num_state_qubits)
circuit.draw("mpl")
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.
Raises:
ValueError: If ``num_result_qubits`` is given and not valid, meaning not
in ``[num_state_qubits, 2 * num_state_qubits]``.
References:
[1] Ruiz-Perez et al., Quantum arithmetic with the Quantum Fourier Transform, 2017.
`arXiv:1411.5949 <https://arxiv.org/pdf/1411.5949.pdf>`_
"""
# define the registers
if num_result_qubits is None:
num_result_qubits = 2 * num_state_qubits
elif num_result_qubits < num_state_qubits or num_result_qubits > 2 * num_state_qubits:
raise ValueError(
f"num_result_qubits ({num_result_qubits}) must be in between num_state_qubits "
f"({num_state_qubits}) and 2 * num_state_qubits ({2 * num_state_qubits})"
)
qr_a = QuantumRegister(num_state_qubits, name="a")
qr_b = QuantumRegister(num_state_qubits, name="b")
qr_out = QuantumRegister(num_result_qubits, name="out")
# build multiplication circuit
circuit = QuantumCircuit(qr_a, qr_b, qr_out)
# Use the local QFT implementation to avoid circular imports
# Altered from `qft = QFTGate(num_result_qubits)``
qft = QFT(num_result_qubits).to_gate()
circuit.append(qft, 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, num_result_qubits + 1):
lam = (2 * np.pi) / (2 ** (i + j + k - 2 * num_state_qubits))
# note: if we can synthesize the QFT without swaps, we can implement this circuit
# more efficiently and just apply phase gate on qr_out[(k - 1)] instead
circuit.append(
PhaseGate(lam).control(2, annotated=False),
[qr_a[num_state_qubits - j], qr_b[num_state_qubits - i], qr_out[~(k - 1)]],
)
circuit.append(qft.inverse(), qr_out[:])
return circuit
def _build(self):
"""Populates self.definition with some decomposition of this gate."""
# Use the local multiplier implementation to avoid circular imports
# from qiskit.synthesis.arithmetic import multiplier_qft_r17
# This particular decomposition does not use any ancilla qubits.
# Note that the transpiler may choose a different decomposition
# based on the number of ancilla qubits available.
#self.definition = multiplier_qft_r17(self.num_state_qubits) # Before
sub_circuit = self._multiplier_qft_r17(self.num_state_qubits) # After
self.compose(sub_circuit.to_gate(), inplace=True)