-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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 QI modifying input transpiled circuit on execute #7484
Conversation
Pull Request Test Coverage Report for Build 1722989690
💛 - Coveralls |
if not had_transpiled: | ||
if had_transpiled: | ||
if isinstance(circuits, list): | ||
circuits = circuits.copy() |
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.
Just a minor thing - I wonder whether it might be an idea to state why the copy is done here in a comment for future reference so that the reason is know and no-one might be tempted to remove the copy thinking they are improving performance. I guess the copy is only needed when we are doing measurement mitigation that will add to the list of circuits being executed. Since this is a shallow copy of the list I guess it should be fine having this done all the time and not dependent on when we know we would add extra ccts.
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.
I will add a comment. Yes, currently the error is just when using measurement mitigation but who knows someone could add code to change the input array somewhere else in the future. The method should not be changing the input without mentioning it in the documentation in any situation.
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.
If this does only a shallow copy, is it what we want? If we go on modifying the shallow copied circuits
list, then the original objects will also be changed, right?
from qiskit.circuit.library import EfficientSU2
original = EfficientSU2(2, reps=1)
circuits = [original]
circuits = circuits.copy()
circuits[0].x(0)
print(original.draw()) # has a new X gate
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.
The QI code never changes the original circuit. In fact QI expects it to be an array and was changing the input array in place, not the contained circuits, in the mitigation part. There is only the need to make a shallow copy to avoid it. A deep copy is overkill and would slow the code.
Making just a shallow copy was intentional.
If you passed a non transpiled circuit or a list of them, there was no problem because it was being transpiled and
a new list of transpiled circuits created. If you passed a transpiled circuit, nothing was being done and the rest of the code was treating it as a list. So there were two things fixed for transpiled circuit: create a list if it is just a circuit and make a shallow copy if it is a list. I am surprised nobody had a problem before. I guess nobody ever sent a single transpiled circuit to QI before.
There is a comment in the code as per Steve's request above.
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.
Ah, ok now I understand. Thanks! 🙂
@@ -332,7 +332,7 @@ def test_measurement_error_mitigation_with_vqe_ignis(self, config): | |||
|
|||
@unittest.skipUnless(HAS_AER, "qiskit-aer is required for this test") | |||
@unittest.skipUnless(HAS_IGNIS, "qiskit-ignis is required to run this test") | |||
def test_callibration_results(self): | |||
def test_calibration(self): |
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.
Does this test cover the new changes? If yes, why? 🙂
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.
I added a transpiled circuit input to this method to test the change in Quantum Instance. Since I fixed the method name spelling, I made it a shorter name.
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.
Now this method went back to the way it used to be and a new unit test method was created for this PR.
if not had_transpiled: | ||
if had_transpiled: | ||
if isinstance(circuits, list): | ||
circuits = circuits.copy() |
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.
If this does only a shallow copy, is it what we want? If we go on modifying the shallow copied circuits
list, then the original objects will also be changed, right?
from qiskit.circuit.library import EfficientSU2
original = EfficientSU2(2, reps=1)
circuits = [original]
circuits = circuits.copy()
circuits[0].x(0)
print(original.draw()) # has a new X gate
The QI code never changes the original circuit. In fact QI expects it to be an array and was changing the array in place, not the contained circuits, in the mitigation part. There is only the need to make a shallow copy to avoid it. A deep copy is overkill and would slow the code. If you passed a non transpiled circuit or a list of them, there was no problem because it was being transpiled and |
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.
The shallow copy is the right thing here - we need to be better across Qiskit about setting contracts for what our functions do, and not just reaching for copy.deepcopy
because it's easy. QuantumInstance.execute
doesn't need to modify its circuit arguments and never should, so it only needs to shallow copy.
@@ -367,6 +367,34 @@ def test_callibration_results(self): | |||
counts_array[0], counts_array[1], msg="Counts different with/without fitter." | |||
) | |||
|
|||
@unittest.skipUnless(HAS_AER, "qiskit-aer is required for this test") | |||
@unittest.skipUnless(HAS_IGNIS, "qiskit-ignis is required to run this test") |
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.
We should not need ignis anymore right so this skip can be removed as ignis is now deprecated and for now the mitigation classes that are used were copied until Terra here until such time as this is all updated with the new mitigation logic.
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.
I removed the Ignis dependency from this new unit test method
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.
Sorry, I'd lost that this was waiting on me.
* Fix QI modifying input transpiled circuit on execute * Add comments * Add separate regression test * Remove Ignis dependency in the newly created test method Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> (cherry picked from commit d662fbb)
* Fix QI modifying input transpiled circuit on execute * Add comments * Add separate regression test * Remove Ignis dependency in the newly created test method Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> (cherry picked from commit d662fbb) Co-authored-by: Manoel Marques <Manoel.Marques@ibm.com>
* Fix QI modifying input transpiled circuit on execute * Add comments * Add separate regression test * Remove Ignis dependency in the newly created test method Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Summary
Fixes #7449
Details and comments
When a an instance or array of transpiled circuits is passed to the method
execute
in QuantumInstance, it needs to be changed to array if it is an instance and if it is an array, it needs to be copied so that the input array doesn't get modified in place. For non transpiled circuits, all works because the methodtranspile
is called and it always returns a new array.