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
from qiskit import QuantumCircuit, transpile
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
/home/runner/work/fiqci-ems/fiqci-ems/src/fiqci/ems/fiqci_backend.py:89: UserWarning: Mitigation level 2 (M3 + Dynamical Decoupling) not implemented yet. Level 2 will currently only apply M3 readout error mitigation without dynamical decoupling.
  warnings.warn(
{'zne': {'enabled': True,
  'fold_gates': None,
  'scale_factors': [1, 3, 5],
  'folding_method': 'local',
  'extrapolation_method': 'exponential',
  'extrapolation_degree': None},
 'rem': {'enabled': True,
  'calibration_shots': 1000,
  'calibration_file': None,
  'mitigator': <fiqci.ems.mitigators.rem.M3IQM at 0x7f4081b7bf50>}}

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
qc_transpiled = transpile(qc, backend=backend)

qc_transpiled.draw("mpl")
../_images/56afb9e645980a3328453bb569a05b0508009c24a69635f8625088a11651c5b9.png
# Define observables to calculate expectation values for (here Z on each qubit)
observables = SparsePauliOp.from_list([("ZIIII", 1), ("IZIII", 1), ("IIZII", 1), ("IIIZI", 1), ("IIIIZ", 1)])

# Map observables to the layout of the transpiled circuit
observables_device = observables.apply_layout(qc_transpiled.layout)

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

# Retrieve mitigated expectation values
job.expectation_values()
[[0.9728054013268412,
  0.9728054013268412,
  0.9728054013268412,
  0.9728054013268412,
  0.9728054013268412]]

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': 'local',
  'extrapolation_method': 'richardson',
  'extrapolation_degree': None},
 'rem': {'enabled': False,
  'calibration_shots': 1000,
  'calibration_file': None,
  'mitigator': None}}
# Execute the same circuit as above with only ZNE enabled (no readout error mitigation)
job = estimator.run([qc_transpiled], observables=observables_device, shots=2**10)
job.expectation_values()
[[0.7890625, 0.7890625, 0.7890625, 0.7890625, 0.7890625]]