Quantum Volume

Here we show off using the expectation value functionality of the M3 distribution classes using Quantum Volume (QV) as an example. Here we formulate QV as an expectation value of a projector onto the heavy-output elements on a distribution.

[1]:
import numpy as np
from qiskit import *
from qiskit.quantum_info import Statevector
from qiskit.circuit.library import QuantumVolume
import mthree
[2]:
from qiskit_ibm_runtime.fake_provider import FakeAthensV2
noisy_sim = FakeAthensV2()

QV is defined in terms of heavy-ouputs of a distribution. Heavy-outputs are those bit-strings that are those that have probabilities above the median value of the distribution. Below we define the projection operator onto the set of bit-strings that are heavy-outputs for a given distribution.

[3]:
def heavy_projector(qv_probs):
    """Forms the projection operator onto the heavy-outputs of a given probability distribution.

    Parameters:
        qv_probs (dict): A dictionary of bitstrings and associated probabilities.

    Returns:
        dict: Projector onto the heavy-set.
    """
    median_prob = np.median(list(qv_probs.values()))
    heavy_strs = {}
    for key, val in qv_probs.items():
        if val > median_prob:
            heavy_strs[key] = 1
    return heavy_strs

Now we generate 10 QV circuits as our dataset.

[4]:
# Generate QV circuits
N = 10
qv_circs = [QuantumVolume(5) for _ in range(N)]

Next, we have to determine the heavy-set of each circuit from the ideal answer, and then pass this along to our heavy-set projector function that we defined above.

[5]:
# Compute ideal distributions and projectors on the heavy set.
ideal_probs = [Statevector.from_instruction(circ).probabilities_dict() for circ in qv_circs]
heavy_projectors = [heavy_projector(probs) for probs in ideal_probs]

Now, in preparation for actual execution, we add measurements to the end of our QV circuits, and compile them for the target device

[6]:
# Add meauserements to circuits and transpile
trans_circs = transpile([circ.measure_all(inplace=False) for circ in qv_circs], noisy_sim,
                        layout_method='sabre', routing_method='sabre', optimization_level=3)

Because the SWAP mapping of the circuit permutes the qubit states, we need the final measurement mapping for each circuit to know which physical qubit corresponds to each measured bit:

[7]:
# Determine final qubit mappings
maps = mthree.utils.final_measurement_mapping(trans_circs)

We now calibrate our M3 mitigator over only those qubits used in the QV circuits. (We use the independent method since it is much faster to do so on a simulator.) We can directly pass the maps to the calibrtion routine and it will calibrate over the ful lset of qubits used in the mappings.

[8]:
mit = mthree.M3Mitigation(noisy_sim)
mit.cals_from_system(maps, method='independent')
[8]:
[<qiskit_aer.jobs.aerjob.AerJob at 0x7fc74098b9a0>]

We are now ready to execute the circuits on the target backend, and mitigate the resulting raw counts. Here we directly pass the maps which will internally be converted into the correct qubit lists:

[9]:
# Execute circuits and mitigate
raw_counts = noisy_sim.run(trans_circs, shots=8192).result().get_counts()
quasi_collection = mit.apply_correction(raw_counts, maps)

The value needed to determine if each circuit represents a passing QV value is determined by the expectation value of the heavy projector for each circuit. First let us evaluate the raw counts using the M3 utils function expval:

[10]:
raw_expvals = mthree.utils.expval(raw_counts, heavy_projectors)
raw_expvals
[10]:
array([0.70410156, 0.75268555, 0.7623291 , 0.73254395, 0.677124  ,
       0.77441406, 0.68847656, 0.7252197 , 0.85266113, 0.736084  ],
      dtype=float32)

A passing QV score is one where the expectation value is above 2/3:

[11]:
raw_expvals > 2/3
[11]:
array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
        True])

Now let us do the same analysis for the mitigated data:

[12]:
# Determine expectation value of heavy set prjector
mit_expvals = quasi_collection.expval(heavy_projectors)
mit_expvals
[12]:
array([0.7180824 , 0.7609179 , 0.78509986, 0.7496967 , 0.6923127 ,
       0.7885045 , 0.7121728 , 0.7494231 , 0.88176143, 0.7537768 ],
      dtype=float32)
[13]:
# Check if the scores are passing or not
mit_expvals > 2/3
[13]:
array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
        True])
[ ]: