-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix qpy serialization of substitution of type ParameterExpression
#13890
Conversation
When substitution history `ParameterExpression._qpy_replay` is serialized, there was no branch for the case that the substituted value is of type `ParameterExpression`. This commit fixes this oversight.
Pull Request Test Coverage Report for Build 13609395583Details
💛 - Coveralls |
EDIT: Fixed in fa0d254
|
* Added a branch for reading `ParameterExpression` in _qpy_replay * Added a missing argument (version) in an existing call in code path that was previously untested * Remove a bit of useless code introduced in last commit * run black
One or more of the following people are relevant to this code:
|
I ported this to Qiskit v1.3.2 and verified that it works. On the main branch, I can't test with qiskit-ibm-runtime, because it is incompatible with the qiskit development branch. The fix applied to the stable release Qiskit v1.3.2 is not worth much, because deserializing on the server end fails of course. It will need to be updated, too. Because of this, it looks like this will require a version bump in qpy. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this LGTM, I left one small comment inside about trying to minimize the diff for backport reasons. The other thing which might be good is adding a backwards compatibilty test to https://github.com/Qiskit/qiskit/blob/main/test/qpy_compat/test_qpy.py to cover this case so that we ensure we can load payloads with these types of expressions from qiskit >= 1.4.1
with QPY 13.
Because of this, it looks like this will require a version bump in qpy.
I'm on the fence about this. From the way the format spec is written this was just an oversight in the implementation so I view it as a straight bug in the v13 implementation that neither serialization or deserialization works with nested substituted parameter expressions. But your also correct that no v13 implementation out there works with this model yet which makes it feel like doing this in v14 is a better choice.
I'm inclined to just leave this as a v13 fix, and not require v14 for it since the format is still parsable in v13, it will just error because of the deserialization bug.
qiskit/qpy/binary_io/value.py
Outdated
@@ -229,14 +232,22 @@ def _write_parameter_expression(file_obj, obj, use_symengine, *, version): | |||
file_obj.write(elem_header) | |||
file_obj.write(symbol_data) | |||
file_obj.write(value_data) | |||
for symbol in extra_symbols.values(): | |||
for symbol in extra_expressions.values(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mind reverting the name change here? I agree with the change in the naming, but since we're probably going to backport this PR for 1.4.1 I think it's better to minimize the diff to reduce the risk of regression. So we can keep the change here to just the functional component below adding the missing handling of the PARAMETER_EXPRESSION
type in the extra expression/symbols.
We can do the renaming in a standalone PR that we don't backport.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree it's better to revert.
I was uncomfortable with the fact that most of the diff is due to this name change. The motivation is that in various places, the substituted items are referred to as "symbols". Now that we know they are not always symbols, this is misleading. The logic is already kind of hard to follow. I wanted to make it a bit easier for the next person. Having said that, the keys (not the substituted values) are symbols. And if I recall correctly, I didn't change variable names to make them more descriptive everywhere.
The bottom line is the renaming was not as well-thought-out as it could be.
As you say, renaming could be done later. Then it could be done more carefully. Or maybe "never" is a good time to make the change. Because, hopefully, this code won't need to be touched much before it is ported to rust.
A side comment: This code could serve as a base for a pure-Python QPY implementation. It's common for people to choose a slower pure Python library over a fast, compiled, one because the former is more lightweight and flexible. Although at the moment, I don't know of any demand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 41d61c7
Reverting this to minimize changes in order to make the PR safer to backport.
@mtreinish : In the same spirit, I think I should revert this: - symbol_map.update(_encode_replay_subs(inst, file_obj, version))
+ _encode_replay_subs(inst, file_obj, version)
+ symbol_map.update(inst.binds) This cleaned up the logic: EDIT: I indeed did revert in 06daa34 Regarding your comment on increasing version number from 13 to 14. I had a similar thought after writing the comment suggesting doing it. I agree, it's better not to increase the version number: If you try to deserialize what is written with this fix with the unfixed code you get an error either way: you either hit the bug, or fail because of version mismatch. Furthermore, it is really a bug fix. It's not the kind of bug that people have inadvertently come to rely on. Going to v14 incurs complexity. |
This was a good change. But not necessary for the bug fix, which is the main point of this PR.
EDIT: Fixed
CI is failing when running the qpy compat tests.
Fixed. I have added a new test file with one test: To test qpy compatibility: I added a function: def generate_replay_with_expression_substitutions():
"""Circuits with parameters that have substituted expressions in the replay"""
a = Parameter("a")
b = Parameter("b")
a1 = a * 2
a2 = a1.subs({a: 3 * b})
qc = QuantumCircuit(1)
qc.rz(a2, 0)
return [qc] Then I added at the bottom of the function if version_parts >= (1, 4, 1):
output_circuits["replay_with_expressions.qpy"] = (
generate_replay_with_expression_substitutions()
) I try running this: Note that I verify that I try the same thing after cd'ing to ./test/qpy_compat . No difference. What is going on here? Trying solutions at random: I run the In [1]: %run -i ./test/qpy_compat/test_qpy.py generate --version 2.0.0 All the expected qpy files are generated silently. Now I run: In [2]: %run -i ./test/qpy_compat/test_qpy.py load --version 2.0.0
Loading qpy file: full.qpy
Loading qpy file: unitary.qpy
Loading qpy file: multiple.qpy
Loading qpy file: string_parameters.qpy
Loading qpy file: register_edge_cases.qpy
Reference Circuit 0:
[blah blah]
is not equivalent to qpy loaded circuit 0: Notice that this is failing on a circuit that has nothing to do with the bug or the fix. Now in CI, the failure does look like it is indeed failing when trying to load the compat test for this bug fix: I am continuing to debug. But, if anyone has clarifying insights.... EDIT: I verify this: > python -c "import qiskit; print(qiskit.__version__)"
I confirm again: > python ./test/qpy_compat/test_qpy.py generate --version 2.0.0
AttributeError: 'ParameterExpression' object has no attribute 'name'. Did you mean: '_names'? This is the error you get if the bug fix is not in place EDIT: There is really something state dependent or stochastic going on with the qpy compat testing: If I call Occasionally, I am getting the same error seen in CI |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LGTM now, thanks for catching this, debugging it, and fixing the issue.
…13890) * Fix qpy serialization of substitution of type ParameterExpression When substitution history `ParameterExpression._qpy_replay` is serialized, there was no branch for the case that the substituted value is of type `ParameterExpression`. This commit fixes this oversight. * Fix deserializing qpy written by previous writing fix * Added a branch for reading `ParameterExpression` in _qpy_replay * Added a missing argument (version) in an existing call in code path that was previously untested * Remove a bit of useless code introduced in last commit * run black * Add tests * Revert renaming extra_symbols to extra_expressions Reverting this to minimize changes in order to make the PR safer to backport. * Revert cleaning up logic in _encode_replay_subs This was a good change. But not necessary for the bug fix, which is the main point of this PR. * Add qpy compat test * Bind parameter when checking for equality * Run black after merge in browser --------- Co-authored-by: Luciano Bello <bel@zurich.ibm.com> (cherry picked from commit a6fa6f8) # Conflicts: # test/qpy_compat/test_qpy.py
…backport #13890) (#13943) * Fix qpy serialization of substitution of type `ParameterExpression` (#13890) * Fix qpy serialization of substitution of type ParameterExpression When substitution history `ParameterExpression._qpy_replay` is serialized, there was no branch for the case that the substituted value is of type `ParameterExpression`. This commit fixes this oversight. * Fix deserializing qpy written by previous writing fix * Added a branch for reading `ParameterExpression` in _qpy_replay * Added a missing argument (version) in an existing call in code path that was previously untested * Remove a bit of useless code introduced in last commit * run black * Add tests * Revert renaming extra_symbols to extra_expressions Reverting this to minimize changes in order to make the PR safer to backport. * Revert cleaning up logic in _encode_replay_subs This was a good change. But not necessary for the bug fix, which is the main point of this PR. * Add qpy compat test * Bind parameter when checking for equality * Run black after merge in browser --------- Co-authored-by: Luciano Bello <bel@zurich.ibm.com> (cherry picked from commit a6fa6f8) # Conflicts: # test/qpy_compat/test_qpy.py * Update test_qpy.py * Update test/qpy_compat/test_qpy.py --------- Co-authored-by: John Lapeyre <jlapeyre@users.noreply.github.com> Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com> Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
* Prepare 1.4.1 * prelude * cargo build * unify version notation, some broken links, and fix circuit output * missing reno #13890 * exclude aer 0.16.4 * neither 0.16.3 * Apply suggestions from code review Co-authored-by: Matthew Treinish <mtreinish@kortar.org> * link --------- Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
When substitution history
ParameterExpression._qpy_replay
is serialized, there was no branch for the case that the substituted value is of typeParameterExpression
.This commit fixes this oversight.
Tests need to be added.Closes #13879