-
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
Expose seed in Estimator
and StatevectorEstimator
#12862
Conversation
One or more of the following people are relevant to this code:
|
Estimator
and StatevectorEstimator
with reset
Pull Request Test Coverage Report for Build 10298480890Details
💛 - Coveralls |
tl;dr: It seems good to expose the While I would agree that we can expose the import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.primitives import Estimator
from qiskit.quantum_info import SparsePauliOp
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.reset(0)
op = SparsePauliOp("ZI")
n = 100
est = Estimator(options={"seed": 1})
values = est.run([qc for _ in range(n)], n * [op]).result().values
print(np.mean(values)) # should be ~0, but with the seed it is either +1 or -1 If the user wants correct behavior they should probably average over multiple runs or use another primitive? |
(Commenting as the user who filed the issue)
Since default is |
Updated: the next commit seems to work fine. |
I updated this PR to make the result of this case #12862 (comment) stochastic but reproducible by fixing a random seed. output of this PR
It also works #12519 case too if we set I updated the document of
|
updated description #12862 (comment) |
8bf8554
to
6e6f284
Compare
tl;dr: The statevector class cannot represent classical mixtures (states that require a density matrix representation) and randomly picks one of the valid statevectors. The statevector simulation is only that -- simulation of statevectors by means of unitary evolution 🙂 with If we start from a Bell state which is mixed ( So to get an exact solution to your circuits that include resets, you might want to use a density matrix simulator (Aer should have one). Alternatively you can run a large number of statevector simulations, and average over the results, which equals a manual average over the classical mixture. |
Thank you for taking the time to explain it. I will be reading up on the Kraus operators. Let me start by saying that I am and was not questioning your expertise. Quite the opposite, you definitely understand the code and physics much better than me. But due to that expertise your expectations are based on the implementation, whereas user expectations come from the documentation. Therefore when the documentation states 'exact' it is not strange that the user thinks it is deterministic. Apparently that is not possible due to the way it is implemented (which probably make a lot of sense), but in my opinion it is not fair to assume the user knows implementation details. As someone with more of a background in EE and software, I see the reset gate as just a (non-unitary) matrix multiplication. My knowledge reaches as far as that non-unitary operations are forbidden in a real circuit, but running locally is not a real circuit. So when I read 'exact', that makes me think no such limitations apply (running locally). PS |
Thank you for the detailed explanation, @Cryoris. Yes, since the statevector class cannot represent the classical mixture, I suggest rewording "exact" in the documentation in this PR. As for the issue #12519, the script expects the same statevector for all observables and this PR helps do it. Of course, they still need to conduct the experiments multiple times with different seeds to deal with the stochastic statevector behavior due to reset for subsystems. |
estimator = Estimator(options={"seed": seed}) | ||
result = estimator.run([qc for _ in range(n)], [op] * n).result() | ||
# expectation values should be stochastic due to reset for subsystems | ||
np.testing.assert_allclose(result.values.mean(), 0, atol=1e-1) |
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 runs very fast, could we increase n
(maybe 1000)? If I did the math right this has still a ~1% chance of failing, which could be quite a lot in the long run. If it is not very fast I think we can also skip this test, as the behavior of reset
should be tested in the Statevector
class.
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.
n = 1000 takes around 0.3 sec on mac M1 (while n = 250 takes around 0.09 sec).
I will increase the number to n = 1000.
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 discussed with the team and the failure likelihood (now ~0.1%) is still too high as it would likely fail the CI once a day. Could you move that test to test/randomized
? Then CI will not be blocked on the failures but we can still get a notice 🙂
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.
Because I fixed seed
, it does not fail as far as there is no breaking change in np.random
or Statevector.reset
.
I printed result.values.mean()
several times and the values are consistent.
(dev-qiskit) ima@mbp3 ~/t/4/q/terra (primitives/estimator-reset)> python -m unittest test/python/primitives/test_estimator.py test/python/primitives/test_statevector_estimator.py
.....0.006
0.032
........................0.006
0.03223976655569642
......
----------------------------------------------------------------------
Ran 35 tests in 1.942s
OK
(dev-qiskit) ima@mbp3 ~/t/4/q/terra (primitives/estimator-reset)> python -m unittest test/python/primitives/test_estimator.py test/python/primitives/test_statevector_estimator.py
.....0.006
0.032
........................0.006
0.03223976655569642
......
----------------------------------------------------------------------
Ran 35 tests in 1.838s
OK
(dev-qiskit) ima@mbp3 ~/t/4/q/terra (primitives/estimator-reset)> python -m unittest test/python/primitives/test_estimator.py test/python/primitives/test_statevector_estimator.py
.....0.006
0.032
........................0.006
0.03223976655569642
......
----------------------------------------------------------------------
Ran 35 tests in 1.834s
OK
diff to display mean values.
diff --git a/test/python/primitives/test_estimator.py b/test/python/primitives/test_estimator.py
index 2c251af65..f17b0a7d7 100644
--- a/test/python/primitives/test_estimator.py
+++ b/test/python/primitives/test_estimator.py
@@ -370,7 +370,8 @@ class TestEstimator(QiskitTestCase):
estimator = Estimator(options={"seed": seed})
result = estimator.run([qc for _ in range(n)], [op] * n).result()
# expectation values should be stochastic due to reset for subsystems
- np.testing.assert_allclose(result.values.mean(), 0, atol=1e-1)
+ np.testing.assert_allclose(z:=result.values.mean(), 0, atol=1e-1)
+ print(z)
with self.assertWarns(DeprecationWarning):
result2 = estimator.run([qc for _ in range(n)], [op] * n).result()
@@ -383,7 +384,8 @@ class TestEstimator(QiskitTestCase):
estimator = Estimator(options={"seed": seed})
result = estimator.run([qc for _ in range(n)], [op] * n, shots=shots).result()
# expectation values should be stochastic due to reset for subsystems
- np.testing.assert_allclose(result.values.mean(), 0, atol=1e-1)
+ np.testing.assert_allclose(z:=result.values.mean(), 0, atol=1e-1)
+ print(z)
with self.assertWarns(DeprecationWarning):
result2 = estimator.run([qc for _ in range(n)], [op] * n, shots=shots).result()
diff --git a/test/python/primitives/test_statevector_estimator.py b/test/python/primitives/test_statevector_estimator.py
index 0cd76550f..2913e8c53 100644
--- a/test/python/primitives/test_statevector_estimator.py
+++ b/test/python/primitives/test_statevector_estimator.py
@@ -321,7 +321,8 @@ class TestStatevectorEstimator(QiskitTestCase):
with self.subTest("precision=0"):
result = estimator.run([(qc, [op] * n)]).result()
# expectation values should be stochastic due to reset for subsystems
- np.testing.assert_allclose(result[0].data.evs.mean(), 0, atol=1e-1)
+ np.testing.assert_allclose(z:=result[0].data.evs.mean(), 0, atol=1e-1)
+ print(z.item())
result2 = estimator.run([(qc, [op] * n)]).result()
# expectation values should be reproducible due to seed
@@ -331,7 +332,8 @@ class TestStatevectorEstimator(QiskitTestCase):
precision = 0.01
result = estimator.run([(qc, [op] * n)], precision=precision).result()
# expectation values should be stochastic due to reset for subsystems
- np.testing.assert_allclose(result[0].data.evs.mean(), 0, atol=1e-1)
+ np.testing.assert_allclose(z:=result[0].data.evs.mean(), 0, atol=1e-1)
+ print(z.item())
result2 = estimator.run([(qc, [op] * n)], precision=precision).result()
# expectation values should be reproducible due to seed
@SillieWous no worries, happy to discuss 🙂 @t-imamichi maybe we could add a note to the |
Co-authored-by: Julien Gacon <gaconju@gmail.com>
Estimator
and StatevectorEstimator
with resetEstimator
and StatevectorEstimator
Summary
The Estimator implementations based on
Statevector
, i.e.,Estimator
andStatevectorEstimator
may return expectation values in a stochastic way if input circuits include reset gates for a some subsystems.This is because
Statevector
behaves in a stochastic way in such a situation and we need to set a random seed to make it reproducible. Here are the details.qiskit/qiskit/quantum_info/states/statevector.py
Lines 611 to 616 in b75c2e0
This PR introduces a private utility to function to set a random number generator to make the output reprodicible.
Fixes #12519 (result with this PR #12519 (comment))
Details and comments
Based on #12862 (comment)
main (the values are close to zero, but random)
this PR (the values are close to zero and reproducible thanks to
seed=123
)