Add Clifford decoration on output nodes of OpenGraph#510
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #510 +/- ##
==========================================
+ Coverage 88.84% 88.85% +0.01%
==========================================
Files 49 49
Lines 7127 7135 +8
==========================================
+ Hits 6332 6340 +8
Misses 795 795 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
thierry-martinez
left a comment
There was a problem hiding this comment.
Looks nice! I've some minor comments.
| # To comply with mypy, we could define a runtime-checkable Protocol: | ||
| # | ||
| # from typing import Protocol, Self, runtime_checkable | ||
| # @runtime_checkable | ||
| # class HasClifford(Protocol): | ||
| # def clifford(self, clifford_gate: Clifford) -> Self: ... | ||
| # | ||
| # if not isinstance(um, HasClifford): | ||
| # raise OpenGraphError("...") | ||
| # measurements[u] = um.clifford(vc) | ||
| # | ||
| # This informs mypy that `um` has the `clifford` attribute | ||
| # without narrowing the type of `um` so we can still assign it to | ||
| # measurements: dict[int, _AM_co] | ||
| # However, `isinstance` with protocols is disadvised since it can decrease | ||
| # performance significantly. | ||
| # https://typing.python.org/en/latest/reference/protocols.html |
There was a problem hiding this comment.
I propose to implement the clifford method on all AbstractMeasurement instead: thierry-martinez@67b0ab1
There was a problem hiding this comment.
Thanks for the suggestion. Much cleaner and general now
| """ | ||
| lc = self.extract_clifford() if show_local_clifford else None | ||
| options.setdefault("local_clifford", lc) | ||
| local_clifford_map = self.extract_clifford() if options.get("local_clifford") else None |
There was a problem hiding this comment.
I believe it is better to rely on the Clifford map stored in the extracted open graph.
pranav97nair
left a comment
There was a problem hiding this comment.
Nice work, Mateo! I just made a very minor comment.
| if vm is not None and um is not None and not vm.isclose(um): | ||
| raise OpenGraphError(f"Cannot merge nodes with different measurements: {v, vm} -> {u, um}.") | ||
|
|
||
| shift = max(*self.graph.nodes, *mapping.values()) + 1 | ||
| # Apply other's output Cliffords onto self's measurement | ||
| if um is not None and (vc := other.output_cliffords.get(v)) is not None: | ||
| measurements[u] = um.clifford(vc) | ||
|
|
||
| # Apply self's output Cliffords onto other's measurement | ||
| if vm is not None and (uc := self.output_cliffords.get(u)) is not None: |
There was a problem hiding this comment.
Is there a particular reason you specify is not None instead of just saying if vm and um...? If not I think it would be better to do the latter for improved readability.
There was a problem hiding this comment.
Indeed we could do that, but I'm not sure if it improves readability.
When we test the truthiness of an object x, the Python interpreter checks for the x.__bool__ and the x.__len__ methods. When they are not implemented (as it is the case for AbstractMeasurement and children), it always assigns a truthy value so your suggestion would work.
However, someone reading the code would need to know or assume that AbstractMeasurement does not implement __bool__, and may wonder what is the truth value of 0-angle measurements. (In Python, the zero of any numeric type is False).
Checking against None is indeed more verbose, but less ambiguous imo.
This PR fixes two known (but unreported) issues concerning patterns in the LC fragement and open graphs.
Issue 1
Pattern.extract_opengraphignores Clifford commands on measured nodes. Therefore, this method returns different open graphs depending on whether the pattern has been standardized first or not:To ensure canonicality, extracting an open graph from a pattern should always proceed by standardizing the pattern first.
Issue 2
Pattern.extract_opengraphignores Clifford commands on output nodes. Therefore, it does not preserve the semantics of the pattern:To guarantee the round trip
Pattern->OpenGraph->Flow->XZCorrections->Patternfor patterns in the LC fragment:Clifford commands on measured nodes should be absorbed into measurements via standardization (issue 1),
Open graphs should conserve a mapping between output nodes and Clifford operators.
Summary of changes
To fix issues above, we introduce a new attribute to the
OpenGraph.output_cliffordsmapping output nodes to Clifford operations. This corresponds toc_dictinStandardizedPattern. Further:Pattern.extract_opengraphstandardizes the pattern first.XZCorrections.to_patternapplies Cliffords inOpenGraph.output_cliffordsat the end of the pattern.OpenGraph.iscloseandOpenGraph.is_equal_structurallycheck equality ofOpenGraph.output_cliffords.OpenGraph.composemerges Clifford decorations with measurements or other Clifford decorations on outputs if required..drawmethods allow to show Clifford commands in the outputs.PauliFlow.extract_circuitraisesNotImplementedErrorif the open graph has Clifford decorations. This will be left for a future PR to simplify the review.Additionally, a minor bug in
Pattern.composeandOpenGraph.composewhich appeared when trying to compose single-node objects with an empty mapping is fixed, and the corresponding test added.