Qiskit / qiskit

Qiskit is an open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives.
https://www.ibm.com/quantum/qiskit
Apache License 2.0
5.16k stars 2.35k forks source link

Large error on expectation value even with zero noise fake backends V2 #11384

Open simonecantori opened 10 months ago

simonecantori commented 10 months ago

If I use V2 fake backends, I can set a certain noise equal to 0 with

backend.target['measure'][(0,)].error = 0

and I can do that for each kind of instruction ('id', 'rz', 'sx', 'x', 'cx', 'measure') and for each qubit.

If I do that, I still have noisy expectation values. The noise is not due to the fine amount of shots since with ideal quantum circuits with the same amount of shots I get much better results.

Which kind of noise is still acting on the circuit?

A reproducible example:

import numpy as np
from qiskit import QuantumCircuit, Aer, transpile
from qiskit_aer import AerSimulator
from qiskit.opflow import CircuitStateFn, Z, I
from sklearn.metrics import mean_absolute_error
from qiskit.providers.fake_provider import FakeAthensV2

op = I^I^I^I^Z

backend = FakeAthensV2()
for k in backend.target.keys():                
    for i in backend.target[k].keys():
        if k != 'reset' and k != 'delay':
            backend.target[k][i].error = 0 # Put the noise equal to 0 for each instruction
sim = AerSimulator.from_backend(backend) # Fake backend with 0 noise simulator

sim_ideal = Aer.get_backend('aer_simulator') # Ideal simulator

shot = 10000 # number of shots per circuit

ideal_expval = []
ideal_shots_expval = []
fake_backend_expval = []
for samples in range(10): # I calculate the mean absolute error of the expectation value over 10 circuits

    theta = np.random.uniform(0,1)

    qc = QuantumCircuit(5)
    for j in range(30):
        for i in range(5):
            qc.rx(theta,i) 
        for i in range(4):
            qc.cx(i,i+1)

    qc = transpile(qc, sim, optimization_level=0)    
    psi = CircuitStateFn(qc)
    exact_expval = psi.adjoint().compose(op).compose(psi).eval().real # exact expectation value
    ideal_expval.append(exact_expval)
    qc.measure_all()

    # Fake Backend
    result = sim.run(qc, shots=shot).result()
    key = [list(i) for i in (list(result.get_counts().keys()))] # output bit strings
    key = np.array(key)
    val = np.array(list(result.get_counts().values())) # output bit strings probabilities
    indices_plus = np.where(key[:, -1] == '0')
    indices_minus = np.where(key[:, -1] != '0')
    expval_fake = np.sum(val[indices_plus]) - np.sum(val[indices_minus]) 
    fake_backend_expval.append(expval_fake/shot)

    # Ideal Backend with shots
    result = sim_ideal.run(qc, shots=shot).result()
    key = [list(i) for i in (list(result.get_counts().keys()))] # output bit strings
    key = np.array(key)
    val = np.array(list(result.get_counts().values())) # output bit strings probabilities
    indices_plus = np.where(key[:, -1] == '0')
    indices_minus = np.where(key[:, -1] != '0')
    expval_ideal_shots = np.sum(val[indices_plus]) - np.sum(val[indices_minus])
    ideal_shots_expval.append(expval_ideal_shots/shot)

print('MAE between exact expectation values and expectation value calculated with ideal backend with 10000 shots:',mean_absolute_error(ideal_expval, ideal_shots_expval))
print('MAE between exact expectation values and expectation value calculated with fake backend with 0 noise:',mean_absolute_error(ideal_expval, fake_backend_expval))

The output is:

MAE between exact expectation values and expectation value calculated with ideal backend with 10000 shots: 0.006748880604561936
MAE between exact expectation values and expectation value calculated with fake backend with 0 noise: 0.06720803337803631
FabianBrings commented 8 months ago

The noise model seems to be added upon initializing the simulator. Even if I explicitly add an empty noise model to the backend beforehand, after initalizing the simulator, there is a noise model present again. If you add:

noise_model = NoiseModel() sim.set_options(noise_model=noise_model)

after sim = AerSimulator.from_backend(backend) # Fake backend with 0 noise simulator it works as expected. The initial values probably get overridden somewhere inside the class. Best regards!

I guess this behaviour is not so relevant as to merit any code updates, since using a FakeBackend to simulate an ideal backend does not seem like a hard usecase.