From f30dea81e48ad07017da6b7aa71f92778742ec3a Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Sun, 20 Oct 2024 16:03:48 -0400 Subject: [PATCH] Add support for noise model and level 1 data to local sampler This change passes through the `simulator.noise_model` option to the `BackendSamplerV2` or `BackendEstimatorV2` as a `noise_model` option if the primitive supports the `noise_model` option (the primitives in the current release of Qiskit, 1.2.4, do not but there is an open pull request to add support). Additionally, this change translates the `execution.meas_type` option into `meas_level` and `meas_return` options if the `BackendSamplerV2` supports them. This change allows support for level 1 data in local testing mode, where otherwise the default is only to return classified data. --- .../fake_provider/local_service.py | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/qiskit_ibm_runtime/fake_provider/local_service.py b/qiskit_ibm_runtime/fake_provider/local_service.py index 438c60ad73..cfdb498fd6 100644 --- a/qiskit_ibm_runtime/fake_provider/local_service.py +++ b/qiskit_ibm_runtime/fake_provider/local_service.py @@ -14,6 +14,7 @@ from __future__ import annotations +import dataclasses import math import copy import logging @@ -215,12 +216,46 @@ def _run_backend_primitive_v2( """ options_copy = copy.deepcopy(options) + # Create a dummy primitive to check which options it supports + if primitive == "sampler": + dummy_prim = BackendSamplerV2(backend=backend) + else: + dummy_prim = BackendEstimatorV2(backend=backend) + supported_options = {f.name for f in dataclasses.fields(dummy_prim)} + + + prim_options = {} - if seed_simulator := options_copy.pop("simulator", {}).pop("seed_simulator", None): + sim_options = options_copy.get("simulator", {}) + if seed_simulator := sim_options.pop("seed_simulator", None): prim_options["seed_simulator"] = seed_simulator + if "noise_model" in supported_options and ( + noise_model := sim_options.pop("noise_model", None) + ): + prim_options["noise_model"] = noise_model + if not sim_options: + options_copy.pop("simulator", None) if primitive == "sampler": if default_shots := options_copy.pop("default_shots", None): prim_options["default_shots"] = default_shots + if {"meas_type", "meas_return"} <= supported_options and ( + meas_type := options_copy.get("execution", {}).pop("meas_type", None) + ): + if meas_type == "classified": + prim_options["meas_level"] = 2 + elif meas_type == "kerneled": + prim_options["meas_level"] = 1 + prim_options["meas_return"] = "single" + elif meas_type == "avg_kerneled": + prim_options["meas_level"] = 1 + prim_options["meas_return"] = "avg" + else: + # Put unexepcted meas_type back so it is in the warning below + options_copy["execution"]["meas_type"] = meas_type + + if not options_copy["execution"]: + del options_copy["execution"] + primitive_inst = BackendSamplerV2(backend=backend, options=prim_options) else: if default_shots := options_copy.pop("default_shots", None):