Closed igres26 closed 4 years ago
This sounds like moving the noise to a callback, right?
What I had in mind was to implement a method with_noise(px, py, pz)
under models.Circuit
that returns the same circuit but with a noise channel after every gate. The user could then do
# define a noiseless circuit
circuit = Circuit(5)
circuit.add(gates.X(0))
circuit.add(gates.Y(1))
# etc. ...
noisy_circuit = circuit.with_noise(px=0.1, pz=0.1)
# creates a new circuit that has the same gates as `circuit` including
# a NoiseChannel with the given probabilities after each gate.
# execute noiseless circuit
noiseless_final_state = circuit()
# execute noisy circuit
noisy_final_state = noisy_circuit()
This is straightforward to implement and I believe it is also flexible from the user perspective as one can do things like:
circuit
(just by calling circuit.with_noise()
with different p's),noisy_circuit
by adding other gates (eg. more noise in the end).I am not sure if the callback approach is useful here because the idea of callbacks was to track specific quantities (like the entanglement entropy) as the states propagates through the circuit. The equivalent here would have to return the full density matrix after every gate which would consume a lot of memory and is not required as people are typically interested only in the final density matrix (or measurement results).
Thanks for the explanation, this approach is quite transparent. Maybe we should also consider the possibility to implement a circuit.copy
function.
Thanks for the explanation, this approach is quite transparent. Maybe we should also consider the possibility to implement a
circuit.copy
function.
circuit.copy
should also be easy to implement. I don't have many use-cases in mind, but it could certainly be useful somewhere. The only thing to decide is whether the gates in the copied circuit are the same Python objects as the original or we want to construct new gates with the same qubit indices. For with_noise
I was planning to use the same objects, as I don't see any issues with this and we also avoid recalculating the cached indices, etc..
We probably can consider the distinction like copy
and deepcopy
, so I think a copy method should be ok, and eventually could be the return of with_noise
.
This with_noise
implementation sounds just like the thing I was looking for.
For with_noise I was planning to use the same objects, as I don't see any issues with this and we also avoid recalculating the cached indices, etc..
yes, this sounds like a sensible option.
Just to unlock this discussion, I think we can have for the time being something simple like:
def with_noise(self, px=0, py=0, pz=0):
c = BaseCircuit(self.nqubits)
c.queue = self.queue
c.add(...) # add noise after each gate
return c
And keep the copy/deepcopy for later, if really needed.
@scarrazza, yes I already started implementing this yesterday, I just left in the middle to look at the multi-GPU benchmarks. I was planning to add copy
and with_noise
for now and leave deepcopy
, which is slightly more complicated for later. I will probably open a PR for this later today.
Perfect thanks.
Quick issue to outline the noise discussion we had in the meeting.
Having the possibility to add noise exactly where we want to as an extra gate is really useful. However, if one wanted to add noise to an already designed circuit, adding a gate after every gate of the system would be very time consuming.
Therefore, an option to run an existing (noiseless) circuit with an extra variable to automatically implement a noise gate after every circuit gate would be really helpful.