psephology
v0.1.0
Published
Post-process quantum measurement shot counts — normalize to probabilities, compute Z-basis expectation values, and marginalize qubits. Zero deps. (Z-basis statistics only, not tomography.)
Maintainers
Readme
psephology
Post-process quantum measurement shot counts — normalize to probabilities, compute Z-basis expectation values, and marginalize qubits. Zero deps. (Z-basis statistics only, not tomography.)
The problem
Quantum hardware and simulators return shot counts: { "00": 512, "11": 488 }. You need to normalize to probabilities, compute expectation values, and marginalize over qubits — small but easy-to-get-wrong steps (bit-order conventions bite everyone). Qiskit does this in Python, but JS/TS options are sparse.
Install
npm install psephology
# or
pnpm add psephology
# or
yarn add psephologyUse
import { probabilities, expectation } from "psephology";
const counts = { "00": 512, "11": 488 };
const probs = probabilities(counts); // { "00": 0.512, "11": 0.488 }
const correlation = expectation(counts, [0, 1]); // ⟨Z0 Z1⟩ ≈ +1Bell-state example
const bellCounts = { "00": 498, "11": 502 };
const probs = probabilities(bellCounts);
const correlation = expectation(bellCounts, [0, 1]); // ≈ 1.0 (perfect correlation)
const z0 = expectation(bellCounts, [0]); // ≈ 0.0 (equal superposition)API
probabilities(counts)
Normalizes shot counts to probabilities. Returns count / total_shots for each key.
probabilities({ "00": 512, "11": 488 }); // { "00": 0.512, "11": 0.488 }
probabilities({}); // {} (empty counts)expectation(counts, qubits, conv?)
Computes ⟨∏ Z_q⟩ for specified qubits. Parity is XOR of selected qubits' bits. Sign is +1 for even parity, -1 for odd. Result is weighted sum: Σ sign × probability.
expectation({ "00": 498, "11": 502 }, [0, 1]); // ≈ 1.0 (perfect correlation)
expectation({ "01": 500, "10": 500 }, [0, 1]); // -1.0 (anti-correlation)
expectation({ "0": 700, "1": 300 }, [0]); // 0.4 (single qubit)
expectation({ "00": 250, "11": 250 }, []); // 1.0 (identity)marginal(counts, keep, conv?)
Projects each bitstring onto kept qubit indices, summing counts of identical projections.
marginal({ "000": 100, "010": 200, "110": 150, "111": 50 }, [1]);
// { "0": 100, "1": 400 }
marginal({ "00": 100, "11": 200 }, []); // { "": 300 } (total shots)shots(counts), mostLikely(counts)
shots({ "00": 512, "11": 488 }); // 1000
mostLikely({ "00": 100, "11": 500 }); // "11"Error Classes
RaggedCounts: Mixed-length bitstringsInvalidBitstring: Invalid characters (not "0" or "1")EmptyCounts: Empty counts for expectation
Bit-Order Convention
Default: Big-Endian (rightmost = qubit 0)
// Bitstring "0101": position 3 = qubit 0, position 2 = qubit 1, etc.
const counts = { "01": 300, "10": 700 };
expectation(counts, [0]); // 0.4 (rightmost bit)Little-Endian (leftmost = qubit 0, Qiskit convention)
expectation(counts, [0], { bitOrder: "little-endian" }); // -0.4 (leftmost bit)Be explicit about your convention and document your hardware's mapping.
Non-goals
This does NOT: simulate circuits, perform tomography, handle non-Z observables, do error mitigation. For non-Z measurements, apply basis rotation on-device.
Related Packages
- @azghr/filterkit — Framework-agnostic, type-safe filtering for TypeScript
- @azghr/shorn — Truncate strings by byte budget without breaking graphemes
- @azghr/singlet — Deduplicate concurrent async calls
- forbear — Read server rate-limit instructions from HTTP responses
- quiesce — Ordered, timeboxed graceful shutdown for Node
- sortition — Deterministic percentage rollouts and A/B bucketing
Note: This list should be kept in sync with the packages in pnpm-workspace.yaml. When adding a new package to the monorepo, update this list to include all sibling packages.
License
MIT
