Make PauliString efficient via a symplectic bit representation (#47) [unitaryHACK]#67
Make PauliString efficient via a symplectic bit representation (#47) [unitaryHACK]#67mk0dz wants to merge 1 commit into
Conversation
Store each Pauli string as two Python ints (x, z bit vectors) instead of a dict, turning __mul__, anticommutes, collapse_by and hashing into single big-integer bitwise ops. Preserve the full public API. Make PauliString immutable (slots + identity copy) to avoid deepcopy churn. Separately, cache BoundaryStabilizer.after_collapse (an immutable property recomputing the hot collapse_by on every access) - the largest single win. Benchmark (d=5 rotated surface code, 100 rounds): annotate_detectors_ automatically 96.8 ms -> 41.0 ms (2.36x). pytest/mypy/ruff all green.
|
Hey, #63 exists and has already been through several rounds of review. Because the hackathon is close to an end, we might not have the time to iterate on this PR. I'll try to provide you feedback on it anyway, but know that there is a high chance that #63 is merged instead, because I am reasonably certain it is correct after 2 rounds of review. In that case, my review will likely focus on the differences between this PR and #63 to find where the potential additional performance is gained in this PR. |
|
Thanks @nelimee, that's completely fair — #63 being correct after two review There are two largely independent changes here:
Benchmark (annotate_detectors_automatically, d=5 rotated surface code, 100 rounds,
If #63 merges, I'm happy to rebase this on top of it and open a smaller follow-up |
|
Thanks for understanding! I'll let you know when I can focus on this PR. Expect around the end of this week, or maybe next week, so don't worry if you do not hear back in the next few days. |
you find my implementation worthy enough to use in your core codebase , thats actually a win for me , otherwise #63 is a clear winner because @Danop404 actually worked hard on it. |
/claim #47
The ask
PauliStringis dict-based (dict[int, "X"/"Y"/"Z"]); the hot methods(
anticommutes,collapse_by,__mul__,__hash__,to_int) do per-qubitdictionary lookups, dominating
annotate_detectors_automaticallyon a rotatedsurface code. Reimplement it (stim/numpy/int bit-array) to reduce benchmark runtime.
What I changed
1.
pauli.py— symplectic (x, z) bit-integer representationEach Pauli string is two non-negative Python ints
_x,_z; bitqencodes theX / Z component at qubit
q((0,0)=I, (1,0)=X, (0,1)=Z, (1,1)=Y). The hotoperations become single big-integer bitwise ops:
__mul__(_x ^ _x', _z ^ _z')anticommutes((_x & _z') ^ (_z & _x')).bit_count() & 1collapse_by&= ~(op._x | op._z)__hash__/__eq__(_x, _z)The anticommutation identity was verified per-Pauli (X·Z, X·Y, Y·Z = 1; I·*, equal
= 0) and is exact because GF(2) parity is linear. Full public API preserved
(
.qubits,.qubit,[q],to_int,to_stim_pauli_string,from_stim_pauli_string,contains,overlaps,after, …).from_stim_pauli_stringusesstim'sto_numpy(). Added__slots__and immutable__copy__/__deepcopy__(returnself) — safe because the object is now frozen — which removes object churn fromthe
deepcopyof flow structures inmatch.py.2.
boundary.py— cacheafter_collapse(the next bottleneck)Profiling showed
BoundaryStabilizer.after_collapse(which runscollapse_by)was recomputed on every access, and it's accessed several times per stabilizer.
A
BoundaryStabilizeris immutable (its stabilizer + frozenset of collapsingoperations are fixed at construction), so the property is now
@cached_property.This was the single biggest win (the issue invites finding the "next bottleneck").
Results
after_collapseAll checks green:
pytest115 passed,mypyclean (19 files),ruffclean.Remaining hotspots (noted for the PR as future work):
PauliString.to_intin theGF(2) cover search, and the
deepcopyofFragmentFlowsinmatch.py.Files changed (PR contents)
(
bench_pauli.pyis kept out of the PR; the issue provides its own benchmark.)