Skip to content

Quantum Phase Estimation

The quantum phase estimation (QPE) function estimates the phase of an eigenvector of a unitary function. More precisely, given a unitary function \(F\) and an input containing a quantum variable with a state \(|\psi\rangle\) such that \(F(|\psi\rangle)=e^{2\pi i\nu}|\psi\rangle\), the phase estimation function outputs an estimation of \(\nu\) as a fixed-point binary number.

Phase estimation is frequently used as a subroutine in other quantum algorithms such as Shor's algorithm and quantum algorithms for solving linear systems of equations (HHL algorithm). Theoretical details are in Ref. [1].

Function: qpe

Arguments:

  • unitary: QCallable - The unitary operation for which the qpe estimation the eigenvalues
  • phase: QNum - The output of the qpe, holding the phase as a number in the range \([0, 1)\)

Function: qpe_flexible

The function is suitalbe when wants to specialize the way the power of a unitary is defined, other than using the naive power. For example it can used with exponentiaing hamiltonians or for shor's algorithm.

Arguments:

  • unitary_with_power: QCallable[CInt] - Power of a unitary. Accepts as argument the power of the unitary to apply.
  • phase: QNum

Examples

Example 1: QPE of a function

This example shows how to perform a simple phase estimation:

  1. Initialize the state \(|3\rangle\) over two qubits.
  2. Apply a phase estimation on the the controlled-RZ gate, represeneted by the unitary matrix:
\[ \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & e^{-i\frac{\lambda}{2}} & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & e^{i\frac{\lambda}{2}} \end{pmatrix} \]

The expected phase variable should encode \(\frac{\lambda}{4\pi}\), the phase of the eigenvalue of the \(|3\rangle\) state. Choosing \(\lambda = \pi\), the expected result is \(\frac{1}{4}\), represented in binary by 01.

from classiq import (
    CRZ,
    Output,
    QArray,
    QBit,
    QNum,
    allocate,
    allocate_num,
    create_model,
    inplace_prepare_int,
    qfunc,
    qpe,
)
from classiq.qmod.symbolic import pi

QPE_RESOLUTION = 2


@qfunc
def main(state: Output[QArray[QBit]], phase: Output[QNum]):
    allocate(2, state)
    allocate_num(QPE_RESOLUTION, False, QPE_RESOLUTION, phase)

    inplace_prepare_int(3, state)
    qpe(unitary=lambda: CRZ(pi, state[0], state[1]), phase=phase)


qmod = create_model(main)
from classiq import synthesize, write_qmod

write_qmod(qmod, "qpe")
qprog = synthesize(qmod)

Show the actual results:

from classiq import execute

res = execute(qprog).result()[0].value
print("Results:", res.parsed_counts)
Results: [{'state': 3.0, 'phase': 0.25}: 1000]

Example 2: Flexible QPE

The following examples will specifiy directly how to take powers in the QPE. The unitary function is suzuki_trotter, where the number of repetitions will be 1. In the case of diagonal hamiltonian it be exact exponentiation of the hamiltoian.

Take the following matrix: $$ \begin{pmatrix} 0 & 0 & 0 & 0 \ 0 & \tfrac{1}{4} & 0 & 0 \ 0 & 0 & \tfrac{1}{2} & 0 \ 0 & 0 & 0 & \tfrac{3}{4} \ \end{pmatrix} $$

Represented by the hamiltonian:

\(H = -\frac{1}{8}Z_0I_1 - \frac{1}{4}I_0Z_1 + \frac{3}{8}I_0I_1\)

from classiq import (
    Output,
    Pauli,
    PauliTerm,
    QArray,
    QBit,
    QNum,
    allocate,
    allocate_num,
    create_model,
    hadamard_transform,
    qfunc,
    qpe_flexible,
    suzuki_trotter,
)
from classiq.qmod.symbolic import pi

QPE_RESOLUTION = 2

HAMILTONIAN = [
    PauliTerm(pauli=[Pauli.I, Pauli.Z], coefficient=-0.125),
    PauliTerm(pauli=[Pauli.Z, Pauli.I], coefficient=-0.25),
    PauliTerm(pauli=[Pauli.I, Pauli.I], coefficient=0.375),
]


@qfunc
def main(state: Output[QArray[QBit]], phase: Output[QNum]):
    allocate(2, state)
    allocate_num(QPE_RESOLUTION, False, QPE_RESOLUTION, phase)

    hadamard_transform(state)
    qpe_flexible(
        lambda power: suzuki_trotter(
            HAMILTONIAN,
            evolution_coefficient=-2 * pi * (power),
            order=1,
            repetitions=1,
            qbv=state,
        ),
        phase,
    )


qmod = create_model(main)
from classiq import synthesize, write_qmod

write_qmod(qmod, "qpe_flexible")
qprog = synthesize(qmod)

Show the actual results:

from classiq import execute

res = execute(qprog).result()[0].value
print("Results:", res.parsed_counts)
Results: [{'state': 3.0, 'phase': 0.75}: 265, {'state': 0.0, 'phase': 0.0}: 252, {'state': 1.0, 'phase': 0.25}: 245, {'state': 2.0, 'phase': 0.5}: 238]

References

[1] A. Yu. Kitaev Barenco et al, Quantum Measurements and the Abelian Stabilizer Problem, (1995). https://doi.org/10.48550/arXiv.quant-ph/9511026