Shortcuts

Utility functions

There are a couple of functionalities that are needed for easy mitigation, but are not part of the Qiskit SDK and thus are included in M3. Each is detailed below.

Final-measurement mapping

We have already seen the mthree.utils.final_measurement_mapping() routine in the section on detailing with Transpiled circuits, where the addition of SWAP gates made it difficult to determine exactly which physical qubits are being used, and which classical bits they correspond to. Here we show another example of this usage. First we start with a 7-qubit GHZ state:

from qiskit import *
from qiskit.test.mock.backends import FakeCasablanca
import mthree

qc = QuantumCircuit(7)
qc.h(0)
qc.cx(0, range(1,7))
qc.measure_all()
qc.draw('mpl')
_images/utils_0_0.png

and then transpile it:

backend = FakeCasablanca()
trans_qc = transpile(qc, backend, optimization_level=3, seed_transpiler=54321)
trans_qc.draw('mpl')
_images/utils_1_0.png

Once again we see that the physical qubits used and to which classical bits they map to is non-trivial to find. Yet this information is critical for successfully mitigating the results. This is where the mthree.utils.final_measurement_mapping() plays a critical role:

mapping = mthree.utils.final_measurement_mapping(trans_qc)
mapping
{1: 0, 6: 1, 5: 2, 4: 3, 2: 4, 3: 5, 0: 6}

The keys of this mapping index the physical qubits that are measured, whereas the values tell you which classical bits the corresponding measurements are stored in. The mapping is ordered in terms of the classical bit indices. This allows one to simply do

list(mapping)
[1, 6, 5, 4, 2, 3, 0]

to get the needed qubits in the correct order. You can also just pass the generated mapping into M3 functions and the required conversion will be done internally.

Evaluating raw counts data

When mitigating results one often wants to know how much better the results are with mitigation compared to without. However, Qiskit does not have great support for computing things like expectation values. So M3 includes the generic functions mthree.utils.expval(), mthree.utils.stddev(), and mthree.utils.expval_and_stddev() that operate on the native Counts objects in Qiskit.

For example let us compare raw data verse the mitigated results in a simple case.

from qiskit.test.mock.backends import FakeAthens
backend = FakeAthens()
qc = QuantumCircuit(4)
qc.h(2)
qc.cx(2, 1)
qc.cx(2, 3)
qc.cx(1, 0)
qc.measure_all()

raw_counts = execute(qc, backend).result().get_counts()
mit = mthree.M3Mitigation(backend)
mit.cals_from_system()
mit_counts = mit.apply_correction(raw_counts, qubits=range(4),
                                  return_mitigation_overhead=True)

print('Raw counts expval', mthree.utils.expval(raw_counts))
print('Mitigated expval', mit_counts.expval())
Raw counts expval 0.828125
Mitigated expval 0.9346144341459945

We can also compare things like upper-bounds on the standard deviation:

print('Raw counts uncertainty', mthree.utils.stddev(raw_counts))
print('Mitigated uncertainty', mit_counts.stddev())
Raw counts uncertainty 0.03125
Mitigated uncertainty 0.03781634888735359

where the uncertainty for the raw Counts data is just \(1/\sqrt{\rm{shots}}\).

These convenence functions work in the same manner as the methods for the distribution classes mthree.classes.QuasiDistribution and mthree.classes.ProbDistribution and collections mthree.classes.QuasiCollection and mthree.classes.ProbCollection. That is to say that, for example, I can pass operators to expval function:

print('These should be equal:', mthree.utils.expval(raw_counts, 'IIII'),
      mit_counts.expval('IIII'))
These should be equal: 1.0 1.0

The routines also allow you to pass the native M3 distributions and collections. E.g.

print(mthree.utils.expval(mit_counts), mit_counts.expval())
0.9346144341459945 0.9346144341459945

Finally we note that you can pass multiple values at the same time. Here we run and mitigate 5 circuits:

raw_counts = execute([qc]*5, backend).result().get_counts()
mit = mthree.M3Mitigation(backend)
mit.cals_from_system()
mit_counts = mit.apply_correction(raw_counts, qubits=range(4),
                                  return_mitigation_overhead=True)


print('Raw counts expval', mthree.utils.expval(raw_counts))
print('Mitigated expval', mit_counts.expval())
Raw counts expval [0.84179688 0.83203125 0.8515625  0.82226562 0.83398438]
Mitigated expval [0.95394607 0.93916368 0.96081077 0.93267396 0.9462507 ]