uniqc.algorithmics.measurement.classical_shadow module#
Classical Shadow state characterisation.
Implements the single-qubit classical shadow protocol from “Aaronson & Rothblum, ‘Measurement Non-Classicality’, 2019” and the multi-qubit extension from “Chen et al., ‘Efficient Classical Shadow Tomography’, 2022”.
The shadow uses the single-qubit Clifford 2-design: random measurement bases drawn uniformly from {I, H, SH} (equivalent to Z, X, Y bases), each followed by computational-basis measurement.
- class uniqc.algorithmics.measurement.classical_shadow.ShadowSnapshot(unitary_indices, outcomes, counts=<factory>)[source]#
Bases:
objectA single snapshot from the classical shadow protocol.
- Variables:
unitary_indices (Tuple[int, ...]) –
Tuple encoding which Clifford unitary was applied to each qubit before measurement.
Mapping (index → unitary → basis measured):
0 → I (Z basis) 1 → H (X basis) 2 → S·H (Y basis, S = diag(1, i))
outcomes (Tuple[int, ...]) – Tuple of bits (0/1) sampled from the computational-basis distribution for each qubit. Bits are LSB-first —
outcomes[i]is qubiti’s measurement.counts (Dict[int, int]) – Empirical outcome counts for this basis setting (integer keys are LSB-first qubit-bit-packed outcomes). Populated from all simulated shots; enables probability-scoring / exact-Born estimators in
shadow_expectation()with much lower variance than the single-outcome HKP estimator.
- Parameters:
- uniqc.algorithmics.measurement.classical_shadow.classical_shadow(circuit, qubits=None, shots=4096, n_shadow=None)[source]#
Generate classical-shadow snapshots of a quantum state.
Each snapshot is obtained by:
For each qubit, choosing uniformly at random one of the three single-qubit Cliffords {I, H, S·H} — corresponding to measurement in the Z, X, or Y basis.
Injecting the corresponding gates before the existing MEASURE instructions in the circuit QASM.
Simulating the modified circuit once and recording the computational-basis outcomes.
Storing (unitary_indices, outcomes) as one snapshot.
The collection of snapshots enables estimating the expectation value of any Pauli string via
shadow_expectation()with sample complexity \(O(\log M / ε^2)\) for M observables.- Parameters:
circuit (Circuit) – Quantum circuit (must already contain MEASURE instructions).
qubits (List[int] | None) – Indices of qubits to include.
Nonemeans all qubits used by the circuit.shots (int) – Number of simulated measurement shots per snapshot. Higher shots reduce per-snapshot variance but are slower.
n_shadow (int | None) – Number of independent shadow snapshots.
Noneauto-computes as2 * n * log(2/δ)withδ = 0.01. This bound guarantees fidelity error ≤ ε with high probability for up to M = exp(ε² n / 10) observables.
- Returns:
List of
ShadowSnapshotobjects.- Raises:
ValueError –
shotsorn_shadowis not a positive integer.- Return type:
Example
>>> from uniqc.circuit_builder import Circuit >>> from uniqc.algorithmics.measurement import ( ... classical_shadow, shadow_expectation ... ) >>> c = Circuit() >>> c.h(0) >>> c.cx(0, 1) >>> c.measure(0, 1) >>> shadows = classical_shadow(c, shots=1024, n_shadow=32) >>> est_ZZ = shadow_expectation(shadows, "ZZ") >>> abs(est_ZZ - 1.0) < 0.1 True
- uniqc.algorithmics.measurement.classical_shadow.shadow_expectation(shadows, pauli_string)[source]#
Estimate the expectation value of a Pauli string from classical-shadow snapshots.
Computes the mean of single-snapshot HKP estimators. For a single observable the mean is optimal; median-of-means buys robustness only when estimating many Paulis with uniform tail-bound guarantees.
For each snapshot the single-qubit estimator is (Huang-Kueng-Preskill, single-qubit Clifford shadow inverse channel
M^{-1}(X) = 3X - I):s_i = 1 if Pauli_i = I s_i = 3 * (-1)^outcome_i if Pauli_i ≠ I and aligned with measured basis s_i = 0 if Pauli_i ≠ I and misaligned with measured basis
The n-qubit estimator is the product \(\hat{P}=\prod_i s_i\).
- Parameters:
shadows (List[ShadowSnapshot]) – List of
ShadowSnapshotfromclassical_shadow().pauli_string (str) – Case-insensitive Pauli string (e.g.
"XYZ","IZI").
- Returns:
Estimated expectation value
<P>.- Raises:
ValueError –
pauli_stringlength does not match snapshot size.ValueError –
pauli_stringcontains invalid characters.
- Return type:
Example
>>> shadows = classical_shadow(circuit, shots=1024, n_shadow=32) >>> shadow_expectation(shadows, "ZZ") # estimate <ZZ>