qadence icon indicating copy to clipboard operation
qadence copied to clipboard

[Feature] Add digital noise from PyQ

Open jpmoutinho opened this issue 5 months ago • 0 comments

Description

Ongoing work in adding the noise from PyQ. Supersedes https://github.com/pasqal-io/qadence/pull/469.

Currently in this branch the basics should be working:

from qadence import NoiseType, DigitalNoise
from qadence import RX, run

noise = DigitalNoise(NoiseType.BITFLIP, error_probability = 0.2)
noise = DigitalNoise.bitflip(error_probability = 0.2) # equivalent

op = RX(0, torch.pi, noise = noise)

run(op)

---

DensityMatrix([[[0.2000+0.0000e+00j, 0.0000+3.6739e-17j],
                [0.0000-3.6739e-17j, 0.8000+0.0000e+00j]]])

Note that currently the DigitalNoise is simply a name alias for the NoiseProtocol class directly from PyQ, which you can find here: https://github.com/pasqal-io/pyqtorch/blob/main/pyqtorch/noise/protocol.py.

The approach in https://github.com/pasqal-io/qadence/pull/469 was instead to change the Noise dataclass in qadence.noise.protocols to include the same options, but then there was already an option there called DEPOLARIZING which got changed to DEPOLARIZING_PYQ... I am not so sure what is the best approach here, this should be revised.

Furthermore, I have also drafted a transpilation function to add noise to gates within a composite block or circuit. It works like this:

from qadence import RX, DigitalNoise, set_noise, chain, run

n_qubits = 2

block = chain(RX(i, f"theta_{i}") for i in range(n_qubits))

noise = DigitalNoise.bitflip(error_probability = 0.1)

# The function changes the block in place:
set_noise(block, noise)

run(block)

The code above should add the defined bitflip protocol to every gate in the block. It should also work if it is a QuantumCircuit. There is an extra optional argument to specify the type of block we want to apply noise to. E.g., let's say we want to apply noise only to X gates, then it will run an if isinstance(block, target_class): ... at every leaf block in the block tree:

from qadence import RX, X, DigitalNoise, set_noise, chain, run

n_qubits = 2

block = chain(RX(i, f"theta_{i}") for i in range(n_qubits))

noise = DigitalNoise.bitflip(error_probability = 0.1)

# The function changes the block in place:
set_noise(block, noise, target_class = X)

run(block)

The snippet above will just return a statevector because there exists no X gates in that composite block. We can also try to print the noise for each of the gates and see that it works:

from qadence import RX, X, DigitalNoise, set_noise, chain, run

n_qubits = 2

block = chain(RX(0, "theta"), X(0))

noise = DigitalNoise.bitflip(error_probability = 0.1)

set_noise(block, noise, target_class = X)

for block in block.blocks:
    print(block.noise)

---

> None
> BitFlip(prob: 0.1)

What is left to do:

  • Serialization: to check the _to_dict and _from_dict stuff in https://github.com/pasqal-io/qadence/pull/469
  • Including the noise in each block string: if added, check how it behaves with visualization.
  • Properly review the code, and check if something else from https://github.com/pasqal-io/qadence/pull/469 should be added. See about harmonizing the DigitalNoise with the Noise and etc... I like the interface of the NoiseProtocol from pyq, so it would be nice to still keep it when using it in Qadence.
  • Add tests! While writing some finals things for this MR description I think I ran an instance of set_noise that did not apply the noise correctly... But now I was trying to replicate it to put it here and I couldn't 😅 So yes for sure test it thoroughly.
  • Docs! I think this should have a nice separate page in the Contents section: https://pasqal-io.github.io/qadence/latest/content/block_system/

jpmoutinho avatar Sep 06 '24 15:09 jpmoutinho