Skip to content

Commit 63f1a79

Browse files
authored
daa_sim: Add support of Qiskit 2.0 (#59)
1 parent 0074e05 commit 63f1a79

19 files changed

+104
-306
lines changed

daa_sim/README.md

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ Following Direct Access APIs are provided by the current version of this API sim
2727
* `GET /v1/backends/{backend-name}`
2828
* `GET /v1/backends/{backend-name}/configuration`
2929
* `GET /v1/backends/{backend-name}/properties`
30-
* `GET /v1/backends/{backend-name}/defaults`
3130
* Jobs
3231
* `POST /v1/jobs`
3332
* `GET /v1/jobs`
@@ -65,13 +64,13 @@ pip install .
6564
```bash
6665
% pip show direct-access
6766
Name: direct-access
68-
Version: 0.1.0
67+
Version: 0.7.0
6968
Summary: Direct Access API Simulator
7069
Home-page: https://quantum.ibm.com/
7170
Author: IBM Quantum
7271
Author-email: [email protected]
7372
License: Apache 2.0
74-
Location: /Users/ohtanim/py311_daa_sim_1006_0/lib/python3.11/site-packages
73+
Location: /Users/ohtanim/py311venv_daa_sim/lib/python3.11/site-packages
7574
Requires: boto3, fastapi, numpy, pyjwt, PyYAML, qiskit, qiskit-aer, qiskit-ibm-runtime, qiskit-qasm3-import, sympy, urllib3, uvicorn
7675
Required-by:
7776
```
@@ -159,10 +158,6 @@ You will have two different API documents available if you run this API simulato
159158
- `status` in the returned backends is always `online`
160159
- Optional fields (`message`, `version`) are not included in the response.
161160

162-
### Get backend pulse defaults (GET /v1/backends/{backend_name}/defaults)
163-
164-
- Returns an empty JSON if `backend_name` is specified as `aer`.
165-
166161
### Get jobs (GET /v1/jobs)
167162

168163
- The value of `reason_message` and `reason_solution` fields may differ from the values in the Direct Access API production deployment.

daa_sim/direct_access_client/daa_sim/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ class Version(BaseModel):
282282
)
283283
def get_version() -> Version:
284284
"""Get the current version of the service"""
285-
return Version(version=SERVICE_VERSION).dict()
285+
return Version(version=SERVICE_VERSION).model_dump()
286286

287287

288288
def _create_fastapi(fastapi_app: FastAPI):

daa_sim/direct_access_client/daa_sim/consts.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22

