Source code for qolumbina.programs.quantum_adder.full_adder

# 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/adders/adder.py#L184-L235
# https://github.com/Qiskit/qiskit/blob/main/qiskit/synthesis/arithmetic/adders/cdkm_ripple_carry_adder.py#L19
#
# 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()
#
# 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 __future__ import annotations

from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.circuit import QuantumRegister, AncillaRegister

# ---------- benchmark registration ----------
from ..benchmark_registry import register_benchmark
from pathlib import Path
@register_benchmark(
    Path(__file__).stem,
    family=Path(__file__).resolve().parent.name,
    description="Full adder circuit",
    class_name="FullAdder",
    source={
        "repo": "https://github.com/Qiskit/qiskit/tree/f14e0b29a484795034447ea5bfb637fe845c194f",
        "file": "qiskit/circuit/library/arithmetic/adders/adder.py",
        "sdk": "Qiskit",
        "available_doc": True
    },
    testability_refactoring=[
        "Structure reorganization",
        "Dependency decoupling"
    ]     
)
def create_full_adder(num_state_qubits: int) -> QuantumCircuit:
    return FullAdder(num_state_qubits=num_state_qubits)



[docs] class FullAdder(QuantumCircuit): r""" Compute the sum of two :math:`n`-sized qubit registers, including carry-in and -out bits. """ def __init__(self, num_state_qubits: int, label: str | None = None) -> None: """ Args: num_state_qubits: The number of qubits :math:`n` 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) super().__init__(2 * num_state_qubits + 2, name=label or "FullAdder") self._num_state_qubits = num_state_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 def _adder_ripple_c04(self, num_state_qubits: int, kind: str = "half") -> QuantumCircuit: r"""A ripple-carry circuit to perform in-place addition on two qubit registers. This circuit uses :math:`2n + O(1)` CCX gates and :math:`5n + O(1)` CX gates, at a depth of :math:`2n + O(1)` [1]. The constant depends on the kind of adder implemented. As an example, a full ripple-carry adder on 3-qubit registers applies a MAJ cascade from the least significant bit to the most significant bit, then an UMA cascade in reverse order to uncompute the carries and write the sum. Here *MAJ* and *UMA* 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. Two different kinds of adders are supported. By setting the ``kind`` argument, you can also choose a half-adder, which doesn't have a carry-in, and a fixed-sized-adder, which has neither carry-in nor carry-out, and thus acts on fixed register sizes. Unlike the full-adder, these circuits need one additional helper qubit. For the fixed-sized adder (``kind="fixed"``), the same MAJ/UMA structure is used without a carry-in or carry-out wire. 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. 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. kind: The kind of adder, can be ``"full"`` for a full adder, ``"half"`` for a half adder, or ``"fixed"`` for a fixed-sized adder. A full adder includes both carry-in and carry-out, a half only carry-out, and a fixed-sized adder neither carry-in nor carry-out. Raises: ValueError: If ``num_state_qubits`` is lower than 1. References: [1] Cuccaro et al., A new quantum ripple-carry addition circuit, 2004. `arXiv:quant-ph/0410184 <https://arxiv.org/pdf/quant-ph/0410184.pdf>`_ [2] Vedral et al., Quantum Networks for Elementary Arithmetic Operations, 1995. `arXiv:quant-ph/9511018 <https://arxiv.org/pdf/quant-ph/9511018.pdf>`_ """ if num_state_qubits < 1: raise ValueError("The number of qubits must be at least 1.") circuit = QuantumCircuit() if kind == "full": qr_c = QuantumRegister(1, name="cin") circuit.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") circuit.add_register(qr_a, qr_b) if kind in ["full", "half"]: qr_z = QuantumRegister(1, name="cout") circuit.add_register(qr_z) if kind != "full": circuit.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() # 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]]) return circuit def _build(self): """Populates self.definition with a decomposition of this gate.""" # In the case of a full adder, this method does not use any ancilla qubits # self.definition = adder_ripple_c04(self.num_state_qubits, kind="full") subcircuit = self._adder_ripple_c04(self.num_state_qubits, kind="full") self.compose(subcircuit.to_gate(), inplace=True)