Closed sportwagon closed 2 years ago
This is because measure_all
/measure_active
add a new creg
to the circuit, so the returned results are over 4 cbits instead of the expected 2. This is documented in the docstrings, but even still may be unexpected behavior.
What would the expected behavior be here? If no creg is present? If a creg exists? But its size is larger/smaller than num_qubits/num_active_qubits?
ping @sportwagon
I observed similar behavior in a 2 qreg
, 2 creg
circuit using the circuit below:
qc = QuantumCircuit(2,2)
qc.h([0,1])
qc.measure_all()
print("measure_all() circuit, correct # cregs declared: \n")
print(qc.qasm())
job = execute(qc, backend, shots=4000)
res = job.result()
my_counts = res.get_counts()
print ("Counts returned by get_counts:")
print (my_counts)
print ("data in result object")
print (res.data(), '\n')
Output when run on qasm_simulator
:
measure_all() circuit, correct # cregs declared:
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
creg meas[2];
h q[0];
h q[1];
barrier q[0],q[1];
measure q[0] -> meas[0];
measure q[1] -> meas[1];
Counts returned by get_counts:
{'00 00': 1028, '01 00': 969, '10 00': 1004, '11 00': 999}
data in result object
{'counts': {'0x0': 1028, '0x4': 969, '0x8': 1004, '0xc': 999}}
If this circuit is declared without a creg
and relies on the one in measure_all
, it behaves no differently than if it were explicitly measured.
qc2 = QuantumCircuit(2,2)
qc2.h([0,1])
qc2.barrier()
qc2.measure([0,1],[0,1])
print("Normal measure circuit, correct # cregs declared: \n")
print(qc2.qasm())
job2 = execute(qc2, backend, shots=4000)
res2 = job2.result()
my_counts2 = res2.get_counts()
print ("Counts returned by get_counts:")
print (my_counts2)
print ("data in result object")
print (res2.data(), '\n')
qc3 = QuantumCircuit(2)
qc3.h([0,1])
qc3.measure_all()
print("measure_all() circuit, cregs not declared: \n")
print(qc3.qasm())
job3 = execute(qc3, backend, shots=4000)
res3 = job3.result()
my_counts3 = res3.get_counts()
print ("Counts returned by get_counts:")
print (my_counts3)
print ("data in result object")
print (res3.data(), '\n')
Both versions' outputs when run on qasm_simulator
:
Normal measure circuit, correct # cregs declared:
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
h q[1];
barrier q[0],q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];
Counts returned by get_counts:
{'00': 999, '01': 989, '10': 1033, '11': 979}
data in result object
{'counts': {'0x0': 999, '0x1': 989, '0x2': 1033, '0x3': 979}}
measure_all() circuit, cregs not declared:
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg meas[2];
h q[0];
h q[1];
barrier q[0],q[1];
measure q[0] -> meas[0];
measure q[1] -> meas[1];
Counts returned by get_counts:
{'00': 939, '01': 1035, '10': 1039, '11': 987}
data in result object
{'counts': {'0x0': 939, '0x1': 1035, '0x2': 1039, '0x3': 987}}
As stated earlier, the discrepancy is definitely not the result of the barrier but instead the arbitrary addition of two classical registers.
If a QuantumCircuit
is declared with a creg
size not equal to num_active_qubits
, the behavior of measure_all
does not change. It continues to add a creg
of the correct size and outputs measurements larger than expected.
qc4 = QuantumCircuit(2,1)
qc4.h([0,1])
qc4.measure_all()
print("measure_all() circuit, too few cregs declared: \n")
print(qc4.qasm())
job4 = execute(qc4, backend, shots=4000)
res4 = job4.result()
my_counts4 = res4.get_counts()
print ("Counts returned by get_counts:")
print (my_counts4)
print ("data in result object")
print (res4.data(), '\n')
qc5 = QuantumCircuit(2,3)
qc5.h([0,1])
qc5.measure_all()
print("measure_all() circuit, too many cregs declared: \n")
print(qc5.qasm())
job5 = execute(qc5, backend, shots=4000)
res5 = job5.result()
my_counts5 = res5.get_counts()
print ("Counts returned by get_counts:")
print (my_counts5)
print ("data in result object")
print (res5.data(), '\n')
Output on qasm_simulator
:
measure_all() circuit, too few cregs declared:
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[1];
creg meas[2];
h q[0];
h q[1];
barrier q[0],q[1];
measure q[0] -> meas[0];
measure q[1] -> meas[1];
Counts returned by get_counts:
{'00 0': 987, '01 0': 1023, '10 0': 998, '11 0': 992}
data in result object
{'counts': {'0x0': 987, '0x2': 1023, '0x4': 998, '0x6': 992}}
measure_all() circuit, too many cregs declared:
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[3];
creg meas[2];
h q[0];
h q[1];
barrier q[0],q[1];
measure q[0] -> meas[0];
measure q[1] -> meas[1];
Counts returned by get_counts:
{'00 000': 1000, '10 000': 1017, '11 000': 1027, '01 000': 956}
data in result object
{'counts': {'0x0': 1000, '0x10': 1017, '0x18': 1027, '0x8': 956}}
I think a possible solution is to give measure_all an option to include the extra creg, but exclude it by default if one is already declared in the QuantumCircuit. The elongated binary data may be confusing to beginners, but it may have functionality that we are unaware of here.
Measure_all adds its own classical register, so you should not define one in QuantumCircuit.
Am aware, but there ought to be error checking for those who aren't.
Would this solution be acceptable?
If same size:
from qiskit import *
qc = QuantumCircuit(2, 2)
qc.h([0,1])
qc.measure_all(new_creg=False)
qc.draw()
┌───┐ ░ ┌─┐
q_0: ┤ H ├─░─┤M├───
├───┤ ░ └╥┘┌─┐
q_1: ┤ H ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
c: 2/═════════╩══╩═
0 1
If smaller size (or maybe just error?):
qc = QuantumCircuit(2, 1)
qc.h([0,1])
qc.measure_all(new_creg=False)
print(qc)
┌───┐ ░ ┌─┐
q_0: ┤ H ├─░─┤M├───
├───┤ ░ └╥┘┌─┐
q_1: ┤ H ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
c: 1/═════════╩══╬═
0 ║
║
meas: 1/════════════╩═
0
If larger size (or maybe just error?):
qc = QuantumCircuit(2, 3)
qc.h([0,1])
qc.measure_all(new_creg=False)
print(qc)
┌───┐ ░ ┌─┐
q_0: ┤ H ├─░─┤M├───
├───┤ ░ └╥┘┌─┐
q_1: ┤ H ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
c: 3/═════════╩══╩═
0 1
Default is new_creg=True
to keep current behavior.
ping @sclang16 @sportwagon ?
Yes, this looks to be a good solution.
Changing to feature request
and renaming it. Thanks!
I like the proposed solution by @1ucian0 and I'd be willing to fix this bug if it's possible. Can I get assigned to it?
Thanks! I've assigned you (after a false start...).
Given that the last discussion didn't seem to finish completely, I'd suggest that it might be best just to implement the behaviour of new_creg=False
be to work if the number of clbits in the circuit is exactly equal to the number of qubits, and raise CircuitError
if not, at least at the beginning. If others further respond in this issue or your forthcoming PR, you could always add extra functionality. After a very quick read over the discussion, this only seems to make sense to me with new_creg=False
if there's an exact match, and maybe if there are more clbits than qubits, but then you could easily argue about which clbits you should use. Better just to raise an error in those cases, since the original issue was about "why is the number of clbits different after this function"?
Information
What is the current behavior?
If you use
measure_all
rather thanmeasure
the count values returned byget_counts
have extra data in the key values. This seems to be directly related tomeasure_all
inserting a barrier before the measurement, as this is the only difference in the resulting circuits. See section on reproducing the bug for the exact behaviorSteps to reproduce the problem
The following code will reproduce the problem
When run on
ibmq_almaden
it generates the following outputAs can be seen from the counts object in the result, the values returned from the backend are correct, but the keys in the dictionary returned by
get_counts
have an extra set of00
s in them. That spurious field is as long as the number of qubits in the circuit (e.g. if we do the same thing with 3 qubits we would see000
included at the end of each key).What is the expected behavior?
measure_all
or any measure with a barrier in front should return counts correctly.Suggested solutions