3-
# (C) Copyright 2024 IBM. All Rights Reserved.
3+
# (C) Copyright 2024, 2025 IBM. All Rights Reserved.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -16,7 +16,7 @@
1616
"""
1717

1818
# Direct Access API service version, consistent with the latest deployment
19-
SERVICE_VERSION = "1.0.0"
19+
SERVICE_VERSION = "0.7.0"
2020

2121
# Default max execution lanes
2222
# This default value can be overridden by $.max_execution_lanes in the config.yaml

daa_sim/direct_access_client/daa_sim/daa_service.py

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22

3-
# (C) Copyright 2024 IBM. All Rights Reserved.
3+
# (C) Copyright 2024, 2025 IBM. All Rights Reserved.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -29,6 +29,7 @@
2929
import multiprocessing as mp
3030
import threading
3131
import importlib
32+
from collections import OrderedDict
3233

3334
import numpy as np
3435
import urllib3
@@ -40,9 +41,6 @@
4041
BitArray,
4142
DataBin,
4243
)
43-
from qiskit.transpiler.target import target_to_backend_properties
44-
45-
from qiskit_aer import AerSimulator
4644
from qiskit_aer.primitives import SamplerV2, EstimatorV2
4745

4846
from qiskit_ibm_runtime import RuntimeEncoder, RuntimeDecoder
@@ -164,15 +162,14 @@ class DAAService:
164162

165163
DEFAULT_SAMPLER_SHOTS: int = 1000
166164

167-
DEFAULT_BACKEND = "aer"
168-
169-
DEFAULT_AVALIABLE_BACKENDS = {
170-
DEFAULT_BACKEND: AerSimulator,
171-
FakeBrisbane.backend_name: FakeBrisbane, # 127Q
172-
FakeCairoV2.backend_name: FakeCairoV2, # 27Q
173-
FakeLagosV2.backend_name: FakeLagosV2, # 7Q
174-
FakeTorino.backend_name: FakeTorino, # 133Q
175-
}
165+
DEFAULT_AVALIABLE_BACKENDS = OrderedDict(
166+
[
167+
(FakeBrisbane.backend_name, FakeBrisbane), # 127Q
168+
(FakeCairoV2.backend_name, FakeCairoV2), # 27Q
169+
(FakeLagosV2.backend_name, FakeLagosV2), # 7Q
170+
(FakeTorino.backend_name, FakeTorino), # 133Q
171+
]
172+
)
176173

177174
def __init__(
178175
self,
@@ -206,7 +203,7 @@ def __init__(
206203
if backends is None:
207204
self._available_backends = DAAService.DEFAULT_AVALIABLE_BACKENDS
208205
else:
209-
self._available_backends = {}
206+
self._available_backends = OrderedDict()
210207
for backend in backends:
211208
try:
212209
backend_name, clazz = self.load_backend(
@@ -245,6 +242,13 @@ def __init__(
245242
else:
246243
self._status_lock = threading.RLock()
247244

245+
@property
246+
def default_backend_name(self):
247+
"""Returns the name of the first backend in self._available_backends as default.
248+
This is used by pytest testcases only.
249+
"""
250+
return list(self._available_backends.keys())[0]
251+
248252
def _assert_if_inactive(self):
249253
"""throw execption if service is closed"""
250254
if not self._active:
@@ -442,9 +446,7 @@ def get_backend_properties(self, backend_name: str) -> Dict:
442446
}
443447

444448
# if backend != aer
445-
props = target_to_backend_properties(
446-
self._get_backend(backend_name).target
447-
).to_dict()
449+
props = self._get_backend(backend_name).properties().to_dict()
448450
config_dict = self.get_backend_configuration(backend_name)
449451
for fill_key in ["backend_name", "backend_version"]:
450452
if props.get(fill_key) in {None, ""}:
@@ -454,17 +456,6 @@ def get_backend_properties(self, backend_name: str) -> Dict:
454456
props["last_update_date"] = props["qubits"][0][0]["date"]
455457
return props
456458

457-
def get_backend_pulse_defaults(self, backend_name: str) -> Dict:
458-
backend = self._get_backend(backend_name) # check backend name is correct
459-
if (
460-
hasattr(backend, "defs_filename")
461-
and backend.defs_filename is not None
462-
and hasattr(backend, "_load_json")
463-
):
464-
return backend._load_json(backend.defs_filename)
465-
# return an empty JSON if not available
466-
return {}
467-
468459
def _get_storage(self, storage_type: str) -> SharedStorage:
469460
if storage_type in self._storage_options:
470461
return self._storage_options[storage_type]
@@ -700,7 +691,7 @@ def execute_samplerV2(self, job: Dict[str, str]) -> None:
700691

701692
storage = job["storage"]
702693
backend_name = (
703-
job["backend"] if "backend" in job else DAAService.DEFAULT_BACKEND
694+
job["backend"] if "backend" in job else self.default_backend_name
704695
)
705696
backend = self._get_backend(backend_name)
706697

@@ -772,10 +763,7 @@ def execute_samplerV2(self, job: Dict[str, str]) -> None:
772763
)
773764
)
774765

775-
if backend_name == DAAService.DEFAULT_BACKEND:
776-
sampler = SamplerV2(options=options)
777-
else:
778-
sampler = SamplerV2.from_backend(backend, options=options)
766+
sampler = SamplerV2.from_backend(backend, options=options)
779767

780768
result = sampler.run(pubs, shots=shots).result()
781769
usage_nanoseconds = 0
@@ -826,7 +814,7 @@ def execute_estimatorV2(self, job: Dict[str, str]) -> None:
826814

827815
storage = job["storage"]
828816
backend_name = (
829-
job["backend"] if "backend" in job else DAAService.DEFAULT_BACKEND
817+
job["backend"] if "backend" in job else self.default_backend_name
830818
)
831819
backend = self._get_backend(backend_name)
832820

@@ -880,10 +868,7 @@ def execute_estimatorV2(self, job: Dict[str, str]) -> None:
880868
if isinstance(input_publike[0], str):
881869
input_publike[0] = qasm3.loads(input_publike[0])
882870

883-
if backend_name == DAAService.DEFAULT_BACKEND:
884-
estimator = EstimatorV2(options=options)
885-
else:
886-
estimator = EstimatorV2.from_backend(backend, options=options)
871+
estimator = EstimatorV2.from_backend(backend, options=options)
887872

888873
if "precision" in estimator_input:
889874
result = estimator.run(

daa_sim/direct_access_client/daa_sim/v1/backend.py

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22

3-
# (C) Copyright 2024 IBM. All Rights Reserved.
3+
# (C) Copyright 2024, 2025 IBM. All Rights Reserved.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -14,7 +14,7 @@
1414

1515
"""Backend API Mock"""
1616
import logging.config
17-
from typing import Union, Annotated
17+
from typing import Annotated
1818
from fastapi import APIRouter, Request, Depends
1919
from fastapi.security import HTTPAuthorizationCredentials
2020
from direct_access_client.daa_sim.auth import (
@@ -28,8 +28,6 @@
2828
BackendResponse,
2929
ErrorResponse,
3030
BackendConfigurationResponse,
31-
PulseDefaultsResponse,
32-
EmptyPulseDefaultsResponse,
3331
BackendPropertiesResponse,
3432
)
3533

@@ -188,41 +186,3 @@ def get_backend_properties(
188186
verify_token(request, access_token, internal_shared_key)
189187
service = request.app.daa_service
190188
return service.get_backend_properties(backend_name)
191-
192-
193-
@router.get(
194-
"/v1/backends/{backend_name}/defaults",
195-
summary="Get backend pulse defaults",
196-
description="Returns pulse defaults of a backend.",
197-
responses={
198-
200: {"model": PulseDefaultsResponse},
199-
401: {"model": ErrorResponse},
200-
404: {"model": ErrorResponse},
201-
},
202-
response_model_exclude_none=True,
203-
tags=["Backends"],
204-
)
205-
def get_backend_pulse_defaults(
206-
request: Request,
207-
backend_name: str,
208-
access_token: Annotated[
209-
HTTPAuthorizationCredentials, Depends(access_token_security)
210-
],
211-
internal_shared_key: Annotated[
212-
HTTPAuthorizationCredentials, Depends(internal_shared_key_security)
213-
],
214-
) -> Union[PulseDefaultsResponse, EmptyPulseDefaultsResponse]:
215-
"""Returns pulse defaults of a backend.
216-
217-
Args:
218-
request(Request): Incoming HTTP request
219-
backend_name(str): backend name
220-
access_token(HTTPAuthorizationCredentials): access token credentials
221-
internal_shared_key(HTTPAuthorizationCredentials): internal shared key credentials
222-
223-
Returns:
224-
PulseDefaultsResponse: API response
225-
"""
226-
verify_token(request, access_token, internal_shared_key)
227-
service = request.app.daa_service
228-
return service.get_backend_pulse_defaults(backend_name)

0 commit comments

Comments
 (0)