Source code for qrisp.algorithms.qmci
"""
********************************************************************************
* Copyright (c) 2024 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.alg_primitives import IQAE
from qrisp import h, cx, x, z, auto_uncompute, QuantumBool, control
def uniform(*args):
    for arg in args:
        h(arg)
[docs]
def QMCI(qargs, function, distribution=None, mes_kwargs={}):
    r"""
    Implements a general algorithm for `Quantum Monte Carlo Integration <https://www.nature.com/articles/s41598-024-61010-9>`_.
    This implementation utilizes :ref:`IQAE`. A detailed explanation can be found in the `QMCI tutorial <https://www.qrisp.eu/general/tutorial/QMCItutorial.html>`_.
    QMCI performs numerical integration of (high-dimensional) functions w.r.t. probability distributions:
    .. math::
        \int_{[0,1]^n} f(x_1 ,\dotsc , x_n) \mathrm{d}\mu (x_1 ,\dotsc , x_n)
    Parameters
    ----------
    qargs : list[:ref:`QuantumFloat`]
        The quantum variables representing the $x$-axes (the variables on which the given ``function`` acts), and a quantum variable representing the $y$-axis.
    function : function
        A Python function which takes :ref:`QuantumFloats <QuantumFloat>` as inputs,
        and returns a :ref:`QuantumFloat` containing the values of the integrand.
    distribution : function, optional
        A Python function which takes :ref:`QuantumFloats <QuantumFloat>` as inputs and applies the distribution over which to integrate.
        By default, the uniform distribution is applied.
    mes_kwargs : dict, optional
        The keyword arguments for the measurement function. Default is an empty dictionary.
    Returns
    -------
    float
        The result of the numerical integration.
    Examples
    --------
    We integrate the function $f(x)=x^2$ over the integral $[0,1]$.
    Therefore, the function is evaluated at $8=2^3$ sampling points as specified by ``QuantumFloat(3,-3)``.
    The $y$-axis is representend by ``QuantumFloat(6,-6)``.
    ::
        from qrisp import QuantumFloat
        from qrisp.qmci import QMCI
        def f(qf):
            return qf*qf
        qf_x = QuantumFloat(3,-3)
        qf_y = QuantumFloat(6,-6)
        QMCI([qf_x,qf_y], f)
        # Yields: 0.27373180511103606
    This result is consistent with numerically calculating the integral by evaluating the function $f$ at 8 sampling points:
    ::
        N = 8
        sum((i/N)**2 for i in range(N))/N
        # Yields: 0.2734375
    A detailed explanation of QMCI and its implementation in Qrisp can be found in the `QMCI tutorial <https://www.qrisp.eu/general/tutorial/QMCItutorial.html>`_.
    """
    if distribution == None:
        distribution = uniform
    # dupl_args = [arg.duplicate() for arg in qargs]
    # dupl_res_qf = function(*dupl_args)
    # qargs.append(dupl_res_qf.duplicate())
    # for arg in dupl_args:
    #    arg.delete()
    # dupl_res_qf.delete()
    V0 = 1
    for arg in qargs:
        V0 *= 2 ** (arg.size + arg.exponent)
    qargs.append(QuantumBool())
    @auto_uncompute
    def state_function(*args):
        qf_x = args[:-2]
        qf_y = args[-2]
        tar = args[-1]
        distribution(*qf_x)
        h(qf_y)
        with qf_y < function(*qf_x):
            x(tar)
    a = IQAE(qargs, state_function, eps=0.01, alpha=0.01, mes_kwargs=mes_kwargs)
    V = V0 * a
    return V