qiskit-aer icon indicating copy to clipboard operation
qiskit-aer copied to clipboard

Aer stabilizer simulator measurement outcomes depend on whether other measurements are done concurrently for large circuits

Open sam-craig-smith opened this issue 1 year ago • 3 comments

Informations

  • Qiskit Aer version: 0.12.2
  • Python version: 3.9.6
  • Operating system:
    • Name of the operating system: posix
    • Name of the OS system: Darwin
    • Version of the operating system: 22.5.0

What is the current behavior?

I have been using large Clifford circuits (65 qubits total, 33 data qubit register, 32 ancilla qubit register, and one 32 bit classical register). The circuits consist of hadamards, cnots, resets, and measurement. I have found some strange behaviour in the measurement statistics that I see when run with the Aer stabilizer simulator.

In particular, the statistics from a measurement of one ancilla qubit depend on whether other measurements are performed concurrently on other ancilla qubits. This should not be the case. I see some ancilla qubit measurements deterministally give a 0 outcome when other measurements are performed concurrently, and if the other measurements are removed from the circuit then a deterministic 1 outcome is seen. (The 0 outcomes is the one that I expect).

Steps to reproduce the problem

I have an example of a circuit that demonstrates this behaviour. The circuit is a large example, I when I look at smaller circuits this behaviour disappears, so this is the smallest example I have.

Here is a circuit when all ancilla are measured

circ_measure_all_ancilla.pdf

And here is a plot of the outcomes seen for each ancilla measurement over 10000 counts:

counts_measure_all_ancilla.pdf

Now, here is the same circuit but with some measurements removed:

circ_do_not_measure_all_ancilla.pdf

And here is the plot of the outcomes for the second circuit, again over 10000 counts

counts_do_not_measure_all_ancilla.pdf

The code to produce this output is:

from qiskit import qasm3, Aer, transpile, ClassicalRegister 
from qiskit import ClassicalRegister
import copy 
from matplotlib import pyplot as plt 
import numpy as np 

