Ejemplo n.º 1
0
    def __init__(self,
                 oracle,
                 superposition_circuit=None,
                 initial_state_circuit=None,
                 superposition_qubits=None,
                 superposition_size=None,
                 number_solutions=None,
                 target_amplitude=None,
                 check=None,
                 check_args=(),
                 iterative=False):

        self.oracle = oracle
        self.initial_state_circuit = initial_state_circuit

        if superposition_circuit:
            self.superposition = superposition_circuit
        else:
            if not superposition_qubits:
                raise_error(
                    ValueError, "Cannot create Grover model if the "
                    "superposition circuit or number of "
                    "qubits is not specified.")
            self.superposition = Circuit(superposition_qubits)
            self.superposition.add(
                [gates.H(i) for i in range(superposition_qubits)])

        if superposition_qubits:
            self.sup_qubits = superposition_qubits
        else:
            self.sup_qubits = self.superposition.nqubits

        if superposition_size:
            self.sup_size = superposition_size
        else:
            self.sup_size = int(2**self.sup_qubits)

        assert oracle.nqubits > self.sup_qubits

        self.anc_qubits_sup = self.superposition.nqubits - self.sup_qubits
        self.anc_qubits_ora = self.oracle.nqubits - self.sup_qubits - 1

        self.nqubits = self.sup_qubits + max(self.anc_qubits_sup,
                                             self.anc_qubits_ora) + 1

        self.check = check
        self.check_args = check_args
        self.num_sol = number_solutions
        self.targ_a = target_amplitude
        self.iterative = iterative

        self.space_sup = list(range(self.sup_qubits + self.anc_qubits_sup))
        self.space_ora = list(
            range(self.sup_qubits + self.anc_qubits_ora)) + [self.nqubits - 1]
Ejemplo n.º 2
0
    def circuit(self, iterations):
        """Creates circuit that performs Grover's algorithm with a set amount of iterations.

        Args:
            iterations (int): number of times to repeat the Grover step.

        Returns:
            :class:`qibo.core.circuit.Circuit` that performs Grover's algorithm.
        """
        c = Circuit(self.nqubits)
        c += self.initialize()
        for _ in range(iterations):
            c += self.step()
        c.add(gates.M(*range(self.sup_qubits)))
        return c
Ejemplo n.º 3
0
 def initialize(self):
     """Initialize the Grover algorithm with the superposition and Grover ancilla."""
     c = Circuit(self.nqubits)
     c.add(gates.X(self.nqubits-1))
     c.add(gates.H(self.nqubits-1))
     if self.initial_state_circuit:
         c.add(self.initial_state_circuit.invert().on_qubits(*range(self.initial_state_circuit.nqubits)))
     c.add(self.superposition.on_qubits(*self.space_sup))
     return c
Ejemplo n.º 4
0
 def step(self):
     """Combine oracle and diffusion for a Grover step."""
     c = Circuit(self.nqubits)
     c.add(self.oracle.on_qubits(*self.space_ora))
     c.add(self.diffusion().on_qubits(*(self.space_sup +
                                        [self.nqubits - 1])))
     return c
