Skip to content

Measurement ​

Measurement is how we extract classical information from quantum states. When you measure a qubit, its superposition collapses to either 0 or 1.

The measure() Function ​

measure(q: Qubit) returns a typed Measurement value — a handle that references a classical-bit slot in the circuit. To read out the bit value, coerce it with read_measurement:

A Measurement has two consumers:

To...Use
Read the bit value into host code (Int)read_measurement(m) — this is the "bump" that resolves the bit
Apply a classically-conditioned gatecgate(m, gate, qubit) — emits if (c[bit] == 1) gate q[X]; into the circuit

Prefer cgate for adaptive corrections (teleportation, Pauli updates) — the conditional is encoded in the QASM itself, so the simulator/hardware applies the gate using its own observed bit. read_measurement reads the bit into host code, which is what you want when the value flows out as the program's result.

Whole-register measurement is different

measure(QInt) and measure(QubitArray[N]) return Int directly — there's no per-bit-index handle to thread through cgate for an N-qubit register. Only single-qubit measure(q: Qubit) returns a Measurement.

Measurement Collapses State ​

Before measurement, a qubit in superposition has probabilities for both outcomes. Measurement forces it into a definite state:

|0>                    -> always measures 0
|1>                    -> always measures 1
(|0> + |1>)/sqrt(2)    -> measures 0 or 1 with 50% probability each

After measurement, the qubit is consumed - it cannot be used again.

Running Multiple Shots ​

Quantum algorithms are probabilistic. To see the distribution of outcomes, run multiple shots:

The shots Parameter ​

The quantum_simulator(shots = N) handler runs your quantum function N times:

kettle
fn main() -> Unit using quantum_simulator(shots = 100), file_io
  results = my_quantum_fn()  -- returns List[T] with 100 elements
end fn

Without shots, you get a single execution:

The counts() Function ​

Use counts() to get a frequency distribution:

Mid-Circuit Measurement ​

You can measure qubits in the middle of a circuit and use the results to control later operations:

cgate(m: Measurement, gate: Gate, q: Qubit) -> Qubit emits a classically-conditioned gate into the circuit (if (c[bit_index] == 1) gate q[X];). The simulator or hardware decides whether to apply the gate at execution time, using its own observed bit. Pass Measurement directly — do not wrap it in read_measurement first, that would resolve the bit into host code and break the conditional's self-consistency under re-execution.

IBM portability

read_measurement reads a bit into host code, which IBM hardware cannot service mid-shot. The typechecker rejects programs that call read_measurement under using ibm_quantum(...) and points you at cgate instead. Adaptive control logic must be encoded in the QASM circuit, not in the host language, for IBM-targeted programs.

Teleportation Example ​

Quantum teleportation uses mid-circuit measurement and classical feedback. Note that m1 and m2 stay as Measurement values — the corrections are encoded as conditional QASM:

This demonstrates:

  1. Creating an entangled Bell pair
  2. Bell measurement (mid-circuit, returns Measurement handles)
  3. Classical corrections via cgate (encoded in the circuit, not host-side)
  4. Final readout via read_measurement at the function boundary

Expectation Values ​

Sometimes you need to know the expected measurement outcome without actually collapsing the qubit. The expect_z function computes the Pauli-Z expectation value ⟨ψ|Z|ψ⟩:

Temporarily disabled

expect_z is currently disabled in the build that ships with this beta — it returns an error pointing at the unified-execution-path refactor. It will be reintroduced via the session trace in a follow-up. Programs that need observable estimation today should stick to sampled measure reads with shots = N and aggregate classically.

How It Works ​

The expect_z function returns an Expectation record with two fields:

  • exp.value: The expectation value (weighted average of measurement outcomes)
  • exp.std_error: The standard error of the estimate

The expectation value tells you:

  • +1 for |0⟩ (would always measure 0)
  • -1 for |1⟩ (would always measure 1)
  • 0 for equal superposition (50/50 chance)
  • cos(θ) after ry(θ) rotation

Simulation Modes ​

By default, expect_z uses statistical sampling. For debugging with exact values, use quantum_simulator(exact = True). See Backends for details.

Non-Destructive ​

Unlike measure, expect_z does not collapse the qubit:

kettle
(exp, q) = expect_z(q)  -- Returns (Expectation, Qubit) tuple
-- Access the value and error
print(exp.value)
print(exp.std_error)
-- q can still be used!
q <- hadamard
measure(q)

This is useful for variational algorithms (VQE, QAOA) where you compute energies many times during optimization.

Use Cases ​

  • VQE: Compute molecular ground state energies
  • QAOA: Evaluate cost function for optimization problems
  • Quantum machine learning: Gradient computation for training