Background
The results endpoints (get_job_probabilities, get_variant_probabilities, get_variant_histogram) return IonQ's register-keyed probability mapping (state key → probability) as-is via probs.additional_properties. There are no helpers for the post-processing callers keep rewriting — counts, bitstring relabeling, marginals, expectation values. A small pure-Python module would remove that duplication and give the wrappers (qiskit-ionq, cirq-ionq, pennylane-ionq) a common base.
What to build
A new hand-written module ionq_core/results.py, pure-Python and NumPy-free (like ionq_core/gates.py), with helpers over the probabilities mapping. Suggested set (names open to discussion in the PR):
probabilities_to_counts(probabilities, shots) — integer counts summing exactly to shots (largest-remainder rounding).
relabel_to_bitstrings(probabilities, num_qubits) — integer state keys → zero-padded bitstrings.
marginal(probabilities, qubits, num_qubits) — marginal over a subset of qubits.
expectation_z(probabilities, num_qubits) — ⟨Z⊗…⟩ parity, Σ p(x)·(−1)^popcount(x).
Each key is the integer encoding of the measured bitstring (Bell state → {"0": ~0.5, "3": ~0.5}), so int(key) parses it. Capture a real simulator response as a fixture, and document the bit-ordering convention consistently across helpers.
Definition of done
Notes
Operate on plain Mapping[str, float] so it works for both the job and variant endpoints and is testable without HTTP. No new runtime deps; no NumPy. References: ionq_core/api/default/get_job_probabilities.py (+ variant endpoints), the pure-Python style in ionq_core/gates.py, and AGENTS.md.
Background
The results endpoints (
get_job_probabilities,get_variant_probabilities,get_variant_histogram) return IonQ's register-keyed probability mapping (state key → probability) as-is viaprobs.additional_properties. There are no helpers for the post-processing callers keep rewriting — counts, bitstring relabeling, marginals, expectation values. A small pure-Python module would remove that duplication and give the wrappers (qiskit-ionq,cirq-ionq,pennylane-ionq) a common base.What to build
A new hand-written module
ionq_core/results.py, pure-Python and NumPy-free (likeionq_core/gates.py), with helpers over the probabilities mapping. Suggested set (names open to discussion in the PR):probabilities_to_counts(probabilities, shots)— integer counts summing exactly toshots(largest-remainder rounding).relabel_to_bitstrings(probabilities, num_qubits)— integer state keys → zero-padded bitstrings.marginal(probabilities, qubits, num_qubits)— marginal over a subset of qubits.expectation_z(probabilities, num_qubits)— ⟨Z⊗…⟩ parity,Σ p(x)·(−1)^popcount(x).Each key is the integer encoding of the measured bitstring (Bell state →
{"0": ~0.5, "3": ~0.5}), soint(key)parses it. Capture a real simulator response as a fixture, and document the bit-ordering convention consistently across helpers.Definition of done
ionq_core/results.pywith the helpers (docstrings + type hints); public names in__all__and re-exported fromionq_core/__init__.py.tests/test_results.pywith a Bell-state fixture and rounding edge cases; 100% branch coverage (the repo gates on--cov-fail-under=100).AGENTS.md);uv run pytest,uv run ruff check,uv run ruff format --check, anduv run ty check ionq_core/pass.Notes
Operate on plain
Mapping[str, float]so it works for both the job and variant endpoints and is testable without HTTP. No new runtime deps; no NumPy. References:ionq_core/api/default/get_job_probabilities.py(+ variant endpoints), the pure-Python style inionq_core/gates.py, andAGENTS.md.