Example: Zero Noise Extrapolation#

For folding methods, extrapolation options, and parameter details, see the ZNE guide.

Import everything needed#

from fiqci.ems import FiQCIEstimator
from iqm.qiskit_iqm import IQMProvider, transpile_to_IQM
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp

Setup Estimator#

url = None
quantum_computer = None

# Connect to an IQM quantum computer using the provider
if url is not None and quantum_computer is not None:
	provider = IQMProvider(url=url, quantum_computer=quantum_computer)
	backend = provider.get_backend()
else:
	# Or using a noisy simulator
	from iqm.qiskit_iqm import IQMFakeAdonis

	backend = IQMFakeAdonis()

# Initialise FiQCI estimator with mitigation level 3 (readout error mitigation + zero-noise extrapolation)
estimator = FiQCIEstimator(backend=backend, mitigation_level=3)

# We can view the default settings enabled for mitigation level 3
estimator.mitigator_options
{'zne': {'enabled': True,
  'fold_gates': None,
  'scale_factors': [1, 3, 5],
  'folding_method': 'local',
  'extrapolation_method': 'exponential',
  'extrapolation_degree': None,
  'seed': None},
 'rem': {'enabled': True,
  'calibration_shots': 1000,
  'calibration_file': None,
  'mitigator': <fiqci.ems.mitigators.rem.M3IQM at 0x79956265fec0>},
 'dd': {'enabled': True,
  'gate_sequences': [(9, 'XYXYYXYX', 'asap'),
   (5, 'YXYX', 'asap'),
   (2, 'XX', 'center')]},
 'pauli_twirl': {'enabled': False,
  'num_twirls': 0,
  'gates_to_twirl': None,
  'seed': None}}

Example circuit with ZNE + REM#

# Define a simple 5-qubit circuit
qc = QuantumCircuit(5)
qc.rx(0.5, 0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.cx(1, 2)
qc.cx(1, 3)

# Transpile for backend
tr_qc = transpile_to_IQM(qc, backend, remove_final_rzs=False, optimization_level=3)

tr_qc.draw("mpl")
../_images/fdaf85dd81aca2d960e9d7d811ac50828522ba9e889155d03366876dfa803938.png
# Define observables to calculate expectation values for (here Z on each qubit)
observables = [SparsePauliOp(label) for label in ["ZIIII", "IZIII", "IIZII", "IIIZI", "IIIIZ"]]

# Map observables to the layout of the transpiled circuit
observables_device = [obs.apply_layout(tr_qc.layout) for obs in observables]

# Execute on FiQCI Estimator with specified observables and shots
job = estimator.run(tr_qc, observables=observables_device, shots=2**10)

# Retrieve mitigated expectation values
job.expectation_values()
/home/runner/work/fiqci-ems/fiqci-ems/src/fiqci/ems/backend/core.py:733: UserWarning: M3 produced negative quasi-probabilities for 1 of 3 circuit(s); projecting onto the nearest physical distribution clipped them to zero. Mitigated counts from those circuits are biased towards the physical boundary, so expectation values computed from them can be pinned to +-1 with an understated shot error (FiQCIEstimator avoids this by using the unprojected quasi-probabilities from the result header). Raising calibration_shots reduces this.
  warnings.warn(
[[0.8732263014097161,
  1.101201848346429,
  1.0159841070090498,
  0.9076770499627902,
  0.8626025506101114]]

Manual Configuration#

# Initialise FiQCI estimator
estimator = FiQCIEstimator(backend=backend, mitigation_level=0)

# Configure ZNE
estimator.zne(enabled=True, scale_factors=[1, 5], extrapolation_method="richardson", folding_method="global")

# Print ZNE settings
estimator.mitigator_options
{'zne': {'enabled': True,
  'fold_gates': None,
  'scale_factors': [1, 5],
  'folding_method': 'global',
  'extrapolation_method': 'richardson',
  'extrapolation_degree': None,
  'seed': None},
 'rem': {'enabled': False,
  'calibration_shots': 1000,
  'calibration_file': None,
  'mitigator': None},
 'dd': {'enabled': False, 'gate_sequences': []},
 'pauli_twirl': {'enabled': False,
  'num_twirls': 0,
  'gates_to_twirl': None,
  'seed': None}}
# Execute the same circuit as above with only ZNE enabled (no readout error mitigation)
job = estimator.run(tr_qc, observables=observables_device, shots=2**10)
job.expectation_values()
[[0.8056640625, 0.8701171875, 0.89990234375, 0.78955078125, 0.7900390625]]