-
Notifications
You must be signed in to change notification settings - Fork 37
Refactor of flow tools - Part I #358
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 #358 +/- ##
==========================================
+ Coverage 79.68% 80.40% +0.71%
==========================================
Files 42 44 +2
Lines 5854 6251 +397
==========================================
+ Hits 4665 5026 +361
- Misses 1189 1225 +36 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
|
||
| from graphix.pattern import Pattern | ||
|
|
||
| # TODO: Maybe move these definitions to graphix.fundamentals and graphix.measurements ? Now they are redefined in graphix.flow._find_gpflow, not very elegant. |
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.
Agree these definitions would make more sense in those files rather than in opengraph.py
emlynsg
left a comment
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.
I have reviewed the PR.
Two points to make before approving:
- Some of the renamed attributes (e.g. in OpenGraph) will break existing external code referencing them (e.g. inside to obtain the
nxgraph), but I don't think it's a big problem. - It would be good for my understanding to discuss the differences between how we have written the cflow algorithm and what is in Mhalla & Perdrix (2008), as we seem to have made some subtle changes.
I'm not too concerned given the tests, so if someone else is able to verify the flow parts of the PR, I am happy to approve it.
|
I noticed that |
Thanks for the review @emlynsg. About the renaming I think that we don't guarantee backwards compatibility, but I'm open to discuss it. The rationale is as follows:
About the causal flow algorithm: indeed the implementation seems to be more succinct than the algorithm in the original paper, but this was the case before the refactor. I think I convinced myself that they are equivalent, we can of course discuss it in person. |
Summary
This is the first of a series of PRs refactoring the flow tools in the library.
In the current implementation, the transformation "Open graph"$\rightarrow$ "Flow" $\rightarrow$ "XZ-corrections" $\rightarrow$ "Pattern" occurs under the hood in the
:func: OpenGraph.to_pattern()method. This refactor aims at exposing the intermediate objects to offer users more control on pattern extraction and ease the analysis of flow objects.Related issues: #120, #306, #196, #276, #181
File structure
graphix.opengraph.pyintroduces the newOpenGraphobjects. The associated filetests.test_opengraph.pycontains the bulk of the unit tests verifying the correctness of the flow-finding algorithms (these tests have been refactor fromtests.test_gflow.pyandtests.test_find_pflow).graphix.flow.core.pycontains all the flow-related functionalities, except the flow-finding algorithms which are placed ingraphix.flow._find_cflow.pyandgraphix.flow._find_gpflow.pyThe existing files
graphix.gflow.py,graphix.find_pflow.pywill be eliminated once the refactor is completed. Their modification consists of just minor changes to comply with the new interface ofOpenGraph. The modulegraphix.patternstill depends on the old flow implementation.Refactor overview
Public API
XZCorrectionscontains anOpenGraph.Examples
Define an open graph without measurement angles and extract a gflow
Define an open graph with measurement angles and extract flow, corrections and pattern
Attributes of the objects in the public API
OpenGraph (frozen dataclass)
graph: nx.Graph[int]input_nodes: Sequence[int]output_nodes: Sequence[int]measurements: Mapping[int, _M_co]PauliFlow (frozen dataclass)
og: OpenGraph[_M_co]correction_function: Mapping[int, AbstractSet[int]]partial_order_layers: Sequence[AbstractSet[int]]GFlow, CausalFlow
Same as PauliFlow but with
og: OpenGraph[_PM_co]XZCorrections (frozen dataclass)
og: OpenGraph[_M_co]x_corrections: Mapping[int, AbstractSet[int]]z_corrections: Mapping[int, AbstractSet[int]]partial_order_layers: Sequence[AcstractSet[int]]All the new classes inherit from
Generic[_M_co]. We introduce the following parametric types:_M_co = TypeVar("_M_co", bound=AbstractMeasurement, covariant=True)_PM_co = TypeVar("_PM_co", bound=AbstractPlanarMeasurement, covariant=True)Q&A attributes
Why do
PauliFlow, its children andXZCorrectionshave apartial_order_layersattribute ?In Ref. [1], it's noted that the correction function and the partial order of a flow are not independent, but rather, that the former determines the latter:
"In the problem of finding Pauli flow, instead of looking for correction function and partial order, it now suffices to look only for a (focused) extensive correction function. [...] Given a correction function$c$ we can define a minimal relation induced by the Pauli flow definition, and consider whether this relation extends to a partial order."
This observation suggests that flow objects should not have a "partial order" attribute, but instead, that it should be computed from the correction function when needed, e.g., when computing the$X,Z$ -corrections for a Pauli flow. However, this operation is costly and, crucially, the partial order is a byproduct of the flow-finding algorithms. Therefore, it is reasonable to store it as an attribute at initialization time instead.
We store in the form of layers owing to the following reasons:
Nevertheless, we endow the flow objects with class methods allowing to initialize them directly from a correction matrix or a correction function.
Similarly, the mappings
XZCorrections.x_correctionsandXZCorrections.z_correctionsdefine a partial order which is necessary to extract a pattern (specifically, the input "total order" has to be compatible with the intrinsic partial order of the XZ-corrections). Computing this partial order is expensive (it amounts to extracting a DAG and doing a topological sort as well), therefore we also store it as an attribute. Further, when theXZCorrectionsinstance is derived from a flow object, the flow's partial order is a valid partial order for the XZ-corrections, so it is useful to pass it as a parameter to the dataclass constructor.We also introduce an static method allowing to initialize
XZCorrectionsinstances directly from thex_correctionsand thez_correctionsmappings.Why do we treat open graphs as parametric objects ?
XZCorrectionsobject) requires information on the measurement angles. Treating the angles list as an object independent of the open graph demands a rather cumbersome bookkeeping (e.g., angles must be defined w.r.t. measured nodes, for Pauli axes the notion of angle is replaced by aSign, etc.). At the same time, certain operations on an open graph (namely, trying to find flows), do not need information on the angles. Therefore, it should be possible to have "plane/axis-open graphs" and "measurement-open graphs", respectively without and with information about the angles. The static type-checker should only allow "to_pattern" conversions on open graphs with angle information.We propose to achieve this by implementing a "type superstructure" on top of the existing classes
Axis,PlaneandMeasurementto avoid modifying the existing functionalities unrelated to flows. We detail this next.Measurement types
flowchart TD n(**ABC**) n0("**AbstractMeasurement**") n1("**AbstractPlanarMeasurement**") n2("**Enum**") n3("**Plane**") n4("**Axis**") n5("**Measurement** (dataclass)") n .-> n0 n0 .-> n1 n0 .-> n4 n1 .-> n5 n2 .-> n3 n2 .-> n4 n1 .-> n3This type structure should allow the following:
OpenGraph[Axis|Plane]we can look for Pauli flow only (enforced statically).OpenGraph[Plane]we can look for causal, g- and Pauli flow.OpenGraph[Measurement]we can look for causal, g- and Pauli flow.To that purpose, we introduce the following abstract class methods:
:func: AbstractMeasurement.to_plane_or_axis:func: AbstractPlanarMeasurement.to_planeFurther, a conversion to
Patternis only possible fromOpenGraph[Measurement]andXZCorrections[Measurement]objects (which have information on the measurement angles).Methods implemented in this PR
(See docstrings for further details)
OpenGraphfrom_pattern(static method)to_patternneighborsodd_neighborsfind_causal_flowfind_gflowfind_pauli_flowPauliFlow(and children)from_correction_matrix(class method)to_correctionsXZCorrectionsfrom_measured_nodes_mapping(static method)to_patterngenerate_total_measurement_orderextract_dagis_compatibleComments
The old function
graphix.generator._pflow2pattern,graphix.generator._gflow2patternandgraphix.generator._flow2patternare respectively implemented inPauliFlow/GFlow/CausalFlow.to_correctionsandXZCorrections.to_pattern.The method
OpenGraph.to_patternprovides an immediate way to extract a deterministic pattern on a given resource open graph state in similar way to the old API: it attempts to compute a flow (first causal, then Pauli) and then uses the recipe in Ref. [3] to write the pattern.However, the new API also allows the following:
Finding flows of an open graph
We provide here additional details on the interface of the
OpenGraphobject with the flow-finding algorithms.Causal flow
flowchart TD n0["OpenGraph.find_causal_flow()"] n1["find_cflow(self)"] n2("None") n3("CausalFlow") n0 --> n1 n1 --> n2 n1 --> n3 style n2 stroke:#A6280A, stroke-width:4px style n3 stroke:#0AA643, stroke-width:4pxThe function$O(N^2)$ complexity in Ref. [2]
graphix.flow._find_cflow.find_cflowimplements the layer-by-layer algoritm withGflow and Pauli flow
--- config: layout: elk --- flowchart TD n0["OpenGraph.find_gflow()"] n00["PlanarAlgebraicOpenGraph(self)"] n1["compute_correction_matrix(aog)"] n3["GFlow.from_correction_matrix(correction_matrix)"] n9("GFlow") n2("None") nn0["OpenGraph.find_pauli_flow()"] nn00["AlgebraicOpenGraph(self)"] nn1["compute_correction_matrix(aog)"] nn3["PauliFlow.from_correction_matrix(correction_matrix)"] nn9("PauliFlow") nn2("None") n0 --> n00 n00 --"aog"--> n1 n1 --> n2 n1 -- "correction_matrix"--> n3 n3 --> n9 n3 --> n2 nn0 --> nn00 nn00 --"aog"--> nn1 nn1 --> nn2 nn1 -- "correction_matrix"--> nn3 nn3 --> nn9 nn3 --> nn2 style n2 stroke:#A6280A, stroke-width:4px style n9 stroke:#0AA643, stroke-width:4px style nn2 stroke:#A6280A, stroke-width:4px style nn9 stroke:#0AA643, stroke-width:4pxThe function
graphix.flow._find_gpflow.compute_correction_matrixperforms the first part of the algebraic flow-finding algorithm in Ref. [1]. The second part (i.e., verifying that a partial order compatible with the correction matrix exists) is done by the class method.from_correction_matrix:--- config: layout: elk --- flowchart TD n3[".from_correction_matrix(correction_matrix)"] n7["compute_partial_order_layers(correction_matrix)"] n5["correction_matrix.to_correction_function()"] n2("None") n9("cls( aog.og, correction_function, partial_order_layers)") n3 --> n5 n3 --> n7 n7 -- "partial_order_layers" --> n9 n5 -- "correction_function" --> n9 n7 --> n2 style n2 stroke:#A6280A, stroke-width:4px style n9 stroke:#0AA643, stroke-width:4pxDetails on the interface with the algebraic flow-finding algorithm
We introduce two parametric dataclasses:
flowchart TD n0("**AlgebraicOpenGraph[_M_co]**") n1("**PlanarAlgebraicOpenGraph[_PM_co]**") n0 --> n1 linkStyle 0 stroke: #276cf5ffwhere the parametric type variable
_M_coand_PM_cowhere defined above.AlgebraicOpenGraphis a dataclass to manage the mapping between the open graph nodes and the row and column indices of the matrices involved in the algebraic flow-finding algorithm. This class replaces the classOpenGraphIndexintroduced in PR 337.The class$M$ and the order-demand $N$ matrices by reading out the measurement planes (or axes) of the open graph. In particular,
PlanarAlgebraicOpenGraphonly rewrites the method_compute_og_matriceswhich calculates the flow-demandAlgebraicOpenGraph._compute_og_matricescallsself.og.measurements[node].to_plane_or_axiswhich will intepret measurements with a Pauli angle as axes.This is the adequate behavior when we seek to compute a Pauli flow.
PlanarAlgebraicOpenGraph._compute_og_matricescallsself.og.measurements[node].to_planewhich will intepret measurements with a Pauli angle as planes.This is the adequate behavior when we seek to compute a gflow or a causal flow.
Feautures to be included in the next PR of this series
Methods to verify the correctness of flow and XZ-corrections objects:
PauliFlow.is_well_formedGFlow.is_well_formedCausalFlow.is_well_formedXZCorrections.is_well_formedAdapt exisiting methods in the class
OpenGraph:OpenGraph.composeOpenGraph.is_closeMethods to replace parametric angles with float values in
PauliFlow, its children andXZCorrections.Method to extract
XZCorrectionsfrom aPatterninstance.Class method
.from_correction_functionforPauliFlowand its children.Method to a extract a
GFlowfrom aPatterninstance. This requires analysis of the existing algorithms in the codebase.Re-introduce benchmark tests on flow algorithms (formerly in
tests.test_find_pflow.py.Visualization features (optional)
References
[1] Mitosek and Backens, 2024 (arXiv:2410.23439).
[2] Mhalla and Perdrix, (2008), Finding Optimal Flows Efficiently, doi.org/10.1007/978-3-540-70575-8_70
[3] Browne et al., 2007 New J. Phys. 9 250 (arXiv:quant-ph/0702212).