Repository navigation
feat: qudit noise model (2/5) (#1854) - #1859
Conversation
|
| Project | pyquil |
| Branch | 1854-qudit-noise-model |
| Testbed | ci-runner-linux |
⚠️ WARNING: No Threshold found!Without a Threshold, no Alerts will ever be generated.
Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the--ci-only-thresholdsflag.
Click to view all benchmark results
| Benchmark | Latency | seconds (s) |
|---|---|---|
| test/benchmarks/test_program.py::test_copy_everything_except_instructions | 📈 view plot | 11.08 s |
| test/benchmarks/test_program.py::test_instructions | 📈 view plot | 3.74 s |
| test/benchmarks/test_program.py::test_iteration | 📈 view plot | 3.73 s |
asaites
left a comment
There was a problem hiding this comment.
I've made several comments, which I hope will be useful. I might spend a little more time looking through _noise_model.py, but for the most part, I think I've gone through this pretty thoroughly (and am doing my best to keep up with the physics).
Most of my comments are more style than anything, but I have a few concerns. Overall, though, this looks pretty solid!
Introduce the new noise model under pyquil/noise/ (Channel, MeasurementChannel, ResetChannel, CycleChannel and the quax-based NoiseModel), preserving the legacy pyquil.noise API via _legacy_noise.py. Add qudit DefGate support in quilbase and enable float64 for tests. Part of splitting PR #1848 into a reviewable stack. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
a2e40e8 to
65aa919
Compare
☂️ Python Coverage
Overall Coverage
New Files
Modified Files
|
|
I've done a substantial refactor to add support for Lindbladian channels. The way this works is as follows:
|
|
I think there's a problem with the |
- Remove the awkward `SupportsReal` protocol; type `_resolve_params` against the canonical `Sequence[ParameterDesignator]` (drops the `ResolvableParam` alias and the unused `Protocol`/`SupportsFloat`/ `runtime_checkable` imports). - Inline the one-line noise combination into `Channel.__add__` and have `__matmul__` delegate to it (drop `_combine_noise_keeping_gate`). - Fix D401 (imperative mood) on the two `as_post_gate_noise` docstrings. - Drop `# type: ignore[attr-defined]` in the channel `__eq__` methods: mypy narrows `other` after the `type(self) is not type(other)` guard, so the comments were flagged unused. - Sort the `quilbase` import block (ruff I001). Makes `ruff check`, `ruff format --check`, and `mypy pyquil` pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViuQ59tEfPw6z36i1fkFhg
The locked jax 0.10.0 predates the `enable_eigvec_derivs` kwarg that rigetti-quax 0.7.0 passes to `jax.lax.linalg.eig`, so every quax-backed Channel construction raised `TypeError` in CI. Update the lock to the newest jax each supported interpreter allows — jax 0.10.2 on Python 3.11 and jax 0.11.0 on Python 3.12 (0.11.0 dropped 3.11) — both of which include the kwarg. Proper version pinning to follow upstream in quax. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViuQ59tEfPw6z36i1fkFhg
The pinned `pandoc` 2.4b0 (a beta) imports the removed `pkg_resources` module in `pandoc/utils.py`, which fails under the project's setuptools >=83 (setuptools dropped `pkg_resources` in 81). The docs `conf.py` imports `pandoc` in its `builder-inited` handler to render the changelog, so `make doctest` aborted with `No module named 'pkg_resources'`. The stable 2.4 release switched to `importlib.resources` and imports cleanly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViuQ59tEfPw6z36i1fkFhg
Confirmed, we need to bump the minimum version of quax |
erichulburd
left a comment
There was a problem hiding this comment.
I've reviewed everything up to quilbase and test_noise_model, which I'll shoot to cover tomorrow. This round's comments are all discretionary.
erichulburd
left a comment
There was a problem hiding this comment.
Largely looks good to me. I'm going to go through and react to my comments that I care more about with ❗ . Close everything out at your discretion and then I'll approve.
Correctness fixes, each with a regression test: - from_coherence_times: the T2 -> Tphi conversion omitted the factor of two on T1 (1/T2 = 1/(2*T1) + 1/Tphi), so the default t2 = 2*t1 produced a negative pure-dephasing time, NaN fidelities and a LinAlgError. Every physical T1 < T2 <= 2*T1 was affected. Fixed in Channel and ResetChannel, with a T2 <= 2*T1 guard. - to_stochastic_channel: hand-rolled Kronecker products used the opposite superoperator convention to quax (kron(conj(U), U)) and an inverse that is not the superoperator of any unitary, yielding a non-CP, non-TP channel with process fidelity 0.003 instead of 0.996. Now composed through quax. - pauli_twirl: twirled the full process, gate included, which deletes any gate whose transfer matrix is not already diagonal (RX(pi/2): 0.9925 -> 0.4975). Now twirls error_process and recomposes the gate, which for Clifford gates is exactly the standard twirl (1/4^n) sum_k P'_k^dag E P_k with P'_k = U P_k U^dag. - SuperopChannel.from_pauli_noise: an explicit identity key overrode the implicit normalization, breaking trace preservation. Now rejected. - Channel.__add__: raise on a gate_time mismatch instead of silently reinterpreting the other operand's per-unit-time rates. - SuperopResetChannel.from_reset_fidelity: the argument was applied as a depolarizing shrink factor, so requesting 0.9 gave 0.95. Now inverted through F = (1 + (d-1)p)/d so the channel reports back the requested fidelity. - CycleChannel.average_gate_fidelity: average gate fidelity does not factorize over a tensor product; convert once from the exact process-fidelity product. - Channel.__matmul__: no longer aliased to __add__. `@` is the exact superoperator composition (returning SuperopChannel); `+` is the generator-level noise sum. The two agree only for commuting generators. - from_random_coherent_error: solve the rotation angle numerically so the requested process fidelity is hit for multi-qubit gates too. - Raise instead of silently clipping out-of-range depolarizing constants, readout fidelities and asymmetries, and imaginary gate parameters. - Guard the qubit-only Pauli constructors and analyses against qudits with a message naming the operation, rather than a raw JAX shape error. - DefPermutationGate.num_args: read prime-power lengths like DefGate does. - from_isa: crashed on a complex ISA parameter; skipped perfect gate fidelities but not perfect readout fidelities. Review comments addressed: - Rename the fidelity properties to match quax: fidelity -> average_gate_fidelity, pauli_fidelity -> process_fidelity (and the infidelities). `fidelity` previously meant an average gate fidelity on gate channels but a process fidelity on reset channels. - Rename unitary -> ideal_unitary, and as_post_gate_noise() -> the error_process property, so the error-only object carries the error name. - CycleChannel: serialize the cycle instruction and DEFCIRCUIT so an externally-built cycle survives a round trip; add __or__ so `a | b | c` builds one flat cycle; validate by instruction equality rather than str(); return expanded_instructions as a tuple. - NoiseModel.from_isa now takes a qcs_sdk InstructionSetArchitecture; the rpcq CompilerISA path is from_compiler_isa, deprecated for pyQuil v5. - Replace NoiseModel's hand-written __init__ with __post_init__ validation. - Drop the @deprecated decorator from the legacy Kraus NoiseModel: it fired from pyQuil's own internals (get_qc(noisy=True)) while the replacement is still private. - Re-export the remaining private names from the old pyquil/noise.py. - TypeAlias on the union aliases, TypedDict for the packed JSON payloads, @Final on the concrete channels, _DEFAULT_GATE_TIME, _resolve_params -> _evaluate_parameter_designators, _as_channel -> _to_superop_channel, _qudit_dims -> _operator_dims_from_dimension. - Say "prime power" consistently in the DefGate dimension errors, and add qudit table tests for num_args and _integer_base_and_exponent. - Memoize DepolarizingNoiseModel's synthesized channels; resolve each gate unitary once per construction; use math.prod in estimate_program_fidelity; drop an unreachable serialization branch. - Fix the quax keyword in test_from_depolarizing_constant that was failing CI. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QB5EGDshZbFtBvyfPPQqU
Part of the effort to land the experimental noise model and simulation module (originally PR #1848, branch
nonbreaking-noise-model) as a reviewable stack. This is PR 2 of 5, stacked on #1858 — review/merge in order. Closes #1854.This PR introduces the new quax-backed qudit noise model under
pyquil/noise/:Channel,MeasurementChannel,ResetChannel, andCycleChannel(_channels.py), and the newNoiseModelcontainer plusDepolarizingNoiseModel/CompositeNoiseModeland fidelity estimators (_noise_model.py).pyquil.noisepublic API is preserved:pyquil/noise.pyis moved topyquil/noise/_legacy_noise.pyand re-exported frompyquil/noise/__init__.py, with the legacyNoiseModel/KrausModelnow marked deprecated in favor of the quax-based model.It also adds qudit
DefGatesupport inquilbase.py(matrix dimensions may now be any perfect power, e.g. 3, 9, …, not just powers of two), which the qutrit noise channels and later simulators rely on, and enables float64 (jax_enable_x64) for the test suite.A handful of
ResetChanneltests verify behavior end-to-end through the density-matrix simulator; since that simulator lands in the next PR, those specific assertions are deferred to #1855 (they are restored there). Everything else in the noise module is fully tested here (test_noise_model.py).Stack
Tracked in #1863.