Skip to content

Commit

Permalink
initial changes
Browse files Browse the repository at this point in the history
  • Loading branch information
dcmckayibm committed Oct 3, 2024
1 parent 02572a3 commit a5a01eb
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 22 deletions.
37 changes: 36 additions & 1 deletion qiskit_experiments/framework/base_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
from qiskit_experiments.framework.configs import ExperimentConfig
from qiskit_experiments.database_service import Qubit

from qiskit_ibm_runtime import SamplerV2 as Sampler


class BaseExperiment(ABC, StoreInitArgs):
"""Abstract base class for experiments."""
Expand All @@ -40,6 +42,7 @@ def __init__(
analysis: Optional[BaseAnalysis] = None,
backend: Optional[Backend] = None,
experiment_type: Optional[str] = None,
use_sampler: Options[bool] = False
):
"""Initialize the experiment object.
Expand Down Expand Up @@ -82,6 +85,7 @@ def __init__(
# attributes created during initialization
self._backend = None
self._backend_data = None
self._use_sampler = use_sampler
if isinstance(backend, Backend):
self._set_backend(backend)

Expand Down Expand Up @@ -199,6 +203,7 @@ def run(
backend: Optional[Backend] = None,
analysis: Optional[Union[BaseAnalysis, None]] = "default",
timeout: Optional[float] = None,
use_sampler: Optional[bool] = None,
**run_options,
) -> ExperimentData:
"""Run an experiment and perform analysis.
Expand Down Expand Up @@ -235,6 +240,9 @@ def run(
else:
experiment = self

if use_sampler is not None:
self._use_sampler = use_sampler

if experiment.backend is None:
raise QiskitError("Cannot run experiment, no backend has been set.")

Expand Down Expand Up @@ -348,7 +356,34 @@ def _run_jobs(self, circuits: List[QuantumCircuit], **run_options) -> List[Job]:
job_circuits = [circuits]

# Run jobs
jobs = [self.backend.run(circs, **run_options) for circs in job_circuits]
if self._use_sampler:
sampler = Sampler(self.backend)

#have to hand set some of these options
#see https://docs.quantum.ibm.com/api/qiskit-ibm-runtime/qiskit_ibm_runtime.options.SamplerExecutionOptionsV2
if 'init_qubits' in run_options:
sampler.options.execution.init_qubits = run_options['init_qubits']
if 'rep_delay' in run_options:
sampler.options.execution.rep_delay = run_options['rep_delay']
if 'meas_level' in run_options:
if run_options['meas_level'] == 2:
sampler.options.execution.meas_type = "classified"
elif run_options['meas_level'] == 1:
if 'meas_return' in run_options:
if run_options['meas_return'] == 'avg':
sampler.options.execution.meas_type = "avg_kerneled"
else:
sampler.options.execution.meas_type = "kerneled"
else:
#assume this is what is wanted if no meas return specified
sampler.options.execution.meas_type = "kerneled"
else:
raise QiskitError('Only meas level 1 + 2 supported by sampler')


jobs = [sampler.run(circs, shots=run_options.get('shots', None)) for circs in job_circuits]
else:
jobs = [self.backend.run(circs, **run_options) for circs in job_circuits]

return jobs

Expand Down
75 changes: 54 additions & 21 deletions qiskit_experiments/framework/experiment_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@

from .containers.figure_data import FigureData, FigureType

from qiskit.primitives import BitArray, SamplerPubResult

if TYPE_CHECKING:
# There is a cyclical dependency here, but the name needs to exist for
# Sphinx on Python 3.9+ to link type hints correctly. The gating on
Expand Down Expand Up @@ -985,27 +987,58 @@ def _add_result_data(self, result: Result, job_id: Optional[str] = None) -> None
job_id: The id of the job the result came from. If `None`, the
job id in `result` is used.
"""
if job_id is None:
job_id = result.job_id
if job_id not in self._jobs:
self._jobs[job_id] = None
self.job_ids.append(job_id)
with self._result_data.lock:
# Lock data while adding all result data
for i, _ in enumerate(result.results):
data = result.data(i)
data["job_id"] = job_id
if "counts" in data:
# Format to Counts object rather than hex dict
data["counts"] = result.get_counts(i)
expr_result = result.results[i]
if hasattr(expr_result, "header") and hasattr(expr_result.header, "metadata"):
data["metadata"] = expr_result.header.metadata
data["shots"] = expr_result.shots
data["meas_level"] = expr_result.meas_level
if hasattr(expr_result, "meas_return"):
data["meas_return"] = expr_result.meas_return
self._result_data.append(data)
if hasattr(result, 'results'):
#backend run results
if job_id is None:
job_id = result.job_id
if job_id not in self._jobs:
self._jobs[job_id] = None
self.job_ids.append(job_id)
with self._result_data.lock:
# Lock data while adding all result data
for i, _ in enumerate(result.results):
data = result.data(i)
data["job_id"] = job_id
if "counts" in data:
# Format to Counts object rather than hex dict
data["counts"] = result.get_counts(i)
expr_result = result.results[i]
if hasattr(expr_result, "header") and hasattr(expr_result.header, "metadata"):
data["metadata"] = expr_result.header.metadata
data["shots"] = expr_result.shots
data["meas_level"] = expr_result.meas_level
if hasattr(expr_result, "meas_return"):
data["meas_return"] = expr_result.meas_return
self._result_data.append(data)
else:
#sampler results
if job_id is None:
raise QiskitError('job_id must be provided, not available in the sampler result')
if job_id not in self._jobs:
self._jobs[job_id] = None
self.job_ids.append(job_id)
with self._result_data.lock:
# Lock data while adding all result data
# Sampler results are a list
for i, _ in enumerate(result):
data = {}
#convert to a Sampler Pub Result (can remove this later when the bug is fixed)
testres = SamplerPubResult(result[i].data, result[i].metadata)
data["job_id"] = job_id
if type(testres.data[next(iter(testres.data))]) is BitArray:
#bit results so has counts
data["meas_level"] = 2
data["meas_return"] = 'avg'
#join the data
data["counts"] = testres.join_data(testres.data.keys()).get_counts()
#number of shots
data["shots"] = testres.data[next(iter(testres.data))].num_shots
else:
raise QiskitError("Sampler with meas level 1 support TBD")

data["metadata"] = testres.metadata['circuit_metadata']

self._result_data.append(data)

def _retrieve_data(self):
"""Retrieve job data if missing experiment data."""
Expand Down

0 comments on commit a5a01eb

Please sign in to comment.