Advanced usage

Options for M3Mitigation class

iter_threshold

The main mthree.M3Mitigation class accepts the iter_threshold keyword argument that determines when the automated method selector pickes the iterative method over direct LU factorization (selection also depends on free memory).

from qiskit_ibm_runtime.fake_provider import FakeCasablancaV2
import mthree

backend = FakeCasablancaV2()
mit = mthree.M3Mitigation(backend, iter_threshold=4321)

Options for calibration

shots

When calibrating the mitigator, it is possible to vary the number of shots per calibration circuit:

mit = mthree.M3Mitigation(backend)
mit.cals_from_system(range(5), shots=4321)
[<qiskit_aer.jobs.aerjob.AerJob at 0x7fbbd83d4fa0>]

method

There are three ways to do the calibration. The default balanced method executes \(2N\) circuits with varying bitstring patterns such that the \(|0\rangle\) and \(|1\rangle\) states are each prepared \(N\) times and averaged over. For example, the balanced bit-strings over four qubits are

mthree.circuits.balanced_cal_strings(4)
['0101', '1010', '0011', '1100', '0001', '1110', '0000', '1111']

The independent method also sends \(2N\) circuits but measures only a single qubit at a time. As such, this is a truely uncorrelated calibration process.

Finally, a marginal calibration can also be done that sends only two circuits, \(|0\rangle^{\otimes N}\) and \(|1\rangle^{\otimes N}\), and marginalizes over the results to get the needed \(2N\) error rates. These two states are a sub-set of the balanced calibrations, and are what Qiskit uses in its readout mitigation. This method is very sensitive to state preparation errors, and should not be used in practice.

An example setting the method is

mit = mthree.M3Mitigation(backend)
mit.cals_from_system(range(5), method='independent')
[<qiskit_aer.jobs.aerjob.AerJob at 0x7fbbd833a050>]

rep_delay

Caution

Do not set this unless you know what you are doing.

The rep_delay keyword argument sets the time between calibration circuits on IBM Quantum systems. This option exists to test and reduce the presence of state prep errors. The calibration circuits in M3 all contain conditional resets at the beginning so as to minimize the need for setting this option.

mit = mthree.M3Mitigation(backend)
mit.cals_from_system(range(5), rep_delay=400e-6)
[<qiskit_aer.jobs.aerjob.AerJob at 0x7fbbd83d50f0>]

initial_reset

Caution

Do not set this unless you know what you are doing.

A boolean value that specifies whether reset instructions should be used at the beginning of the calibration circuits. Ideally this helps to supress any residual state prep errors that occur from imperfect reset of the qubits. Can be used in concert with, or as an alternative to rep_delay. Note that, in order for this to work, the circuits that need to be mitigated must also have reset instructions at the beginning. Otherwise you are calibrating for no state-prep errors, but the actual circuits may still suffer from these errors.

Options when applying corrections

Here we first calibrate a mitigator and generate raw counts:

from qiskit import *

qc = QuantumCircuit(6)
qc.reset(range(6))
qc.h(3)
qc.cx(3,1)
qc.cx(3,5)
qc.cx(1,0)
qc.cx(5,4)
qc.cx(1,2)
qc.measure_all()

mit = mthree.M3Mitigation(backend)
mit.cals_from_system(range(6))

trans_qc = transpile(qc, backend)
raw_counts = backend.run(trans_qc, shots=8192).result().get_counts()

method

There are two ways to solve the linear system of equations generated by M3. First, the direct method uses LU-factorization by constructing the reduced assignment matrix. Second the iterative method uses preconditioned iterative solvers to find the solution vector without explicit matrix construction. By default M3 uses an auto method that selects the appropriate solution method based on the number of unique bit-strings and the available free memory on the computer. To override this, one can simply set the option:

quasis = mit.apply_correction(raw_counts, range(6), method='iterative')

distance

Optionally one may trucate the M3 assignment matrix to only those elements of the matrix that are transistions between elements less than or equal to a given Hamming distance away from each other. This does not change the dimensionality of the underlying matrix, but rather changes the sparsity pattern of the elements. This is done using the distance keyword argument. By default, M3 computes the corrections out to the full distance. In practice, including only up to distance=3 elements yields accurate answers in most cases.

quasis = mit.apply_correction(raw_counts, range(6), distance=3)

details

Allows one to see additional information about the solution. This changes the return of the :method:mthree.M3Mitigation.apply_correction method to a tuple of two values:

quasis, details = mit.apply_correction(raw_counts, range(6), details=True)
print(details)
{'method': 'direct', 'time': 0.0001660210000125062, 'dimension': 44, 'col_norms': array([0.9991987 , 0.9628587 , 0.98538595, 0.97123784, 0.98513776,
       0.99302775, 0.97786385, 0.9782937 , 0.9859417 , 0.9609599 ,
       0.948159  , 0.9786799 , 0.9984028 , 0.9780926 , 0.95648533,
       0.92860085, 0.9835394 , 0.9321074 , 0.9581357 , 0.95899475,
       0.9598778 , 0.99740773, 0.96948814, 0.9568221 , 0.9568263 ,
       0.9758898 , 0.9544127 , 0.9515043 , 0.9508917 , 0.9569788 ,
       0.9977843 , 0.9694043 , 0.90190464, 0.90020055, 0.93124384,
       0.9951148 , 0.9983702 , 0.9550446 , 0.933478  , 0.99592066,
       0.95833814, 0.9966683 , 0.99698824, 0.9998342 ], dtype=float32)}

max_iter

Caution

Do not set this unless you know what you are doing.

Sets the maximum number of iterations performed by the iterative solver.

quasis = mit.apply_correction(raw_counts, range(6), method='iterative', max_iter=10)

tol

Caution

Do not set this unless you know what you are doing.

Sets the tolerance of the iterative solver. Might need adjustments to max_iter if value is set too low.

quasis = mit.apply_correction(raw_counts, range(6), method='iterative', tol=1e-5)