Source code for qrisp.alg_primitives.arithmetic.isqrt.isqrt_algorithm

"""
********************************************************************************
* Copyright (c) 2025 the Qrisp authors
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is
* available at https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************
"""

from qrisp.circuit import Qubit
from qrisp.qtypes import QuantumFloat
from qrisp.core.gate_application_functions import x, cx, mcx, swap

def peres(a : Qubit, b : Qubit, c : Qubit):
    mcx([a, b], c)
    cx(a, b)

def add(A: list[Qubit], B: list[Qubit]):
    n = len(A)
    # Step 1
    for i in range(1, n, 1):
        cx(B[i], A[i])
        
    # Step 2
    for i in range(n-2, 0, -1):
        cx(B[i], B[i+1])

    # Step 3
    for i in range(0, n-1, 1):
        mcx([B[i], A[i]], B[i+1])

    # Step 4
    for i in range(n-1, -1, -1):
        if(i == n - 1):
            cx(B[i], A[i])
        else:
            peres(B[i], A[i], B[i+1])

    # Step 5
    for i in range(1, n-1, 1):
        cx(B[i], B[i+1])

    # Step 6
    for i in range(1, n, 1):
        cx(B[i], A[i])

def control_add(z: Qubit, B: list[Qubit], A: list[Qubit]):
    n = len(B)

    # Step 1
    for i in range(1, n):
        cx(A[i], B[i])

    # Step 2
    for i in range(n - 2, 0, -1):
        cx(A[i], A[i+1])

    # Step 3
    for i in range(0, n - 1):
        mcx([B[i], A[i]], A[i+1])

    # Step 4
    mcx([z, A[n-1]], B[n-1])

    # Step 5
    for i in range(n-2, -1, -1):
        mcx([B[i], A[i]], A[i+1])
        mcx([z, A[i]], B[i])

    # Step 6
    for i in range(1, n-1):
        cx(A[i], A[i+1])

    # Step 7
    for i in range(1, n):
        cx(A[i], B[i])

def control_add_sub(z: Qubit, A: list[Qubit], B: list[Qubit]):
    for i in A:
        cx(z, i)

    add(A, B)

    for i in A:
        cx(z, i)

def initial_subtraction(R : QuantumFloat, F : QuantumFloat, z : QuantumFloat):
    n = R.size

    # Step 1
    x(R[n-2])

    # Step 2
    cx(R[n-2], R[n-1])

    # Step 3
    cx(R[n-1], F[1])

    # Step 4
    mcx([R[n-1]], z[0], ctrl_state=0)
    
    # Step 5
    mcx([R[n-1]], F[2], ctrl_state=0)

    # Step 6
    control_add_sub(z, [R[n-4], R[n-3], R[n-2], R[n-1]], [F[0], F[1], F[2], F[3]])

def conditional_addition_or_subtraction(R : QuantumFloat, F : QuantumFloat, z : QuantumFloat):
    n = R.size

    for i in range(2, n // 2):
        # Step 1
        mcx(z, F[1], ctrl_state=0)

        # Step 2
        cx(F[2], z)

        # Step 3
        cx(R[n-1], F[1])

        # Step 4
        mcx([R[n-1]], z[0], ctrl_state=0)

        # Step 5
        mcx([R[n-1]], F[i+1], ctrl_state=0)

        # Step 6
        for j in range(i + 1, 2, -1):
            swap(F[j], F[j-1])

        # Step 7
        R_sum_qubits = [R[j] for j in range(n - 2 * i - 2, n)]
        F_sum_qubits = [F[j] for j in range(0, 2 * i + 2)]
        control_add_sub(z, R_sum_qubits, F_sum_qubits)

def remainder_restoration(R : QuantumFloat, F : QuantumFloat, z : QuantumFloat):
    n = R.size

    # Step 1
    mcx(z, F[1], ctrl_state=0)

    # Step 2
    cx(F[2], z)

    # Step 3
    mcx([R[n-1]], z[0], ctrl_state=0)

    # Step 4
    mcx([R[n-1]], F[n//2+1], ctrl_state=0)

    # Step 5
    x(z)

    # Step 6
    control_add(z, R, F)

    # Step 7
    x(z)

    # Step 8
    for j in range(n//2 + 1, 2, -1):
        swap(F[j], F[j-1])

    # Step 9
    cx(F[2], z)

[docs] def q_isqrt(R: QuantumFloat) -> QuantumFloat: """ Computes the integer square root of a QuantumFloat, as well as the remainder using the `non-Restoring square root algorithm <https://arxiv.org/abs/1712.08254>`_. Does not yield plausible output when applied to negative integers. Parameters ---------- R : QuantumFloat QuantumFloat value to compute the integer square root of. Returns ------- QuantumFloat The integer square root of R. Also transforms input value R into the remainder. Examples -------- Calculate the integer square root of 131: >>> from qrisp import QuantumFloat, q_isqrt >>> R = QuantumFloat(8, 0) >>> R[:] = 131 >>> res = q_isqrt(R) >>> print(res.get_measurement()) >>> print(R.get_measurement()) {11: 1.0} {10: 1.0} """ n = R.size e = R.exponent if e != 0: raise Exception("Tried to compute integer square root for QuantumFloat with non-zero exponent") if n == 1: F = QuantumFloat(1, 0) cx(R[0],F[0]) cx(F[0],R[0]) return F # To ensure that first qubit is 0 and total amount of qubits is even we extend the input variable delta = 1 if n % 2 == 1 else 2 n += delta R.extend(delta) F = QuantumFloat(n, 0) z = QuantumFloat(1, 0) F[:] = 1 z[:] = 0 initial_subtraction(R, F, z) conditional_addition_or_subtraction(R, F, z) remainder_restoration(R, F, z) x(F[0]) for i in range(2, n // 2 + 2): swap(F[i], F[i - 2]) R.reduce(R[-delta:]) F.reduce(F[-delta:]) z.delete() return F