Ejemplo n.º 5
0
class Grover(object):
    """Model that performs Grover's algorithm.

    For Grover's original search algorithm: `arXiv:quant-ph/9605043 <https://arxiv.org/abs/quant-ph/9605043>`_
    For the iterative version with unknown solutions:`arXiv:quant-ph/9605034 <https://arxiv.org/abs/quant-ph/9605034>`_
    For the Grover algorithm with any superposition:`arXiv:quant-ph/9712011 <https://arxiv.org/abs/quant-ph/9712011>`_

    Args:
        oracle (:class:`qibo.core.circuit.Circuit`): quantum circuit that flips
            the sign using a Grover ancilla initialized with -X-H-. Grover ancilla
            expected to be last qubit of oracle circuit.
        superposition_circuit (:class:`qibo.core.circuit.Circuit`): quantum circuit that
            takes an initial state to a superposition. Expected to use the first
            set of qubits to store the relevant superposition.
        initial_state_circuit (:class:`qibo.core.circuit.Circuit`): quantum circuit
            that initializes the state. If empty defaults to ``|000..00>``
        superposition_qubits (int): number of qubits that store the relevant superposition.
            Leave empty if superposition does not use ancillas.
        superposition_size (int): how many states are in a superposition.
            Leave empty if its an equal superposition of quantum states.
        number_solutions (int): number of expected solutions. Needed for normal Grover.
            Leave empty for iterative version.
        target_amplitude (float): absolute value of the amplitude of the target state. Only for
            advanced use and known systems.
        check (function): function that returns True if the solution has been
            found. Required of iterative approach.
            First argument should be the bitstring to check.
        check_args (tuple): arguments needed for the check function.
            The found bitstring not included.
        iterative (bool): force the use of the iterative Grover

    Example:
        .. testcode::

            import numpy as np
            from qibo import gates
            from qibo.models import Circuit
            from qibo.models.grover import Grover
            # Create an oracle. Ex: Oracle that detects state |11111>
            oracle = Circuit(5 + 1)
            oracle.add(gates.X(5).controlled_by(*range(5)))
            # Create superoposition circuit. Ex: Full superposition over 5 qubits.
            superposition = Circuit(5)
            superposition.add([gates.H(i) for i in range(5)])
            # Generate and execute Grover class
            grover = Grover(oracle, superposition_circuit=superposition, number_solutions=1)
            solution, iterations = grover()
    """

    def __init__(self, oracle, superposition_circuit=None, initial_state_circuit=None,
                 superposition_qubits=None, superposition_size=None, number_solutions=None,
                 target_amplitude = None, check=None, check_args=(), iterative=False):

        self.oracle = oracle
        self.initial_state_circuit = initial_state_circuit

        if superposition_circuit:
            self.superposition = superposition_circuit
        else:
            if not superposition_qubits:
                raise_error(ValueError, "Cannot create Grover model if the "
                                        "superposition circuit or number of "
                                        "qubits is not specified.")
            self.superposition = Circuit(superposition_qubits)
            self.superposition.add([gates.H(i) for i in range(superposition_qubits)])

        if superposition_qubits:
            self.sup_qubits = superposition_qubits
        else:
            self.sup_qubits = self.superposition.nqubits

        if superposition_size:
            self.sup_size = superposition_size
        else:
            self.sup_size = int(2 ** self.sup_qubits)

        assert oracle.nqubits > self.sup_qubits

        self.anc_qubits_sup = self.superposition.nqubits - self.sup_qubits
        self.anc_qubits_ora = self.oracle.nqubits - self.sup_qubits - 1

        self.nqubits = self.sup_qubits + max(self.anc_qubits_sup, self.anc_qubits_ora) + 1

        self.check = check
        self.check_args = check_args
        self.num_sol = number_solutions
        self.targ_a = target_amplitude
        self.iterative = iterative

        self.space_sup = list(range(self.sup_qubits + self.anc_qubits_sup))
        self.space_ora = list(range(self.sup_qubits + self.anc_qubits_ora)) + [self.nqubits-1]

    def initialize(self):
        """Initialize the Grover algorithm with the superposition and Grover ancilla."""
        c = Circuit(self.nqubits)
        c.add(gates.X(self.nqubits-1))
        c.add(gates.H(self.nqubits-1))
        if self.initial_state_circuit:
            c.add(self.initial_state_circuit.invert().on_qubits(*range(self.initial_state_circuit.nqubits)))
        c.add(self.superposition.on_qubits(*self.space_sup))
        return c

    def diffusion(self):
        """Construct the diffusion operator out of the superposition circuit."""
        nqubits = self.superposition.nqubits + 1
        c = Circuit(nqubits)
        c.add(self.superposition.invert().on_qubits(*range(nqubits-1)))
        if self.initial_state_circuit:
            c.add(self.initial_state_circuit.invert().on_qubits(*range(self.initial_state_circuit.nqubits)))
        c.add([gates.X(i) for i in range(self.sup_qubits)])
        c.add(gates.X(nqubits-1).controlled_by(*range(self.sup_qubits)))
        c.add([gates.X(i) for i in range(self.sup_qubits)])
        if self.initial_state_circuit:
            c.add(self.initial_state_circuit.on_qubits(*range(self.initial_state_circuit.nqubits)))
        c.add(self.superposition.on_qubits(*range(nqubits-1)))
        return c

    def step(self):
        """Combine oracle and diffusion for a Grover step."""
        c = Circuit(self.nqubits)
        c.add(self.oracle.on_qubits(*self.space_ora))
        c.add(self.diffusion().on_qubits(*(self.space_sup+[self.nqubits-1])))
        return c

    def circuit(self, iterations):
        """Creates circuit that performs Grover's algorithm with a set amount of iterations.

        Args:
            iterations (int): number of times to repeat the Grover step.

        Returns:
            :class:`qibo.core.circuit.Circuit` that performs Grover's algorithm.
        """
        c = Circuit(self.nqubits)
        c += self.initialize()
        for _ in range(iterations):
            c += self.step()
        c.add(gates.M(*range(self.sup_qubits)))
        return c

    def iterative_grover(self, lamda_value=6/5):
        """Iterative approach of Grover for when the number of solutions is not known.

        Args:
            lamda_value (real): parameter that controls the evolution of the iterative method.
                                Must be between 1 and 4/3.

        Returns:
            measured (str): bitstring measured and checked as a valid solution.
            total_iterations (int): number of times the oracle has been called.
        """
        k = 1
        lamda = lamda_value
        total_iterations = 0
        while True:
            it = np.random.randint(k + 1)
            if it != 0:
                total_iterations += it
                circuit = self.circuit(it)
                result = circuit(nshots=1)
                measured = result.frequencies(binary=True).most_common(1)[0][0]
                if self.check(measured, *self.check_args):
                    return measured, total_iterations
            k = min(lamda * k, np.sqrt(self.sup_size))
            if total_iterations > (9/4) * np.sqrt(self.sup_size):
                log.warning("Too many total iterations, output might not be solution.")
                return measured, total_iterations

    def execute(self, nshots=100, freq=False, logs=False):
        """Execute Grover's algorithm.

        If the number of solutions is given, calculates iterations,
        otherwise it uses an iterative approach.

        Args:
            nshots (int): number of shots in order to get the frequencies.
            freq (bool): print the full frequencies after the exact Grover algorithm.

        Returns:
            solution (str): bitstring (or list of bitstrings) measured as solution of the search.
            iterations (int): number of oracle calls done to reach a solution.
        """
        if (self.num_sol or self.targ_a) and not self.iterative:
            if self.targ_a:
                it = int(np.pi * (1/self.targ_a) / 4)
            else:
                it = int(np.pi * np.sqrt(self.sup_size / self.num_sol) / 4)
            circuit = self.circuit(it)
            result = circuit(nshots=nshots).frequencies(binary=True)
            if freq:
                if logs:
                    log.info("Result of sampling Grover's algorihm")
                    log.info(result)
                self.frequencies = result
            if logs:
                log.info(f"Most common states found using Grover's algorithm with {it} iterations:")
            if self.targ_a:
                most_common = result.most_common(1)
            else:
                most_common = result.most_common(self.num_sol)
            self.solution = []
            self.iterations = it
            for i in most_common:
                if logs:
                    log.info(i[0])
                self.solution.append(i[0])
                if logs:
                    if self.check:
                        if self.check(i[0], *self.check_args):
                            log.info('Solution checked and successful.')
                        else:
                            log.info('Not a solution of the problem. Something went wrong.')
        else:
            if not self.check:
                raise_error(ValueError, "Check function needed for iterative approach.")
            measured, total_iterations = self.iterative_grover()
            if logs:
                log.info('Solution found in an iterative process.')
                log.info(f'Solution: {measured}')
                log.info(f'Total Grover iterations taken: {total_iterations}')
            self.solution = measured
            self.iterations = total_iterations
        return self.solution, self.iterations


    def __call__(self, nshots=100, freq=False, logs=False):
        """Equivalent to :meth:`qibo.models.grover.Grover.execute`."""
        return self.execute(nshots=nshots, freq=freq, logs=logs)
