Tomography API Reference¶
Single-qutrit state and process tomography with mutually unbiased bases (MUBs), plus a couple of helpers for plotting the result.
from qutritium.tomography import (
mub_bases, state_tomography_circuits, reconstruct_state,
process_tomography_circuits, reconstruct_process, choi_to_kraus,
plot_density_matrix, plot_tomography_comparison,
)
State tomography¶
mub_bases()
Gives you the four mutually unbiased bases for a qutrit, each as a
change-of-basis unitary. bases[0] is the computational basis; the other three
are the Fourier-type bases
MUBs are the natural set for tomography: they're informationally complete and
spread the measurements out as evenly as possible. To measure in basis \(b\) you
rotate the state by bases[b].conj().T and then measure in the computational
basis.
state_tomography_circuits(prep)
Takes your single-qutrit prep circuit (no measurement on it) and hands back the
four circuits to actually run — a copy of prep, the basis rotation
\(B_b^\dagger\), then measure_all. Run them and keep the counts in the order
you got them; that's the order reconstruct_state expects. It raises if
prep has more than one qutrit or already has a measurement.
reconstruct_state(counts_basis, method="lls")
Takes those four count-dicts and inverts them back into \(\rho\).
The idea: if we write \(\rho\) in the Gell-Mann basis, \(\rho = I/3 + \tfrac12\sum_i s_i \lambda_i\), then every measured probability is linear in the Bloch vector \(s\), so recovering the state is just a least-squares solve \(A s = y\), with \(A_{m,i} = \tfrac12\mathrm{Tr}(P_m\lambda_i)\) and \(y_m = p_m - 1/3\).
One caveat: linear least squares doesn't know \(\rho\) has to be positive, so with
few shots you can get a slightly unphysical estimate (a small negative
eigenvalue). Pass method="projected_lls" to project that estimate onto the
closest physical density matrix — guaranteed positive and unit-trace. The
available methods are "lls" / "linear_least_squares" (raw, unprojected),
"projected_lls", or its alias "mle" (the same projection); anything else
raises.
Process tomography¶
Same idea as state tomography, one level up: instead of reconstructing a state you reconstruct a channel — what a gate actually does, noise included. The recipe prepares an informationally complete set of inputs, sends each through the gate, and does state tomography on every output.
process_tomography_circuits(gate)
Returns (circuits, input_states). It uses the 12 MUB states
\(|v_b^{(k)}\rangle\langle v_b^{(k)}|\) as inputs, applies gate to each, and
wraps every output in the four state-tomography circuits — so you get 12 groups
of 4 circuits to run, plus the list of input density matrices the least-squares
solve needs. It raises on a multi-qutrit gate.
reconstruct_process(counts_per_input, input_states)
Turns those counts into the channel's Choi matrix \(J\) — a \(9\times 9\) Hermitian matrix that fully describes the channel. The channel is linear, so on vectorized density matrices it is a single matrix \(M\) with \(\mathrm{vec}(\mathcal{E}(\rho)) = M\,\mathrm{vec}(\rho)\). Each known input \(\rho_\text{in}\) and its tomographically reconstructed output \(\rho_\text{out}\) give one column equation; stacking all 12 and solving by least squares recovers \(M\), and the Choi matrix follows from its definition,
It needs at least 9 inputs (the MUB set gives 12) and raises if the two lists disagree in length.
choi_to_kraus(choi, atol=1e-8)
Eigendecomposes \(J = \sum_l \lambda_l |u_l\rangle\langle u_l|\) and returns the
Kraus operators \(K_l = \sqrt{\lambda_l}\,\mathrm{mat}(u_l)\), dropping
eigenvalues at or below atol. A unitary gate gives back a single Kraus
operator; a noisy one gives several. It raises if choi is not \(9\times 9\) or
not Hermitian.
Plotting¶
These need matplotlib (pip install qutritium[plot]), and each hands back a
matplotlib.figure.Figure so you can save or tweak it.
plot_density_matrix(rho, style="city", title=...)
\(\mathrm{Re}(\rho)\) and \(\mathrm{Im}(\rho)\) as 3D bars ("city") or a Hinton
diagram ("hinton").
plot_tomography_comparison(rho_ideal, rho_estimated, fidelity=None)
The ideal and reconstructed states side by side, which is usually the figure you actually want.
Examples¶
State tomography¶
from qutritium import QutritCircuit, StatevectorSimulator, DensityMatrixSimulator, state_fidelity
from qutritium.gates import H3
from qutritium.tomography import state_tomography_circuits, reconstruct_state
prep = QutritCircuit(1, None)
prep.append(H3(), first_qutrit=0)
counts = []
for circ in state_tomography_circuits(prep):
sim = StatevectorSimulator(circ)
sim.run(num_shots=20_000)
counts.append(sim.get_counts()) # keep the basis order
rho_est = reconstruct_state(counts)
rho_true = DensityMatrixSimulator(prep).return_final_state()
print(state_fidelity(rho_true, rho_est)) # > 0.95
Process tomography¶
import numpy as np
from qutritium import DensityMatrixSimulator
from qutritium.channels import NoiseModel, depolarizing_channel
from qutritium.gates import X01
from qutritium.tomography import (
process_tomography_circuits, reconstruct_process, choi_to_kraus,
)
nm = NoiseModel()
nm.add_quantum_error(depolarizing_channel(0.2), "X01") # X01 with 20% depolarizing
groups, inputs = process_tomography_circuits(X01())
counts = []
for group in groups:
per_basis = []
for circ in group:
dm = DensityMatrixSimulator(circ)
dm.set_noise_model(nm)
probs = dm.probabilities()
per_basis.append({str(k): round(p * 100_000) for k, p in enumerate(probs)})
counts.append(per_basis)
choi = reconstruct_process(counts, inputs) # (9, 9) Choi matrix
kraus = choi_to_kraus(choi, atol=1e-6) # Kraus operators
print(len(kraus), "Kraus operators")
total = sum(k.conj().T @ k for k in kraus)
print("trace preserving:", np.allclose(total, np.eye(3), atol=1e-2))
Plotting¶
from qutritium.tomography import plot_tomography_comparison
fig = plot_tomography_comparison(rho_true, rho_est, fidelity=state_fidelity(rho_true, rho_est))
fig.savefig("state_tomography.png")
References¶
- Ivanović (1981), J. Phys. A 14, 3241; Wootters & Fields (1989), Ann. Phys. 191, 363 — the MUB construction and its optimality.
- Thew, Nemoto, White & Munro (2002), Phys. Rev. A 66, 012303 — the qudit least-squares reconstruction recipe used here.
- Smolin, Gambetta & Smith (2012), Phys. Rev. Lett. 108, 070502 — the
projection onto the closest physical state used by
"projected_lls". - Choi (1975), Linear Algebra Appl. 10, 285; Jamiołkowski (1972), Rep. Math. Phys. 3, 275 — the Choi matrix / channel-state duality behind process tomography.