circ = qasm3.loads("""OPENQASM 3;
include "stdgates.inc";
qubit[33] qreg_dat;
qubit[32] qreg_anc;
h qreg_anc[0];
h qreg_anc[4];
h qreg_anc[6];
h qreg_anc[10];
h qreg_anc[12];
h qreg_anc[16];
h qreg_anc[18];
h qreg_anc[22];
h qreg_anc[24];
h qreg_anc[28];
h qreg_anc[30];
h qreg_anc[31];
barrier qreg_dat[0], qreg_dat[1], qreg_dat[2], qreg_dat[3], qreg_dat[4], qreg_dat[5], qreg_dat[6], qreg_dat[7], qreg_dat[8], qreg_dat[9], qreg_dat[10], qreg_dat[11], qreg_dat[12], qreg_dat[13], qreg_dat[14], qreg_dat[15], qreg_dat[16], qreg_dat[17], qreg_dat[18], qreg_dat[19], qreg_dat[20], qreg_dat[21], qreg_dat[22], qreg_dat[23], qreg_dat[24], qreg_dat[25], qreg_dat[26], qreg_dat[27], qreg_dat[28], qreg_dat[29], qreg_dat[30], qreg_dat[31], qreg_dat[32], qreg_anc[0], qreg_anc[1], qreg_anc[2], qreg_anc[3], qreg_anc[4], qreg_anc[5], qreg_anc[6], qreg_anc[7], qreg_anc[8], qreg_anc[9], qreg_anc[10], qreg_anc[11], qreg_anc[12], qreg_anc[13], qreg_anc[14], qreg_anc[15], qreg_anc[16], qreg_anc[17], qreg_anc[18], qreg_anc[19], qreg_anc[20], qreg_anc[21], qreg_anc[22], qreg_anc[23], qreg_anc[24], qreg_anc[25], qreg_anc[26], qreg_anc[27], qreg_anc[28], qreg_anc[29], qreg_anc[30], qreg_anc[31];
cx qreg_dat[1], qreg_anc[1];
cx qreg_dat[3], qreg_anc[3];
cx qreg_dat[5], qreg_anc[5];
cx qreg_dat[7], qreg_anc[7];
cx qreg_dat[9], qreg_anc[9];
cx qreg_dat[11], qreg_anc[11];
cx qreg_dat[13], qreg_anc[13];
cx qreg_dat[15], qreg_anc[15];
cx qreg_dat[17], qreg_anc[17];
cx qreg_dat[19], qreg_anc[19];
cx qreg_dat[21], qreg_anc[21];
cx qreg_dat[23], qreg_anc[23];
cx qreg_dat[25], qreg_anc[25];
cx qreg_dat[27], qreg_anc[27];
cx qreg_dat[29], qreg_anc[29];
cx qreg_anc[0], qreg_dat[0];
cx qreg_anc[4], qreg_dat[4];
cx qreg_anc[6], qreg_dat[6];
cx qreg_anc[10], qreg_dat[10];
cx qreg_anc[12], qreg_dat[12];
cx qreg_anc[16], qreg_dat[16];
cx qreg_anc[18], qreg_dat[18];
cx qreg_anc[22], qreg_dat[22];
cx qreg_anc[24], qreg_dat[24];
cx qreg_anc[28], qreg_dat[28];
cx qreg_anc[30], qreg_dat[30];
barrier qreg_dat[0], qreg_dat[1], qreg_dat[2], qreg_dat[3], qreg_dat[4], qreg_dat[5], qreg_dat[6], qreg_dat[7], qreg_dat[8], qreg_dat[9], qreg_dat[10], qreg_dat[11], qreg_dat[12], qreg_dat[13], qreg_dat[14], qreg_dat[15], qreg_dat[16], qreg_dat[17], qreg_dat[18], qreg_dat[19], qreg_dat[20], qreg_dat[21], qreg_dat[22], qreg_dat[23], qreg_dat[24], qreg_dat[25], qreg_dat[26], qreg_dat[27], qreg_dat[28], qreg_dat[29], qreg_dat[30], qreg_dat[31], qreg_dat[32], qreg_anc[0], qreg_anc[1], qreg_anc[2], qreg_anc[3], qreg_anc[4], qreg_anc[5], qreg_anc[6], qreg_anc[7], qreg_anc[8], qreg_anc[9], qreg_anc[10], qreg_anc[11], qreg_anc[12], qreg_anc[13], qreg_anc[14], qreg_anc[15], qreg_anc[16], qreg_anc[17], qreg_anc[18], qreg_anc[19], qreg_anc[20], qreg_anc[21], qreg_anc[22], qreg_anc[23], qreg_anc[24], qreg_anc[25], qreg_anc[26], qreg_anc[27], qreg_anc[28], qreg_anc[29], qreg_anc[30], qreg_anc[31];
cx qreg_dat[4], qreg_anc[1];
cx qreg_dat[6], qreg_anc[3];
cx qreg_dat[8], qreg_anc[5];
cx qreg_dat[10], qreg_anc[7];
cx qreg_dat[12], qreg_anc[9];
cx qreg_dat[14], qreg_anc[11];
cx qreg_dat[16], qreg_anc[13];
cx qreg_dat[18], qreg_anc[15];
cx qreg_dat[20], qreg_anc[17];
cx qreg_dat[22], qreg_anc[19];
cx qreg_dat[24], qreg_anc[21];
cx qreg_dat[26], qreg_anc[23];
cx qreg_dat[28], qreg_anc[25];
cx qreg_dat[30], qreg_anc[27];
cx qreg_dat[32], qreg_anc[29];
cx qreg_anc[0], qreg_dat[1];
cx qreg_anc[4], qreg_dat[5];
cx qreg_anc[6], qreg_dat[7];
cx qreg_anc[10], qreg_dat[11];
cx qreg_anc[12], qreg_dat[13];
cx qreg_anc[16], qreg_dat[17];
cx qreg_anc[18], qreg_dat[19];
cx qreg_anc[22], qreg_dat[23];
cx qreg_anc[24], qreg_dat[25];
cx qreg_anc[28], qreg_dat[29];
cx qreg_anc[30], qreg_dat[31];
barrier qreg_dat[0], qreg_dat[1], qreg_dat[2], qreg_dat[3], qreg_dat[4], qreg_dat[5], qreg_dat[6], qreg_dat[7], qreg_dat[8], qreg_dat[9], qreg_dat[10], qreg_dat[11], qreg_dat[12], qreg_dat[13], qreg_dat[14], qreg_dat[15], qreg_dat[16], qreg_dat[17], qreg_dat[18], qreg_dat[19], qreg_dat[20], qreg_dat[21], qreg_dat[22], qreg_dat[23], qreg_dat[24], qreg_dat[25], qreg_dat[26], qreg_dat[27], qreg_dat[28], qreg_dat[29], qreg_dat[30], qreg_dat[31], qreg_dat[32], qreg_anc[0], qreg_anc[1], qreg_anc[2], qreg_anc[3], qreg_anc[4], qreg_anc[5], qreg_anc[6], qreg_anc[7], qreg_anc[8], qreg_anc[9], qreg_anc[10], qreg_anc[11], qreg_anc[12], qreg_anc[13], qreg_anc[14], qreg_anc[15], qreg_anc[16], qreg_anc[17], qreg_anc[18], qreg_anc[19], qreg_anc[20], qreg_anc[21], qreg_anc[22], qreg_anc[23], qreg_anc[24], qreg_anc[25], qreg_anc[26], qreg_anc[27], qreg_anc[28], qreg_anc[29], qreg_anc[30], qreg_anc[31];
cx qreg_dat[3], qreg_anc[0];
cx qreg_dat[5], qreg_anc[2];
cx qreg_dat[7], qreg_anc[4];
cx qreg_dat[9], qreg_anc[6];
cx qreg_dat[11], qreg_anc[8];
cx qreg_dat[13], qreg_anc[10];
cx qreg_dat[15], qreg_anc[12];
cx qreg_dat[17], qreg_anc[14];
cx qreg_dat[19], qreg_anc[16];
cx qreg_dat[21], qreg_anc[18];
cx qreg_dat[23], qreg_anc[20];
cx qreg_dat[25], qreg_anc[22];
cx qreg_dat[27], qreg_anc[24];
cx qreg_dat[29], qreg_anc[26];
cx qreg_dat[31], qreg_anc[28];
cx qreg_anc[1], qreg_dat[2];
cx qreg_anc[3], qreg_dat[4];
cx qreg_anc[7], qreg_dat[8];
cx qreg_anc[9], qreg_dat[10];
cx qreg_anc[13], qreg_dat[14];
cx qreg_anc[15], qreg_dat[16];
cx qreg_anc[19], qreg_dat[20];
cx qreg_anc[21], qreg_dat[22];
cx qreg_anc[25], qreg_dat[26];
cx qreg_anc[27], qreg_dat[28];
cx qreg_anc[31], qreg_dat[32];
barrier qreg_dat[0], qreg_dat[1], qreg_dat[2], qreg_dat[3], qreg_dat[4], qreg_dat[5], qreg_dat[6], qreg_dat[7], qreg_dat[8], qreg_dat[9], qreg_dat[10], qreg_dat[11], qreg_dat[12], qreg_dat[13], qreg_dat[14], qreg_dat[15], qreg_dat[16], qreg_dat[17], qreg_dat[18], qreg_dat[19], qreg_dat[20], qreg_dat[21], qreg_dat[22], qreg_dat[23], qreg_dat[24], qreg_dat[25], qreg_dat[26], qreg_dat[27], qreg_dat[28], qreg_dat[29], qreg_dat[30], qreg_dat[31], qreg_dat[32], qreg_anc[0], qreg_anc[1], qreg_anc[2], qreg_anc[3], qreg_anc[4], qreg_anc[5], qreg_anc[6], qreg_anc[7], qreg_anc[8], qreg_anc[9], qreg_anc[10], qreg_anc[11], qreg_anc[12], qreg_anc[13], qreg_anc[14], qreg_anc[15], qreg_anc[16], qreg_anc[17], qreg_anc[18], qreg_anc[19], qreg_anc[20], qreg_anc[21], qreg_anc[22], qreg_anc[23], qreg_anc[24], qreg_anc[25], qreg_anc[26], qreg_anc[27], qreg_anc[28], qreg_anc[29], qreg_anc[30], qreg_anc[31];
cx qreg_dat[0], qreg_anc[0];
cx qreg_dat[2], qreg_anc[2];
cx qreg_dat[4], qreg_anc[4];
cx qreg_dat[6], qreg_anc[6];
cx qreg_dat[8], qreg_anc[8];
cx qreg_dat[10], qreg_anc[10];
cx qreg_dat[12], qreg_anc[12];
cx qreg_dat[14], qreg_anc[14];
cx qreg_dat[16], qreg_anc[16];
cx qreg_dat[18], qreg_anc[18];
cx qreg_dat[20], qreg_anc[20];
cx qreg_dat[22], qreg_anc[22];
cx qreg_dat[24], qreg_anc[24];
cx qreg_dat[26], qreg_anc[26];
cx qreg_dat[28], qreg_anc[28];
cx qreg_anc[1], qreg_dat[1];
cx qreg_anc[3], qreg_dat[3];
cx qreg_anc[7], qreg_dat[7];
cx qreg_anc[9], qreg_dat[9];
cx qreg_anc[13], qreg_dat[13];
cx qreg_anc[15], qreg_dat[15];
cx qreg_anc[19], qreg_dat[19];
cx qreg_anc[21], qreg_dat[21];
cx qreg_anc[25], qreg_dat[25];
cx qreg_anc[27], qreg_dat[27];
cx qreg_anc[31], qreg_dat[31];
barrier qreg_dat[0], qreg_dat[1], qreg_dat[2], qreg_dat[3], qreg_dat[4], qreg_dat[5], qreg_dat[6], qreg_dat[7], qreg_dat[8], qreg_dat[9], qreg_dat[10], qreg_dat[11], qreg_dat[12], qreg_dat[13], qreg_dat[14], qreg_dat[15], qreg_dat[16], qreg_dat[17], qreg_dat[18], qreg_dat[19], qreg_dat[20], qreg_dat[21], qreg_dat[22], qreg_dat[23], qreg_dat[24], qreg_dat[25], qreg_dat[26], qreg_dat[27], qreg_dat[28], qreg_dat[29], qreg_dat[30], qreg_dat[31], qreg_dat[32], qreg_anc[0], qreg_anc[1], qreg_anc[2], qreg_anc[3], qreg_anc[4], qreg_anc[5], qreg_anc[6], qreg_anc[7], qreg_anc[8], qreg_anc[9], qreg_anc[10], qreg_anc[11], qreg_anc[12], qreg_anc[13], qreg_anc[14], qreg_anc[15], qreg_anc[16], qreg_anc[17], qreg_anc[18], qreg_anc[19], qreg_anc[20], qreg_anc[21], qreg_anc[22], qreg_anc[23], qreg_anc[24], qreg_anc[25], qreg_anc[26], qreg_anc[27], qreg_anc[28], qreg_anc[29], qreg_anc[30], qreg_anc[31];
""")
                
