Refactor of flow tools - Part I#358
Conversation
|
LGTM! Thanks! |
|
Hi @thierry-martinez, I've being thinking and I'm not fully convinced by 0c21256 By introducing the new functions In addition, these new functions lead to a circular import structure (which, if not a red boundary on its own, can be symptomatic of bad design). At the same time, as discussed above, having functions which return one type only is very handy for chaining method calls without getting static-typing errors. Should we add two methods to (Sorry to bring this up after your pass!) |
|
I think that having both |
shinich1
left a comment
There was a problem hiding this comment.
Shouldn't we add/update examples? otherwise, LGTM.
Thanks for reviewing! Yes, we definitely should, but I propose that adding tutorials/documentation beyond the docstrings is done in separate PRs for conciseness. |
understood! |
This commit adapts the existing method `graphix.opengraph.OpenGraph.isclose` to the new API introduced in #358. Additionally, it introduces the new methods `graphix.opengraph.OpenGraph.is_equal_structurally` which compares the underlying structure of two open graphs, and `graphix.fundamentals.AbstractMeasurement.isclose` which defaults to `==` comparison.
This commit adapts the existing method `:func: OpenGraph.compose` to the new API introduced in #358.
This commit adapts the existing method `graphix.opengraph.OpenGraph.isclose` to the new API introduced in #358. Additionally, it introduces the new methods `graphix.opengraph.OpenGraph.is_equal_structurally` which compares the underlying structure of two open graphs, and `graphix.fundamentals.AbstractMeasurement.isclose` which defaults to `==` comparison.
This commit adapts the existing method `:func: OpenGraph.compose` to the new API introduced in #358.
This commit adapts the existing method `graphix.opengraph.OpenGraph.isclose` to the new API introduced in #358. Additionally, it introduces the new methods `graphix.opengraph.OpenGraph.is_equal_structurally` which compares the underlying structure of two open graphs, and `graphix.fundamentals.AbstractMeasurement.isclose` which defaults to `==` comparison.
This commit adapts the existing method `:func: OpenGraph.compose` to the new API introduced in #358.
This commit adapts the existing method `graphix.opengraph.OpenGraph.isclose` to the new API introduced in #358. Additionally, it introduces the new methods `graphix.opengraph.OpenGraph.is_equal_structurally` which compares the underlying structure of two open graphs, and `graphix.fundamentals.AbstractMeasurement.isclose` which defaults to `==` comparison.
This commit adapts the existing method `:func: OpenGraph.compose` to the new API introduced in #358.
* trying to remove BackendState still causing problems * trying to remove BackendState bad fixed version * Fixing typing. * Fixed typing after removing backend state. * Introduce `BaseN` and `PrepareMethod` (#383) This commit introduces the classes `BaseN` and `PrepareMethod`, which allow the user to customize how `N` commands are handled. For instance, in the VBQC context of Veriphix, `N` commands do not necessarily prepare the node in the |+> state: for each prepared qubit, the preparation should be performed by the client, so that the server does not even see the state. * trying to remove BackendState still causing problems * Refactor of flow tools - `OpenGraph.isclose` (#374) This commit adapts the existing method `graphix.opengraph.OpenGraph.isclose` to the new API introduced in #358. Additionally, it introduces the new methods `graphix.opengraph.OpenGraph.is_equal_structurally` which compares the underlying structure of two open graphs, and `graphix.fundamentals.AbstractMeasurement.isclose` which defaults to `==` comparison. * Refactor of flow tools - `OpenGraph.compose` (#375) This commit adapts the existing method `:func: OpenGraph.compose` to the new API introduced in #358. * Fix #349: ensure flow for patterns transpiled from circuit (#362) This commit fixes domains in the transpiler so that every pattern transpiled from a circuit has a flow. Previously, some domains were incompatible with any flow, despite the patterns being deterministic, because some measurement angles were zero, causing the measurements to ignore certain signals. This commit also adds `optimization.remove_useless_domains` to remove the domains ignored by measurements with angle zero, to recover the same "optimized" patterns as before when needed. This commit also adds `rand_state_vector` to draw a random state vector. * Fixing typing. * Refactor of flow tools - Verification of flow objects (#378) This PR introduces the methods `:func: PauliFlow.check_well_formed`, `:func: GFlow.check_well_formed` and `:func: CausalFlow.check_well_formed` which verify the correctness of flow objects and raise exceptions when the flow is incorrect. Exception classes are grouped in the new module `graphix.flow.exceptions`. The error messages explain which proposition in the flow is violated. Flow-finding algorithms always (in principle) output well-formed flows, but it is possible to instantiate flow objects by passing arbitrary parameters to the constructors. In such cases, there is not any guarantee that the flow objects are well formed. The methods introduced here can be useful for debugging or researching. The exception handling adapts the pattern introduced in #364. The new methods subsume `:func: gflow.verify_flow`, `:func: gflow.verify_gflow`, `:func: gflow.verify_pauli_flow`. Additionally, this PR introduces the methods `:func: PauliFlow.get_measurement_label`, `:func: GFlow.get_measurement_label` which return the measurement label of a given node. They follow same criteria employed in the flow-finding algorithms, that is, querying this method on a node with a measurement `Measurement(0, Plane.XY)` will return `Plane.XY` in a `GFlow` or `CausalFlow` and `Axis.X` in a `PauliFlow`. **Additional information on the exception management** ```mermaid --- config: layout: elk elk: mergeEdges: false nodePlacementStrategy: LINEAR_SEGMENTS --- flowchart TD a0(**Exception**) n0(**FlowError**) n1(FlowGenericError) n2(FlowPropositionError) n3(FlowPropositionOrderError) n4(PartialOrderError) n5(PartialOrderLayerError) a0 --> n0 n0 --FGEReason--> n1 n0 --FPEReason--> n2 n0 --FPOEReason--> n3 n0 --POEReason--> n4 n0 --POLEReason--> n5 ``` - Arrows indicate inheritance (from parent to child). - "Reasons" (`FlowPropositionErrorReason`, `PartialOrderLayerErrorReason`, etc.) are `Enum` classes. - Error subclasses: - `FlowPropositionError` - Violations of the flow-definition propositions which concern the correction function only (C0, C1, G1, G3, G4, G5, P4, P5, P6, P7, P8, P9). - Additional parameters: - `node` - `correction_set` - `FlowPropositionOrderError` - Violations of the flow-definition propositions which concern the correction function and the partial order (C2, C3, G1, G2, P1, P2, P3). - Additional parameters: - `node` - `correction_set` - `past_and_present_nodes` - `FlowGenericError` - General errors in the flow correction function, XY planes in causal flow. - Does not require additional parameters. - `PartialOrderError` - General flow and XZ-corrections errors in the partial order. - Does not require additional parameters. - `PartialOrderLayerError` - Flow and XZ-corrections errors concerning a specific layer of the partial order. - Additional parameters: - `layer_index` - `layer` * Fixed typing after removing backend state. * trying to rebase * fixed rebasing and conflicts * fixed changelog and vscode * final fix of changelog * aligning changelog * Ruff fixes * added change to CHANGELOG * Fixed typing of str and Backend functions in and * Final typing fix * Remove Any import. * Pushing again to check CI. * Disabled qasm parser CI tests causing Graphix CI to fail. * Added qasm test back in. * Fixed merge from master. * Fixed merge from master. * fixing added code * fixing CI * fixing imports * fixing typing in tests * fixing typing in tests * fixed test typing * fixed test typing * fixed test typing * fixed test typing * fixing merge problems * fixing import issue --------- Co-authored-by: Emlyn Graham <egraham@PTB-02009984.paris.inria.fr> Co-authored-by: thierry-martinez <thierry.martinez@inria.fr> Co-authored-by: matulni <m.uldemolins@gmail.com>
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).