Measuring quantum states in QuAIRKit

Copyright (c) 2024 QuAIR team. All Rights Reserved.

This tutorial demonstrates how to perform quantum measurement using QuAIRKit.

Table of Contents

[1]:
import time
import traceback

import quairkit as qkit
from quairkit.database import *
from quairkit.loss import *
from quairkit.qinfo import *

qkit.set_dtype("complex128")

Quantum measurement

The idea of measurement comes from one of the four postulates in quantum mechanics.

Postulate 3 [1]

Quantum measurements are described by a collection \(\{M_m\}\) of measurement operators. These are operators acting on the state space of the system being measured. The index \(m\) refers to the measurement outcomes that may occur in the experiment. If the state of the quantum system is \(|\psi\rangle\) immediately before the measurement, then the probability that result \(m\) occurs is given by

\[p(m) = \langle\psi| M_m^\dagger M_m |\psi\rangle \tag{1}\]

and the state of the system after the measurement is

\[\frac{M_m |\psi\rangle}{\sqrt{\langle\psi| M_m^\dagger M_m |\psi\rangle}}\tag{2}\]

where the measurement operators satisfy the completeness equation,

\[\sum_m M_m^\dagger M_m = I.\tag{3}\]

The completeness equation expresses the fact that probabilities sum to one,

\[\sum_m p(m) = \sum_m \langle \psi \vert M_m^\dagger M_m \vert \psi \rangle= 1\tag{4}\]

Such operator set \(\{M_m\}\) is called a Positive Operator-Valued Measure (POVM). When all \(M_m\) are orthogonal projectors (i.e., \(M_m M_{m'} = \delta_{m,m'}M_m\) and \(M = M^\dagger\)), this set is called a Projection-valued Measure (PVM). The quantum measurement described by PVM is called a projective measurement.

Users can perform projective measurements based on the eigenbasis of an observable. For example, one can generate a PVM for the Pauli matrix \(X\) as an observable. According to the spectral decomposition theorem, the Pauli matrix \(X\) has the decomposition: \(X = \sum_m P_m\), where the set \(\{P_m\}\) forms a Projection-valued Measure.

[2]:
pvm = pauli_str_povm("x")
print(f"Projection-valued measure: \n{pvm}")
Projection-valued measure:
tensor([[[ 0.5000+0.j,  0.5000+0.j],
         [ 0.5000+0.j,  0.5000+0.j]],

        [[ 0.5000+0.j, -0.5000+0.j],
         [-0.5000+0.j,  0.5000+0.j]]])

Perform measurement

Projective measurements in QuAIRKit are mainly called by a torch Module Measure. There are several ways to initialize a Measure instance:

  1. Set computational measurement by default, i.e., \(M_m = \{|m\rangle\langle m|\}\)

  2. Set measurement by given Pauli string(s)

  3. Set measurement by given PVM(s) in torch.Tensor

For Measure instances initialized in the first two ways, if the measurement is across all qubits, then the output state(s) will always be recognized as pure state(s).

[3]:
op = Measure()  # computational measure
op = Measure("x")  # x measure on a qubit
op = Measure(pvm)  # measure with a pvm

Measure accepts the State instances and an (optional) measure position as input and returns the measurement result. Note that if the measure only happens on a part of the system, then the argument qubits_idx should be specified. The following example is to measure the first subsystem of state \(\rho\) with the PVM defined before.

[4]:
op = Measure(pvm)
rho = random_state(num_qubits=2, rank=2)
prob = op(rho, qubits_idx=[0])  # measure rho
print("The probability distribution of outcome", prob)
The probability distribution of outcome tensor([0.6718, 0.3282])

Users can get the collapsed state by setting keep_state = True.

[5]:
prob, collapsed_state = op(rho, qubits_idx=[0], keep_state=True)
print("The collapsed state for each outcome is", collapsed_state)
The collapsed state for each outcome is
-----------------------------------------------------
 Backend: default-mixed
 System dimension: [2, 2]
 System sequence: [0, 1]
 Batch size: [2]

 # 0:
[[0.1 +0.j   0.13-0.09j 0.1 +0.j   0.13-0.09j]
 [0.13+0.09j 0.4 +0.j   0.13+0.09j 0.4 +0.j  ]
 [0.1 +0.j   0.13-0.09j 0.1 +0.j   0.13-0.09j]
 [0.13+0.09j 0.4 +0.j   0.13+0.09j 0.4 +0.j  ]]
 # 1:
[[ 0.33+0.j   0.01+0.1j -0.33+0.j  -0.01-0.1j]
 [ 0.01-0.1j  0.17+0.j  -0.01+0.1j -0.17+0.j ]
 [-0.33+0.j  -0.01-0.1j  0.33+0.j   0.01+0.1j]
 [-0.01+0.1j -0.17+0.j   0.01-0.1j  0.17+0.j ]]
-----------------------------------------------------

The probability of particular measurement outcome is obtained via setting desired_result=x.

[6]:
x = "1"
prob, collapsed_state = op(
    rho, qubits_idx=[0], keep_state=True, desired_result=x
)  # return the second outcome
print(
    f"The probability for obtaining outcome {x} is {prob}, with outcome state",
    collapsed_state,
)
The probability for obtaining outcome 1 is tensor([0.3282]), with outcome state
-----------------------------------------------------
 Backend: default-mixed
 System dimension: [2, 2]
 System sequence: [0, 1]
 Batch size: [1]

 # 0:
[[ 0.33+0.j   0.01+0.1j -0.33+0.j  -0.01-0.1j]
 [ 0.01-0.1j  0.17+0.j  -0.01+0.1j -0.17+0.j ]
 [-0.33+0.j  -0.01-0.1j  0.33+0.j   0.01+0.1j]
 [-0.01+0.1j -0.17+0.j   0.01-0.1j  0.17+0.j ]]
