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 register 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].

Syntax

Function: PhaseEstimation

Parameters:

  • size: int provides the number of digits in the fixed-point representation of the phase.
  • unitary: str may be omitted when using the SDK and should otherwise contain the name of the function \(F\).
  • unitary_params: FunctionParams provides the function parameters of the unitary function \(F\).

Example

This example shows how to perform a simple phase estimation:

  1. Initialize the state "10" over two qubits using XGate on the first qubit.
  2. Apply a phase estimation on the 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} \]

The output register is in the state "10", corresponding to the number 0.10. This is \(\frac{1}{2}\) in binary, implying that \(\nu=\frac{1}{2}\), as expected for input state "10".

{
  "functions": [
    {
      "name": "main",
      "body": [
        {
          "function": "XGate",
          "function_params": {},
          "outputs": {
            "TARGET": "x_qpe"
          }
        },
        {
          "function": "PhaseEstimation",
          "function_params": {
            "size": 2,
            "unitary": "Exponentiation",
            "unitary_params": {
              "pauli_operator": {
                "pauli_list": [
                  ["ZI", -0.125],
                  ["IZ", -0.25],
                  ["II", 0.375]
                ]
              },
              "evolution_coefficient": -6.283185307179586
            }
          },
          "inputs": {
            "IN[0]": "x_qpe"
          }
        }
      ]
    }
  ]
}
from math import pi
from classiq.builtin_functions.exponentiation import PauliOperator
from classiq.builtin_functions import Exponentiation, PhaseEstimation, XGate
from classiq import Model, synthesize, show

model = Model()

x_qpe = model.XGate(params=XGate())
qpe_out = model.PhaseEstimation(
    params=PhaseEstimation(
        size=2,
        unitary_params=Exponentiation(
            pauli_operator=PauliOperator(
                pauli_list=[
                    ("ZI", -0.125),
                    ("IZ", -0.25),
                    ("II", 0.375),
                ]
            ),
            evolution_coefficient=-2 * pi,
        ),
    ),
    in_wires={"IN[0]": x_qpe["TARGET"]},
)
quantum_program = synthesize(model.get_model())
show(quantum_program)

 Mcx example

Using Quantum Phase Estimation with Exponentiation

Specify parameters using the ExponentiationSpecification object, which bypasses the parameters of the Exponentiation. See Exponentiation.

PhaseEstimation:

  • exponentiation_specification: Optional[ExponentiationSpecification, ExponentiationScaling] takes one of these options:

    • ExponentiationSpecification:

      • max_depths: Tuple[int, ...] sets the depth of the ExponentiationConstraints directly for each qubit of the phase estimation, starting from the most significant bit.
      • scaling: Optional[ExponentiationScaling] provides a rule for determining the depth.
    • ExponentiationScaling:

      • max_depth: int determines the depth of the most significant bit.
      • max_depth_scaling_factor: float provides an exponential growth factor for consecutive qubits.

Inputting exponentiation_specification automatically sets ExponentiationOptimization.MINIMIZE_ERROR.

Using Custom Functions and Composite Functions

Define custom functions or composite functions in the unitary_params argument of PhaseEstimation. Make sure the input and output names are identical.

If using the textual model to provide a custom or composite function, you must define the inputs and outputs in the logic flow as defined in the function.

Define the parameters using the unitary_params argument of the CustomFunction:

  • input_decls: ArithmeticIODict
  • output_decls: ArithmeticIODict

Example

{
  "functions": [
    {
      "name": "main",
      "body": [
        {
          "function": "PhaseEstimation",
          "function_params": {
            "unitary": "my_custom_function",
            "unitary_params": {
              "input_decls": {"custom_input": {"size": 1}},
              "output_decls": {"custom_output": {"size": 1}}
            },
            "size": 2
          }
        }
      ]
    },
    {
      "name": "my_custom_function",
      "implementations": [
        {
          "serialized_circuit": "OPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[1];\nh q[0];"
        }
      ],
      "register_mapping": {
        "output_registers": [{"name": "custom_output", "qubits": [0]}],
        "input_registers": [{"name": "custom_input", "qubits": [0]}]
      }
    }
  ]
}
from classiq import FunctionLibrary, QReg, qfunc
from classiq.builtin_functions import PhaseEstimation
from classiq import Model, synthesize, show


@qfunc
def my_custom(custom_input: QReg[1]) -> QReg[1]:
    return 'OPENQASM 2.0;\ninclude "qelib1.inc";\nqreg q[1];\nh q[0];\n'


func_library = FunctionLibrary()
func_library.add_function(my_custom)

qpe_params = PhaseEstimation(
    size=2, unitary="my_custom", unitary_params=func_library.get_function("my_custom")
)

model = Model()
model.include_library(func_library)
model.PhaseEstimation(params=qpe_params)

quantum_program = synthesize(model.get_model())
show(quantum_program)

 QPE_Custom example

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