Ejemplo n.º 6
0
 def diffusion(self):
     """Construct the diffusion operator out of the superposition circuit."""
     nqubits = self.superposition.nqubits + 1
     c = Circuit(nqubits)
     c.add(self.superposition.invert().on_qubits(*range(nqubits-1)))
     if self.initial_state_circuit:
         c.add(self.initial_state_circuit.invert().on_qubits(*range(self.initial_state_circuit.nqubits)))
     c.add([gates.X(i) for i in range(self.sup_qubits)])
     c.add(gates.X(nqubits-1).controlled_by(*range(self.sup_qubits)))
     c.add([gates.X(i) for i in range(self.sup_qubits)])
     if self.initial_state_circuit:
         c.add(self.initial_state_circuit.on_qubits(*range(self.initial_state_circuit.nqubits)))
     c.add(self.superposition.on_qubits(*range(nqubits-1)))
     return c
Ejemplo n.º 7
0
Archivo: hep.py Proyecto: qiboteam/qibo
def ansatz_Weighted(layers, qubits=1):
    """Fourier Ansatz implementation. 4 parameters per layer and
    qubit: Ry(wx + a), Rz(v log(x) + b)

    Args:
        layers (int): number of layers.
        qubits (int): number of qubits.

    Returns:
        The circuit, the rotation function and the total number of
        parameters.
    """
    circuit = Circuit(qubits)
    for _ in range(layers - 1):
        for q in range(qubits):
            circuit.add(gates.RY(q, theta=0))
            circuit.add(gates.RZ(q, theta=0))
        if qubits > 1:
            for q in range(0, qubits, 2):
                circuit.add(gates.CU1(q, (q + 1) % qubits, theta=0))
        if qubits > 2:
            for q in range(1, qubits + 1, 2):
                circuit.add(gates.CU1(q, (q + 1) % qubits, theta=0))
    for q in range(qubits):
        circuit.add(gates.RY(q, theta=0))
        circuit.add(gates.RZ(q, theta=0))

    def rotation(theta, x):
        p = circuit.get_parameters()
        i = 0
        j = 0
        for l in range(layers - 1):
            for q in range(qubits):
                p[i] = theta[j] + theta[j + 1] * map_to(x)
                p[i + 1] = theta[j + 2] + theta[j + 3] * maplog_to(x)
                i += 2
                j += 4

            if qubits > 1:
                for q in range(0, qubits, 2):
                    p[i] = theta[j]
                    i += 1
                    j += 1
            if qubits > 2:
                for q in range(1, qubits + 1, 2):
                    p[i] = theta[j]
                    i += 1
                    j += 1

        for q in range(qubits):
            p[i] = theta[j] + theta[j + 1] * map_to(x)
            p[i + 1] = theta[j + 2] + theta[j + 3] * maplog_to(x)
            i += 2
            j += 4

        return p

    nparams = 4 * layers * qubits + \
        (layers - 1) * int(K.np.ceil(qubits / 2)) * \
        (int(qubits > 1) + int(qubits > 2))
    return circuit, rotation, nparams