# add all measurements 

circ_all_meas = copy.deepcopy(circ) 
qreg_anc = circ_all_meas.qregs[1] 
creg = ClassicalRegister(32) 
circ_all_meas.add_register(creg) 
for i in range(32): 
    circ_all_meas.measure(qreg_anc[i], creg[i])

fig, ax = plt.subplots() 
circ_all_meas.draw("mpl", scale = 0.6, fold = -1, ax=ax  )
fig.savefig("circ_measure_all_ancilla.pdf")

sim = Aer.get_backend("aer_simulator_stabilizer") 
circ_all_meas = transpile(circ_all_meas, sim)
result = sim.run(circ_all_meas, shots=10000).result()
counts = result.get_counts()  

n_anc = 32 
totals = np.zeros(n_anc, dtype=int) 
for outcomes, num_counts in counts.items(): 
    new_totals = num_counts * np.array([int(bit) for bit in outcomes][::-1])
    assert len(new_totals) == n_anc  # double check qiskit returns outcomes for bits with no attached measurements 
    totals += new_totals  # reverse for list indexing 

fig, ax = plt.subplots() 
xs = np.arange(len(totals)) 
ax.bar(xs, totals)
ax.plot(xs, totals, linestyle='', marker='.')  # so that deterministically zero outcomes can be seen 
ax.set_xlabel("Ancilla qubit index")
ax.set_ylabel("Counts of 1")
ax.set_title("Measure all ancilla") 
fig.savefig("counts_measure_all_ancilla.pdf")

