Conversion methods for PauliString#507
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #507 +/- ##
==========================================
- Coverage 89.58% 89.57% -0.02%
==========================================
Files 48 48
Lines 7001 7039 +38
==========================================
+ Hits 6272 6305 +33
- Misses 729 734 +5 ☔ 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.
LGTM! I've added a few comments, but they're mainly nit-picking.
| if len(ps) < 2: | ||
| raise ValueError("Input string must have at least 2 characters (a sign followed by operators).") |
There was a problem hiding this comment.
Should we disallow empty Pauli strings?
Should the default sign be +?
For comparison:
>>> stim.PauliString("X")
stim.PauliString("+X")
>>> stim.PauliString("")
stim.PauliString("+")In addition, PauliString.from_str(str(PauliString(0, {}))) now raises an exception. I'm not sure whether this behavior is intentional.
There was a problem hiding this comment.
Nice catch, I missed the zero-dimensional case. I agree is best to be compatible with stim. Done in 256a303
| raise ValueError("Input string must have at least 2 characters (a sign followed by operators).") | ||
|
|
||
| References | ||
| sign_char, ops = ps[0], ps[1:] # Mypy disallows string unpacking |
There was a problem hiding this comment.
In Python, string unpacking is just a particular case of iterable unpacking. For example,
sign_char, ops = ps[0], ps[1:]uses slicing, so ops becomes a string. In contrast,
sign_char, *ops = psproduces a list of characters for ops. For the rest of the code, the difference is irrelevant.
If you prefer the more declarative string-unpacking style, you can be explicit about using iterable unpacking; mypy accepts this:
| sign_char, ops = ps[0], ps[1:] # Mypy disallows string unpacking | |
| sign_char, *ops = iter(ps) |
| sign_char, ops = ps[0], ps[1:] # Mypy disallows string unpacking | ||
|
|
||
| _sign_map = {"+": Sign.PLUS, "-": Sign.MINUS} | ||
| _axis_map = {"X": Axis.X, "Y": Axis.Y, "Z": Axis.Z} |
There was a problem hiding this comment.
The class Axis itself behaves much like a dictionary: Axis["X"] returns Axis.X. However, the expression "X" in Axis returns False. The proper mapping is Axis.__members__: "X" in Axis.__members__ returns True.
| """Return a string representation of the Pauli string.""" | ||
| pauli_str = ( | ||
| str(self.sign), | ||
| *(getattr(self.axes.get(node), "name", "I") for node in range(self.dim)), |
There was a problem hiding this comment.
I would prefer to avoid getattr because it isn't type-safe. Moreover, it's clearer to test explicitly whether an axis exists or not rather than relying on the fact that there is no field name in None.
| *(getattr(self.axes.get(node), "name", "I") for node in range(self.dim)), | |
| *(axis.name if (axis := self.axes.get(node)) else "I" for node in range(self.dim)), |
There was a problem hiding this comment.
Yes, much cleaner this way. Done in 256a303
| ) | ||
|
|
||
| tab = MatGF2(np.zeros((2 * n, 2 * n + 1))) | ||
| return MatGF2(np.vstack((*(ps.to_tableau() for ps in self.x_map), *(ps.to_tableau() for ps in self.z_map)))) |
There was a problem hiding this comment.
| return MatGF2(np.vstack((*(ps.to_tableau() for ps in self.x_map), *(ps.to_tableau() for ps in self.z_map)))) | |
| return MatGF2(np.vstack([ps.to_tableau() for psmap in (self.x_map, self.z_map) for ps in psmap])) |
This commit introduces new methods for the
PauliStringclass:__str__andfrom_strprovide an interface with strings to simplify debugging,to_tableauandfrom_tableauprovide an interface with the_linalgmodule to facilitate implementing algorithms based on the stabilizer formalism.Additionally, the static method
PauliString.from_measured_nodeis subsumed by the functionextraction_ps_from_corrected_node. This follows from the observation that the returned Pauli string is truly specific to the circuit extraction algorithm, and it's not the canonical stabilizer given by a flow's correction function. The latter readswhereas the
extraction_ps_from_corrected_nodereturns a Pauli string acting on the output nodes only. Work on MBQC fidelity shows thatPauliStringobjects are useful beyond the circuit extraction routine, so this refactor aims at giving thePauliStringclass more independence.