-
Notifications
You must be signed in to change notification settings - Fork 57
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 sampling when using circuit.add()
#564
Conversation
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.
Thanks for finding the issue. I believe this solves the problem, just two comments below.
Codecov Report
@@ Coverage Diff @@
## master #564 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 84 84
Lines 12681 12704 +23
=========================================
+ Hits 12681 12704 +23
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
circ.add(gates.PauliNoiseChannel(0, pz=0.01)) | ||
circ.add(new_gate) | ||
circ_no_noise.add(new_gate) | ||
circ.add(gates.PauliNoiseChannel(0, pz=0.01)) |
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've implemented a test that checks the sampling after fixing the seed (using K.set_seed(123)
.
However, I've noticed that the parameter seed
of the PauliNoiseChannel
does not affect the results of the sampling. That's why here I've decided to omit it. Is this the correct behaviour, @stavros11 ?
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.
Thanks for adding the test.
However, I've notice that the parameter
seed
of thePauliNoiseChannel
does not affect the results of the sampling. That's why here I've decided to omit it. Is this the correct behaviour, @stavros11 ?
Can you give an example where it doesn't affect? For example, the following script:
from qibo import models, gates
import numpy as np
circ = models.Circuit(1)
circ_no_noise = models.Circuit(1)
for _ in range(10):
new_gate = gates.H(0)
circ.add(gates.PauliNoiseChannel(0, pz=0.1))
circ.add(new_gate)
circ_no_noise.add(new_gate)
circ.add(gates.PauliNoiseChannel(0, pz=0.1))
circ += circ_no_noise.invert()
circ.add(gates.M(0))
print(circ(nshots=100).samples()[:, 0])
gives me different samples every time I run, while if I add a seed
in the last noise channel, it gives me the same samples every time.
circ.add(gates.PauliNoiseChannel(0, pz=0.01)) | ||
circ.add(new_gate) | ||
circ_no_noise.add(new_gate) | ||
circ.add(gates.PauliNoiseChannel(0, pz=0.01)) |
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.
It doesn't really matter for testing purposes, but the last noise channel is outside the for loop in #563. Also it is fine to use a smaller circuit for testing (eg. less than 100 gates).
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. I can lower it down to 10.
EDIT: I will also move the last noise out of the loop, thanks for spotting this.
circ.add(gates.PauliNoiseChannel(0, pz=0.01)) | ||
circ.add(new_gate) | ||
circ_no_noise.add(new_gate) | ||
circ.add(gates.PauliNoiseChannel(0, pz=0.01)) |
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.
Thanks for adding the test.
However, I've notice that the parameter
seed
of thePauliNoiseChannel
does not affect the results of the sampling. That's why here I've decided to omit it. Is this the correct behaviour, @stavros11 ?
Can you give an example where it doesn't affect? For example, the following script:
from qibo import models, gates
import numpy as np
circ = models.Circuit(1)
circ_no_noise = models.Circuit(1)
for _ in range(10):
new_gate = gates.H(0)
circ.add(gates.PauliNoiseChannel(0, pz=0.1))
circ.add(new_gate)
circ_no_noise.add(new_gate)
circ.add(gates.PauliNoiseChannel(0, pz=0.1))
circ += circ_no_noise.invert()
circ.add(gates.M(0))
print(circ(nshots=100).samples()[:, 0])
gives me different samples every time I run, while if I add a seed
in the last noise channel, it gives me the same samples every time.
I've noticed it with the test that I've added, but I can reproduce it also using your example. from qibo import models, gates, K
import numpy as np
K.set_seed(123)
circ = models.Circuit(1)
circ_no_noise = models.Circuit(1)
for i in range(10):
new_gate = gates.H(0)
circ.add(gates.PauliNoiseChannel(0, pz=0.1, seed=i))
circ.add(new_gate)
circ_no_noise.add(new_gate)
circ.add(gates.PauliNoiseChannel(0, pz=0.1))
circ += circ_no_noise.invert()
circ.add(gates.M(0))
print(circ(nshots=100).samples()[:, 0]) But if I set the seed at the beginning the output remains the same. |
Co-authored-by: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com>
The example from the last post gives me
every time I run the script, regardless of whether I use If I read the code correctly, using Regarding the tests, I still get a failure for qibojit-cupy and tensorflow, but now because the output differs from the target. Have you tested in an environment with tensorflow or cupy? |
I understand. I've implemented the fix and now tests are passing for all backends locally on cpu and gpu. Thanks!
Yes I get the same here. Instead if I do something like this: from qibo import models, gates, K
import numpy as np
K.set_seed(123)
circ = models.Circuit(1)
circ_no_noise = models.Circuit(1)
for i in range(10):
new_gate = gates.H(0)
circ.add(gates.PauliNoiseChannel(0, pz=0.1, seed=np.random.randint(100)))
circ.add(new_gate)
circ_no_noise.add(new_gate)
circ.add(gates.PauliNoiseChannel(0, pz=0.1))
circ += circ_no_noise.invert()
circ.add(gates.M(0))
print(circ(nshots=5).samples()[:, 0]) The result is always the same only if I put the set_seed at the beginning, which makes sense since I am fixing the numpy seed at the beginning with |
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.
Thanks, everything works for me too now.
The result is always the same only if I put the set_seed at the beginning, which makes sense since I am fixing the numpy seed at the beginning with
K.set_seed
.
Yes, but I believe this is expected because you are using a random seed in the PauliNoiseChannel
and K.set_seed
is fixing this. If you don't use a random seed but a fixed number, the channel seed should work and give the same results in every run.
Closes #563.
As already discussed in today's meeting, the problem was related to the
add
method ofAbstractCircuit
.When the new circuit is created the
repeated_execution
attribute, which allows to re-execute the circuit for everyshot, was set by default to
False
. This is why when adding the noisy circuit with its inverse the sampling was odd.Now
repeated_execution
is set to:Therefore if one of the two circuits requires to re-execute the circuit the new circuit will also be re-executed for every shot.
I've also added tests about this.
Let me know what you think @igres26 @scarrazza @stavros11.