-----------------------------------------------------

Users can also directly call the attribute measure of State instances for simple measurement.

[7]:
rho = random_state(num_qubits=1, rank=2)
prob = rho.measure(measure_op=pvm)  # same as Measure(pvm)(rho)
print("The probability distribution of outcome", prob)
prob, collapsed_state = rho.measure(measure_op=pvm, keep_state=True)  # same as Measure(pvm)(rho, keep_state=True)
print("The collapsed state for each outcome is", collapsed_state)
The probability distribution of outcome tensor([0.0478, 0.9522])
The collapsed state for each outcome is
-----------------------------------------------------
 Backend: default-mixed
 System dimension: [2]
 System sequence: [0]
 Batch size: [2]

 # 0:
[[0.5+0.j 0.5+0.j]
 [0.5+0.j 0.5+0.j]]
 # 1:
[[ 0.5+0.j -0.5+0.j]
 [-0.5+0.j  0.5+0.j]]
-----------------------------------------------------

Positive operator-valued measurement

POVMs are the generalization of PVMs, describing the effect on a subsystem of a projective measurement performed on a larger system. In QuAIRKit, we can perform positive operator-valued measurement by State.measure, with is_povm set to True.

Computation for POVM is often more efficient than that for PVM, as it directly computes the probability. However, its potential lack of a unique post-measurement state makes it less useful in practice.

[8]:
start_time = time.time()
prob = rho.measure(measure_op=pvm)
print(f"Time for measuring with pvm: {time.time() - start_time:.10f}s")


start_time = time.time()
prob = rho.measure(measure_op=pvm, is_povm=True)
print(f"Time for measuring with povm: {time.time() - start_time:.10f}s")

try:
    rho.measure(measure_op=pvm, is_povm=True, keep_state=True)
except ValueError:
    traceback.print_exc()
Time for measuring with pvm: 0.0002276897s
Time for measuring with povm: 0.0001401901s
Traceback (most recent call last):
  File "/tmp/ipykernel_337664/3381646845.py", line 11, in <module>
    rho.measure(measure_op=pvm, is_povm=True, keep_state=True)
  File "/home/leiz/QuAIRKit-Dev/build/__editable__.quairkit-0.5.0-cp310-cp310-linux_x86_64/quairkit/core/state/backend/simulator.py", line 1014, in measure
    raise ValueError(
ValueError: `is_povm` and `keep_state` cannot be both True, since a general POVM does not distinguish states.

QuAIRKit supports batched measurement under the broadcasting rule, i.e., accept one or multiple input states.

Sampled measurements

Users can use quairkit.qinfo.prob_sample to determine shots of measurement based on given probability distributions. The function is used to simulate the outcomes of quantum measurements. When users perform a quantum measurement, the result is probabilistic, namely, outcomes are generated with different probabilities. The prob_sample function allows users to simulate this by generating samples (or “shots”) based on a provided probability distribution.

For example, if users simulate 1024 shots, the output might be

{'00': tensor([98, ...]), '01': tensor([230, ...]), '10': tensor([300, ...]), '11': tensor([396, ...])}

which means that:

  • 00 occurred 98 times,

  • 01 occurred 230 times,

  • 10 occurred 300 times,

  • 11 occurred 396 times.

Users can also adjust the argument binary and proportional to change the output format:

  • binary is False: the dictionary index is in the decimal system.

  • proportional is True: values are transformed into proportions.

[9]:
batch_size = 3
rho = random_state(num_qubits=1, size=batch_size)
prob = rho.measure(measure_op=pvm)

print(f"{batch_size} probability distributions are\n", prob)

print(f"\nThe outcomes of quantum measurements:\n{prob_sample(prob)}")
print(
    f"\nThe outcomes of quantum measurements with the decimal system of dictionary system:\n",
    prob_sample(prob, binary=False),
)
print(
    f"\nThe outcomes of quantum measurements in proportion:\n",
    prob_sample(prob, proportional=True),
)
3 probability distributions are
 tensor([[0.1785, 0.8215],
        [0.3893, 0.6107],
        [0.8171, 0.1829]])

The outcomes of quantum measurements:
{'0': tensor([188, 435, 861]), '1': tensor([836, 589, 163])}

The outcomes of quantum measurements with the decimal system of dictionary system:
 {'0': tensor([183, 374, 864]), '1': tensor([841, 650, 160])}

The outcomes of quantum measurements in proportion:
 {'0': tensor([0.1777, 0.3828, 0.8232]), '1': tensor([0.8223, 0.6172, 0.1768])}

References

[1] Nielsen, Michael A., and Isaac L. Chuang. Quantum computation and quantum information. Vol. 2. Cambridge: Cambridge university press, 2001.

Table: A reference of notation conventions in this tutorial.

Symbol

Variant

Description

\(p\)

\(p(x),p(m)\)

probability distribution

\(M_m\)

measurement operator

\(M_m^\dagger\)

conjugate transpose of \(M_m\)

\(\{\vert m \rangle \langle m \vert\}\)

computational basis

\(\delta_{m,m'}\)

Kronecker delta

[10]:
qkit.print_info()

---------VERSION---------
quairkit: 0.5.1
torch: 2.11.0+cu130
torch cuda: 13.0
numpy: 2.2.6
scipy: 1.15.3
matplotlib: 3.10.5
---------SYSTEM---------
Python version: 3.10.18
OS: Linux
OS version: #1 SMP PREEMPT_DYNAMIC Thu Jun  5 18:30:46 UTC 2025
---------DEVICE---------
CPU:  13th Gen Intel(R) Core(TM) i9-13980HX
GPU: (0) NVIDIA GeForce RTX 4090 Laptop GPU