-
Notifications
You must be signed in to change notification settings - Fork 36
$O(N^3)$ Pauli-flow finding algorithm #337
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #337 +/- ##
==========================================
+ Coverage 79.59% 79.63% +0.03%
==========================================
Files 40 41 +1
Lines 5892 5858 -34
==========================================
- Hits 4690 4665 -25
+ Misses 1202 1193 -9 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Give me some time for further reviews.
import numpy.typing as npt | ||
|
||
|
||
class MatGF2: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use inheritance if possible, as MatGF2
seems to be exactly the same as the implementation type (that of .data
).
for k in range(m): | ||
if mat_data[k, j] == 1 and k != p: | ||
for l in range(n): | ||
mat_data[k, l] ^= mat_data[p, l] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use numpy
's element-wise operation if possible (though for
is also efficient here due to the jit
decorator)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, numpy
element-wise operations are used everywhere but in the two jit
-decorated functions in linalg
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still work in progress. Please be patient!
meas_planes = self.get_meas_plane() | ||
flow, l_k = find_gflow(g, vin, vout, meas_planes=meas_planes) | ||
if not flow: | ||
if flow is None or l_k is None: # We check both to avoid typing issues with `get_layers`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MEMO: Let's return None
rather than tuple[None, None]
for better typing.
def __getitem__(self, key) -> MatGF2: | ||
"""Allow numpy-style slicing.""" | ||
return MatGF2(self.data.__getitem__(key)) | ||
|
||
def __setitem__(self, key, value: gt.ElementLike | gt.ArrayLike | MatGF2) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's annotate key
. I know it's very difficult but still we need it (or improve current design so that we don't have to annotate by ourselves).
iset: AbstractSet[int], | ||
oset: AbstractSet[int], | ||
meas_planes: Mapping[int, Plane], | ||
mode: str = "single", # noqa: ARG001 Compatibility with old API |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove it. I believe we need to prioritize simplicity over compatibility in v0 developments.
import galois | ||
import numpy as np | ||
import numpy.typing as npt | ||
import sympy as sp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's prune sympy
from the requirements.
) | ||
pf = _find_pflow(og) | ||
if pf is None: | ||
return None, None # This is to comply with old API. It will be change in the future to `None`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make it simple.
self.non_outputs_optim = deepcopy(self.non_outputs) | ||
|
||
|
||
def _get_reduced_adj(ogi: OpenGraphIndex) -> MatGF2: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be a method (many other occurrences)
self.non_outputs_optim = deepcopy(self.non_outputs) | ||
|
||
|
||
def _get_reduced_adj(ogi: OpenGraphIndex) -> MatGF2: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please do not use the word get
(and also set
) for ordinary functions. This is only allowed for getters.
""" | ||
adj_mat = ordering_matrix.data | ||
|
||
indegree_map = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General remark: could you annotate untyped literals, especially when you add something completely new? It greatly improves DX in the future as...
- we can readily detect type mismatches
- we are benefited by better intellisense
- it makes
pyright
outputs less noisy
This PR introduces the$O(N^3)$ Pauli flow-finding algorithm presented in [1] which improves the previous version with $O(N^5)$ complexity [2]. Since a Pauli flow in an open graph without Pauli measurements defines a gflow, this algorithm also improves the previous implementation of the gflow-finding algorithm with $O(N^4)$ complexity [3].
The implementation proposed here considers the suggestion in [1] to eliminate the rows that are identically 0 in the order-demand matrix to accelerate the flow-finding routine in open graphs with$n_I \neq n_O$ .
Finally, the new algorithm solves the issue discussed in qat-inria#17 , whereby the previous flow-finding functions could return a correction function containing input nodes in the codomain, which is not allowed by the definition of gflow or Pauli flow.
In summary:
graphix.find_pflow
, whilegraphix.gflow
and related modules are adapted accordingly to avoid modifying the current API in this PR.linalg.py
has been updated with more efficient functions for gaussian elimination, back substitution, kernel-finding and right-inverse. New implementation isnumba
. The dependence onsimpy
is dropped. Tests are simplified and updated accordingly, and module is now typed.pytest-benchmark
is incorporated to allow for easier benchmarking between different commits (courtesy of @thierry-martinez).Additional comments
graphix.gflow
is currently functioning but rather messy. We leave its restructuration for a future PR to simply the review process.References
[1] Mitosek and Backens, 2024 (arXiv:2410.23439)
[2] Simmons, 2021 (arXiv:2109.05654)
[3] Backens et al., Quantum 5, 421 (2021)