Qermit¶
Qermit is a python module for running error-mitigation protocols. It contains an assortment of ready to use error-mitigation schemes. Qermit supports straightforward composition of these existing error-mitigation methods, and the development of new error-mitigation schemes.
Qermit is an extension to the pytket quantum software development kit.
Qermit uses the pytket Backend
class,
and so supports an array of backends.
Getting Started¶
pip install qermit
Please visit the github repository if you would like to install from source. We also welcome contributions to Qermit there!
If you notice any bugs while using Qermit we would much appreciate you raising them as issues at that repository.
If you would like an further support with Qermit please contact tket-support@quantinuum.com.
Key Notions¶
Error-mitigation methods in Qermit are either one of two types:
MitRes
methods which modify a distribution of counts.MitEx
methods which modify the expectation value of some observable.
While Qermit includes many advanced error-mitigation schemes,
in their basic capacity, MitRes
and
MitEx
objects will run
experiments without error-mitigation, as in the following example.
from qermit import MitRes, CircuitShots
from pytket import Circuit
from pytket.extensions.qiskit import AerBackend
# Define the experiment to be run.
# In this case, a Bell pair is prepared and measured 1000 times.
circ = Circuit(2,2).H(0).CX(0,1).measure_all()
circ_shots = CircuitShots(Circuit=circ, Shots=1000)
# Define the way the experiment will be run.
# In this case the default MitRes, without error-mitigation,
# will be performed.
mitres = MitRes(backend = AerBackend())
# Finally, run the experiment.
results = mitres.run([circ_shots])
results[0].get_counts()
Counter({(1, 1): 513, (0, 0): 487})
Here we have introduced the CircuitShots
.
This can be thought of a defining experiment to run to generated a
distribution of shots. In this case the Bell state preparation circuit will
be run 1000 times to generate the desired distribution. The
MitRes
definition can then be thought
of as specifying how the experiment will run, in this case without
error-mitigation,
and by using AerBackend
.
The input to the run()
method
is then a list CircuitShots
,
with the output being a list of
BackendResult
.
MitRes
and
MitEx
are built from a graph of
MitTask
objects.
A MitTask
object is a pure function
which computes some basic step in an experiment. These
MitTask
are composed into a
TaskGraph
which defined how
inputs and outputs pass between the tasks.
Indeed, MitRes
and
MitEx
are instances of a
TaskGraph
.
In its default construction, a MitRes
object will simply run each circuit through the backend it is defined by.
In the following we see one task, CircuitsToHandles
, submitting the circuits
and generating handles for those experiments, and a second, HandlesToResults
,
retrieving the results using those handles.
The information passed along the central wire is simply
those handles.
mitres.get_task_graph()
Similarly, in its default construction a
MitEx
object will simply estimate
the expectation of each observable
desired without applying any mitigation method.
from qermit import (
MitEx,
AnsatzCircuit,
ObservableExperiment,
ObservableTracker,
SymbolsDict,
)
from pytket import Qubit
from pytket.pauli import Pauli, QubitPauliString
from pytket.utils import QubitPauliOperator
# Define the experiment to be conducted. In this case that consists
# of two parts.
# 1. The circuit to run. In the case of MitEx we use AnsatzCircuit
# which allows parametrised circuit. In this case we do not use
# this capability however.
ansatz_circuit = AnsatzCircuit(
Circuit=Circuit(3,3).X(0).X(1),
Shots=50,
SymbolsDict=SymbolsDict(),
)
# 2. The observable to be measured. In this case the observable
# is only the ZZ observable on qubits 1 and 2.
qubit_pauli_string = QubitPauliString([Qubit(1), Qubit(2)], [Pauli.Z, Pauli.Z])
qubit_pauli_operator = QubitPauliOperator({qubit_pauli_string: 1.0})
# These are combined to define the experiment.
experiment = ObservableExperiment(
AnsatzCircuit = ansatz_circuit,
ObservableTracker = ObservableTracker(qubit_pauli_operator)
)
# Now we can define how the experiment is run.
# In this case the default MitEx, without error-mitigation, is performed.
mitex = MitEx(backend = AerBackend())
mitex.run([experiment])
[{(Zq[1], Zq[2]): -1.00000000000000}]
The run()
method takes a list of
ObservableExperiment
as an argument. Each
ObservableExperiment
contains the basic information required to estimate the expectation value
of an observable; a state preparation circuit, a dictionary between symbols
and parameter values (where appropriate),
a QubitPauliOperator
detailing the
operator being measured and used for preparing measurement circuits,
and the number of shots to run for each measurement circuit.
Each experiment returns a QubitPauliOperator
containing an expectation value for each internal
QubitPauliString
. In its default
version, this is achieved by appending a measurement circuit for each
QubitPauliString
to the ansatz circuit and
executing through the Backend
the MitEx
object is defined by.
mitex.get_task_graph()
Contents¶
- qermit.taskgraph
- qermit.mitres
- qermit.mitex
- qermit.utils
- qermit.noise_model
- qermit.mittask
- qermit.measurement_reduction
- qermit.spam
- qermit.frame_randomisation
- qermit.postselection
- qermit.leakage_detection
- qermit.coherent_pauli_checks
- qermit.clifford_noise_characterisation
- qermit.zero_noise_extrapolation
- qermit.probabilistic_error_cancellation
- qermit.spectral_filtering
gen_spectral_filtering_MitEx()
gen_result_extraction_task()
gen_mitigation_task()
gen_fft_task()
gen_ndarray_to_dict_task()
gen_inv_fft_task()
gen_flatten_task()
gen_reshape_task()
gen_obs_exp_grid_gen_task()
gen_symbol_val_gen_task()
gen_wire_copy_task()
gen_param_grid_gen_task()
SmallCoefficientSignalFilter
SignalFilter