Ejemplo n.º 8
0
Archivo: hep.py Proyecto: qiboteam/qibo
def ansatz_Fourier(layers, qubits=1):
    """Fourier Ansatz implementation. It is composed by 3 parameters per layer
    and qubit: U3(a, b, c) Ry(x) || U3(a, b, c) Ry(log x).

    Args:
        layers (int): number of layers.
        qubits (int): number of qubits.

    Returns:
        The circuit, the rotation function and the total number of parameters.
    """
    circuit = Circuit(qubits)
    for l in range(layers - 1):
        for q in range(qubits):
            for _ in range(2):
                circuit.add(gates.RY(q, theta=0))
                circuit.add(gates.RZ(q, theta=0))
                circuit.add(gates.RY(q, theta=0))

        if qubits > 1:
            for q in range(0, qubits, 2):
                circuit.add(gates.CU1(q, q + 1, theta=0))
        if qubits > 2:
            for q in range(1, qubits + 1, 2):
                circuit.add(gates.CU1(q, (q + 1) % qubits, theta=0))

    for q in range(qubits):
        for _ in range(2):
            circuit.add(gates.RY(q, theta=0))
            circuit.add(gates.RZ(q, theta=0))
            circuit.add(gates.RY(q, theta=0))

    def rotation(theta, x):
        p = circuit.get_parameters()
        i = 0
        j = 0
        for l in range(layers - 1):
            for q in range(qubits):
                p[i] = map_to(x)
                p[i + 1:i + 3] = theta[j:j + 2]
                i += 3
                j += 2

                p[i] = .5 * maplog_to(x)
                p[i + 1:i + 3] = theta[j:j + 2]
                i += 3
                j += 2
            if qubits > 1:
                for q in range(0, qubits, 2):
                    p[i] = theta[j]
                    i += 1
                    j += 1
            if qubits > 2:
                for q in range(1, qubits + 1, 2):
                    p[i] = theta[j]
                    i += 1
                    j += 1
        for q in range(qubits):
            p[i] = .5 * map_to(x)
            p[i + 1:i + 3] = theta[j:j + 2]
            i += 3
            j += 2

            p[i] = .5 * maplog_to(x)
            p[i + 1:i + 3] = theta[j:j + 2]
            i += 3
            j += 2
        return p

    nparams = 4 * layers * qubits + \
        (layers - 1) * int(K.np.ceil(qubits / 2)) * \
        (int(qubits > 1) + int(qubits > 2))

    return circuit, rotation, nparams