Skip to content

Commit

Permalink
Merge pull request #248 from nonhermitian/update-for-runtime
Browse files Browse the repository at this point in the history
[WIP] Simplify runtime usage
  • Loading branch information
nonhermitian authored Oct 23, 2024
2 parents ddde897 + 88d1502 commit 6fe0f8a
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-package-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
else
python setup.py build_ext --inplace
fi
black mthree
black --check mthree
- name: Run tests with pytest
run: |
pip install pytest
Expand Down
6 changes: 4 additions & 2 deletions docs/runtime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Using Runtime execution modes
#############################

The Qiskit Runtime has two execution modes, `Batch` and `Session`, that allow for grouping
multiple jobs. You can include M3 calibration jobs in these modes using the `mode` argument
multiple jobs. You can include M3 calibration jobs in these modes using the `runtime_mode` argument
in `mthree.M3Mitigation.cals_from_system`. For example:

.. jupyter-execute::
Expand All @@ -18,5 +18,7 @@ in `mthree.M3Mitigation.cals_from_system`. For example:
batch = Batch(backend=backend)

mit = mthree.M3Mitigation(backend)
mit.cals_from_system(mode=batch); # This is where the Batch or Session goes
mit.cals_from_system(runtime_mode=batch); # This is where the Batch or Session goes


Note that if no `runtime_mode` is set, and the passed system is an IBM backend, then jobs are submitted in `Batch` mode automatically. This mode is NOT closed by default, allowing users to include additional jobs. This mode can be accessed via `mode = mit.system.get_mode()`
20 changes: 10 additions & 10 deletions mthree/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
"""
Helper functions
"""
from qiskit.providers import BackendV2
from qiskit_ibm_runtime import IBMBackend
from mthree.exceptions import M3Error


def system_info(backend):
Expand All @@ -32,15 +29,18 @@ def system_info(backend):
config = backend.configuration()
info_dict["name"] = backend.name
info_dict["num_qubits"] = config.num_qubits
_max_shots = backend.configuration().max_shots
_max_shots = config.max_shots
info_dict["max_shots"] = _max_shots if _max_shots else int(1e6)

if isinstance(backend, IBMBackend):
info_dict["simulator"] = False
elif isinstance(backend, BackendV2):
info_dict["simulator"] = config.simulator
if "fake" in backend.name:
info_dict["simulator"] = True
else:
raise M3Error("Invalid backend passed.")
# max_circuits can be set a couple of ways
max_circuits = getattr(config, "max_experiments", 1)
if max_circuits == 1:
max_circuits = getattr(config, "max_circuits", 1)
if max_circuits == 1 and config.simulator:
max_circuits = 1024
info_dict["max_circuits"] = max_circuits
# Look for faulty qubits. Renaming to 'inoperable' here
if hasattr(backend, "properties"):
if hasattr(backend.properties(), "faulty_qubits"):
Expand Down
53 changes: 22 additions & 31 deletions mthree/mitigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@
import psutil
import numpy as np
import orjson
from qiskit.providers import BackendV2
from qiskit_ibm_runtime import SamplerV2

import runningman as rm
from runningman.utils import is_ibm_backend

