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
{'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 0x7ffa1c3ba1e0>},
 '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}}

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/ff72b408f5d4bb8f5df01c92cef3cac488a7e242a7a3e9f17421e11683b5313a.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.8532716220074535,
  0.9922542538150924,
  1.0072045055241983,
  0.9727520846753374,
  0.9240568554908671]]

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},
 'dd': {'enabled': False, 'gate_sequences': []},
 'pauli_twirl': {'enabled': False, 'num_twirls': 0, 'gates_to_twirl': 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.85107421875, 0.80419921875, 0.84765625, 0.70947265625, 0.75146484375]]