Download dense_evolution/utils/measurement.py from Tatopenn/dense-Evolution: direct link, hf CLI and curl.
- Browser
- Download file 3.58 kB
-
https://huggingface.co/Tatopenn/dense-Evolution/resolve/main/dense_evolution/utils/measurement.py
- Command line
-
hf download hf://Tatopenn/dense-Evolution/dense_evolution/utils/measurement.py
-
curl -L -o measurement.py https://huggingface.co/Tatopenn/dense-Evolution/resolve/main/dense_evolution/utils/measurement.py
3.58 kB
| """ | |
| Measurement-related conveniences: turning a statevector into finite-shot | |
| counts the way real hardware and every other SDK report results (Qiskit's | |
| `get_counts()`, PennyLane's `qml.counts()`), and comparing two pure states | |
| directly without building a density matrix first. | |
| Sampling a statevector into shot counts is another pattern found | |
| hand-duplicated across this package's own experiment scripts -- always | |
| the same `rng.choice(dim, p=probs)` + `np.bincount` (or `np.unique`) | |
| couple of lines, rewritten fresh each time. | |
| """ | |
| import numpy as np | |
| __all__ = ['sample_counts', 'statevector_fidelity'] | |
| def sample_counts(statevector, n_shots, rng=None): | |
| """ | |
| Simulate n_shots projective measurements of every qubit in the | |
| computational basis, returning a Qiskit-style counts dict. | |
| Parameters | |
| ---------- | |
| statevector : array-like, shape (2**n_qubits,) | |
| n_shots : int | |
| Number of measurement shots to sample, must be >= 1. | |
| rng : numpy.random.Generator, optional | |
| A fresh `numpy.random.default_rng()` is used if not given -- | |
| pass one explicitly for reproducible sampling. | |
| Returns | |
| ------- | |
| dict[str, int] | |
| Bitstring keys read left-to-right as qubit 0 upward (matching | |
| DenseSVSimulator's own qubit-0-is-MSB layout and | |
| pauli_expectation's string convention), each mapped to how many | |
| of the n_shots samples landed on it. Only bitstrings with at | |
| least one hit appear (unobserved outcomes are simply absent, as | |
| in Qiskit's get_counts()). | |
| Examples | |
| -------- | |
| >>> import dense_evolution as de | |
| >>> sim = de.DenseSVSimulator(2) | |
| >>> sim.run_circuit([('h', 0), ('cx', 0, 1)]) | |
| >>> de.sample_counts(sim.get_statevector(), 1000) | |
| {'00': 494, '11': 506} | |
| """ | |
| statevector = np.asarray(statevector) | |
| dim = statevector.shape[0] | |
| n_qubits = dim.bit_length() - 1 | |
| if 1 << n_qubits != dim: | |
| raise ValueError(f"statevector length {dim} is not a power of 2") | |
| if n_shots < 1: | |
| raise ValueError(f"n_shots must be >= 1, got {n_shots}") | |
| probs = np.abs(statevector) ** 2 | |
| total = probs.sum() | |
| if not np.isfinite(total) or total <= 0: | |
| raise ValueError("statevector has zero or non-finite total probability") | |
| probs = probs / total | |
| if rng is None: | |
| rng = np.random.default_rng() | |
| outcomes = rng.choice(dim, size=n_shots, p=probs) | |
| values, freqs = np.unique(outcomes, return_counts=True) | |
| return {format(int(v), f'0{n_qubits}b'): int(f) for v, f in zip(values, freqs)} | |
| def statevector_fidelity(statevector_a, statevector_b): | |
| """ | |
| Fidelity |<a|b>|^2 between two pure statevectors -- the cheap, direct | |
| pure-state counterpart to `uhlmann_fidelity` (which compares two full | |
| density matrices, needed for the mixed/noisy states that | |
| `zne_density_matrix` and friends work with). Use this one whenever | |
| both states are pure; it never builds an O(dim^2) density matrix. | |
| Parameters | |
| ---------- | |
| statevector_a, statevector_b : array-like, shape (2**n_qubits,) | |
| Must have the same shape. | |
| Returns | |
| ------- | |
| float | |
| In [0, 1] up to floating-point error. 1.0 for identical states | |
| (up to global phase), 0.0 for orthogonal states. | |
| """ | |
| a = np.asarray(statevector_a) | |
| b = np.asarray(statevector_b) | |
| if a.shape != b.shape: | |
| raise ValueError(f"statevector shapes differ: {a.shape} vs {b.shape}") | |
| return float(np.abs(np.vdot(a, b)) ** 2) | |