Fix #181: Structural separation of Pauli measurements#423
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #423 +/- ##
==========================================
+ Coverage 86.72% 88.73% +2.00%
==========================================
Files 44 44
Lines 6163 6303 +140
==========================================
+ Hits 5345 5593 +248
+ Misses 818 710 -108 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
2ecdeed to
8945ed4
Compare
This commit introduces a structural separation between Pauli measurements and arbitrary measurements. Technically, the class `Measurement` is now abstract and has two concrete subclasses: `PauliMeasurement` and `BlochMeasurement`. In patterns, `M` commands are now parameterized by an instance `Measurement` (instead of carrying a plane and an angle). With these changes, an M command on an arbitrary measurement is written in the following form: ```python M(node, BlochMeasurement(angle, plane), s_domain, t_domain) ``` and an M command on a Pauli measurement is written as: ```python M(node, PauliMeasurement(axis, sign), s_domain, t_domain) ``` There are convenient notations for common cases: - for an arbitrary measurement on a statically known plane, the user can use `Measurement.XY(angle)`, `Measurement.YZ(angle)`, or `Measurement.XZ(angle)`; - for a positive Pauli measurement on a statically known axis, the user can use `Measurement.X`, Measurement.Y`, `Measurement.Z`; - for a negative Pauli measurement on a statically known axis, the user can use `-Measurement.X`, -Measurement.Y`, `-Measurement.Z`. By default, `M(node)` is `M(node, Measurement.X)`. There is no implicit conversion between Pauli measurements represented as `PauliMeasurement` and measurements represented as `BlochMeasurement`. `Measurement` class provides methods to perform these conversions explicitly: - `to_bloch()` returns a `BlochMeasurement` equivalent to the given measurement; note that there are multiple possible Bloch descriptions for the same Pauli measurement. `to_bloch()` uses `Plane.XY` for `X` and `Y` and `Plane.YZ` for `Z`. - `try_to_pauli()` returns a `PauliMeasurement` equivalent to the given measurement if it exists (up to a precision error specified by optional parameters `rel_tol` and `abs_tol`), or `None` otherwise. - `to_pauli_or_bloch()` is similar to `try_to_pauli()`, but returns the original measurement instead of `None` in the non-Pauli case. There are conversion functions at the level of `Pattern`, `StandardizedPattern`, and `OpenGraph` to convert all measurements: - `to_bloch()` converts all measurements to `BlochMeasurement`; - `infer_pauli_measurements()` converts all measurements that are Pauli (up to a precision error) into `PauliMeasurement`. In particular, Pauli presimulation, flow extraction algorithms and visualization routines do not perform any implicit conversion: - Pauli presimulation only applies on `M` commands carrying a `PauliMeasurement`; - causal flows and gflows may only exist if all measurements are `BlochMeasurement`; - Pauli flows considers a measurement to be a Pauli measurement only if it carries a `PauliMeasurement`; - visualization routines now take an `OpenGraph` directly instead of taking planes and angles separately, and displayed Pauli nodes and labels reflect whether the measurement is a `BlochMeasurement` or a `PauliMeasurement`. In particular, since there is no Pauli-flow extraction from pattern, visualization of flows extracted from patterns only work if all measurements are `BlochMeasurement` (and no Pauli nodes will be displayed).
8945ed4 to
b2a106e
Compare
shinich1
left a comment
There was a problem hiding this comment.
Thank you, the overall idea looks great. I feel that a few more tests could be added to test_measurements to ensure that the new feature (new class, conversion to bloch/pauli, etc) will work well in the future.
|
I added more documentation in |
emlynsg
left a comment
There was a problem hiding this comment.
Left a few comments. We can discuss at the next PR meeting.
There was a problem hiding this comment.
Thanks a lot for this great PR @thierry-martinez . Since it already has 2 approvals, I didn't spend much time to avoid blocking it.
I made two minor comments, and I have a question: since now OpenGraph.find_pauli_flow doesn't do any implicit interpretation of measurements with Pauli angle as PauliMeasurements, I think the method PauliFlow.node_measurement_label is redundant at best (I'd argue it's misleading). Doing PauliFlow.og.measurements[node] should be the unambiguous way to retrieve the measurement of a given node from a flow object. If you agree, I'd remove that method from the flow class.
Edit: in fact, since now BlochMeasurement implements the method to_plane_or_axis as return self.plane (implementation inherited from AbstractPlanarMeasurement), do we need to make the distinction between AlgebraicOpenGraph and PlanarAlgebraicOpenGraph at all (in _find_gpflow.py)? I would say that we don't have to, but maybe I'm missing something. We can discuss it in person.
|
I'll try to nuance what I was saying about the distinction between Consider the following example: import networkx as nx
from graphix.opengraph import OpenGraph
from graphix.measurements import Measurement
graph = nx.Graph([(0, 1), (1, 2)])
measurements = {0: Measurement.XY(0.5), 1: Measurement.XY(0.5)}
og = OpenGraph(graph, [0], [2], measurements)
print(og.extract_gflow()) # No error
print(og.extract_pauli_flow()) # No errorThe only difference in the gflow and Pauli flow calculation is that in the former we initialize a print(og.measurements[0].to_plane_or_axis()) # Plane.XY
Plane.XY
print(og.measurements[0].to_plane()) # Plane.XY
Plane.XYreturn the same. This is why I suggest discussing if it's worth removing the In the current form, the following code yields an error: import networkx as nx
from graphix.opengraph import OpenGraph
from graphix.measurements import Measurement
graph = nx.Graph([(0, 1), (1, 2)])
measurements = {0: Measurement.XY(0.5), 1: Measurement.XY(0.5)}
og = OpenGraph(graph, [0], [2], measurements).infer_pauli_measurements()
print(og.extract_gflow()) # Error
print(og.extract_pauli_flow())because If we removed the class The issue occurs when the type checker cannot infer the type variable of the open graph, e.g., import networkx as nx
from graphix.opengraph import OpenGraph
from graphix.measurements import Measurement
graph = nx.Graph([(0, 1), (1, 2)])
measurements = {0: Measurement.X, 1: Measurement.XY(0.5)}
og = OpenGraph(graph, [0], [2], measurements).infer_pauli_measurements()
print(og.extract_gflow())
print(og.extract_pauli_flow())Here the type-checker doesn't warn us about the misuse of TLDR We can leave the PR as is, since everything works as it should, I was just worried that the previous architecture was too complex in the new framework where we don't implicitly convert planar measurement into Pauli measurements. |
This commit introduces a structural separation between Pauli measurements and arbitrary measurements. Technically, the class
Measurementis now abstract and has two concrete subclasses:PauliMeasurementandBlochMeasurement. In patterns,Mcommands are now parameterized by an instanceMeasurement(instead of carrying a plane and an angle).With these changes, an M command on an arbitrary measurement is written in the following form:
and an M command on a Pauli measurement is written as:
There are convenient notations for common cases:
Measurement.XY(angle),Measurement.YZ(angle), orMeasurement.XZ(angle);Measurement.X,Measurement.Y,Measurement.Z;-Measurement.X,-Measurement.Y,-Measurement.Z.By default,
M(node)isM(node, Measurement.X).There is no implicit conversion between Pauli measurements represented as
PauliMeasurementand measurements represented asBlochMeasurement.Measurementclass provides methods to perform these conversions explicitly:to_bloch()returns aBlochMeasurementequivalent to the given measurement; note that there are multiple possible Bloch descriptions for the same Pauli measurement.to_bloch()usesPlane.XYforXandYandPlane.YZforZ.try_to_pauli()returns aPauliMeasurementequivalent to the given measurement if it exists (up to a precision error specified by optional parametersrel_tolandabs_tol), orNoneotherwise.to_pauli_or_bloch()is similar totry_to_pauli(), but returns the original measurement instead ofNonein the non-Pauli case.There are conversion functions at the level of
Pattern,StandardizedPattern, andOpenGraphto convert all measurements:to_bloch()converts all measurements toBlochMeasurement;infer_pauli_measurements()converts all measurements that are Pauli (up to a precision error) intoPauliMeasurement.In particular, Pauli presimulation, flow extraction algorithms and visualization routines do not perform any implicit conversion:
Pauli presimulation only applies on
Mcommands carrying aPauliMeasurement;causal flows and gflows may only exist if all measurements are
BlochMeasurement;Pauli flows considers a measurement to be a Pauli measurement only if it carries a
PauliMeasurement;visualization routines now take an
OpenGraphdirectly instead of taking planes and angles separately, and displayed Pauli nodes and labels reflect whether the measurement is aBlochMeasurementor aPauliMeasurement. In particular, since there is no Pauli-flow extraction from pattern, visualization of flows extracted from patterns only work if all measurements areBlochMeasurement(and no Pauli nodes will be displayed).