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 | 9.66 s |
| test/benchmarks/test_program.py::test_instructions | 📈 view plot | 2.54 s |
| test/benchmarks/test_program.py::test_iteration | 📈 view plot | 2.55 s |
f1884bc to
414f175
Compare
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!
| if rows < 2: | ||
| return 0 | ||
| for base in range(2, rows + 1): | ||
| k = int(round(math.log(rows, base))) | ||
| if base**k == rows: | ||
| return k | ||
| return 1 |
There was a problem hiding this comment.
This might not matter much for the expected values of rows/reasonable values of len(self.matrix), but it is very slow for large numbers. As a much faster alternative, you can calculate it by iteratively checking if k, and you can reduce the problem to finding the solution for that new base, then multiply its factors by the one you've found.
Concretely:
def _int_n_root(x: int, n: int) -> int | None:
"""Return the largest integer less than or equal to n-th root of x."""
# This helper quickly checks if `x` is a perfect `n-th` root,
# and will do so without the numerical instability
# of the floating point check.
# There are faster algorithms for very large numbers,
# but this is already O(log(x)), and simple to implement.
low, high = 1, x
while low <= high:
mid = (low + high) // 2
if (mid_pow_n := mid ** n) == x:
return mid
elif mid_pow_n < x:
low = mid + 1
else:
high = mid - 1
return low - 1
SMALL_PRIMES = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61]
def find_k(r: int) -> tuple[int, int]:
"""For positive integer `r`, return integers `(b, k)` such that `r = b**k`
and `k` is the largest integer for any integer `b >= 1` that satisfies this relation.
If `r` is not a perfect power, this function returns `(r, 1)`.
"""
if r < 2:
return r, 1
# Check if `r` is a perfect p-th power of a small prime `p`.
# When it is, reduce `r` to that p-th root and build up the exponent `k`.
base, k = r, 1
primes = iter(SMALL_PRIMES)
p = next(primes, None)
while p:
if (b := _int_n_root(base, p)) < 2:
return base, k
elif b ** p != base:
p = next(primes, None)
else:
k *= p
base = b
raise ValueError("r is too large to efficiently find k")This approach is
There was a problem hiding this comment.
On further thought, this entire concept has a huge problem - it doesn't support quarts. Besides qubits and qutrits this the most likely qudit size. I think I'll need to add a option to specify the qudit dims
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
|
| For a matrix of dimension d^k, returns k where d is the smallest | ||
| integer base >= 2 such that rows = d^k. | ||
| """ | ||
| decomposition = _integer_base_and_exponent(len(self.matrix)) |
There was a problem hiding this comment.
This doesn't work if the exponent is composite.
| """ | ||
| decomposition = _integer_base_and_exponent(len(self.matrix)) | ||
| if decomposition is None: | ||
| return 0 |
There was a problem hiding this comment.
I believe this should only happen if n < 2 which means 0 qubits. But on further thought, this should probably just be an error.
| return None | ||
| # Find the smallest prime factor; it is the qudit dimension (base). | ||
| base = n | ||
| for p in range(2, int(math.isqrt(n)) + 1): |
There was a problem hiding this comment.
The whole point of isqrt is that it returns an integer, so there's no need to wrap it in int.
This method requires less work than the previous approach, but still does significantly more work than the one I proposed. Again, I'm not sure how large realistic values of n are, so maybe this doesn't matter much, my previous suggestion was based on that context, and this doesn't much address that concern.
| num_qubits = int(round(np.log2(hilbert_dim))) | ||
| hilbert_dim = round(float(np.sqrt(shape[0]))) if superoperator else int(shape[0]) | ||
| num_qubits = round(float(np.log2(hilbert_dim))) | ||
| if 2**num_qubits != hilbert_dim: |
There was a problem hiding this comment.
Why have you added float conversions?
There was a problem hiding this comment.
Thought it was a type error but maybe not.
|
I've done a substantial refactor to add support for Lindbladian channels. The way this works is as follows:
|
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.