Fix #45: Parameterized measurement patterns
This commit is yet another tentative to implement parameterized measurement patterns, to fulfill issue #45 (previous tentative: #68).
This commit adds two methods to the class Pattern:
-
is_parameterized()returns True if there is at least one measurement angle that is not just an instance of numbers.Number: indeed, a parameterized pattern is a pattern where at least one measurement angle is an expression that is not a number, typically an instance ofsympy.Expr(but we don't force to choose sympy here). -
subs(variable, substitute)returns a copy of the pattern where occurrences of the given variable in measurement angles are substituted by the given value. Substitution is performed by calling the methodsubson measurement angles, if the method exists, which is the case in particular forsympy.Expr. If the substitution returns a number, this number is coerced tofloat, to get numbers that implement the full number protocol (in particular, sympy numbers don't implementcos).
Codecov Report
Attention: Patch coverage is 81.87050% with 126 lines in your changes missing coverage. Please review.
Project coverage is 72.95%. Comparing base (
ec4c582) to head (5feac05). Report is 5 commits behind head on master.
| Files | Patch % | Lines |
|---|---|---|
| graphix/parameter.py | 80.54% | 122 Missing :warning: |
| graphix/transpiler.py | 80.95% | 4 Missing :warning: |
Additional details and impacted files
@@ Coverage Diff @@
## master #158 +/- ##
==========================================
+ Coverage 71.82% 72.95% +1.13%
==========================================
Files 30 31 +1
Lines 5359 6038 +679
==========================================
+ Hits 3849 4405 +556
- Misses 1510 1633 +123
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
@thierry-martinez great, is the intention to merge @pafloxy's #68-equivalent code first (when it's ready), before merging this?
@thierry-martinez great, is the intention to merge @pafloxy's #68-equivalent code first (when it's ready), before merging this?
I adapted #68 code for Parameter class in the commit 81bbeb7 I just pushed. #68 was incorrect because arithmetic operators changed parameters in place (computing alpha + 1 did change the value of alpha to alpha + 1!). In this PR, we distinguish between Parameter (alpha) and ParameterExpression (alpha + 1), and a Parameter is a particular case of ParameterExpression.
The test test_parameter_simulation is illustrative: we can either substitute a parameter by a value in a pattern then simulate, or simulate symbolically (given that the pattern is deterministic!) then substitute in the (symbolic) state vector or the density matrix, and we get the same result.
@thierry-martinez @pafloxy great! give us a few days to look through. A few quick comments:
- is TN backend working with parameters, or should we throw error in TN backend if pattern is parametrised?
~~2. could you add sympy to
requirements.txt?~~ - will
circuit.simulate_statevector,pattern.perform_pauli_measurements, as well as visualization tool, work with parameters?
Let me review it later.
Anyway, is it absolutely necessary to use sympy? I'm concerned with...
- performance: Personally I feel sympy is far from performant.
- type stability: sympy has no type annotations, and it seems that not even planned.
- developer experience: Sometimes sympy makes linter (ex. flake8, Ruff) extremely slow.
Additionally, let me point out that @masa10-f is planning to completely eliminate sympy from the source.
If sympy is absolutely necessary, it's OK, but we may need to verify that we're using it wisely.
@thierry-martinez @pafloxy great! give us a few days to look through. A few quick comments:
- is TN backend working with parameters, or should we throw error in TN backend if pattern is parametrised? ~2. could you add sympy to
requirements.txt?~- will
circuit.simulate_statevector,pattern.perform_pauli_measurements, as well as visualization tool, work with parameters?
@shinich1 @thierry-martinez
Maybe you guys figured it already but I think we need to make some small modification to the visualization.py module as well. There are some checks to determine if a measurement angle is Pauli in some of the class methods for GraphVisualizer of the form
elif (
show_pauli_measurement
and self.meas_angles is not None
and (
2 * self.meas_angles[node] == int(2 * self.meas_angles[node])
) # measurement angle is integer or half-integer
):
where we might need to add another line or so to exclude the case where the measurement angles is an instance of Parameter/ParameterExpression.
Hi @pafloxy @thierry-martinez
I think the best way is to move parameter implementation with sympy out of main graphix repository, for example a separate repo (wrapper/module) dedicated to parameterized patterns and their executions (it seems possible, given the relatively small changes required to implement?). I am happy to initiate a creation of such repository in teamgraphix organization, if that makes sense to you? for example, that can be part of extra pip installation.
The reason mostly follows @EarlMilktea 's. Sustaining maintainability is going to help a lot in the long run for everyone contributing to this repo and will help a lot those maintaining.
-
sympyhas quite a performance issue. Looking at their github repo, it seems like a recurring issue inqiskit(which hassympyincorporated) and quite a bit of effort can be saved by deciding to move it out at this point (nb as @EarlMilktea mentionedsympywill be removed fromrequirements.txtwhengflowis refactored for performance soon.) - typing, linter compatibility, as in @EarlMilktea's comment
I propose in my last commits a version which does not require mypy.
-
The module
parameter.pyimplements symbolic expressions so as to support parameters, but without all the symbolic. machinery ofmypy(computations with known values are done numerically, not symbolically). -
The rest of the code does not depend on
parameter.py: in particular, the code for simulators is generic. If we still need thesympyversion of parameters, we can implement it separately, and simulators should work without any specific modification in the code of graphix. -
Tests for parameters are done in a specific
test_parameter.pymodule, and the tests include circuit simulation.
I propose in my last commits a version which does not require
mypy.
- The module
parameter.pyimplements symbolic expressions so as to support parameters, but without all the symbolic. machinery ofmypy(computations with known values are done numerically, not symbolically).- The rest of the code does not depend on
parameter.py: in particular, the code for simulators is generic. If we still need thesympyversion of parameters, we can implement it separately, and simulators should work without any specific modification in the code of graphix.- Tests for parameters are done in a specific
test_parameter.pymodule, and the tests include circuit simulation.
Thank you! I believe you mean sympy instead of mypy above? Let me take a detailed look at the code soon.
@thierry-martinez the implementation looks good to me! we need to:
- [ ] fix visualization which seems to throw error in the presence of parameter in pattern
- [ ] test more situations, so we can catch more errors (currently
rzonly with no arithmetic of param covered?) - [ ] do we intend to cover simulations without
subs? if so we should throw error if parameterized and being simulated? - [ ] update QAOA, VQE and QNN
examples. - [ ] add appropriate page to
docs(just autodoc, not much to write - see existing pages) - [ ] device interface - may need care. easy solution is to ask
subsbeforePatternRunnerinitialization.
I'm a little bit concerned about...
- complexity: Is it possible to maintain
parameter.py? It's already really huge. - performance: I feel that current impl. cannot be faster than sympy as it's doing almost the same things.
We suggest the following:
- implement 'placeholder' parameter implementation in
graphix - move symbolic stuff outside, in https://github.com/TeamGraphix/graphix-symbolic/tree/master.
As @shinich1 suggested in the above message, this PR now only provides abstract base classes for parameters, and a minimal concrete class for placeholders (symbolic angles that can solely be substituted, and that cannot appear in computation). The sympy part is implemented as a plugin in the graphix-symbolic repo: https://github.com/TeamGraphix/graphix-symbolic/pull/1 .
@thierry-martinez There are ruff errors remain unresolved. Could you make sure that you're using the CIs correctly?
Sorry, @EarlMilktea, I didn't merge this branch with master yet: it is now done and CI seems to run correctly now.
@thierry-martinez CI config. is again updated. Could you update the branch?
Updated! Thanks.
@thierry-martinez
Checked the updates. As for the organization and quality of the code, I see (almost) no problems, but for the interface or ultimately the approach itself, I'm strongly worried about future maintainability and performance.
I suggest the following approach:
- Use unique identifier to distinguish different angles (not by
sympy.Symbolorfloat) - Define a class that keeps track of measurement angle/plane changes introduced on simplification
- In a separate package, read out the info. and perform symbolic calculus
The point is that, we only need to take into account sign flip $\alpha \to -\alpha$ and constant shift $\alpha \to \alpha + \pi$.
Obviously it does not require full symbolic interfaces or ABCs, just a tiny class.
With this approach, we may even rely on bindings or numba in the future.
@EarlMilktea:
1. Use unique identifier to distinguish different angles (not by `sympy.Symbol` or `float`)
I think we have better to keep things generic at this level: most patterns will only have float angles, and I don't think there is a need for unique identifiers in this case. Placeholder uses object identity (is) in __eq__ and I don't think we can be more efficient than this (and it is a kind of unique identifier...). We don't use sympy anymore in this PR.
2. Define a class that keeps track of measurement angle/plane changes introduced on simplification[...] The point is that, we only need to take into account sign flip α → − α and constant shift α → α + π .
I added support for affine expressions in 05c05a0: the code is simple to maintain and support sign flip, constant shift, and the operations performed in the transpiler.
That's enough to handle transpiling and pattern optimizations, but that's not enough for simulation.
3. In a separate package, read out the info. and perform symbolic calculus
Yes, the package graphix-symbolic uses sympy to perform symbolic simulations.
Placeholder uses object identity (is) in eq and I don't think we can be more efficient than this (and it is a kind of unique identifier...).
Anyway I love this approach!
@thierry-martinez the implementation looks good to me! we need to:
- [ ] fix visualization which seems to throw error in the presence of parameter in pattern
- [x] test more situations, so we can catch more errors (currently
rzonly with no arithmetic of param covered?)- [ ] do we intend to cover simulations without
subs? if so we should throw error if parameterized and being simulated?- [ ] update QAOA, VQE and QNN
examples.- [ ] add appropriate page to
docs(just autodoc, not much to write - see existing pages)- [ ] device interface - may need care. easy solution is to ask
subsbeforePatternRunnerinitialization.
bump above list posted some time ago
Thank you, @shinich1, for the list!
- [x] fix visualization which seems to throw error in the presence of parameter in pattern
Fixed in 34c16ed, with a dedicated test.
- [x] test more situations, so we can catch more errors (currently
rzonly with no arithmetic of param covered?)
Should have been fixed with test on random circuits (05c05a0).
- [x] do we intend to cover simulations without
subs? if so we should throw error if parameterized and being simulated?
Simulations without subs are handled with graphix-symbolic. In 3a6e38d, I added a test to check that there is an exception PlaceholderOperationError if a simulation is run on a circuit with a non-substituted Placeholder.
- [ ] update QAOA, VQE and QNN
examples.
I am not sure what to do with QAOA since there is only one pattern in qaoa.py.
I added a placeholder version in VQE (with performance comparison) in 4cb326b.
I added placeholders for QNN in b337abe.
- [x] add appropriate page to
docs(just autodoc, not much to write - see existing pages)
Placeholders and affine expressions are documented in 149b089.
- [x] device interface - may need care. easy solution is to ask
subsbeforePatternRunnerinitialization.
I added a test to check that there is an exception CircuitError if a PatternRunner is initialized on non-substituted parameterized patterns in 1e2f3c1.
@thierry-martinez
Is it impossible to move symbolic-related ABCs/subs functions to graphix-symbolic ?
Additionally, please do not define unnecessary mathematical/arithmetic abstract methods in AffineExpression, as they're not implemented or even used in graphix: consider extending it in the separate package.
I understand graphix should be updated so that non-float can pass through functions, but the impact should be minimal.
@EarlMilktea I believe that all the functions (subs, xreplace, and the trigonometric functions) are used in graphix. Simple and parallel substitutions apply to both Placeholder and AffineExpression. All trigonometric functions are potentially called during simulation; in these cases, AffineExpression raises an Exception, as expected, since simulation is not supported. The purpose of the parameter module is to define a common interface, which is why an abstract base class is defined.
@thierry-martinez OK, I trust you. Let me do final checks...
I added missing xreplace in Circuit, Statevec and DensityMatrix, and I replace some uses of numbers.Number with typing.SupportsComplex.
I re-reviewed you code and felt that the whole logic of graphix (inclding simulator) is affected.
Could you request reviews from @masa10-f , as I'm not sure your idea is compatible with ZX calculus?
Additionally, please wait until his changes are merged and update your code accordingly.
Meantime, let me carefully examine your code in terms of type stability, performance, and maintainability.
@EarlMilktea I believe this is the direction we agreed on at the meeting in Paris. could you review this PR?
@thierry-martinez (CC: @shinich1 )
I'm basically agree with your idea, but I still feel that this PR should be postponed until other TODOs are resolved, because...
- this PR makes the Pythonic layer thicker
It would make it very difficult to improve/assess the performance of the simulator if this PR were to be merged before simulator optimization/refactoring, because simulator optimization is basically the process of reducing Pythonic tasks (ex. using numpy to delegate heavy tasks to C).
- this PR needs to use
Any
I admit this PR somehow require Any, but as Any can ruin the type checker, I would prefer resolving annotation issues (mypy errors, lacking annotations, etc.) first.