# do not measure some 

circ_some_meas = copy.deepcopy(circ) 
qreg_anc = circ_some_meas.qregs[1] 
creg = ClassicalRegister(32) 
circ_some_meas.add_register(creg) 
do_not_measure = [1, 3, 7] 
for i in range(32): 
    if i not in do_not_measure: 
        circ_some_meas.measure(qreg_anc[i], creg[i])

fig, ax = plt.subplots() 
circ_some_meas.draw("mpl", scale = 0.6, fold = -1 , ax=ax)
fig.savefig("circ_do_not_measure_all_ancilla.pdf")

sim = Aer.get_backend("aer_simulator_stabilizer") 
circ_some_meas = transpile(circ_some_meas, sim)
result = sim.run(circ_some_meas, shots=10000).result()
counts = result.get_counts()  

n_anc = 32 
totals = np.zeros(n_anc, dtype=int) 
for outcomes, num_counts in counts.items(): 
    new_totals = num_counts * np.array([int(bit) for bit in outcomes][::-1])
    assert len(new_totals) == n_anc  # double check qiskit returns outcomes for bits with no attached measurements 
    totals += new_totals  # reverse for list indexing 

fig, ax = plt.subplots() 
xs = np.arange(len(totals)) 
ax.bar(xs, totals)
ax.plot(xs, totals, linestyle='', marker='.')  # so that deterministically zero outcomes can be seen 
ax.set_title("Do not measure all ancilla")
ax.set_xlabel("Ancilla qubit index")
ax.set_ylabel("Counts of 1")
fig.savefig("counts_do_not_measure_all_ancilla.pdf")
    

What is the expected behavior?

The expected behavior is that measurement statistics of a qubit should not depend on whether other measurements were performed on a different qubit concurrently.

Suggested solutions

I don't know what is causing this problem, the only suggestion that I have is that it only appears when I looked at larger code instances.

sam-craig-smith avatar Aug 06 '23 20:08 sam-craig-smith

I found issue in measurement code in stabilizer simulator. I'm now fixing it

doichanj avatar Aug 16 '23 02:08 doichanj

ah ah, what is your meaning of statistics @sam-craig-smith? results outcome of statistics must depend the variants of input params

bkavcaduhq avatar Aug 16 '23 02:08 bkavcaduhq

By statistics I mean what percentage of the time you see +1 outcomes compared to 0 outcomes. In the case of this circuit, each measurement outcome is either 0 all the time, or 0 with 50% probability and 1 with 50% probability, or 1 all the time.

The actual statistics seen depends on the circuit of course, but regardless of the circuit it shouldn't be the case that the statistics of measurement outcomes on one qubit depends on whether another measurement on another qubit was done concurrently.

sam-craig-smith avatar Aug 17 '23 00:08 sam-craig-smith