from mthree.circuits import (
_tensor_meas_states,
Expand Down Expand Up @@ -61,7 +60,8 @@ def __init__(self, system=None, iter_threshold=4096):
cal_timestamp (str): Time at which cals were taken
single_qubit_cals (list): 1Q calibration matrices
"""
self.executor = None
if is_ibm_backend(system):
system = rm.RunningManBackend(system)
self.system = system
self.system_info = system_info(system) if system else {}
self.single_qubit_cals = None
Expand Down Expand Up @@ -138,8 +138,8 @@ def cals_from_system(
initial_reset=False,
rep_delay=None,
cals_file=None,
async_cal=False,
mode=None,
async_cal=True,
runtime_mode=None,
):
"""Grab calibration data from system.
Expand All @@ -151,26 +151,23 @@ def cals_from_system(
initial_reset (bool): Use resets at beginning of calibration circuits, default=False.
rep_delay (float): Delay between circuits on IBM Quantum backends.
cals_file (str): Output path to write JSON calibration data to.
async_cal (bool): Do calibration async in a separate thread, default is False.
mode (Batch or Session): Mode to run jobs in, default=None
async_cal (bool): Do calibration async in a separate thread, default is True.
runtime_mode (Batch or Session): Mode to run jobs in if using IBM system, default=None
Returns:
list: List of jobs submitted.
Raises:
M3Error: Called while a calibration currently in progress.
"""
if mode is not None:
executor = SamplerV2(mode=mode)
if mode.backend() != self.system.name:
raise M3Error(
f"Mode backend {mode.backend()} != M3 backend {self.system.name}"
)
else:
executor = SamplerV2(mode=self.system)
self.executor = executor
if self._thread:
raise M3Error("Calibration currently in progress.")

if isinstance(self.system, rm.RunningManBackend):
if runtime_mode:
self.system.set_mode(runtime_mode, overwrite=True)
elif not self.system.get_mode():
self.system.set_mode("batch")
if qubits is None:
qubits = range(self.num_qubits)
# Remove faulty qubits if any
Expand Down Expand Up @@ -323,8 +320,6 @@ def _grab_additional_cals(
"""
if self.system is None:
raise M3Error("System is not set. Use 'cals_from_file'.")
if self.executor is None:
self.executor = SamplerV2(mode=self.system)
if self.single_qubit_cals is None:
self.single_qubit_cals = [None] * self.num_qubits
if self.cal_shots is None:
Expand Down Expand Up @@ -397,13 +392,7 @@ def _grab_additional_cals(
)

num_circs = len(trans_qcs)
if isinstance(self.system, BackendV2):
max_circuits = getattr(self.system.configuration(), "max_circuits", 300)
# Needed for https://github.com/Qiskit/qiskit-terra/issues/9947
if max_circuits is None:
max_circuits = 300
else:
raise M3Error("Unknown backend type")
max_circuits = self.system_info["max_circuits"]
# Determine the number of jobs required
num_jobs = ceil(num_circs / max_circuits)
logger.info(
Expand All @@ -420,11 +409,13 @@ def _grab_additional_cals(
] + [trans_qcs[(num_jobs - 1) * circ_slice :]]
# Do job submission here
jobs = []
if self.rep_delay:
self.executor.options.execution.rep_delay = self.rep_delay
self.executor.options.environment.job_tags = ["M3 calibration"]
for circs in circs_list:
_job = self.executor.run(circs, shots=shots)
_job = self.system.run(
circs,
shots=shots,
rep_delay=self.rep_delay,
job_tags=["M3 calibration"],
)
jobs.append(_job)

# Execute job and cal building in new thread.
Expand Down Expand Up @@ -763,7 +754,7 @@ def _job_thread(jobs, mit, qubits, num_cal_qubits, cal_strings):
mit._job_error = error
return
else:
_counts = [rr.data.c.get_counts() for rr in res]
_counts = res.get_counts()
# _counts can be a list or a dict (if only one circuit was executed within the job)
if isinstance(_counts, list):
counts.extend(_counts)
Expand Down
2 changes: 2 additions & 0 deletions mthree/test/test_extra_cals.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def test_save_cals(tmp_path):
cal_file = tmp_path / "cal.json"
mit = mthree.M3Mitigation(backend)
mit.cals_from_system(cals_file=cal_file)
_ = mit.single_qubit_cals # Force waiting since async=True by default
with open(cal_file, "r", encoding="utf-8") as fd:
cals = np.array(orjson.loads(fd.read())["cals"], dtype=np.float32)
assert np.array_equal(mit.single_qubit_cals, cals)
Expand All @@ -82,6 +83,7 @@ def test_load_cals(tmp_path):
backend = FakeAthens()
mit = mthree.M3Mitigation(backend)
mit.cals_from_system(cals_file=cal_file)
_ = mit.single_qubit_cals # Force waiting since async=True by default
new_mit = mthree.M3Mitigation(backend)
new_mit.cals_from_file(cal_file)
assert np.array_equal(mit.single_qubit_cals, new_mit.single_qubit_cals)
1 change: 1 addition & 0 deletions mthree/test/test_file_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def test_load_cals_from_file():
raw_counts = backend.run(qc).result().get_counts()
mit = mthree.M3Mitigation(backend)
mit.cals_from_system(cals_file="cals.json")
_ = mit.single_qubit_cals

mit2 = mthree.M3Mitigation()
mit2.cals_from_file(cals_file="cals.json")
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ numpy>=1.23
scipy>=1.11.1
cython>=3.0.10
qiskit>=1.0
qiskit_ibm_runtime>=0.22
psutil
orjson>=3.0.0
runningman>=2.1

0 comments on commit 6fe0f8a

Please sign in to comment.