Algorithms#
Qibo provides circuit constructions of quantum algorithms. They are implemented in
qibo/models/algorithms, and all of them are available from qibo.models, e.g.
from qibo.models import QFT.
Fourier transform and phase estimation#
Quantum Fourier Transform (QFT)#
- class qibo.models.QFT(nqubits, with_swaps=True, accelerators=None, **kwargs)[source]#
Creates a circuit that implements the Quantum Fourier Transform.
The Quantum Fourier Transform (QFT) on \(n\) qubits is the unitary operator that acts on the computational basis states as
\[\text{QFT} \, \ket{j} = \frac{1}{\sqrt{N}} \sum_{k=0}^{N-1} e^{2 \pi i j k / N} \ket{k} \, ,\]where \(N = 2^{n}\), i.e. it applies the discrete Fourier transform to the amplitudes of a state. The integer \(j = j_{1} 2^{n - 1} + \cdots + j_{n} 2^{0}\) is represented by the bitstring \(j_{1} \cdots j_{n}\) of the qubits, with the first qubit being the most significant one. The circuit is the one in Fig. 5.1 of Ref. [1]. For each qubit \(q = 0, \dots, n - 1\), from the first to the last, it applies
a Hadamard gate on the qubit \(q\);
the controlled-\(R_{k}\) gates, implemented with
qibo.gates.CU1, that have the qubit \(q + k - 1\) as control and the qubit \(q\) as target, for \(k = 2, \dots, n - q\), where\[\begin{split}R_{k} = \begin{pmatrix} 1 & 0 \\ 0 & e^{2 \pi i / 2^{k}} \end{pmatrix} \, .\end{split}\]
If
with_swaps=True, \(\lfloor n / 2 \rfloor\)qibo.gates.SWAPgates are added at the end to reverse the order of the qubits. Hence, the circuit has \(n (n + 1) / 2\) Hadamard and controlled phase gates, plus the swaps, which is \(\Theta(n^{2})\) gates. The inverse QFT is obtained withqibo.models.circuit.Circuit.invert().Example
import numpy as np from qibo.backends import NumpyBackend from qibo.models import QFT from qibo.quantum_info import random_statevector backend = NumpyBackend() nqubits = 6 dims = 2**nqubits # Random normalized initial state vector initial_state = random_statevector(dims, backend=backend) circuit = QFT(nqubits) # Execute the circuit final_state = circuit(initial_state)
- Parameters:
nqubits (int) – number of qubits in the circuit.
with_swaps (bool, optional) – If
True, usesqibo.gates.SWAPgates at the end of the circuit so that the qubit order in the final state is the same as the initial state. Otherwise, the qubits of the final state are in the reverse order. Defaults toTrue.accelerators (dict, optional) – Accelerator device dictionary in order to use a distributed circuit. If
None, a simple (non-distributed) circuit will be used.kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.Circuit. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the Quantum Fourier Transform.
- Return type:
qibo.models.Circuit
References
1. M. A. Nielsen and I. L. Chuang, Quantum Computation and Quantum Information, Cambridge University Press (2010), Sec. 5.1.
Quantum Phase Estimation (QPE)#
- qibo.models.QPE(unitary, nqubits_counting, measure=True, **kwargs)[source]#
Creates a circuit that implements the canonical Quantum Phase Estimation.
Given a unitary \(U\) and one of its eigenstates \(|\psi\rangle\), such that \(U|\psi\rangle = e^{2 \pi i \varphi}|\psi\rangle\), the circuit estimates \(\varphi \in [0, \, 1)\) with \(t\) bits of precision, where \(t\) is the number of counting qubits. It acts on \(t + n\) qubits, with \(n\) being the number of qubits of \(U\), and it is composed of the following steps:
a Hadamard gate on each of the \(t\) counting qubits, which are the first qubits of the circuit;
\(U^{2^{t - 1 - j}}\), controlled by the counting qubit \(j\), acting on the last \(n\) qubits of the circuit, the target qubits;
the inverse
qibo.models.QFTon the counting qubits;the measurement of the counting qubits, if
measure=True.
The target qubits have to be prepared in \(|\psi\rangle\) before the circuit is applied, e.g. by adding the circuit that prepares it to a larger circuit before the one returned by this function. At the end, the counting qubits hold a bitstring \(b_{1} \cdots b_{t}\), with \(b_{1}\) being the outcome of the first qubit, such that \(\varphi\) is approximated by \(0.b_{1} \cdots b_{t}\) in binary, which is exact if \(\varphi\) is an integer multiple of \(2^{-t}\). The conversion from the bitstring into the phase can be done with
qibo.quantum_info.bitstring_to_phase(), and the phase of the most probable outcome, i.e. the estimate of \(\varphi\), withqibo.quantum_info.estimate_phase().Note
The power \(U^{2^{k}}\) is implemented by applying the controlled \(U\) a number \(2^{k}\) of times. Hence, \(U\) is applied \(2^{t} - 1\) times in total.
- Parameters:
unitary (
qibo.models.circuit.Circuitorqibo.gates.Gateor ndarray) – unitary \(U\), given as a circuit, as a gate, e.g.qibo.gates.Unitary, or as a matrix. A matrix must be unitary and of dimension \(2^{n} \times 2^{n}\) with \(n > 0\), and it is converted into aqibo.gates.Unitaryacting on \(n\) qubits. The qubits of a gate are relabeled, in increasing order, as the qubits of a circuit with the same number of qubits as the gate. All of the gates must be supported byqibo.models.circuit.Circuit.controlled_by().nqubits_counting (int) – number of counting qubits \(t\). The phase is estimated with a resolution of \(2^{-t}\).
measure (bool, optional) – If
True, adds a measurement of the counting qubits at the end of the circuit. Defaults toTrue.kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the Quantum Phase Estimation.
- Return type:
Example
import math from qibo import Circuit, gates from qibo.models import QPE from qibo.quantum_info import estimate_phase # unitary with eigenstate |1> and eigenvalue exp(2 pi i phase) phase = 5 / 16 unitary = Circuit(1) unitary.add(gates.U1(0, theta=2 * math.pi * phase)) # phase estimation with 4 counting qubits nqubits_counting = 4 qpe = QPE(unitary, nqubits_counting) # prepare the eigenstate in the target qubit and apply the phase estimation circuit = Circuit(qpe.nqubits) circuit.add(gates.X(nqubits_counting)) circuit.add(qpe) result = circuit(nshots=100) print(estimate_phase(result))
0.3125
References
1. R. Cleve, A. Ekert, C. Macchiavello, and M. Mosca, Quantum algorithms revisited, Proc. R. Soc. Lond. A 454, 339 (1998).
2. M. A. Nielsen and I. L. Chuang, Quantum Computation and Quantum Information, Cambridge University Press (2010), Sec. 5.2.
Iterative Quantum Phase Estimation (IQPE)#
- qibo.models.IQPE(unitary, nbits, **kwargs)[source]#
Creates a circuit that implements the Iterative Quantum Phase Estimation.
Given a unitary \(U\) and one of its eigenstates \(|\psi\rangle\), such that \(U|\psi\rangle = e^{2 \pi i \varphi}|\psi\rangle\), the circuit estimates \(\varphi \in [0, \, 1)\) with \(t\) bits of precision, where \(t\) is
nbits, asqibo.models.QPE()does. However, a single counting qubit is measured \(t\) times, instead of using \(t\) counting qubits and the inverseqibo.models.QFT. The circuit acts on \(1 + n\) qubits, with \(n\) being the number of qubits of \(U\). The first qubit is the counting qubit and the last \(n\) qubits are the target qubits.The bits of \(\varphi\) are obtained one at a time, starting from the least significant one. In each step \(k = 1, \dots, t\):
a Hadamard gate is applied to the counting qubit;
\(U^{2^{t - k}}\) is applied to the target qubits, controlled by the counting qubit;
a phase gate is applied to the counting qubit, with the angle \(-2 \pi \sum_{j=1}^{k-1} m_{j} \, 2^{-(k - j + 1)}\), where \(m_{j}\) is the outcome of the step \(j\), which removes the contribution of the bits that were already measured;
a Hadamard gate is applied to the counting qubit, which is measured with
collapse=True, and the outcome \(m_{k}\) is the bit \(b_{t - k + 1}\) of \(\varphi = 0.b_{1} \cdots b_{t}\);if it is not the last step, the counting qubit is reset to \(|0\rangle\) with an \(X\) rotation conditioned on \(m_{k}\).
The circuit finishes with a measurement of the counting qubit that is not collapsed, so that it can be simulated with state vectors. The target qubits have to be prepared in \(|\psi\rangle\) before the circuit is applied, e.g. by adding the circuit that prepares it to a larger circuit before the one returned by this function.
The outcomes of the measurements are not merged into the result of the execution of the circuit. They are returned as a list of
qibo.measurements.MeasurementResult, which is ordered from the most to the least significant bit, i.e. in the reverse order of the measurements. Hence, they can be joined withqibo.measurements.MeasurementResult.join()and the estimate of \(\varphi\) is obtained withqibo.quantum_info.estimate_phase().Note
As in
qibo.models.QPE(), \(U\) is applied \(2^{t} - 1\) times in total. The circuit has fewer qubits, but when it is simulated, each shot has to be simulated separately because of the measurements with collapse.Example
import math from qibo import Circuit, gates from qibo.measurements import MeasurementResult from qibo.models import IQPE from qibo.quantum_info import estimate_phase # unitary with eigenstate |1> and eigenvalue exp(2 pi i phase) phase = 5 / 16 unitary = Circuit(1) unitary.add(gates.U1(0, theta=2 * math.pi * phase)) # phase estimation with 4 bits iqpe, results = IQPE(unitary, 4) # prepare the eigenstate in the target qubit and apply the phase estimation circuit = Circuit(iqpe.nqubits) circuit.add(gates.X(1)) circuit.add(iqpe) circuit(nshots=100) print(estimate_phase(MeasurementResult.join(*results)))
0.3125
- Parameters:
unitary (
qibo.models.circuit.Circuitorqibo.gates.Gateor ndarray) – unitary \(U\), given as a circuit, as a gate, e.g.qibo.gates.Unitary, or as a matrix. Seeqibo.models.QPE()for the details.nbits (int) – number of bits \(t\) of the phase to be estimated, which sets the resolution \(2^{-t}\) and the number of measurements.
kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
the circuit that implements the Iterative Quantum Phase Estimation and the list of
qibo.measurements.MeasurementResultwith the outcomes of the measurements of the counting qubit, from the most to the least significant bit.- Return type:
References
1. A. Yu. Kitaev, Quantum measurements and the Abelian Stabilizer Problem, arXiv:quant-ph/9511026.
2. R. B. Griffiths and C.-S. Niu, Semiclassical Fourier Transform for Quantum Computation, Phys. Rev. Lett. 76, 3228 (1996).
Amplitude amplification and search#
Grover’s Algorithm#
- class qibo.models.Grover(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)[source]#
Model that performs Grover’s search algorithm.
Given a Boolean function \(\chi\) over \(N\) states, which is \(1\) for the \(t\) states that are solutions and \(0\) otherwise, the algorithm finds a solution with \(O(\sqrt{N / t})\) calls to the oracle \(\mathbf{S}_{\chi}\), which changes the sign of the amplitudes of the solutions [1, 2]. Let \(\mathcal{A}\) be the circuit that prepares the initial superposition \(\ket{\psi} = \mathcal{A}\ket{0}\) from the all-zero state \(\ket{0}\). In the original algorithm it is the Walsh-Hadamard transform, so that \(\ket{\psi}\) is the equal superposition of all the states. The algorithm applies \(j\) times the Grover iteration
\[\mathbf{G} = \mathcal{A} \, \mathbf{S}_{0} \, \mathcal{A}^{-1} \, \mathbf{S}_{\chi}\]to \(\ket{\psi}\), where \(\mathbf{S}_{0}\) changes the sign of the amplitude of \(\ket{0}\), and then it measures the state. For the Walsh-Hadamard transform, \(\mathbf{G}\) is minus the iteration of Refs. [1, 2], and this global sign has no effect on the measurements. After \(j\) iterations, the probability of measuring a solution is \(\sin^{2}((2 j + 1) \theta)\), with \(\sin^{2}(\theta) = t / N\) [2]. The number of iterations is chosen as follows:
if
number_solutions\(t\) is given, it is \(\lfloor \pi / (4 \theta) \rfloor\) [2], for which the probability of failure is at most \(t / N\);if
target_amplitude\(a\) is given, it is \(\lfloor \pi / (4 a) \rfloor\) [3], where \(a\) is the absolute value of the amplitude of the target state in \(\ket{\psi}\). This allows using any superposition;otherwise, \(t\) is unknown, and the iterative algorithm of Ref. [2], Sec. 4, is used. See
iterative_grover().
The oracle changes the sign of the amplitudes of the solutions by phase kickback. It flips the Grover auxiliary qubit, which is the last qubit of the oracle circuit and is prepared in the state \(\ket{-} = H X \ket{0}\) by the algorithm, when the other qubits are in a solution.
Example
from qibo import Circuit, gates from qibo.models import Grover # oracle that detects the state |11111>, with the Grover auxiliary qubit being the # last qubit oracle = Circuit(5 + 1) oracle.add(gates.X(5).controlled_by(*range(5))) # superposition circuit: equal superposition over 5 qubits superposition = Circuit(5) superposition.add(gates.H(qubit) for qubit in range(5)) # create and execute the Grover model grover = Grover(oracle, superposition_circuit=superposition, number_solutions=1) solution, iterations = grover() print(solution, iterations)
['11111'] 4
- Parameters:
oracle (
qibo.models.circuit.Circuit) – circuit that implements \(\mathbf{S}_{\chi}\) by phase kickback. The last qubit is the Grover auxiliary qubit, which is prepared in the state \(\ket{-}\), and the first qubits store the superposition.superposition_circuit (
qibo.models.circuit.Circuit, optional) – circuit \(\mathcal{A}\) that takes the initial state to the superposition. It is expected to use the first qubits to store the relevant superposition. IfNone, Hadamard gates are applied tosuperposition_qubitsqubits. Defaults toNone.initial_state_circuit (
qibo.models.circuit.Circuit, optional) – circuit that prepares the initial state, which is applied beforesuperposition_circuit. IfNone, the initial state is the all-zero state \(\ket{0 \cdots 0}\). Defaults toNone.superposition_qubits (int, optional) – number of qubits that store the relevant superposition, which are the ones that are measured. If
None, it is the number of qubits ofsuperposition_circuit, which then must not use auxiliary qubits. It is required ifsuperposition_circuitis not given. Defaults toNone.superposition_size (int, optional) – number \(N\) of states in the superposition. If
None, it is \(2^{n}\), with \(n\) being the number of qubits that store the superposition, i.e. an equal superposition of all their states. Defaults toNone.number_solutions (int, optional) – number \(t\) of solutions. It is needed unless the iterative algorithm is used. Defaults to
None.target_amplitude (float, optional) – absolute value \(a\) of the amplitude of the target state in the superposition. Only for advanced use and known systems. Defaults to
None.check (Callable, optional) – function that returns
Trueif the bitstring is a solution. Its first argument is the bitstring to check, and it is required by the iterative algorithm. Defaults toNone.check_args (tuple, optional) – additional arguments of
check, without the bitstring. Defaults to().iterative (bool, optional) – If
True, uses the iterative algorithm even ifnumber_solutionsortarget_amplitudeis given. Defaults toFalse.
References
1. L. K. Grover, A fast quantum mechanical algorithm for database search, Proc. 28th Annu. ACM Symp. Theory Comput. (STOC ‘96), 212 (1996).
2. M. Boyer, G. Brassard, P. Høyer, and A. Tapp, Tight bounds on quantum searching, Fortschr. Phys. 46, 493 (1998).
3. L. K. Grover, Quantum computers can search rapidly by using almost any transformation, Phys. Rev. Lett. 80, 4329 (1998).
- initialize()[source]#
Creates the circuit that prepares the initial state of the algorithm.
The circuit acts on all the qubits. It prepares the state \(\ket{-}\) in the Grover auxiliary qubit, which is the last one, with a
qibo.gates.Xgate followed by aqibo.gates.Hgate. Then, it appliesinitial_state_circuit, if any, andsuperposition_circuit.- Returns:
circuit that prepares the initial state.
- Return type:
- diffusion()[source]#
Creates the circuit of the diffusion operator.
The operator is \(\mathcal{A} \, \mathbf{S}_{0} \, \mathcal{A}^{-1}\), where \(\mathcal{A}\) is the circuit that prepares the superposition from the all-zero state, i.e.
initial_state_circuitfollowed bysuperposition_circuit, and \(\mathbf{S}_{0}\) changes the sign of the amplitude of the state in which the qubits that store the superposition are all in the state \(\ket{0}\). It is implemented with a multi-controlledqibo.gates.Xgate that acts on the Grover auxiliary qubit, between layers ofqibo.gates.Xgates. If the superposition circuit does not use auxiliary qubits, the operator is \(\mathbf{I} - 2 \ket{\psi}\!\bra{\psi}\), which for the equal superposition is minus the inversion about the average, i.e. the diffusion transform of Ref. [1].- Returns:
circuit of the diffusion operator, which acts on the qubits of the superposition circuit and the Grover auxiliary qubit.
- Return type:
- step()[source]#
Creates the circuit of a Grover iteration.
It applies the oracle, which changes the sign of the amplitudes of the solutions, followed by the diffusion operator of
diffusion(). Hence, the circuit implements the operator \(\mathbf{G} = \mathcal{A} \, \mathbf{S}_{0} \, \mathcal{A}^{-1} \, \mathbf{S}_{\chi}\), and it calls the oracle once.- Returns:
circuit that acts on all the qubits.
- Return type:
- circuit(iterations)[source]#
Creates the circuit that performs Grover’s algorithm with a given number of iterations.
The circuit is composed of
initialize(),iterationstimesstep()and a measurement of the qubits that store the superposition, which are the first ones.- Parameters:
iterations (int) – number of times to repeat the Grover iteration, i.e. number of calls to the oracle.
- Returns:
circuit that performs Grover’s algorithm.
- Return type:
- iterative_grover(lamda_value=1.2, backend=None)[source]#
Finds a solution with the iterative algorithm, for an unknown number of solutions.
It is the algorithm of Ref. [2], Sec. 4, whose expected number of Grover iterations is in \(O(\sqrt{N / t})\), where \(t\) is the unknown number of solutions. Starting with \(m = 1\), in each round the algorithm:
draws an integer \(j\) uniformly at random among the non-negative integers smaller than \(m\);
applies \(j\) Grover iterations to the initial state, which are none if \(j = 0\), measures the qubits that store the superposition once, and returns the bitstring if
checkisTruefor it;sets \(m \leftarrow \min(\lambda \, m, \sqrt{N})\), where \(\lambda\) is
lamda_value.
If the total number of iterations exceeds \((9 / 4) \sqrt{N}\), which is the bound of Ref. [2] on the expected number of iterations for \(t = 1\), a warning is logged and the last measured bitstring is returned, even if it is not a solution. The function
checkis required.- Parameters:
lamda_value (float, optional) – parameter \(\lambda\) that controls the growth of \(m\). It must be strictly between \(1\) and \(4 / 3\). Defaults to \(6 / 5\).
backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the execution of the circuits. IfNone, it uses the current backend. Defaults toNone.
- Returns:
the bitstring that was measured and checked as a solution, and the total number of Grover iterations, i.e. the number of calls to the oracle.
- Return type:
- execute(nshots=100, freq=False, logs=False, backend=None)[source]#
Executes Grover’s algorithm.
If
number_solutionsortarget_amplitudeis given anditerativeisFalse, the number of iterations is computed as described in the documentation of the class. The circuit ofcircuit()is samplednshotstimes, and the solutions are thenumber_solutionsmost frequent bitstrings, or only the most frequent one iftarget_amplitudeis given. Otherwise, it uses the iterative algorithm ofiterative_grover(), which requirescheck, and aValueErroris raised if it is not given. The solution and the number of iterations are also stored in the attributessolutionanditerations.- Parameters:
nshots (int, optional) – number of shots used to sample the circuit when the number of iterations is computed. Defaults to \(100\).
freq (bool, optional) – If
True, stores the frequencies of the measured bitstrings in the attributefrequencies, and logs them iflogsisTrue. It only applies when the number of iterations is computed. Defaults toFalse.logs (bool, optional) – If
True, logs the solutions found, and whether they passcheck, if given. Defaults toFalse.backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the execution of the circuits. IfNone, it uses the current backend. Defaults toNone.
- Returns:
the solution, which is a list of bitstrings, or a single bitstring if the iterative algorithm is used, and the number of Grover iterations, i.e. the number of calls to the oracle.
- Return type:
Quantum Amplitude Amplification#
Quantum Amplitude Amplification (QAA) generalizes Grover’s algorithm to an arbitrary circuit \(\mathcal{A}\) that prepares the state \(|\Psi\rangle = \mathcal{A}|0\rangle\) from the all-zero state \(|0\rangle\). Given a Boolean function \(\chi\), which marks the “good” computational basis states, let \(a\) be the probability of measuring a good state in \(|\Psi\rangle\). Repeating the circuit and measuring it takes \(O(1 / a)\) runs on average to find a good state, whereas amplification needs only \(O(1 / \sqrt{a})\) applications of \(\mathcal{A}\) and its inverse \(\mathcal{A}^{-1}\). This is done by applying the operator
where \(\mathbf{S}_{\chi}\) changes the sign of the amplitude of the good states and \(\mathbf{S}_{0}\) changes the sign of the amplitude of the state \(|0\rangle\). After \(m\) applications, the probability of measuring a good state is \(\sin^{2}((2m + 1)\theta_{a})\), where \(a = \sin^{2}(\theta_{a})\). Depending on what is known about \(a\), the following can be used:
qibo.models.amplification_operator()builds the circuit of \(\mathbf{Q}\);qibo.models.QAA()builds the circuit \(\mathbf{Q}^{m}\mathcal{A}\), either with a chosen number \(m\) of iterations, or with the number that is computed from a known \(a\), which can also find a good state with certainty;qibo.models.FPQAA()builds a fixed-point version, which only needs a lower bound of \(a\), and whose failure probability stays below a chosen bound if more iterations are applied, while keeping the quadratic speedup;qibo.models.OAA()builds an oblivious version, which amplifies the amplitude with which a circuit implements a unitary on an input state that is unknown, so that the reflection about that state, which is not available, is not needed;qibo.models.QSearch()finds a good state when \(a\) is unknown, by running circuits with an exponentially increasing number of iterations.
- qibo.models.amplification_operator(algorithm, oracle, **kwargs)[source]#
Creates the circuit of the amplitude amplification operator.
Given a circuit \(\mathcal{A}\), which prepares the state \(|\Psi\rangle = \mathcal{A}|0\rangle\) from the all-zero state \(|0\rangle\), and a Boolean function \(\chi\), which marks the “good” computational basis states, the amplification operator is
\[\mathbf{Q} = -\mathcal{A} \, \mathbf{S}_{0} \, \mathcal{A}^{-1} \, \mathbf{S}_{\chi}.\]Here, \(\mathbf{S}_{\chi}\) changes the sign of the amplitude of every good state and \(\mathbf{S}_{0}\) changes the sign of the amplitude of the state \(|0\rangle\) only. The operator \(\mathbf{Q}\) rotates \(|\Psi\rangle\), in the plane spanned by its good and bad components, by an angle \(2\theta_{a}\), where \(a = \sin^{2}(\theta_{a})\) is the probability of measuring a good state in \(|\Psi\rangle\). For \(\mathcal{A}\) equal to Hadamard gates on every qubit, \(\mathbf{Q}\) is the iterate of Grover’s search algorithm.
The operator is applied to the circuit as \(\mathbf{S}_{\chi}\), \(\mathcal{A}^{-1}\), \(\mathbf{S}_{0}\), \(\mathcal{A}\) and a global phase of \(\pi\), which accounts for the minus sign and matters if the circuit is controlled.
- Parameters:
algorithm (
qibo.models.circuit.Circuit) – circuit \(\mathcal{A}\). It cannot contain measurements.oracle (
qibo.models.circuit.Circuit) – circuit \(\mathbf{S}_{\chi}\), a diagonal operator that multiplies the good states by \(-1\), e.g. a multi-controlledqibo.gates.Zgate. It must act on the same number of qubits asalgorithm, and it cannot contain measurements.kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
circuit that implements \(\mathbf{Q}\), with the same number of qubits as
algorithm.- Return type:
Example
from qibo import Circuit, gates from qibo.models import amplification_operator # Hadamard gates on 2 qubits, and the oracle that marks the state |11> algorithm = Circuit(2) algorithm.add(gates.H(qubit) for qubit in range(2)) oracle = Circuit(2) oracle.add(gates.CZ(0, 1)) # for 2 qubits and 1 good state, one application of Q finds the good state circuit = Circuit(2) circuit.add(algorithm) circuit.add(amplification_operator(algorithm, oracle)) circuit.add(gates.M(0, 1)) result = circuit(nshots=10) print(result.frequencies())
Counter({'11': 10})References
1. G. Brassard, P. Høyer, M. Mosca, and A. Tapp, Quantum Amplitude Amplification and Estimation, Contemporary Mathematics 305, 53 (2002).
- qibo.models.FPQAA(algorithm, oracle, delta, niterations=None, probability=None, measure=True, backend=None, **kwargs)[source]#
Creates a circuit that implements fixed-point Quantum Amplitude Amplification (FPQAA).
Let \(\mathcal{A}\) be the circuit
algorithm, \(\chi\) a Boolean function that marks “good” computational basis states, and \(a\) the probability of measuring a good state in \(|\Psi\rangle = \mathcal{A}|0\rangle\), where \(|0\rangle\) is the all-zero state. The success probability ofqibo.models.QAA()oscillates with the number of iterations, so too many iterations decrease it, and the right number depends on \(a\). In contrast, this circuit, which follows Yoder, Low and Chuang, only needs a lower bound for \(a\), and more iterations never push its success probability below the bound.The circuit applies \(\mathcal{A}\) and then \(l\) generalized Grover iterates,
\[\mathcal{S}_{L} = G(\alpha_{l}, \beta_{l}) \cdots G(\alpha_{1}, \beta_{1}), \qquad G(\alpha, \beta) = -\mathbf{S}_{\Psi}(\alpha) \, \mathbf{S}_{\chi}(\beta),\]with \(L = 2l + 1\). Here, \(\mathbf{S}_{\chi}(\beta)\) multiplies the amplitude of every good state by \(e^{i\beta}\), and \(\mathbf{S}_{\Psi}(\alpha) = \mathcal{A} \, \mathbf{S}_{0}(\alpha) \, \mathcal{A}^{-1}\) multiplies the amplitude of \(|\Psi\rangle\) by \(e^{-i\alpha}\), where \(\mathbf{S}_{0}(\alpha)\) does it for \(|0\rangle\). The phases are
\[\alpha_{j} = -\beta_{l - j + 1} = 2 \cot^{-1}\left(\tan(2 \pi j / L) \sqrt{1 - \gamma^{2}}\right), \qquad \gamma^{-1} = T_{1 / L}(1 / \delta),\]where \(T_{n}(x) = \cos(n \cos^{-1}(x))\) is the Chebyshev polynomial of the first kind of degree \(n\), which is also defined for \(x > 1\) and non-integer \(n\). The probability of measuring a good state is then
\[P_{L} = 1 - \delta^{2} \, T_{L}\left(T_{1 / L}(1 / \delta) \sqrt{1 - a}\right)^{2} \geq 1 - \delta^{2} \quad \text{for every} \quad a \geq 1 - T_{1 / L}(1 / \delta)^{-2}.\]The lower bound on \(a\) decreases as \(L\) grows, and it is about \((\log(2 / \delta) / L)^{2}\) for large \(L\) and small \(\delta\). Thus, the oracle is used \(2l = L - 1\) times, which is in \(O(\log(2 / \delta) / \sqrt{a})\) for a suitable \(L\), as in
qibo.models.QAA(). For \(\delta = 1\) the circuit is Grover’s one, i.e. the one ofqibo.models.QAA()withniterations=l, and for \(\delta = 0\) and \(l = 1\) it is the \(\pi / 3\) fixed-point search of Grover, whose probability is \(1 - (1 - a)^{L}\).The circuit acts on the qubits of
algorithmand on one auxiliary qubit, which is the last qubit, starts in the state \(|0\rangle\) and returns to it. It is used to apply the phase \(e^{i\beta}\) to the good states: the oracle is controlled by the auxiliary qubit and surrounded by Hadamard gates, which flips the auxiliary qubit if and only if the state is good, then a phase gate acts on it and the flip is undone. Hence, all of the gates of the oracle must be supported byqibo.models.circuit.Circuit.controlled_by(). The measurement, ifmeasure=True, is applied to the qubits ofalgorithmonly. The number of iterations \(l\) is determined in one of the following ways:niterationsis given, and \(l\) is its value;probabilityis given, which is a lower bound \(a_{0}\) of \(a\), and \(l\) is the smallest number of iterations for which \(P_{L} \geq 1 - \delta^{2}\) for every \(a \geq a_{0}\), i.e. \(L \geq \cosh^{-1}(1 / \delta) / \tanh^{-1}(\sqrt{a_{0}})\). Note that the bound on the failure probability is trivial for \(\delta = 1\), and then \(l = 0\).
Example
from qibo import Circuit, gates from qibo.models import FPQAA # Hadamard gates on 3 qubits, and the oracle that marks the state |111> nqubits = 3 algorithm = Circuit(nqubits) algorithm.add(gates.H(qubit) for qubit in range(nqubits)) oracle = Circuit(nqubits) oracle.add(gates.CCZ(0, 1, 2)) # the failure probability is at most 0.1^2 if the probability of |111>, which is # 1 / 8, is not smaller than 1 / 16 circuit = FPQAA(algorithm, oracle, delta=0.1, probability=1 / 16) result = circuit(nshots=1000) print(result.frequencies().most_common(1)[0][0])
111
- Parameters:
algorithm (
qibo.models.circuit.Circuit) – circuit \(\mathcal{A}\) without measurements.oracle (
qibo.models.circuit.Circuit) – circuit \(\mathbf{S}_{\chi}\) that multiplies the good states by \(-1\), without measurements and with the same number of qubits asalgorithm. Seeqibo.models.amplification_operator().delta (float) – bound \(\delta \in [0, 1]\) of the failure probability, which is at most \(\delta^{2}\) over the range of \(a\) above.
niterations (int, optional) – number \(l \geq 0\) of generalized Grover iterates. Exactly one of
niterationsandprobabilityhas to be given. Defaults toNone.probability (float, optional) – lower bound \(a_{0} \in (0, 1]\) of the probability of measuring a good state in \(\mathcal{A}|0\rangle\), from which the number of iterations is computed. It requires \(\delta > 0\). Defaults to
None.measure (bool, optional) – If
True, adds a measurement of the qubits ofalgorithmat the end of the circuit. Defaults toTrue.backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the calculation of the number of iterations and of the phases. IfNone, it uses the current backend. Defaults toNone.kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of fixed-point Quantum Amplitude Amplification, with one more qubit than
algorithm.- Return type:
References
1. T. J. Yoder, G. H. Low, and I. L. Chuang, Fixed-Point Quantum Search with an Optimal Number of Queries, Phys. Rev. Lett. 113, 210501 (2014).
- qibo.models.OAA(unitary, nauxiliary, niterations=None, probability=None, exact=False, backend=None, **kwargs)[source]#
Create a circuit that implements Oblivious Amplitude Amplification (OAA).
Let \(U\) be the circuit
unitary, which acts on \(\mu\) auxiliary qubits, the first ones of the circuit, and on \(n\) system qubits, the last ones. Let \(|0^{\mu}\rangle\) be the all-zero state of the auxiliary qubits and \(\Pi = |0^{\mu}\rangle\langle 0^{\mu}| \otimes \mathbb{1}\) the projector onto it. The circuit applies a unitary \(V\) on the system qubits with amplitude \(\sin(\theta)\), for every state \(|\psi\rangle\) of the system qubits, i.e.\[U |0^{\mu}\rangle |\psi\rangle = \sin(\theta) |0^{\mu}\rangle V |\psi\rangle + \cos(\theta) |\Phi^{\perp}\rangle,\]where \(|\Phi^{\perp}\rangle\), which depends on \(|\psi\rangle\), satisfies \(\Pi |\Phi^{\perp}\rangle = 0\). This is the case of a block encoding of \(\sin(\theta) V\), and the probability \(p = \sin^{2}(\theta)\) of measuring the auxiliary qubits in the state \(|0^{\mu}\rangle\) is the same for every \(|\psi\rangle\). The circuit then boosts this amplitude with the operator
\[\mathbf{S} = -U \mathbf{R} U^{\dagger} \mathbf{R}, \qquad \mathbf{R} = 2 \Pi - \mathbb{1},\]which satisfies, for any integer \(l\),
\[\mathbf{S}^{l} U |0^{\mu}\rangle |\psi\rangle = \sin((2l + 1)\theta) |0^{\mu}\rangle V |\psi\rangle + \cos((2l + 1)\theta) |\Phi^{\perp}\rangle.\]In contrast with
qibo.models.amplification_operator(), \(\mathbf{S}\) does not reflect about the state \(U|0^{\mu}\rangle|\psi\rangle\), which is unknown since \(|\psi\rangle\) is not. Thus, the circuit amplifies the implementation of \(V\) without knowing, preparing or measuring the input state, so it can be a part of a larger circuit. It is applied to the circuit as \(U\), followed by \(l\) times a sign flip of \(|0^{\mu}\rangle\), \(U^{\dagger}\), the sign flip, \(U\) and a global phase of \(\pi\), since \(\mathbf{R}\) is minus the sign flip. The number \(l\) of iterations is determined in one of the following ways:niterationsis given, and \(l\) is its value;probabilityis given, which is \(p\), and \(l = \lfloor \pi / (4 \theta) \rfloor\). The probability of measuring the auxiliary qubits in the state \(|0^{\mu}\rangle\) is then at least \(\max(p, 1 - p)\);probabilityis given andexact=True. Then \(V\) is implemented exactly, i.e. the final state is \(|0^{\mu}\rangle V|\psi\rangle\). The circuit uses \(l = \lceil \pi / (4 \theta) - 1 / 2 \rceil\) iterations, which is a fraction of an iteration too many. To compensate it, the amplitude is lowered to \(\sin(\pi / (4l + 2))\) with an extra auxiliary qubit, which is the last qubit of the circuit, and rotated by a \(R_{y}\) gate. The extra qubit is also required to be in the state \(|0\rangle\) at the end, where it is found with certainty. For \(p = 1 / 4\), one iteration is enough, so \(U\) is used three times.
Example
import math from qibo import Circuit, gates from qibo.models import OAA # U acts on 1 auxiliary qubit and 1 system qubit. If the auxiliary qubit is found in # the state 0, which has probability sin(theta)^2 = 1 / 4, V is the X gate theta = math.pi / 6 unitary = Circuit(2) unitary.add(gates.X(1)) unitary.add(gates.RY(0, theta=math.pi - 2 * theta)) unitary.add(gates.H(1).controlled_by(0)) # one iteration implements V with certainty, so the system qubit, in the state 0, # is flipped without measuring the auxiliary qubit before circuit = Circuit(2) circuit.add(OAA(unitary, 1, probability=1 / 4)) circuit.add(gates.M(0, 1)) result = circuit(nshots=100) print(result.frequencies())
Counter({'01': 100})- Parameters:
unitary (
qibo.models.circuit.Circuit) – circuit \(U\) without measurements.nauxiliary (int) – number \(\mu \geq 1\) of auxiliary qubits, which are the first qubits of
unitary.niterations (int, optional) – number \(l \geq 0\) of applications of \(\mathbf{S}\). Exactly one of
niterationsandprobabilityhas to be given. Defaults toNone.probability (float, optional) – probability \(p \in (0, 1]\) of measuring the auxiliary qubits in the state \(|0^{\mu}\rangle\) after \(U\), from which the number of iterations is computed. Defaults to
None.exact (bool, optional) – If
True, \(V\) is implemented exactly, which requiresprobability. Defaults toFalse.backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the calculation of the number of iterations and of the rotation angle. IfNone, it uses the current backend. Defaults toNone.kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of Oblivious Amplitude Amplification, with one more qubit than
unitaryifexact=True.- Return type:
References
1. D. W. Berry, A. M. Childs, R. Cleve, R. Kothari, and R. D. Somma, Exponential improvement in precision for simulating sparse Hamiltonians, Forum Math. Sigma 5, e8 (2017).
- qibo.models.QAA(algorithm, oracle, niterations=None, probability=None, exact=False, measure=True, backend=None, **kwargs)[source]#
Creates a circuit that implements Quantum Amplitude Amplification (QAA).
Given a circuit \(\mathcal{A}\) and a Boolean function \(\chi\) that marks “good” computational basis states, let \(a\) be the probability of measuring a good state in \(\mathcal{A}|0\rangle\), where \(|0\rangle\) is the all-zero state. The circuit applies \(\mathcal{A}\) and then \(m\) times the operator \(\mathbf{Q}\) of
qibo.models.amplification_operator(), such that the probability of measuring a good state becomes \(\sin^{2}((2m + 1)\theta_{a})\), with \(a = \sin^{2}(\theta_{a})\) and \(0 \leq \theta_{a} \leq \pi / 2\). The number \(m\) of iterations is determined in one of the following ways:niterationsis given, and \(m\) is its value;probabilityis given, which is \(a\), and \(m = \lfloor \pi / (4 \theta_{a}) \rfloor\). The probability of measuring a good state is then at least \(\max(a, 1 - a)\), and \(m\) is in \(O(1 / \sqrt{a})\), whereas \(O(1 / a)\) repetitions of \(\mathcal{A}\) are needed without amplification;probabilityis given andexact=True. Then the good states are found with certainty. The circuit uses \(m = \lceil \pi / (4 \theta_{a}) - 1 / 2 \rceil\) iterations, which is a fraction of an iteration too many. To compensate it, the success probability of \(\mathcal{A}\) is lowered to \(\sin^{2}(\pi / (4m + 2))\) with an extra auxiliary qubit, which is the last qubit of the circuit. It is rotated by \(R_{y}\) gate and a state is considered good only if \(\chi\) marks it and the auxiliary qubit is in the state \(|1\rangle\). The oracle is controlled by the auxiliary qubit, so all of its gates must be supported byqibo.models.circuit.Circuit.controlled_by().
The circuit acts on the qubits of
algorithm, plus the auxiliary qubit ifexact=True, and the measurement, ifmeasure=True, is applied to the qubits ofalgorithmonly. If the probability \(a\) is unknown, useqibo.models.QSearch().- Parameters:
algorithm (
qibo.models.circuit.Circuit) – circuit \(\mathcal{A}\) without measurements.oracle (
qibo.models.circuit.Circuit) – circuit \(\mathbf{S}_{\chi}\) that multiplies the good states by \(-1\), without measurements and with the same number of qubits asalgorithm. Seeqibo.models.amplification_operator().niterations (int, optional) – number \(m \geq 0\) of applications of \(\mathbf{Q}\). Exactly one of
niterationsandprobabilityhas to be given. Defaults toNone.probability (float, optional) – probability \(a \in (0, 1]\) of measuring a good state in \(\mathcal{A}|0\rangle\), from which the number of iterations is computed. Defaults to
None.exact (bool, optional) – If
True, the good states are found with certainty, which requiresprobability. Defaults toFalse.measure (bool, optional) – If
True, adds a measurement of the qubits ofalgorithmat the end of the circuit. Defaults toTrue.backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the calculation of the number of iterations and of the rotation angle. IfNone, it uses the current backend. Defaults toNone.kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of Quantum Amplitude Amplification.
- Return type:
Example
from qibo import Circuit, gates from qibo.models import QAA # Hadamard gates on 3 qubits, and the oracle that marks the state |111> nqubits = 3 algorithm = Circuit(nqubits) algorithm.add(gates.H(qubit) for qubit in range(nqubits)) oracle = Circuit(nqubits) oracle.add(gates.CCZ(0, 1, 2)) # the initial probability of measuring |111> is 1 / 8 circuit = QAA(algorithm, oracle, probability=1 / 8) result = circuit(nshots=1000) print(result.frequencies().most_common(1)[0][0])
111
References
1. G. Brassard, P. Høyer, M. Mosca, and A. Tapp, Quantum Amplitude Amplification and Estimation, Contemporary Mathematics 305, 53 (2002).
- qibo.models.QSearch(algorithm, oracle, check, growth=1.2, max_rounds=60, seed=None, backend=None)[source]#
Finds a good state with Quantum Amplitude Amplification when its probability is unknown.
Let \(a\) be the unknown probability of measuring a good state in \(\mathcal{A}|0\rangle\), where \(\mathcal{A}\) is the circuit
algorithmand \(|0\rangle\) is the all-zero state. In each round \(\ell = 1, 2, \dots\), the algorithm:sets \(M = \lceil c^{\ell} \rceil\), where \(c\) is
growth;measures \(\mathcal{A}|0\rangle\), and stops if the outcome is good;
draws an integer \(j\) uniformly at random between \(1\) and \(M\), measures \(\mathbf{Q}^{j}\mathcal{A}|0\rangle\), with \(\mathbf{Q}\) the operator of
qibo.models.amplification_operator(), and stops if the outcome is good.
A good outcome is detected with the classical function
check. If \(a > 0\), the expected number of applications of \(\mathcal{A}\) and \(\mathcal{A}^{-1}\) is in \(\Theta(1 / \sqrt{a})\), and otherwise the search never finishes, which is why the number of rounds is limited bymax_rounds.- Parameters:
algorithm (
qibo.models.circuit.Circuit) – circuit \(\mathcal{A}\) without measurements.oracle (
qibo.models.circuit.Circuit) – circuit \(\mathbf{S}_{\chi}\) that multiplies the good states by \(-1\), without measurements and with the same number of qubits asalgorithm. Seeqibo.models.amplification_operator().check (Callable) – function that receives a measured bitstring, e.g.
"101", and returnsTrueif the state is good.growth (float, optional) – factor \(c\) by which the maximum number of iterations grows in each round, which has to be in the interval \((1, 2)\). Defaults to \(6 / 5\).
max_rounds (int, optional) – maximum number of rounds. Defaults to \(60\).
seed (int or
numpy.random.Generator, optional) – seed or generator used to draw \(j\). The measurements are sampled by the backend, so they are reproducible only if the seed of the backend is set, e.g. withqibo.backends.abstract.Backend.set_seed(). Defaults toNone.backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the execution of the circuits. IfNone, it uses the current backend. Defaults toNone.
- Returns:
the good bitstring, or
Noneif none was found inmax_roundsrounds, and the number of applications of \(\mathcal{A}\) and \(\mathcal{A}^{-1}\) used.- Return type:
Example
from qibo import Circuit, gates from qibo.models import QSearch # Hadamard gates on 4 qubits, and the oracle that marks the state |1011> nqubits = 4 algorithm = Circuit(nqubits) algorithm.add(gates.H(qubit) for qubit in range(nqubits)) oracle = Circuit(nqubits) oracle.add(gates.X(1)) oracle.add(gates.Z(3).controlled_by(0, 1, 2)) oracle.add(gates.X(1)) bitstring, napplications = QSearch( algorithm, oracle, check=lambda bitstring: bitstring == "1011", seed=0 ) print(bitstring)
1011
References
1. G. Brassard, P. Høyer, M. Mosca, and A. Tapp, Quantum Amplitude Amplification and Estimation, Contemporary Mathematics 305, 53 (2002).
Amplitude estimation#
Quantum Amplitude Estimation (QAE)#
- qibo.models.QAE(algorithm, oracle, nqubits_counting, measure=True, **kwargs)[source]#
Creates a circuit that implements the canonical Quantum Amplitude Estimation.
Given a circuit \(\mathcal{A}\), which prepares the state \(|\Psi\rangle = \mathcal{A}|0\rangle\) from the all-zero state \(|0\rangle\), and an oracle that marks the “good” computational basis states, the circuit estimates the probability \(a = \sin^{2}(\theta_{a})\) of measuring a good state in \(|\Psi\rangle\), where \(\theta_{a} \in [0, \, \pi / 2]\). It acts on \(t + n\) qubits, with \(t\) being the number of counting qubits and \(n\) the number of qubits of \(\mathcal{A}\), and it is composed of the following steps:
\(\mathcal{A}\) on the last \(n\) qubits of the circuit;
the
qibo.models.QPE()of the amplification operator \(\mathbf{Q}\) ofqibo.models.amplification_operator(), with the \(t\) first qubits as counting qubits.
The state \(|\Psi\rangle\) is a superposition of two eigenvectors of \(\mathbf{Q}\), with eigenvalues \(e^{\pm 2 i \theta_{a}}\). Hence, if \(y\) is the integer represented by the bitstring of the counting qubits, and \(M = 2^{t}\), then \(y / M\) approximates \(\theta_{a} / \pi\) or \(1 - \theta_{a} / \pi\), and the estimate of \(a\) is \(\tilde{a} = \sin^{2}(\pi y / M)\). The phase \(y / M\) of the most probable outcome is given by
qibo.quantum_info.estimate_phase(). With probability of at least \(8 / \pi^{2}\),\[|\tilde{a} - a| \leq \frac{2 \pi \sqrt{a (1 - a)}}{M} + \frac{\pi^{2}}{M^{2}} \, .\]Note
\(\mathbf{Q}\) is applied \(2^{t} - 1\) times in total.
Example
import math from qibo import Circuit, gates from qibo.models import QAE from qibo.quantum_info import estimate_phase # state cos(theta) |0> + sin(theta) |1> with theta = pi / 8, and good state |1> theta = math.pi / 8 algorithm = Circuit(1) algorithm.add(gates.RY(0, theta=2 * theta)) oracle = Circuit(1) oracle.add(gates.Z(0)) # amplitude estimation with 3 counting qubits circuit = QAE(algorithm, oracle, 3) result = circuit(nshots=100) print(round(math.sin(math.pi * estimate_phase(result)) ** 2, 4))
0.1464
- Parameters:
algorithm (
qibo.models.circuit.Circuit) – circuit \(\mathcal{A}\). It cannot contain measurements.oracle (
qibo.models.circuit.Circuit) – circuit that multiplies the good states by \(-1\), e.g. a multi-controlledqibo.gates.Zgate. It must act on the same number of qubits asalgorithm, and it cannot contain measurements.nqubits_counting (int) – number of counting qubits \(t\), which sets the resolution \(2^{-t}\) of the estimate of \(\theta_{a} / \pi\).
measure (bool, optional) – If
True, adds a measurement of the counting qubits at the end of the circuit. Defaults toTrue.kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the Quantum Amplitude Estimation.
- Return type:
References
1. G. Brassard, P. Høyer, M. Mosca, and A. Tapp, Quantum Amplitude Amplification and Estimation, Contemporary Mathematics 305, 53 (2002).
2. R. Cleve, A. Ekert, C. Macchiavello, and M. Mosca, Quantum algorithms revisited, Proc. R. Soc. Lond. A 454, 339 (1998).
Quantum Counting#
- qibo.models.Counting(oracle, nqubits_counting, measure=True, **kwargs)[source]#
Creates a circuit that implements the Quantum Counting algorithm.
Given a Boolean function \(\chi\) over the \(N = 2^{n}\) computational basis states of \(n\) qubits, the circuit estimates the number \(M\) of states that \(\chi\) marks with \(1\). It is the
qibo.models.QAE()of the uniform superposition of the \(N\) states, i.e. with Hadamard gates on every qubit as the circuit that prepares the state, in which the probability of measuring a marked state is \(M / N = \sin^{2}(\theta)\). The circuit acts on \(t + n\) qubits, with \(t\) being the number of counting qubits. If \(y\) is the integer represented by the bitstring of the counting qubits, and \(L = 2^{t}\), then the estimate of \(M\) is\[\tilde{M} = N \sin^{2}\left( \frac{\pi y}{L} \right) \, .\]The phase \(y / L\) of the most probable outcome is given by
qibo.quantum_info.estimate_phase(). With probability of at least \(8 / \pi^{2}\),\[|\tilde{M} - M| \leq \frac{2 \pi \sqrt{M (N - M)}}{L} + \frac{\pi^{2} N}{L^{2}} \, .\]For \(M = 0\), the estimate is exact, which decides if there are any marked states.
Example
import math from qibo import Circuit, gates from qibo.models import Counting from qibo.quantum_info import estimate_phase # oracle that marks the states 10 and 11 of 2 qubits oracle = Circuit(2) oracle.add(gates.Z(0)) # counting with 3 counting qubits circuit = Counting(oracle, 3) result = circuit(nshots=100) print(round(4 * math.sin(math.pi * estimate_phase(result)) ** 2))
2
- Parameters:
oracle (
qibo.models.circuit.Circuit) – circuit that multiplies the marked states by \(-1\), e.g. a multi-controlledqibo.gates.Zgate. It cannot contain measurements.nqubits_counting (int) – number of counting qubits \(t\), which sets the resolution \(2^{-t}\) of the estimate of \(\theta / \pi\).
measure (bool, optional) – If
True, adds a measurement of the counting qubits at the end of the circuit. Defaults toTrue.kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the Quantum Counting.
- Return type:
References
1. G. Brassard, P. Høyer, and A. Tapp, Quantum Counting, Automata, Languages and Programming, LNCS 1443, 820 (1998).
2. G. Brassard, P. Høyer, M. Mosca, and A. Tapp, Quantum Amplitude Amplification and Estimation, Contemporary Mathematics 305, 53 (2002).
Maximum-likelihood Quantum Amplitude Estimation (MLQAE)#
- qibo.models.MLQAE(algorithm, oracle, check, nlevels=5, nshots=100, backend=None)[source]#
Estimates an amplitude with the Maximum-likelihood Quantum Amplitude Estimation.
Let \(a\) be the probability of measuring a good state in \(\mathcal{A}|0\rangle\), where \(\mathcal{A}\) is the circuit
algorithm, \(|0\rangle\) is the all-zero state, and the good states are marked by the oracle. Let \(\theta_{a} \in [0, \, \pi / 2]\) be such that \(a = \sin^{2}(\theta_{a})\). After \(m\) applications of the operator \(\mathbf{Q}\) ofqibo.models.amplification_operator(), the probability of measuring a good state is \(\sin^{2}((2m + 1)\theta_{a})\). Differently fromqibo.models.QAE(), there are no counting qubits nor controlled operations. Instead, the circuit \(\mathbf{Q}^{m}\mathcal{A}\) is measurednshotstimes for each \(m\) in \(\{0, \, 1, \, 2, \, 4, \, \dots, \, 2^{L - 1}\}\), where \(L\) isnlevels. If \(h_{m}\) of the outcomes are good for a given \(m\), the estimate is \(\sin^{2}(\hat{\theta})\), where \(\hat{\theta}\) maximizes the likelihood\[\prod_{m} \sin^{2}\left((2m + 1)\theta\right)^{h_{m}} \, \cos^{2}\left((2m + 1)\theta\right)^{N - h_{m}} \, ,\]with \(N\) being
nshots. The maximum is found with a grid search on \([0, \, \pi / 2]\), which is refined around the best point. The number of applications of \(\mathcal{A}\) and \(\mathcal{A}^{-1}\) is \(N \sum_{m} (2m + 1)\). With this exponential schedule of \(m\), the error is reported to approach the Heisenberg limit, which is inversely proportional to that number (Ref. [1]).Example
import math from qibo import Circuit, gates from qibo.models import MLQAE # state cos(theta) |0> + sin(theta) |1> with theta = 0.5, and good state |1> algorithm = Circuit(1) algorithm.add(gates.RY(0, theta=2 * 0.5)) oracle = Circuit(1) oracle.add(gates.Z(0)) estimate = MLQAE(algorithm, oracle, check=lambda bitstring: bitstring == "1") # the exact amplitude is sin(0.5)^2, which is approximately 0.23 print(round(estimate, 1))
0.2
- Parameters:
algorithm (
qibo.models.circuit.Circuit) – circuit \(\mathcal{A}\) without measurements.oracle (
qibo.models.circuit.Circuit) – circuit \(\mathbf{S}_{\chi}\) that multiplies the good states by \(-1\), without measurements and with the same number of qubits asalgorithm. Seeqibo.models.amplification_operator().check (Callable) – function that receives a measured bitstring, e.g.
"101", and returnsTrueif the state is good.nlevels (int, optional) – number \(L\) of levels of the schedule of the numbers of iterations, which are \(0\) and \(2^{k}\), for \(k = 0, \dots, L - 1\). Defaults to \(5\).
nshots (int, optional) – number of shots of each circuit. Defaults to \(100\).
backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the execution of the circuits and in the maximization. IfNone, it uses the current backend. Defaults toNone.
- Returns:
estimate of \(a\), which belongs to the interval \([0, \, 1]\).
- Return type:
References
1. Y. Suzuki, S. Uno, R. Raymond, T. Tanaka, T. Onodera, and N. Yamamoto, Amplitude estimation without phase estimation, Quantum Inf. Process. 19, 75 (2020).
2. G. Brassard, P. Høyer, M. Mosca, and A. Tapp, Quantum Amplitude Amplification and Estimation, Contemporary Mathematics 305, 53 (2002).
Iterative Quantum Amplitude Estimation (IQAE)#
- class qibo.models.IQAE(circuit_a, circuit_q, alpha=0.05, epsilon=0.005, n_shots=1024, method='chernoff')[source]#
Iterative Quantum Amplitude Estimation (IQAE).
Let \(\mathcal{A}\) be a circuit on \(n + 1\) qubits, such that
\[\mathcal{A} \ket{0}_{n} \ket{0} = \sqrt{1 - a} \, \ket{\psi_{0}}_{n} \ket{0} + \sqrt{a} \, \ket{\psi_{1}}_{n} \ket{1},\]where \(\ket{\psi_{0}}_{n}\) and \(\ket{\psi_{1}}_{n}\) are normalized states and \(a \in [0, 1]\) is the unknown probability of measuring the last qubit in the state \(\ket{1}\). Let \(\mathbf{Q}\) be the operator of
qibo.models.amplification_operator()for \(\mathcal{A}\) and for the oracle that multiplies by \(-1\) the states with the last qubit in the state \(\ket{1}\), e.g. aqibo.gates.Zgate on the last qubit. For \(a = \sin^{2}(\theta_{a})\) and any integer \(k \geq 0\),\[\mathbf{Q}^{k} \mathcal{A} \ket{0}_{n} \ket{0} = \cos((2k + 1) \theta_{a}) \ket{\psi_{0}}_{n} \ket{0} + \sin((2k + 1) \theta_{a}) \ket{\psi_{1}}_{n} \ket{1},\]so the probability of measuring the last qubit in the state \(\ket{1}\) is \(\sin^{2}((2k + 1) \theta_{a})\). In contrast with
qibo.models.QAE(), IQAE does not use quantum phase estimation, hence it needs neither counting qubits nor controlled powers of \(\mathbf{Q}\). It only applies \(\mathbf{Q}\) to \(\mathcal{A}\ket{0}\) and measures the last qubit. Starting from the interval \([\theta_{l}, \theta_{u}] = [0, \pi / 2]\) for \(\theta_{a}\), each iteration \(i\)finds the largest power \(k_{i}\) such that \(K_{i} = 4 k_{i} + 2 \geq 2 K_{i-1}\) and the interval \(K_{i} [\theta_{l}, \theta_{u}]\), modulo \(2 \pi\), lies completely in \([0, \pi]\) or in \([\pi, 2 \pi]\). If there is none, it keeps \(k_{i-1}\);
measures the last qubit of \(\mathbf{Q}^{k_{i}} \mathcal{A} \ket{0}\)
n_shotstimes, or fewer times in the last iterations, where more shots would not improve the result (the “no-overshooting” condition). With all of the shots of the iterations that share \(k_{i}\), it computes a confidence interval of \(\sin^{2}((2 k_{i} + 1) \theta_{a}) = (1 - \cos(K_{i} \theta_{a})) / 2\);inverts the cosine, which is unambiguous in each half of the circle, and shrinks \([\theta_{l}, \theta_{u}]\).
The algorithm stops when \(\theta_{u} - \theta_{l} \leq 2 \epsilon\), where \(\epsilon\) is
epsilon, and it returns the interval \([a_{l}, a_{u}] = [\sin^{2}(\theta_{l}), \sin^{2}(\theta_{u})]\). The significance level of each confidence interval is the one of the algorithm, \(\alpha\), divided by the maximum number \(\lceil \log_{2}(\pi / (8 \epsilon)) \rceil\) of rounds, a round being the set of iterations with the same \(k_{i}\). Then, \(a_{u} - a_{l} \leq 2 \epsilon\) and \(a\) lies in \([a_{l}, a_{u}]\) with probability at least \(1 - \alpha\). Thus, the estimation \((a_{l} + a_{u}) / 2\) differs from \(a\) by at most \(\epsilon\) with that probability. Ifn_shotsis not larger than\[N_{\max}(\epsilon, \alpha) = \left\lceil \frac{32}{(1 - 2 \sin(\pi / 14))^{2}} \log\left(\frac{2}{\alpha} \log_{2}\left(\frac{\pi}{4 \epsilon}\right)\right) \right\rceil,\]the algorithm ends after at most \(\lceil \log_{2}(\pi / (8 \epsilon)) \rceil\) rounds and \(\mathbf{Q}\) is applied fewer than \((50 / \epsilon) \log((2 / \alpha) \log_{2}(\pi / (4 \epsilon)))\) times. This is the quadratic speedup over the \(O(1 / \epsilon^{2})\) samples of classical Monte Carlo simulation, up to a double-logarithmic factor.
Note
For the default values of
epsilonandalpha, \(N_{\max} = 590\), which is smaller than the defaultn_shots.The circuit
circuit_qcan act on more qubits thancircuit_a, e.g. on auxiliary qubits, as long as it leaves them in the state \(\ket{0}\).Example
import math from qibo import Circuit, gates from qibo.models import IQAE, amplification_operator # A applies a Hadamard gate on the first qubit and, if it is in the state 1, a # rotation of angle 2 on the last qubit, so a = sin(1)^2 / 2 algorithm = Circuit(2) algorithm.add(gates.H(0)) algorithm.add(gates.CRY(0, 1, theta=2.0)) # the oracle multiplies by -1 the states with the last qubit in the state 1 oracle = Circuit(2) oracle.add(gates.Z(1)) operator = amplification_operator(algorithm, oracle) iqae = IQAE(algorithm, operator, alpha=0.05, epsilon=0.01) result = iqae.execute() print(abs(result.estimation - math.sin(1.0) ** 2 / 2) <= 0.01)
True
- Parameters:
circuit_a (
qibo.models.circuit.Circuit) – circuit \(\mathcal{A}\) without measurements. Its last qubit is the one that is measured.circuit_q (
qibo.models.circuit.Circuit) – circuit of the operator \(\mathbf{Q}\), which has to act on at least as many qubits ascircuit_a.alpha (float, optional) – significance level \(\alpha \in (0, 1)\), i.e. the probability that the interval returned does not contain \(a\) is at most
alpha. Defaults to \(0.05\).epsilon (float, optional) – target accuracy \(\epsilon \in (0, 0.5]\) of the estimation of \(a\). Defaults to \(0.005\).
n_shots (int, optional) – number of shots of each iteration. Defaults to \(1024\).
method (str, optional) – method used to compute the confidence intervals of each iteration, either
"chernoff"for the Chernoff-Hoeffding intervals or"beta"for the Clopper-Pearson intervals. Defaults to"chernoff".
References
1. D. Grinko, J. Gacon, C. Zoufal, and S. Woerner, Iterative quantum amplitude estimation, npj Quantum Inf. 7, 52 (2021), arXiv:1912.05559.
2. G. Brassard, P. Høyer, M. Mosca, and A. Tapp, Quantum Amplitude Amplification and Estimation, Contemporary Mathematics 305, 53 (2002).
3. C. J. Clopper and E. S. Pearson, The use of confidence or fiducial limits illustrated in the case of the binomial, Biometrika 26, 404 (1934).
4. W. Hoeffding, Probability inequalities for sums of bounded random variables, J. Am. Stat. Assoc. 58, 13 (1963).
- construct_qae_circuit(k)[source]#
Creates the circuit that applies \(\mathbf{Q}^{k}\) to \(\mathcal{A}\ket{0}\) and measures the last qubit of
circuit_a.- Parameters:
k (int) – number \(k \geq 0\) of applications of
circuit_q.- Returns:
circuit with as many qubits as
circuit_q.- Return type:
- clopper_pearson(count, n, alpha)[source]#
Calculates the Clopper-Pearson confidence interval of the probability \(a\).
- Parameters:
- Returns:
lower and upper bounds of the interval. A bound that cannot be computed, which happens if
countis \(0\) orn, is replaced by \(0\) or \(1\).- Return type:
- h_calc_CP(n_successes, n_total_shots, upper_bound_t)[source]#
Calculates half of the width of the Clopper-Pearson interval of \(K \theta_{a}\).
The Clopper-Pearson interval \([a_{\min}, a_{\max}]\) of the probability \(a\) has significance level
alpha/upper_bound_t, and the result is \(|\arccos(1 - 2 a_{\max}) - \arccos(1 - 2 a_{\min})| / 2\).
- calc_L_range_CP(n_shots, upper_bound_t)[source]#
Calculates the maximum and minimum errors of an iteration with Clopper-Pearson intervals.
They are the maximum and the minimum of
h_calc_CP()over the fractions of successes in \([0, 1]\), divided by \(2 \pi\). The maximum is the quantity \(L_{\max}\) of the no-overshooting condition.
- calc_L_range_CH(n_shots, upper_bound_t)[source]#
Calculates the maximum and minimum errors of an iteration, with Chernoff-Hoeffding.
The maximum error is
\[L_{\max} = \frac{1}{2 \pi} \arcsin\left(\left(\frac{2}{N_{shots}} \log\left(\frac{2 T}{\alpha}\right)\right)^{1 / 4}\right),\]which is used in the no-overshooting condition.
- find_next_k(uppercase_k_i, up_i, theta_l, theta_u, r=2)[source]#
Finds the next factor \(K\), which is the procedure FindNextK of [1].
It is the largest \(K = 4k + 2 \geq r K_{i}\) such that the interval \(K [\theta_{l}, \theta_{u}]\) lies completely in \([0, \pi]\) or in \([\pi, 2 \pi]\), modulo \(2 \pi\). All of the angles are divided by \(2 \pi\).
- Parameters:
uppercase_k_i (int) – current factor \(K_{i} = 4 k_{i} + 2\), where \(k_{i}\) is the power of
circuit_q.up_i (bool) – whether the current interval of \(K_{i} \theta_{a}\) lies in the upper half of the circle, \([0, \pi]\), or in the lower one.
theta_l (float) – lower limit of the current interval of \(\theta_{a}\).
theta_u (float) – upper limit of the current interval of \(\theta_{a}\).
r (int, optional) – minimum ratio \(K / K_{i}\). Defaults to \(2\).
- Returns:
next factor \(K = 4k + 2\), and whether the extrapolated interval lies in the upper half of the circle. If there is no valid \(K\), it returns
uppercase_k_iandup_i.- Return type:
- execute(backend=None)[source]#
Executes the IQAE algorithm.
- Parameters:
backend (
qibo.backends.abstract.Backend, optional) – backend used to execute the circuits. IfNone, it uses the current backend. Defaults toNone.- Returns:
result with the estimation of \(a\), its interval and the information of each iteration.
- Return type:
- class qibo.models.IterativeAmplitudeEstimationResult[source]#
Result of
qibo.models.IQAE.execute().- epsilon_estimated#
estimated accuracy, i.e. half of the width \((a_{u} - a_{l}) / 2\) of the final interval.
- Type:
- estimate_intervals#
initial bounds \(0\) and \(1\) of \(a\), followed by the interval \([a_{l}, a_{u}]\) after each iteration.
- Type:
- theta_intervals#
initial bounds \(0\) and \(1 / 4\) of \(\theta_{a} / (2 \pi)\), followed by the interval \([\theta_{l}, \theta_{u}] / (2 \pi)\) after each iteration.
- Type:
- k_list#
power of
circuit_q, starting with \(k_{0} = 0\) and followed by the one of each iteration.- Type:
Oracle algorithms#
Deutsch-Jozsa#
- qibo.models.DeutschJozsa(oracle, measure=True, **kwargs)[source]#
Creates a circuit that implements the Deutsch-Jozsa algorithm.
Given a Boolean function \(f : \{0, \, 1\}^{n} \to \{0, \, 1\}\) that is promised to be either constant or balanced, i.e. equal to \(1\) for exactly half of the inputs, the algorithm decides which one it is with a single query to the oracle \(U_{f} \ket{x} \ket{y} = \ket{x} \ket{y \oplus f(x)}\), where \(\oplus\) is the sum modulo \(2\). The circuit acts on \(n + 1\) qubits, with the first \(n\) being the input qubits and the last one the output qubit of the oracle, and it is composed of the following steps:
a
qibo.gates.Xgate on the output qubit, and a Hadamard gate on every qubit;the oracle \(U_{f}\);
a Hadamard gate on each of the input qubits;
the measurement of the input qubits, if
measure=True.
The output qubit is in the state \(\ket{-}\), so that the oracle only multiplies \(\ket{x}\) by \((-1)^{f(x)}\). Then, the probability of measuring the input qubits in the state \(\ket{0}^{\otimes \, n}\) is \(1\) if \(f\) is constant, and \(0\) if it is balanced.
Example
from qibo import Circuit, gates from qibo.models import DeutschJozsa # oracle of the balanced function that is equal to the first input bit oracle = Circuit(4) oracle.add(gates.CNOT(0, 3)) circuit = DeutschJozsa(oracle) result = circuit(nshots=10) # the outcome is not 000, so the function is balanced print(result.frequencies())
Counter({'100': 10})- Parameters:
oracle (
qibo.models.circuit.Circuit) – circuit that implements \(U_{f}\), which has \(n + 1\) qubits, with the output qubit being the last one. It cannot contain measurements.measure (bool, optional) – If
True, adds a measurement of the input qubits at the end of the circuit. Defaults toTrue.kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the Deutsch-Jozsa algorithm.
- Return type:
References
1. D. Deutsch and R. Jozsa, Rapid solution of problems by quantum computation, Proc. R. Soc. Lond. A 439, 553 (1992).
2. R. Cleve, A. Ekert, C. Macchiavello, and M. Mosca, Quantum algorithms revisited, Proc. R. Soc. Lond. A 454, 339 (1998).
Bernstein-Vazirani#
- qibo.models.BernsteinVazirani(oracle, measure=True, **kwargs)[source]#
Creates a circuit that implements the Bernstein-Vazirani algorithm.
Given a Boolean function \(f(x) = s \cdot x \oplus c\), where \(s \in \{0, \, 1\}^{n}\) is a hidden string, \(\cdot\) is the bitwise inner product modulo \(2\), and \(c \in \{0, \, 1\}\), the algorithm finds \(s\) with a single query to the oracle \(U_{f} \ket{x} \ket{y} = \ket{x} \ket{y \oplus f(x)}\), where \(\oplus\) is the sum modulo \(2\). The circuit acts on \(n + 1\) qubits, with the first \(n\) being the input qubits and the last one the output qubit of the oracle. It is the same circuit as the one of
qibo.models.DeutschJozsa(): the output qubit is in the state \(\ket{-}\), so that the oracle multiplies \(\ket{x}\) by \((-1)^{s \cdot x}\), which is undone by the Hadamard gates on the input qubits, that are left in the state \(\ket{s}\). Ifmeasure=True, the qubit \(k\) of the measured bitstring is the bit \(s_{k}\).Example
from qibo import Circuit, gates from qibo.models import BernsteinVazirani # oracle of the function with the hidden string s = 101 oracle = Circuit(4) oracle.add(gates.CNOT(0, 3)) oracle.add(gates.CNOT(2, 3)) circuit = BernsteinVazirani(oracle) result = circuit(nshots=10) print(result.frequencies())
Counter({'101': 10})- Parameters:
oracle (
qibo.models.circuit.Circuit) – circuit that implements \(U_{f}\), which has \(n + 1\) qubits, with the output qubit being the last one. It cannot contain measurements.measure (bool, optional) – If
True, adds a measurement of the input qubits at the end of the circuit. Defaults toTrue.kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the Bernstein-Vazirani algorithm.
- Return type:
References
1. E. Bernstein and U. Vazirani, Quantum complexity theory, SIAM J. Comput. 26, 1411 (1997).
2. R. Cleve, A. Ekert, C. Macchiavello, and M. Mosca, Quantum algorithms revisited, Proc. R. Soc. Lond. A 454, 339 (1998).
Quantum arithmetic#
Quantum Adders#
- qibo.models.Adder(nbits, method='Cuccaro', **kwargs)[source]#
Creates a circuit that adds two integers with the adder chosen by
method.Given two \(n\)-bit non-negative integers \(a\) and \(b\), where \(n\) is
nbits, all of the adders perform\[\ket{a} \, \ket{b} \, \ket{0} \mapsto \ket{a} \, \ket{a + b} \, \ket{0} \, ,\]where the last register holds the auxiliary qubits, if the adder has any. The first \(n\) qubits hold \(a\), and the next \(n + 1\) qubits hold \(b\), with the overflow bit being the last one, which must be in the state \(\ket{0}\) at the beginning. In every register, the least significant bit comes first. The adders differ in the number of auxiliary qubits and of gates:
"Cuccaro":qibo.models.Cuccaro(), ripple-carry adder with one auxiliary qubit;"DKRS":qibo.models.DKRS(), carry-lookahead adder with logarithmic depth and a linear number of auxiliary qubits;"Draper":qibo.models.Draper(), adder in the Fourier basis, without auxiliary qubits;"Gidney":qibo.models.Gidney(), ripple-carry adder that uses measurements to uncompute the carries, with \(n - 1\) auxiliary qubits;"TTK":qibo.models.TTK(), ripple-carry adder ofqibo.gates.TOFFOLIandqibo.gates.CNOTgates, without auxiliary qubits;"VBE":qibo.models.VBE(), ripple-carry adder with \(n\) auxiliary qubits.
Example
from qibo import Circuit, gates from qibo.models import Adder nbits = 2 adder = Adder(nbits, method="TTK") # a = 3 in the qubits 0 and 1, and b = 1 in the qubits 2, 3 and 4 circuit = Circuit(adder.nqubits) circuit.add(gates.X(0)) circuit.add(gates.X(1)) circuit.add(gates.X(2)) circuit.add(adder) circuit.add(gates.M(*range(nbits, 2 * nbits + 1))) result = circuit(nshots=1) bitstring = next(iter(result.frequencies(binary=True))) # the least significant bit comes first print(int(bitstring[::-1], 2))
4
- Parameters:
nbits (int) – number of bits \(n\) of each of the integers to be added.
method (str, optional) – adder to be used. Either
"Cuccaro","DKRS","Draper","Gidney","TTK"or"VBE". Defaults to"Cuccaro".kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the chosen adder.
- Return type:
References
1. S. A. Cuccaro, T. G. Draper, S. A. Kutin, and D. P. Moulton, A new quantum ripple-carry addition circuit, arXiv:quant-ph/0410184.
2. T. G. Draper, S. A. Kutin, E. M. Rains, and K. M. Svore, A logarithmic-depth quantum carry-lookahead adder, Quantum Inf. Comput. 6, 351 (2006).
3. T. G. Draper, Addition on a Quantum Computer, arXiv:quant-ph/0008033.
4. C. Gidney, Halving the cost of quantum addition, Quantum 2, 74 (2018).
5. Y. Takahashi, S. Tani, and N. Kunihiro, Quantum addition circuits and unbounded fan-out, Quantum Inf. Comput. 10, 872 (2010).
6. V. Vedral, A. Barenco, and A. Ekert, Quantum networks for elementary arithmetic operations, Phys. Rev. A 54, 147 (1996).
- qibo.models.Cuccaro(nbits, **kwargs)[source]#
Creates a circuit that implements the Cuccaro-Draper-Kutin-Moulton ripple-carry adder.
Given two \(n\)-bit non-negative integers \(a\) and \(b\), where \(n\) is
nbits, the circuit performs\[\ket{a} \, \ket{b} \, \ket{0} \mapsto \ket{a} \, \ket{a + b \bmod 2^{n + 1}} \, \ket{0} \, .\]It acts on \(2n + 2\) qubits, which are split into three registers:
the first \(n\) qubits hold \(a\), which is left unchanged;
the next \(n + 1\) qubits hold \(b\), which is replaced by \(a + b\). The last qubit of this register is the overflow bit, which is in the state \(\ket{0}\) at the beginning if \(b < 2^{n}\), so that the sum is exact;
the last qubit is a single auxiliary qubit, the carry-in. It must be in the state \(\ket{0}\) at the beginning, and it is returned to it at the end.
In every register, the \(k\)-th qubit holds the bit \(2^{k}\), i.e. the least significant bit comes first. The circuit is composed of \(n\) majority (MAJ) blocks, which compute the carries in place, a
qibo.gates.CNOTthat copies the last carry into the overflow qubit, and \(n\) unmajority-and-add (UMA) blocks, which uncompute the carries and write the sum bits, in reverse order. The MAJ and UMA blocks are the ones of Ref. [1], with the version of UMA that has twoqibo.gates.CNOTgates, and the circuit is the simple adder of that reference, in which the blocks are applied in sequence. It has \(2n\)qibo.gates.TOFFOLIgates and \(4n + 1\)qibo.gates.CNOTgates. The depth-optimized circuit of Ref. [1], with \(2n - 1\)qibo.gates.TOFFOLIgates, \(5n - 3\)qibo.gates.CNOTgates, and depth \(2n + 4\), is not implemented.Example
from qibo import Circuit, gates from qibo.models import Cuccaro nbits = 2 adder = Cuccaro(nbits) # a = 3 in the qubits 0 and 1, and b = 1 in the qubits 2, 3 and 4 circuit = Circuit(adder.nqubits) circuit.add(gates.X(0)) circuit.add(gates.X(1)) circuit.add(gates.X(2)) circuit.add(adder) circuit.add(gates.M(*range(nbits, 2 * nbits + 1))) result = circuit(nshots=1) bitstring = next(iter(result.frequencies(binary=True))) # the least significant bit comes first print(int(bitstring[::-1], 2))
4
- Parameters:
nbits (int) – number of bits \(n\) of each of the integers to be added.
kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the Cuccaro ripple-carry adder.
- Return type:
References
1. S. A. Cuccaro, T. G. Draper, S. A. Kutin, and D. P. Moulton, A new quantum ripple-carry addition circuit, arXiv:quant-ph/0410184.
- qibo.models.DKRS(nbits, **kwargs)[source]#
Creates a circuit that implements the Draper-Kutin-Rains-Svore (DKRS) carry-lookahead adder.
Given two \(n\)-bit non-negative integers \(a\) and \(b\), where \(n\) is
nbits, the circuit performs\[\ket{a} \, \ket{b} \, \ket{0} \mapsto \ket{a} \, \ket{a + b} \, \ket{0} \, .\]It acts on \(3n + 2 + m\) qubits, with \(m = n - w(n) - \lfloor \log_{2} n \rfloor\) and \(w(n)\) being the number of ones in the binary representation of \(n\). The qubits are split into three registers:
the first \(n\) qubits hold \(a\), which is left unchanged;
the next \(n + 1\) qubits hold \(b\), which is replaced by \(a + b\). The last qubit of this register is the overflow bit, which must be in the state \(\ket{0}\) at the beginning;
the last \(n + 1 + m\) qubits are auxiliary qubits. They must be in the state \(\ket{0}\) at the beginning, and they are returned to it at the end.
In every register, the \(k\)-th qubit holds the bit \(2^{k}\), i.e. the least significant bit comes first. The carries are computed with a tree of
qibo.gates.TOFFOLIgates, so that the depth of the circuit is logarithmic in \(n\). The sum is first written in the auxiliary qubits by an out-of-place adder. Then, the inverse of the same adder, applied to the complement of the sum, erases \(b\), and the registers are swapped. Each out-of-place adder is the one of Section 4.1 of Ref. [1], with \(5n - 3 w(n) - 3 \lfloor \log_{2} n \rfloor - 1\)qibo.gates.TOFFOLIgates, so the circuit has twice as many, and, for \(n \geq 4\), its Toffoli depth is \(2 (\lfloor \log_{2} n \rfloor + \lfloor \log_{2} (n / 3) \rfloor + 4)\). This is not the in-place construction of Section 4.2 of Ref. [1], which erases only the carries: it needs two more auxiliary qubits than the \(2n - w(n) - \lfloor \log_{2} n \rfloor - 1\) of that construction.Example
from qibo import Circuit, gates from qibo.models import DKRS nbits = 2 adder = DKRS(nbits) # a = 3 in the qubits 0 and 1, and b = 1 in the qubits 2, 3 and 4 circuit = Circuit(adder.nqubits) circuit.add(gates.X(0)) circuit.add(gates.X(1)) circuit.add(gates.X(2)) circuit.add(adder) circuit.add(gates.M(*range(nbits, 2 * nbits + 1))) result = circuit(nshots=1) bitstring = next(iter(result.frequencies(binary=True))) # the least significant bit comes first print(int(bitstring[::-1], 2))
4
- Parameters:
nbits (int) – number of bits \(n\) of each of the integers to be added.
kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the DKRS adder.
- Return type:
References
1. T. G. Draper, S. A. Kutin, E. M. Rains, and K. M. Svore, A logarithmic-depth quantum carry-lookahead adder, Quantum Inf. Comput. 6, 351 (2006).
- qibo.models.Draper(nbits, **kwargs)[source]#
Creates a circuit that implements the Draper quantum Fourier transform (QFT) adder.
Given two \(n\)-bit non-negative integers \(a\) and \(b\), where \(n\) is
nbits, the circuit performs\[\ket{a} \, \ket{b} \mapsto \ket{a} \, \ket{a + b \bmod 2^{n + 1}} \, .\]It acts on \(2n + 1\) qubits, and it does not need auxiliary qubits. The first \(n\) qubits hold \(a\), which is left unchanged, and the last \(n + 1\) qubits hold \(b\), which is replaced by \(a + b\). The last qubit of the second register is the overflow bit, which is in the state \(\ket{0}\) at the beginning if \(b < 2^{n}\), so that the sum is exact.
In every register, the \(k\)-th qubit holds the bit \(2^{k}\), i.e. the least significant bit comes first. The circuit is composed of the following steps:
the
qibo.models.QFTon the register of \(b\), without swaps;qibo.gates.CU1gates, controlled by the qubits of \(a\), which add \(a\) to \(b\) in the Fourier basis, by rotating the phases of the qubits of \(b\);the inverse
qibo.models.QFTon the register of \(b\), without swaps.
The smallest rotation angle is \(2 \pi / 2^{n + 1}\).
Example
from qibo import Circuit, gates from qibo.models import Draper nbits = 2 adder = Draper(nbits) # a = 3 in the qubits 0 and 1, and b = 1 in the qubits 2, 3 and 4 circuit = Circuit(adder.nqubits) circuit.add(gates.X(0)) circuit.add(gates.X(1)) circuit.add(gates.X(2)) circuit.add(adder) circuit.add(gates.M(*range(nbits, 2 * nbits + 1))) result = circuit(nshots=1) bitstring = next(iter(result.frequencies(binary=True))) # the least significant bit comes first print(int(bitstring[::-1], 2))
4
- Parameters:
nbits (int) – number of bits \(n\) of each of the integers to be added.
kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the Draper adder.
- Return type:
References
1. T. G. Draper, Addition on a Quantum Computer, arXiv:quant-ph/0008033.
- qibo.models.Gidney(nbits, **kwargs)[source]#
Creates a circuit that implements the Gidney adder.
Given two \(n\)-bit non-negative integers \(a\) and \(b\), where \(n\) is
nbits, the circuit performs\[\ket{a} \, \ket{b} \, \ket{0} \mapsto \ket{a} \, \ket{a + b} \, \ket{0} \, .\]It acts on \(3n\) qubits, which are split into three registers:
the first \(n\) qubits hold \(a\), which is left unchanged;
the next \(n + 1\) qubits hold \(b\), which is replaced by \(a + b\). The last qubit of this register is the overflow bit, which must be in the state \(\ket{0}\) at the beginning;
the last \(n - 1\) qubits are auxiliary qubits that store the carries. They must be in the state \(\ket{0}\) at the beginning, and they are returned to it at the end.
In every register, the \(k\)-th qubit holds the bit \(2^{k}\), i.e. the least significant bit comes first. Each carry is computed in a qubit in the state \(\ket{0}\) with a temporary logical-AND, which has four
qibo.gates.Torqibo.gates.TDGgates. Then, it is erased without them: the qubit is measured in the \(X\) basis, and, if the outcome is \(1\), aqibo.gates.CZgate, which is applied with aqibo.gates.CU1conditioned on the outcome, fixes the phase. Hence, the circuit has \(4n\) of these gates, and \(n - 1\) measurements withcollapseset toTrue. Because of them, each shot has to be simulated separately, and the circuit cannot be inverted. Ref. [1] presents the adder without the carry out, which has \(n - 1\) temporary logical-ANDs and \(4n - 4\) of these gates. Here, the carry out takes one more temporary logical-AND.Example
from qibo import Circuit, gates from qibo.models import Gidney nbits = 2 adder = Gidney(nbits) # a = 3 in the qubits 0 and 1, and b = 1 in the qubits 2, 3 and 4 circuit = Circuit(adder.nqubits) circuit.add(gates.X(0)) circuit.add(gates.X(1)) circuit.add(gates.X(2)) circuit.add(adder) circuit.add(gates.M(*range(nbits, 2 * nbits + 1))) result = circuit(nshots=1) bitstring = next(iter(result.frequencies(binary=True))) # the least significant bit comes first print(int(bitstring[::-1], 2))
4
- Parameters:
nbits (int) – number of bits \(n\) of each of the integers to be added.
kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the Gidney adder.
- Return type:
References
1. C. Gidney, Halving the cost of quantum addition, Quantum 2, 74 (2018).
- qibo.models.TTK(nbits, **kwargs)[source]#
Creates a circuit that implements the Takahashi-Tani-Kunihiro (TTK) adder.
Given two \(n\)-bit non-negative integers \(a\) and \(b\), where \(n\) is
nbits, the circuit performs\[\ket{a} \, \ket{b} \mapsto \ket{a} \, \ket{a + b \bmod 2^{n + 1}} \, .\]It acts on \(2n + 1\) qubits, and it does not need auxiliary qubits. The first \(n\) qubits hold \(a\), which is left unchanged, and the last \(n + 1\) qubits hold \(b\), which is replaced by \(a + b\). The last qubit of the second register is the overflow bit, which is in the state \(\ket{0}\) at the beginning if \(b < 2^{n}\), so that the sum is exact.
In every register, the \(k\)-th qubit holds the bit \(2^{k}\), i.e. the least significant bit comes first. The circuit only has
qibo.gates.TOFFOLIandqibo.gates.CNOTgates. The carries are computed in place, using the qubits of \(a\), and then uncomputed while the sum bits are written in \(b\). For \(n \geq 2\), it has \(2n - 1\)qibo.gates.TOFFOLIgates and \(5n - 5\)qibo.gates.CNOTgates, which is the size \(7n - 6\) of Ref. [1]. For \(n = 1\), it is a half adder, with one of each.Example
from qibo import Circuit, gates from qibo.models import TTK nbits = 2 adder = TTK(nbits) # a = 3 in the qubits 0 and 1, and b = 1 in the qubits 2, 3 and 4 circuit = Circuit(adder.nqubits) circuit.add(gates.X(0)) circuit.add(gates.X(1)) circuit.add(gates.X(2)) circuit.add(adder) circuit.add(gates.M(*range(nbits, 2 * nbits + 1))) result = circuit(nshots=1) bitstring = next(iter(result.frequencies(binary=True))) # the least significant bit comes first print(int(bitstring[::-1], 2))
4
- Parameters:
nbits (int) – number of bits \(n\) of each of the integers to be added.
kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the TTK adder.
- Return type:
References
1. Y. Takahashi, S. Tani, and N. Kunihiro, Quantum addition circuits and unbounded fan-out, Quantum Inf. Comput. 10, 872 (2010).
- qibo.models.VBE(nbits, **kwargs)[source]#
Creates a circuit that implements the Vedral-Barenco-Ekert (VBE) plain adder.
Given two \(n\)-bit non-negative integers \(a\) and \(b\), where \(n\) is
nbits, the circuit performs\[\ket{a} \, \ket{b} \, \ket{0} \mapsto \ket{a} \, \ket{a + b} \, \ket{0} \, .\]It acts on \(3n + 1\) qubits, which are split into three registers:
the first \(n\) qubits hold \(a\), which is left unchanged;
the next \(n + 1\) qubits hold \(b\), which is replaced by \(a + b\). The last qubit of this register is the overflow bit, and it must be in the state \(\ket{0}\) at the beginning;
the last \(n\) qubits are auxiliary qubits that store the carries. They must be in the state \(\ket{0}\) at the beginning, and they are returned to it at the end.
In every register, the \(k\)-th qubit holds the bit \(2^{k}\), i.e. the least significant bit comes first. The circuit is composed of \(n\) blocks that compute the carries, followed by blocks that compute the sum bits while the carries are uncomputed, in reverse order. It has \(4n - 2\)
qibo.gates.TOFFOLIgates and \(4n\)qibo.gates.CNOTgates. The carries \(c_{0}, \dots, c_{n - 1}\) are stored in the auxiliary qubits, with \(c_{0} = 0\), and the carry \(c_{n}\) in the overflow qubit.Example
from qibo import Circuit, gates from qibo.models import VBE nbits = 2 adder = VBE(nbits) # a = 3 in the qubits 0 and 1, and b = 1 in the qubits 2, 3 and 4 circuit = Circuit(adder.nqubits) circuit.add(gates.X(0)) circuit.add(gates.X(1)) circuit.add(gates.X(2)) circuit.add(adder) circuit.add(gates.M(*range(nbits, 2 * nbits + 1))) result = circuit(nshots=1) bitstring = next(iter(result.frequencies(binary=True))) # the least significant bit comes first print(int(bitstring[::-1], 2))
4
- Parameters:
nbits (int) – number of bits \(n\) of each of the integers to be added.
kwargs (dict, optional) – Additional arguments used to initialize
qibo.models.circuit.Circuit, e.g.density_matrix=True. For details, see the documentation ofqibo.models.circuit.Circuit.
- Returns:
implementation of the VBE plain adder.
- Return type:
References
1. V. Vedral, A. Barenco, and A. Ekert, Quantum networks for elementary arithmetic operations, Phys. Rev. A 54, 147 (1996).
Block encodings and signal processing#
Block Encoding#
- qibo.models.block_encoding_circuit(matrix, method='dense', alpha=None, backend=None)[source]#
Creates a block encoding of a matrix.
Let \(A\) be
matrix, a square matrix that is padded with zeros up to dimension \(2^{n}\) if needed, with \(n \ge 1\). The circuit acts on \(a\) auxiliary qubits, followed by \(n\) system qubits, and the block of its unitary in which the auxiliary qubits start and end in \(\ket{0}\) is \(A / \alpha\), where \(\alpha\) is a normalization. This is a block encoding as in Definition 24 of Ref. [1], with zero error, and the zero padding is the embedding of that reference. Both methods have a cost that grows exponentially with \(n\).method="dense": \(a = 1\). With \(B = A / \alpha\), whose singular values are at most one, the circuit is a single gate for the unitary dilation\[\begin{split}U = \begin{pmatrix} B & \sqrt{\mathbb{1} - B B^{\dagger}} \\ \sqrt{\mathbb{1} - B^{\dagger} B} & -B^{\dagger} \end{pmatrix},\end{split}\]where \(\mathbb{1}\) is the identity and \(B^{\dagger}\) the conjugate transpose of \(B\). The matrix square roots are computed from the singular value decomposition of \(B\). In the basis of the singular vectors, \(U\) is made of the \(2 \times 2\) blocks of Eq. (4) of Ref. [1].
method="lcu": the matrix is decomposed as a sum of Pauli strings, \(A = \sum_{i} c_{i} P_{i}\), with complex coefficients \(c_{i}\), and the circuit is the one ofqibo.models.lcu_circuit(). The normalization is \(\alpha = \sum_{i} |c_{i}|\), and \(a = \max(1, \lceil \log_{2} L \rceil)\), with \(L\) the number of non-zero coefficients. Pauli strings are trivial block encodings of themselves, so this is Lemma 29 of Ref. [1].
Example
Encode \(X / 2\), with \(X\) the Pauli-\(X\) matrix. The amplitudes with the auxiliary qubits in \(\ket{0}\) are those of \(\ket{1} / 2\) for the dense method, and those of \(\ket{1}\) for the LCU method, whose normalization is \(1 / 2\).
from qibo import get_backend from qibo.models import block_encoding_circuit backend = get_backend() matrix = 0.5 * backend.matrices.X circuit, alpha, nauxiliary = block_encoding_circuit(matrix) state = circuit().state()[:2] print(backend.allclose(state, backend.cast([0.0, 0.5])), alpha, nauxiliary) circuit, alpha, nauxiliary = block_encoding_circuit(matrix, method="lcu") state = circuit().state()[:2] print(backend.allclose(state, backend.cast([0.0, 1.0])), alpha, nauxiliary)
True 1.0 1 True 0.5 1
- Parameters:
matrix (ArrayLike) – square matrix \(A\).
method (str, optional) –
"dense"for the unitary dilation or"lcu"for the linear combination of unitaries of the Pauli strings. Defaults to"dense".alpha (float, optional) – normalization \(\alpha\) if
method="dense", which has to be at least the spectral norm ofmatrix. IfNone, it is the spectral norm if this is larger than one, and one otherwise. It is not used bymethod="lcu", for which it has to beNone. Defaults toNone.backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the calculation. IfNone, it uses the current backend. Defaults toNone.
- Returns:
circuit on \(a + n\) qubits, with the auxiliary qubits at the first qubits, the normalization \(\alpha\), and the number \(a\) of auxiliary qubits. The circuit and the number of auxiliary qubits are the arguments of
qibo.models.qsvt_circuit().- Return type:
References
1. A. Gilyén, Y. Su, G. H. Low, and N. Wiebe, Quantum singular value transformation and beyond: exponential improvements for quantum matrix arithmetics, Proceedings of the 51st Annual ACM SIGACT Symposium on Theory of Computing (STOC ‘19), 193-204 (2019).
Linear Combination of Unitaries#
- qibo.models.lcu_circuit(unitaries, coefficients, backend=None)[source]#
Creates a linear combination of unitaries (LCU) circuit.
Let \(U_{i}\) be the unitary of the \(i\)-th circuit of
unitaries, with \(i = 0, \dots, L - 1\), let \(c_{i}\) be its complex coefficient, and let \(\alpha = \sum_{i} |c_{i}|\). The circuit acts on \(a = \max(1, \lceil \log_{2} L \rceil)\) auxiliary qubits, followed by the qubits of the unitaries, and block-encodes the operator \(A / \alpha\), with \(A = \sum_{i} c_{i} U_{i}\). That is, the block of the circuit unitary in which the auxiliary qubits start and end in \(\ket{0}\) is \(A / \alpha\). The circuit is \(\mathrm{PREPARE}_{L}^{\dagger} \, \mathrm{SELECT} \, \mathrm{PREPARE}_{R}\), where \(\mathrm{PREPARE}_{L}\) and \(\mathrm{PREPARE}_{R}\) are created withqibo.models.lcu_prepare(), and \(\mathrm{SELECT}\) is created withqibo.models.lcu_select().This is the block encoding of Lemma 29 of Ref. [2], with \((\mathrm{PREPARE}_{L}, \mathrm{PREPARE}_{R})\) a state-preparation pair (Definition 28 of Ref. [2]). For \(L = 2\) and the coefficients \((\kappa, 1)\), it is the circuit of Lemma 2 of Ref. [1], which fails, i.e. the auxiliary qubit is not in \(\ket{0}\), with probability \(\kappa \| (U_{1} - U_{0}) \ket{\psi} \|^{2} / (\kappa + 1)^{2}\) for the input state \(\ket{\psi}\).
Example
Apply \((X + Z) / 2\) to \(\ket{0}\), where \(X\) and \(Z\) are Pauli matrices. The amplitudes with the auxiliary qubit in \(\ket{0}\) are those of \(\ket{+} / \sqrt{2}\).
from qibo import Circuit, gates, get_backend from qibo.models import lcu_circuit backend = get_backend() pauli_x = Circuit(1) pauli_x.add(gates.X(0)) pauli_z = Circuit(1) pauli_z.add(gates.Z(0)) circuit, alpha, nauxiliary = lcu_circuit([pauli_x, pauli_z], [0.5, 0.5]) target = Circuit(1) target.add(gates.H(0)) state = circuit().state()[:2] print( backend.allclose(state, target().state() / backend.sqrt(2)), alpha, nauxiliary, )
True 1.0 1
- Parameters:
unitaries (list[
qibo.models.circuit.Circuit]) – circuits on the same number of qubits that implement \(U_{i}\). They have to be exact, including their global phase, since they are applied under control.coefficients (ArrayLike) – one-dimensional array of the (complex) coefficients \(c_{i}\), with at least one non-zero element.
backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the calculation. IfNone, it uses the current backend. Defaults toNone.
- Returns:
the circuit, with the auxiliary qubits at the first qubits, the normalization \(\alpha\), and the number \(a\) of auxiliary qubits. The last two are the arguments of
qibo.models.qsvt_circuit()for which the circuit is a block encoding of \(A / \alpha\).- Return type:
References
1. A. M. Childs and N. Wiebe, Hamiltonian simulation using linear combinations of unitary operations, Quantum Information & Computation 12, 901-924 (2012). 2. A. Gilyén, Y. Su, G. H. Low, and N. Wiebe, Quantum singular value transformation and beyond: exponential improvements for quantum matrix arithmetics, Proceedings of the 51st Annual ACM SIGACT Symposium on Theory of Computing (STOC ‘19), 193-204 (2019).
- qibo.models.lcu_prepare(coefficients, backend=None)[source]#
Creates the \(\mathrm{PREPARE}\) oracles of a linear combination of unitaries (LCU).
Let \(c_{i}\) be the \(i\)-th (complex) element of
coefficients, with \(i = 0, \dots, L - 1\), and let \(\alpha = \sum_{i} |c_{i}|\). The oracles act on \(a = \max(1, \lceil \log_{2} L \rceil)\) auxiliary qubits. When applied to \(\ket{0}\), \(\mathrm{PREPARE}_{L}\) and \(\mathrm{PREPARE}_{R}\) load the amplitudes\[\mathrm{PREPARE}_{L} \ket{0} = \sum_{i} \sqrt{\frac{|c_{i}|}{\alpha}} \, \ket{i} \quad \text{and} \quad \mathrm{PREPARE}_{R} \ket{0} = \sum_{i} \sqrt{\frac{|c_{i}|}{\alpha}} \, \frac{c_{i}}{|c_{i}|} \, \ket{i} \, ,\]respectively, with the amplitudes of \(\ket{i}\), for \(i \geq L\), set to zero. For real and non-negative coefficients, they are the same oracle. The oracles are built with
qibo.models.encodings.binary_encoder(), with the Mottonen parametrization, since the resulting circuits have an exact inverse. Together withqibo.models.lcu_select(), they build the block encoding \(\mathrm{PREPARE}_{L}^{\dagger} \, \mathrm{SELECT} \, \mathrm{PREPARE}_{R}\) of \(\sum_{i} c_{i} U_{i} / \alpha\). They are a state-preparation pair in the sense of Definition 28 of Ref. [2].Example
Prepare the amplitudes of the coefficients \(3\) and \(-1\), for which \(\alpha = 4\).
from qibo import get_backend from qibo.models import lcu_prepare backend = get_backend() prepare_left, prepare_right, alpha, nauxiliary = lcu_prepare([3.0, -1.0]) left = backend.cast([0.75**0.5, 0.25**0.5], dtype="complex128") right = backend.cast([0.75**0.5, -(0.25**0.5)], dtype="complex128") print( backend.allclose(prepare_left().state(), left), backend.allclose(prepare_right().state(), right), alpha, nauxiliary, )
True True 4.0 1
- Parameters:
coefficients (ArrayLike) – one-dimensional array of the (complex) coefficients \(c_{i}\), with at least one non-zero element.
backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the calculation. IfNone, it uses the current backend. Defaults toNone.
- Returns:
the circuits of \(\mathrm{PREPARE}_{L}\) and \(\mathrm{PREPARE}_{R}\), both on \(a\) qubits, the normalization \(\alpha\), and the number \(a\) of auxiliary qubits.
- Return type:
References
1. A. M. Childs and N. Wiebe, Hamiltonian simulation using linear combinations of unitary operations, Quantum Information & Computation 12, 901-924 (2012). 2. A. Gilyén, Y. Su, G. H. Low, and N. Wiebe, Quantum singular value transformation and beyond: exponential improvements for quantum matrix arithmetics, Proceedings of the 51st Annual ACM SIGACT Symposium on Theory of Computing (STOC ‘19), 193-204 (2019).
- qibo.models.lcu_select(unitaries, nauxiliary=None, backend=None)[source]#
Creates the \(\mathrm{SELECT}\) oracle of a linear combination of unitaries (LCU).
Let \(U_{i}\) be the unitary of the \(i\)-th circuit of
unitaries, with \(i = 0, \dots, L - 1\). The oracle acts on \(a\) auxiliary qubits, followed by the qubits of the unitaries, and applies \(U_{i}\) to the latter when the auxiliary qubits are in the computational basis state \(\ket{i}\),\[\mathrm{SELECT} = \sum_{i = 0}^{L - 1} \ket{i}\!\bra{i} \otimes U_{i} + \sum_{i = L}^{2^{a} - 1} \ket{i}\!\bra{i} \otimes I \, .\]Each unitary is applied under the control of all the auxiliary qubits, with \(X\) gates that map \(\ket{i}\) into \(\ket{1 \dots 1}\). Gates that are already controlled are supported. A unitary with no gates is the identity, and is skipped. It is the unitary \(W\) of Lemma 29 of Ref. [2].
Example
Apply the Pauli matrices \(X\) and \(Z\) to \(\ket{0}\), selected by an auxiliary qubit in \(\ket{+}\). The state is \((\ket{0}\ket{1} + \ket{1}\ket{0}) / \sqrt{2}\).
from qibo import Circuit, gates, get_backend from qibo.models import lcu_select backend = get_backend() pauli_x = Circuit(1) pauli_x.add(gates.X(0)) pauli_z = Circuit(1) pauli_z.add(gates.Z(0)) select = lcu_select([pauli_x, pauli_z]) circuit = Circuit(2) circuit.add(gates.H(0)) circuit.add(select.on_qubits(0, 1)) target = backend.cast([0.0, 1.0, 1.0, 0.0], dtype="complex128") print(backend.allclose(circuit().state(), target / backend.sqrt(2)))
True
- Parameters:
unitaries (list[
qibo.models.circuit.Circuit]) – circuits on the same number of qubits that implement \(U_{i}\). They have to be exact, including their global phase, since they are applied under control.nauxiliary (int, optional) – number \(a\) of auxiliary qubits. It has to satisfy \(2^{a} \geq L\). If
None, it is \(\max(1, \lceil \log_{2} L \rceil)\). Defaults toNone.backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the calculation. IfNone, it uses the current backend. Defaults toNone.
- Returns:
circuit of \(\mathrm{SELECT}\), with the auxiliary qubits at the first qubits.
- Return type:
References
1. A. M. Childs and N. Wiebe, Hamiltonian simulation using linear combinations of unitary operations, Quantum Information & Computation 12, 901-924 (2012). 2. A. Gilyén, Y. Su, G. H. Low, and N. Wiebe, Quantum singular value transformation and beyond: exponential improvements for quantum matrix arithmetics, Proceedings of the 51st Annual ACM SIGACT Symposium on Theory of Computing (STOC ‘19), 193-204 (2019).
Quantum Signal Processing#
- qibo.models.qsp_phases(coefficients, sine_coefficients=None, method=None, extraction='phase_sums', backend=None)[source]#
Computes the phases of a quantum signal processing (QSP) circuit.
method=None: QSP of Ref. [1]. Given the series \(A(\theta) = \sum_{k = 0}^{N/2} a_{k} \cos(k \theta)\) and \(C(\theta) = \sum_{k = 1}^{N/2} c_{k} \sin(k \theta)\), with even \(N\), it finds \(N\) phases such that \(\langle + | V(\theta) | + \rangle = A(\theta) + i C(\theta)\). Here, \(V(\theta)\) is the product of the \(N\) single-qubit rotations of angle \(\theta\) about the axes of the \(xy\)-plane given by the phases, and \(|+\rangle\) is the \(+1\) eigenstate of the Pauli- \(X\) operator. Since \(V(0)\) is the identity, the target has to satisfy \(A(0) = 1\). It is rescaled if \(|A + i C| > 1\).method="fourier": Fourier-based QSP of Ref. [2]. Given the coefficients \(c_{m}\) of the Fourier series \(\tilde{g}(x) = \sum_{m = -q/2}^{q/2} c_{m} \, e^{i m x}\), with even \(q\), it finds the angles of \(q + 1\) single-qubit gates whose product has \(\tilde{g}(x)\) as its upper-left matrix element. The series is divided by \(\max_{x} |\tilde{g}(x)|\) if it is larger than one.
The complementary series is computed numerically, which requires the target to be strictly bounded by one. Hence, the accuracy of the circuit is limited. If
method="fourier", the block of the circuit matches \(\tilde{g}\) to machine precision if \(\max_{x} |\tilde{g}(x)| \leq 0.99\), but the error can reach \(10^{-2}\) for values closer to one, which includes the series that are divided by \(\max_{x} |\tilde{g}(x)|\). Ifmethod=None, the error can be of the order of \(10^{-6}\), because \(|A + i C| = 1\) at \(\theta = 0\).- Parameters:
coefficients (ArrayLike) – if
method=None, cosine coefficients \((a_{0}, \dots, a_{N/2})\). Ifmethod="fourier", Fourier coefficients \((c_{-q/2}, \dots, c_{q/2})\).sine_coefficients (ArrayLike, optional) – sine coefficients \((c_{1}, \dots, c_{N/2})\). Required if
method=Noneand not used otherwise. Defaults toNone.method (str, optional) –
Nonefor QSP of Ref. [1] or"fourier"for Fourier-based QSP of Ref. [2]. Defaults toNone.extraction (str, optional) – how the phases are extracted if
method=None."phase_sums"follows Ref. [3] and loses precision for \(N \gtrsim 30\), while"layer_stripping"removes one rotation at a time and stays accurate for larger \(N\). Defaults to"phase_sums".backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the calculation. IfNone, it uses the current backend. Defaults toNone.
- Returns:
phases for
qibo.models.qsp_circuit()with the samemethod. An array of length \(N\) ifmethod=None, or an array of shape \((q + 1, 4)\) ifmethod="fourier".- Return type:
ArrayLike
References
1. G. H. Low and I. L. Chuang, Optimal Hamiltonian Simulation by Quantum Signal Processing, Phys. Rev. Lett. 118, 010501 (2017). 2. T. L. Silva, L. Borges, and L. Aolita, Fourier-based quantum signal processing, arXiv:2206.02826. 3. G. H. Low, T. J. Yoder, and I. L. Chuang, The methodology of resonant equiangular composite quantum gates, Phys. Rev. X 6, 041067 (2016).
- qibo.models.qsp_circuit(oracle, phases, method=None)[source]#
Creates a quantum signal processing (QSP) circuit.
The circuit interleaves queries to
oraclewith rotations of an auxiliary qubit, placed at qubit \(0\). The remaining qubits are those oforacle.method=None: QSP of Ref. [1].oracleimplements a unitary \(W\), which is queried \(N\) times. The block of the circuit unitary with the auxiliary qubit starting and ending in \(|0\rangle\) applies \(A(\theta) + i C(\theta)\) to each eigenphase \(\theta\) of \(W\), where \(A\) and \(C\) are the trigonometric polynomials encoded inphases.method="fourier": Fourier-based QSP of Ref. [2].oracleimplements \(e^{-i t H}\) for a Hermitian operator \(H\) and a time \(t\), and is queried \(q\) times under control. The same block of the circuit unitary is \(\tilde{g}(H t)\), where \(\tilde{g}\) is the Fourier series encoded inphases.
Example
Apply \((1 + W) / 2\), with \(W = X\), to \(|0\rangle\). The amplitudes with the auxiliary qubit in \(|0\rangle\) are those of \(|+\rangle / \sqrt{2}\).
from qibo import Circuit, gates, get_backend from qibo.models import qsp_circuit, qsp_phases backend = get_backend() walk = Circuit(1) walk.add(gates.X(0)) # cosine and sine series of (1 + exp(i theta)) / 2 phases = qsp_phases([0.5, 0.5], [0.5]) circuit = qsp_circuit(walk, phases) target = Circuit(1) target.add(gates.H(0)) state = circuit().state()[:2] print(backend.allclose(state, target().state() / backend.sqrt(2), atol=1e-4))
True
Invert a rotation with the Fourier series \(\tilde{g}(x) = e^{i x}\). The amplitudes with the auxiliary qubit in \(|0\rangle\) are those of the inverse rotation applied to \(|0\rangle\).
from qibo import Circuit, gates, get_backend from qibo.models import qsp_circuit, qsp_phases backend = get_backend() evolution = Circuit(1) evolution.add(gates.RX(0, 0.6)) # coefficients of exp(-i x), 1, and exp(i x) phases = qsp_phases([0.0, 0.0, 1.0], method="fourier") circuit = qsp_circuit(evolution, phases, method="fourier") target = Circuit(1) target.add(gates.RX(0, -0.6)) state = circuit().state()[:2] print(backend.allclose(state, target().state(), atol=1e-6))
True
- Parameters:
oracle (
qibo.models.circuit.Circuit) – circuit on \(n\) qubits that implements \(W\) or \(e^{-i t H}\), depending onmethod. It has to be exact, including its global phase, since it is applied under control.phases (ArrayLike) – output of
qibo.models.qsp_phases()for the samemethod.method (str, optional) –
Nonefor QSP of Ref. [1] or"fourier"for Fourier-based QSP of Ref. [2]. Defaults toNone.
- Returns:
circuit on \(n + 1\) qubits, with the auxiliary qubit at qubit \(0\).
- Return type:
References
1. G. H. Low and I. L. Chuang, Optimal Hamiltonian Simulation by Quantum Signal Processing, Phys. Rev. Lett. 118, 010501 (2017). 2. T. L. Silva, L. Borges, and L. Aolita, Fourier-based quantum signal processing, arXiv:2206.02826.
Quantum Singular Value Transformation#
- qibo.models.qsvt_phases(coefficients, backend=None)[source]#
Computes the phases of a quantum singular value transformation (QSVT) circuit.
Given the Chebyshev series \(P(x) = \sum_{k = 0}^{d} c_{k} T_{k}(x)\) of a real polynomial of degree \(d \ge 1\) with the parity of \(d\) and \(|P(x)| \le 1\) for \(x \in [-1, 1]\), it finds the phases for which
qibo.models.qsvt_circuit()applies \(P\) to the singular values (Corollary 11 of Ref. [1]). The polynomial is completed into a unitary matrix whose entries are trigonometric polynomials, using the spectral factorization of \(1 - P^{2}\) thatqibo.models.qsp_phases()uses for the Fourier-based QSP, and the phases are then extracted one at a time as in Ref. [2].The coefficients are divided by its (grid-estimated) \(\max_{x} |P(x)|\) if it is larger than one. If \(|P|\) reaches one, the polynomial is implemented with a relative error of about \(10^{-6}\) for degrees up to \(100\) and \(10^{-5}\) up to \(300\), and the error is much smaller for polynomials that stay away from one.
- Parameters:
coefficients (ArrayLike) – Chebyshev coefficients \((c_{0}, \dots, c_{d})\). The degree is that of the last non-zero coefficient, and the coefficients of the opposite parity must vanish. Those with modulus below \(10^{-12}\) times the largest one are taken as zero.
backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the calculation. IfNone, it uses the current backend. Defaults toNone.
- Returns:
\(d + 1\) phases. The first \(d\) are those of the rotations controlled by the projector, in the order that they are applied, and the last one is the final rotation of the signal qubit.
- Return type:
ArrayLike
References
1. A. Gilyén, Y. Su, G. H. Low, and N. Wiebe, Quantum singular value transformation and beyond: exponential improvements for quantum matrix arithmetics, Proceedings of the 51st Annual ACM SIGACT Symposium on Theory of Computing (STOC ‘19), 193-204 (2019). 2. J. Haah, Product decomposition of periodic functions in quantum signal processing, Quantum 3, 190 (2019).
- qibo.models.qsvt_circuit(block_encoding, phases, nancillas)[source]#
Creates a quantum singular value transformation (QSVT) circuit.
Let \(U\) be the unitary of
block_encoding, which acts on \(a\) auxiliary qubits and \(n\) system qubits, and let \(A = (\bra{0}^{\otimes a} \otimes \mathbb{1}) \, U \, (\ket{0}^{\otimes a} \otimes \mathbb{1})\) be the matrix that it encodes, with singular value decomposition \(A = \sum_{i} \varsigma_{i} \ket{w_{i}}\!\bra{v_{i}}\). The block of the circuit unitary in which the signal qubit and the auxiliary qubits start and end in \(\ket{0}\) applies a polynomial \(P\) of degree \(d\) to the singular values,\[\begin{split}P^{(SV)}(A) = \begin{cases} \sum_{i} P(\varsigma_{i}) \, \ket{w_{i}}\!\bra{v_{i}} & d \text{ odd,} \\ \sum_{i} P(\varsigma_{i}) \, \ket{v_{i}}\!\bra{v_{i}} & d \text{ even.} \end{cases}\end{split}\]The circuit queries \(U\) and \(U^{\dagger}\) alternately \(d\) times (Theorem 2 of Ref. [1]) and uses a single extra qubit, the signal qubit, which controls the sign of the phases of the rotations that are controlled by the projector on the auxiliary qubits in \(|0\rangle\).
Example
Apply the cubic Chebyshev polynomial \(T_{3}(x) = 4 x^{3} - 3 x\) to the singular value \(1/2\) of a block-encoding of \(\ket{+} / 2\). Since \(T_{3}(1/2) = -1\), the amplitudes with the signal qubit and the auxiliary qubit in \(\ket{0}\) are those of \(-\ket{+}\).
import math from qibo import Circuit, gates, get_backend from qibo.models import qsvt_circuit, qsvt_phases backend = get_backend() # prepares |+> with amplitude 1/2 when the auxiliary qubit is in |0> block_encoding = Circuit(2) block_encoding.add(gates.RY(0, 2 * math.pi / 3)) block_encoding.add(gates.H(1)) # Chebyshev series of the polynomial, i.e. only the coefficient of T_3 phases = qsvt_phases([0.0, 0.0, 0.0, 1.0]) circuit = qsvt_circuit(block_encoding, phases, nancillas=1) target = Circuit(1) target.add(gates.H(0)) state = circuit().state()[:2] print(backend.allclose(state, -target().state(), atol=1e-6))
True
- Parameters:
block_encoding (
qibo.models.circuit.Circuit) – circuit that implements \(U\). Its firstnancillasqubits are the auxiliary qubits.phases (ArrayLike) – phases of the polynomial, e.g. the output of
qibo.models.qsvt_phases().nancillas (int) – number \(a\) of auxiliary qubits of
block_encoding.
- Returns:
circuit with the signal qubit at position \(0\), followed by the qubits of
block_encodingin the same order.- Return type:
References
1. A. Gilyén, Y. Su, G. H. Low, and N. Wiebe, Quantum singular value transformation and beyond: exponential improvements for quantum matrix arithmetics, Proceedings of the 51st Annual ACM SIGACT Symposium on Theory of Computing (STOC ‘19), 193-204 (2019).
Hamiltonian diagonalization#
Double-Bracket Iterations#
- class qibo.models.DoubleBracketIteration(hamiltonian, backend=None)[source]#
Diagonalization double-bracket iteration (DBI) of a Hamiltonian.
Given a Hamiltonian \(H_{0}\), the \(k\)-th recursion step of the DBI is
\[H_{k+1} = e^{s_{k} \, W_{k}} \, H_{k} \, e^{-s_{k} \, W_{k}} \, , \quad W_{k} = [D_{k}, H_{k}] \, ,\]where \(D_{k}\) is a diagonal operator and \(s_{k}\) is the step duration. If \(D_{k}\) is not specified, the dephasing \(\Delta(H_{k})\), i.e. the diagonal restriction of \(H_{k}\), is used. This choice, called canonical bracket, gives rise to the Głazek-Wilson-Wegner (GWW) DBI, which decreases the Hilbert-Schmidt norm of the off-diagonal restriction of \(H_{k}\) for sufficiently short durations.
Two types of rotation are available. In the
"double_bracket"rotation, the exact unitary \(e^{-s_{k} \, W_{k}}\) is applied. In the"group_commutator"rotation, the unitary \(e^{i \sqrt{s_{k}} \, D_{k}} \, e^{i \sqrt{s_{k}} \, H_{k}} \, e^{-i \sqrt{s_{k}} \, D_{k}}\) is applied instead, which is the group commutator iteration (GCI) that can be implemented in a quantum computer throughcircuit(). A negativestep_durationcorresponds to flipping the sign of \(D_{k}\).- Parameters:
hamiltonian (ArrayLike or
qibo.hamiltonians.abstract.AbstractHamiltonian) – Hamiltonian \(H_{0}\) to be diagonalized. Must be hermitian.backend (
qibo.backends.abstract.Backend, optional) – backend to be used in the execution. IfNone, it uses the current backend. Defaults toNone.
Example
from qibo.backends import NumpyBackend from qibo.hamiltonians import TFIM from qibo.models import DoubleBracketIteration backend = NumpyBackend() hamiltonian = TFIM(3, h=1.0, backend=backend) dbi = DoubleBracketIteration(hamiltonian, backend=backend) norms = dbi.run(nsteps=5) print(norms[0] > norms[-1])
True
References
1. M. Gluza, Double-bracket quantum algorithms for diagonalization, Quantum 8, 1316 (2024).
- property energy_fluctuations: ArrayLike#
sqrt{langle H_{k}^{2} rangle - langle H_{k} rangle^{2}} of the current Hamiltonian \(H_{k}\) in all computational basis states.
Fluctuations vanish for eigenstates of \(H_{k}\).
- Type:
Energy fluctuations
- Type:
math
- property off_diagonal_norm: float#
Hilbert-Schmidt norm \(\|\sigma(H_{k})\|_{\text{HS}}\) of the off-diagonal restriction of the current Hamiltonian \(H_{k}\).
- circuit(backend=None)[source]#
Circuit \(V_{K}\) of the group commutator iteration (GCI) after \(K\) steps.
The circuit is built recursively from the recorded step durations \(s_{k}\) and diagonal operators \(D_{k}\) as
\[V_{k+1} = V_{k} \, e^{i \sqrt{s_{k}} \, D_{k}} \, V_{k}^{\dagger} \, e^{i \sqrt{s_{k}} \, H_{0}} \, V_{k} \, e^{-i \sqrt{s_{k}} \, D_{k}} \, ,\]with \(V_{0} = \mathbb{1}\), so that \(H_{K} = V_{K}^{\dagger} \, H_{0} \, V_{K}\) and \(V_{K} |\mu\rangle\) approximates an eigenstate of \(H_{0}\) for a computational basis state \(|\mu\rangle\). It only contains evolutions under \(H_{0}\) and under the diagonal operators, and its number of evolutions under \(H_{0}\) is \((3^{K} - 1) / 2\). It reproduces the iteration exactly, and therefore is only available when the
"group_commutator"rotation was used in all steps.- Parameters:
backend (
qibo.backends.abstract.Backend, optional) – backend used to build the evolution gates. IfNone, it uses the backend of the iteration. Defaults toNone.- Returns:
Circuit implementing \(V_{K}\).
- Return type:
- Raises:
ValueError – If the dimension of the Hamiltonian is not a power of two, or if any step of the iteration did not use the
"group_commutator"rotation.
- optimize_step(diagonal=None, rotation='double_bracket', step_max=None, nsamples=100)[source]#
Step duration that maximally decreases the off-diagonal norm in one step.
A grid search over \([-s_{\max}, s_{\max}]\) (\([0, s_{\max}]\) for the canonical bracket) is followed by a bounded local refinement around the best point.
- Parameters:
diagonal (ArrayLike, optional) – diagonal operator \(D_{k}\), given either as the vector of its real diagonal entries or as a real diagonal matrix. If
None, the dephasing of the current Hamiltonian is used. Defaults toNone.rotation (str, optional) – either
"double_bracket"or"group_commutator". Defaults to"double_bracket".step_max (float, optional) – upper limit of the search. If
None, it is set to \(\pi / \|[D_{k}, H_{k}]\|\), with the operator norm. Defaults toNone.nsamples (int, optional) – number of points of the grid search. Defaults to \(100\).
- Returns:
Optimal step duration.
- Return type:
- run(nsteps, diagonals=None, rotation='double_bracket', step_max=None, nsamples=100)[source]#
Runs
nstepsgreedy recursion steps of the iteration.In each step, the step duration is optimized for the canonical bracket and for each diagonal operator in
diagonals, and the choice yielding the smallest off-diagonal norm is applied.- Parameters:
nsteps (int) – number of recursion steps.
diagonals (list[ArrayLike], optional) – candidate real diagonal operators \(D_{k}\), e.g. products of Pauli-\(Z\) operators, to be considered in addition to the canonical bracket in every step. Defaults to
None.rotation (str, optional) – either
"double_bracket"or"group_commutator". Defaults to"double_bracket".step_max (float, optional) – see
optimize_step(). Defaults toNone.nsamples (int, optional) – see
optimize_step(). Defaults to \(100\).
- Returns:
Off-diagonal norms before the first step and after each step.
- Return type:
- step(step_duration, diagonal=None, rotation='double_bracket')[source]#
Performs one recursion step and updates the iterated Hamiltonian.
- Parameters:
step_duration (float) – step duration \(s_{k}\).
diagonal (ArrayLike, optional) – diagonal operator \(D_{k}\), given either as the vector of its real diagonal entries or as a real diagonal matrix. If
None, the dephasing of the current Hamiltonian is used. Defaults toNone.rotation (str, optional) – either
"double_bracket"or"group_commutator". Defaults to"double_bracket".
- Returns:
Iterated Hamiltonian \(H_{k+1}\).
- Return type:
ArrayLike