Skip to content
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

Add noise model #572

Merged
merged 19 commits into from
Apr 20, 2022
Merged

Add noise model #572

merged 19 commits into from
Apr 20, 2022

Conversation

andrea-pasquale
Copy link
Contributor

@andrea-pasquale andrea-pasquale commented Apr 11, 2022

Closes #567.
As already discussed in #567 this PR implement a noise model interface in Qibo.
The noise model implement a map that associate a quantum channel Q to a specific gate G. In particular, the quantum channel Q is added before every instance of the gate G. It is also possible specify for which qubits the noise will be added.
Here is an example
EDIT: I've updated the current noise model API:

  • Noise are added after the gates.
  • noise.add supports directly the qibo gates no longer the qasm representation.
from qibo import gates, models
from qibo.noise import PauliError, ThermalRelaxationError, NoiseModel

# declare quantum errors
pauli = PauliError(0, 0.2, 0.3)
thermal = ThermalRelaxationError(1, 1, 0.3)

# define noise model and add quantum errors
noise = NoiseModel() 
noise.add(pauli, gates.X, 1)
noise.add(pauli, gates.CNOT)
noise.add(thermal, gates.Z, (0,1))

circuit = Circuit(3)
circuit.add(gates.CNOT(0,1))
circuit.add(gates.Z(1))
circuit.add(gates.X(1))
circuit.add(gates.X(2))
circuit.add(gates.Z(2))

# create noisy circuit from noise model
noisy_circuit = noise.apply(circuit)
print(noisy_circuit.draw())

with output

q0: ─o─PN───────────
q1: ─X─PN─Z─TR─X─PN─
q2: ───X──Z─────────

I mark this as WIP in case we want to implement other features related to the noise model in this PR.
This PR is also missing documentation.

  • documentation

Verified

This commit was created on github.com and signed with GitHub’s verified signature.
@codecov
Copy link

codecov bot commented Apr 11, 2022

Codecov Report

Merging #572 (8367b68) into master (0154fc7) will not change coverage.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##            master      #572    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files           84        86     +2     
  Lines        12737     12861   +124     
==========================================
+ Hits         12737     12861   +124     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/qibo/noise.py 100.00% <100.00%> (ø)
src/qibo/tests/test_noise.py 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0154fc7...8367b68. Read the comment docs.

Copy link
Member

@stavros11 stavros11 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for implementing this. In terms of features, this looks complete to me. As far as I understand #567 is mostly focused on the API of adding noise to circuits which is implemented here. One straightforward addition one could propose is to extend the list of noise types, however this can be done later in collaboration with theorists/experimentalists.

In terms of code, my main concern is the use of circ.queue.insert, which may have some unwanted behavior. The issue is that queue is not a Python list but a custom _Queue object defined in abstractions/circuit.py and inserting elements to it using methods other than .append may break some of its features. For example, in your example, if you do

print(circuit.depth)
print(noisy_circuit.depth)

you will see that both are 6, even though the noisy circuit is deeper.

Not related to this PR, but to avoid such issues in the future, perhaps we should remove the inheritance of _Queue from list or rename circuit.queue to circuit._queue to discourage modifying this outside the circuit.

Regarding this PR, one easy solution is to recreate the noisy circuit in NoiseModel.apply, instead of using copy, eg.:

def apply(self, circuit):
    circ = circuit.__class__(**circuit.init_kwargs)
    for gate in circuit.queue:
        circ.add(...) # add channels associated with the gate's noisy
        circ.add(gate) # add the gate

Perhaps this would also simplify the code a bit as you do not need to keep track of num and count to insert the noise in the proper place.

Some other comments below:
The example in the first post misses some imports:

from qibo import gates
from qibo.models import Circuit
from qibo.noise import PauliError, ThermalRelaxationError, NoiseModel

@andrea-pasquale
Copy link
Contributor Author

Thanks for the review @stavros11.
I should have covered all the points that you mentioned. Now the code is much simpler.
Also now tests use both random states and random density matrices.
However, locally tests for me are failing when using qibojit-numba with random density matrices for the PauliError and the ThermalRelaxationError.
Could you please check if you can reproduce the issue?

Copy link
Member

@stavros11 stavros11 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates.

I checked the test issue and why it fails only for density matrix and I believe the behavior is expected. If you don't copy the initial state before passing it to the circuits, the three variables: initial_state, final_state and target_final_state will all point to the same array in memory. So

K.assert_allclose(final_state, target_final_state)

performs a meaningless comparison that this array equals itself and always passes, even if target_circuit is different than circuit. This is not true when using density matrix because the PauliNoiseChannel uses numpy addition which silently creates a copy, so final_state and target_final_state point to different arrays (as they should) and the test fails.

Generally it is a good idea to use a copy when planning to use the same initial state on different circuits on the same script, otherwise the in-place updates may lead to strange behavior as the above.

Regarding this PR, everything looks good to me now. If documentation is added it should be good to go.

@andrea-pasquale andrea-pasquale changed the title [WIP] Add noise model Add noise model Apr 14, 2022
@andrea-pasquale andrea-pasquale marked this pull request as ready for review April 14, 2022 09:45
@andrea-pasquale
Copy link
Contributor Author

I've added the documentation.
If it looks good we should be ready to merge this PR.

Copy link
Member

@stavros11 stavros11 left a 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 docs, looks good to me, some minor comments below.

After reading the updated docs, I believe that at this stage we could remove completely the circuit.with_noise method, since that the new NoiseModel replaces all its functionality and is more clear and flexible. @andrea-pasquale, @scarrazza what do you think?

andrea-pasquale and others added 4 commits April 14, 2022 16:05
@andrea-pasquale
Copy link
Contributor Author

Thanks for the review. I've implemented all your suggestions.

After reading the updated docs, I believe that at this stage we could remove completely the circuit.with_noise method, since that the new NoiseModel replaces all its functionality and is more clear and flexible. @andrea-pasquale, @scarrazza what do you think?

Regarding circuit.with_noise() I am not so sure. It may be useful to have a shortcut to add the same noise after every gate of the circuit instead of building explicitly a custom noise model. A possibility would be to update the circuit.with_noise() such that it accepts a quantum error, such as PauliError, then it updates the circuit by adding that noise after every gate.

@scarrazza
Copy link
Member

I agree, we can keep the with_noise, in some circumstances this API is quite simple and quick to manage.

@scarrazza scarrazza merged commit 69e91b9 into master Apr 20, 2022
@scarrazza scarrazza deleted the noise branch August 17, 2022 07:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Noise model
3 participants