quapp-common 0.0.11.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qapp_common/__init__.py +0 -0
- qapp_common/async_tasks/__init__.py +0 -0
- qapp_common/async_tasks/async_task.py +11 -0
- qapp_common/async_tasks/export_circuit_task.py +119 -0
- qapp_common/async_tasks/post_processing_task.py +59 -0
- qapp_common/component/__init__.py +0 -0
- qapp_common/component/backend/__init__.py +0 -0
- qapp_common/component/backend/invocation.py +255 -0
- qapp_common/component/backend/job_fetching.py +247 -0
- qapp_common/component/callback/__init__.py +0 -0
- qapp_common/component/callback/update_job_metadata.py +39 -0
- qapp_common/component/device/__init__.py +0 -0
- qapp_common/component/device/device_selection.py +81 -0
- qapp_common/config/__init__.py +0 -0
- qapp_common/config/logging_config.py +8 -0
- qapp_common/config/thread_config.py +10 -0
- qapp_common/data/__init__.py +0 -0
- qapp_common/data/async_task/__init__.py +0 -0
- qapp_common/data/async_task/circuit_export/__init__.py +0 -0
- qapp_common/data/async_task/circuit_export/backend_holder.py +12 -0
- qapp_common/data/async_task/circuit_export/circuit_holder.py +11 -0
- qapp_common/data/backend/__init__.py +0 -0
- qapp_common/data/backend/backend_information.py +14 -0
- qapp_common/data/callback/__init__.py +0 -0
- qapp_common/data/callback/callback_url.py +10 -0
- qapp_common/data/device/__init__.py +0 -0
- qapp_common/data/device/circuit_running_option.py +16 -0
- qapp_common/data/promise/__init__.py +0 -0
- qapp_common/data/promise/post_processing_promise.py +18 -0
- qapp_common/data/promise/promise.py +11 -0
- qapp_common/data/request/__init__.py +0 -0
- qapp_common/data/request/invocation_request.py +24 -0
- qapp_common/data/request/job_fetching_request.py +11 -0
- qapp_common/data/request/request.py +23 -0
- qapp_common/data/response/__init__.py +0 -0
- qapp_common/data/response/authentication.py +9 -0
- qapp_common/data/response/job_response.py +32 -0
- qapp_common/data/response/project_header.py +9 -0
- qapp_common/enum/__init__.py +0 -0
- qapp_common/enum/base_enum.py +16 -0
- qapp_common/enum/http_header.py +10 -0
- qapp_common/enum/invocation_step.py +13 -0
- qapp_common/enum/media_type.py +12 -0
- qapp_common/enum/processing_unit.py +10 -0
- qapp_common/enum/provider_tag.py +22 -0
- qapp_common/enum/sdk.py +22 -0
- qapp_common/enum/status/__init__.py +0 -0
- qapp_common/enum/status/job_status.py +10 -0
- qapp_common/enum/status/status_code.py +10 -0
- qapp_common/enum/token_type.py +8 -0
- qapp_common/factory/__init__.py +0 -0
- qapp_common/factory/device_factory.py +22 -0
- qapp_common/factory/handler_factory.py +20 -0
- qapp_common/factory/provider_factory.py +22 -0
- qapp_common/handler/__init__.py +0 -0
- qapp_common/handler/handler.py +20 -0
- qapp_common/model/__init__.py +0 -0
- qapp_common/model/device/__init__.py +0 -0
- qapp_common/model/device/custom_device.py +87 -0
- qapp_common/model/device/device.py +257 -0
- qapp_common/model/provider/__init__.py +0 -0
- qapp_common/model/provider/provider.py +34 -0
- qapp_common/util/__init__.py +0 -0
- qapp_common/util/file_utils.py +23 -0
- qapp_common/util/http_utils.py +37 -0
- qapp_common/util/json_parser_utils.py +75 -0
- qapp_common/util/response_utils.py +36 -0
- quapp_common-0.0.11.dev1.dist-info/METADATA +41 -0
- quapp_common-0.0.11.dev1.dist-info/RECORD +72 -0
- quapp_common-0.0.11.dev1.dist-info/WHEEL +5 -0
- quapp_common-0.0.11.dev1.dist-info/licenses/LICENSE +8 -0
- quapp_common-0.0.11.dev1.dist-info/top_level.txt +1 -0
qapp_common/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""
|
|
2
|
+
QApp Platform Project export_circuit.py Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
3
|
+
"""
|
|
4
|
+
from io import BytesIO
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from .async_task import AsyncTask
|
|
9
|
+
from ..data.async_task.circuit_export.backend_holder import BackendDataHolder
|
|
10
|
+
from ..data.async_task.circuit_export.circuit_holder import CircuitDataHolder
|
|
11
|
+
from ..config.logging_config import logger
|
|
12
|
+
from ..enum.media_type import MediaType
|
|
13
|
+
from ..util.file_utils import FileUtils
|
|
14
|
+
from ..util.http_utils import HttpUtils
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CircuitExportTask(AsyncTask):
|
|
18
|
+
MAX_CIRCUIT_IMAGE_SIZE = 5 * (1024 ** 2)
|
|
19
|
+
|
|
20
|
+
def __init__(self,
|
|
21
|
+
circuit_data_holder: CircuitDataHolder,
|
|
22
|
+
backend_data_holder: BackendDataHolder):
|
|
23
|
+
super().__init__()
|
|
24
|
+
self.circuit_data_holder = circuit_data_holder
|
|
25
|
+
self.backend_data_holder = backend_data_holder
|
|
26
|
+
|
|
27
|
+
def do(self):
|
|
28
|
+
"""
|
|
29
|
+
Export circuit to svg file then send to QuaO server for saving
|
|
30
|
+
"""
|
|
31
|
+
logger.debug("[Circuit export] Start")
|
|
32
|
+
|
|
33
|
+
circuit_export_url = self.circuit_data_holder.export_url
|
|
34
|
+
|
|
35
|
+
if circuit_export_url is None or len(circuit_export_url) < 1:
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
figure_buffer = self.__convert()
|
|
39
|
+
|
|
40
|
+
io_buffer_value, content_type = self.__determine_zip(figure_buffer=figure_buffer)
|
|
41
|
+
|
|
42
|
+
self.__send(io_buffer_value=io_buffer_value,
|
|
43
|
+
content_type=content_type)
|
|
44
|
+
|
|
45
|
+
def __convert(self):
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
@return:
|
|
49
|
+
"""
|
|
50
|
+
logger.debug("[Circuit export] Preparing circuit figure...")
|
|
51
|
+
transpiled_circuit = self._transpile_circuit()
|
|
52
|
+
circuit_figure = transpiled_circuit.draw(output='mpl', fold=-1)
|
|
53
|
+
|
|
54
|
+
logger.debug("[Circuit export] Converting circuit figure to svg file...")
|
|
55
|
+
figure_buffer = BytesIO()
|
|
56
|
+
circuit_figure.savefig(figure_buffer, format='svg', bbox_inches='tight')
|
|
57
|
+
|
|
58
|
+
return figure_buffer
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def __determine_zip(figure_buffer):
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
@param figure_buffer:
|
|
65
|
+
@return:
|
|
66
|
+
"""
|
|
67
|
+
buffer_value = figure_buffer.getvalue()
|
|
68
|
+
content_type = MediaType.SVG_XML
|
|
69
|
+
|
|
70
|
+
logger.debug("[Circuit export] Checking max file size")
|
|
71
|
+
estimated_file_size = len(buffer_value)
|
|
72
|
+
|
|
73
|
+
if estimated_file_size > CircuitExportTask.MAX_CIRCUIT_IMAGE_SIZE:
|
|
74
|
+
logger.debug("[Circuit export] Zip file")
|
|
75
|
+
zip_file_buffer = FileUtils.zip(io_buffer_value=buffer_value,
|
|
76
|
+
file_name="circuit_image.svg")
|
|
77
|
+
|
|
78
|
+
buffer_value = zip_file_buffer.getvalue()
|
|
79
|
+
content_type = MediaType.APPLICATION_ZIP
|
|
80
|
+
|
|
81
|
+
return buffer_value, content_type
|
|
82
|
+
|
|
83
|
+
def __send(self, io_buffer_value, content_type: MediaType):
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
@param io_buffer_value:
|
|
87
|
+
@param content_type:
|
|
88
|
+
"""
|
|
89
|
+
url = self.circuit_data_holder.export_url
|
|
90
|
+
|
|
91
|
+
logger.debug(
|
|
92
|
+
"[Circuit export] Sending circuit svg image to [{0}] with POST method ...".format(
|
|
93
|
+
url))
|
|
94
|
+
|
|
95
|
+
payload = {'circuit': (
|
|
96
|
+
'circuit_image.svg',
|
|
97
|
+
io_buffer_value,
|
|
98
|
+
content_type.value)}
|
|
99
|
+
|
|
100
|
+
response = requests.post(
|
|
101
|
+
url=url,
|
|
102
|
+
headers=HttpUtils.create_bearer_header(self.backend_data_holder.user_token),
|
|
103
|
+
files=payload)
|
|
104
|
+
|
|
105
|
+
if response.ok:
|
|
106
|
+
logger.debug("Sending request to QuaO backend successfully!")
|
|
107
|
+
else:
|
|
108
|
+
logger.debug("Sending request to QuaO backend failed with status {0}!".format(
|
|
109
|
+
response.status_code))
|
|
110
|
+
|
|
111
|
+
logger.debug("[Circuit export] Finish")
|
|
112
|
+
|
|
113
|
+
def _transpile_circuit(self):
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
@return: Transpiled circuit
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
raise NotImplemented('[CircuitExportTask] __transpile_circuit() method must be implemented')
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
QApp Platform Project post_processing_task.py Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
3
|
+
"""
|
|
4
|
+
from .async_task import AsyncTask
|
|
5
|
+
from ..component.callback.update_job_metadata import update_job_metadata
|
|
6
|
+
from ..config.logging_config import logger
|
|
7
|
+
from ..data.promise.post_processing_promise import PostProcessingPromise
|
|
8
|
+
from ..data.response.job_response import JobResponse
|
|
9
|
+
from ..enum.media_type import MediaType
|
|
10
|
+
from ..enum.status.job_status import JobStatus
|
|
11
|
+
from ..enum.status.status_code import StatusCode
|
|
12
|
+
from ..util.json_parser_utils import JsonParserUtils
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PostProcessingTask(AsyncTask):
|
|
16
|
+
def __init__(self,
|
|
17
|
+
post_processing_fn,
|
|
18
|
+
promise: PostProcessingPromise):
|
|
19
|
+
self.post_processing_fn = post_processing_fn
|
|
20
|
+
self.promise = promise
|
|
21
|
+
|
|
22
|
+
def do(self):
|
|
23
|
+
"""
|
|
24
|
+
Execute post_processing and send to backend
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
job_response = JobResponse(
|
|
28
|
+
authentication=self.promise.authentication,
|
|
29
|
+
project_header=self.promise.project_header,
|
|
30
|
+
job_status=JobStatus.DONE.value,
|
|
31
|
+
status_code=StatusCode.DONE)
|
|
32
|
+
|
|
33
|
+
update_job_metadata(job_response=job_response,
|
|
34
|
+
callback_url=self.promise.callback_url.on_start)
|
|
35
|
+
|
|
36
|
+
job_response.content_type = MediaType.APPLICATION_JSON
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
|
|
40
|
+
logger.info("Execute post_processing ...")
|
|
41
|
+
job_result_post_processing = self.post_processing_fn(self.promise.job_result)
|
|
42
|
+
|
|
43
|
+
logger.debug("Parsing job result....")
|
|
44
|
+
print(job_result_post_processing)
|
|
45
|
+
job_response.job_result = JsonParserUtils.parse(job_result_post_processing)
|
|
46
|
+
|
|
47
|
+
update_job_metadata(job_response=job_response,
|
|
48
|
+
callback_url=self.promise.callback_url.on_done)
|
|
49
|
+
|
|
50
|
+
except Exception as exception:
|
|
51
|
+
job_response.job_result = {
|
|
52
|
+
"error": "Error when execute post_processing(): ",
|
|
53
|
+
"exception": str(exception),
|
|
54
|
+
}
|
|
55
|
+
job_response.status_code = StatusCode.ERROR
|
|
56
|
+
job_response.job_status = JobStatus.ERROR.value
|
|
57
|
+
|
|
58
|
+
update_job_metadata(job_response=job_response,
|
|
59
|
+
callback_url=self.promise.callback_url.on_error)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""
|
|
2
|
+
QApp Platform Project backend.py Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
3
|
+
"""
|
|
4
|
+
from abc import abstractmethod, ABC
|
|
5
|
+
|
|
6
|
+
from ..callback.update_job_metadata import update_job_metadata
|
|
7
|
+
from ...component.device.device_selection import DeviceSelection
|
|
8
|
+
from ...config.logging_config import logger
|
|
9
|
+
from ...config.thread_config import circuit_running_pool
|
|
10
|
+
from ...data.backend.backend_information import BackendInformation
|
|
11
|
+
from ...data.device.circuit_running_option import CircuitRunningOption
|
|
12
|
+
from ...data.request.invocation_request import InvocationRequest
|
|
13
|
+
from ...data.response.authentication import Authentication
|
|
14
|
+
from ...data.response.job_response import JobResponse
|
|
15
|
+
from ...data.response.project_header import ProjectHeader
|
|
16
|
+
from ...enum.invocation_step import InvocationStep
|
|
17
|
+
from ...enum.media_type import MediaType
|
|
18
|
+
from ...enum.sdk import Sdk
|
|
19
|
+
from ...enum.status.job_status import JobStatus
|
|
20
|
+
from ...enum.status.status_code import StatusCode
|
|
21
|
+
from ...model.provider.provider import Provider
|
|
22
|
+
|
|
23
|
+
EXPORT_CIRCUIT_SDK = {Sdk.QISKIT, Sdk.BRAKET}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Invocation(ABC):
|
|
27
|
+
def __init__(self, request_data: InvocationRequest):
|
|
28
|
+
self.sdk: Sdk = Sdk.resolve(request_data.sdk)
|
|
29
|
+
self.input = request_data.input
|
|
30
|
+
self.device_id = request_data.device_id
|
|
31
|
+
self.backend_information: BackendInformation
|
|
32
|
+
self.authentication: Authentication = request_data.authentication
|
|
33
|
+
self.project_header: ProjectHeader = request_data.project_header
|
|
34
|
+
self.options = CircuitRunningOption(
|
|
35
|
+
shots=request_data.shots,
|
|
36
|
+
processing_unit=request_data.processing_unit,
|
|
37
|
+
executor=circuit_running_pool,
|
|
38
|
+
max_job_size=1,
|
|
39
|
+
)
|
|
40
|
+
self.device_selection_url: str = request_data.device_selection_url
|
|
41
|
+
self.circuit_export_url: str = request_data.circuit_export_url
|
|
42
|
+
self.callback_dict: dict = {
|
|
43
|
+
InvocationStep.PREPARATION: request_data.preparation,
|
|
44
|
+
InvocationStep.EXECUTION: request_data.execution,
|
|
45
|
+
InvocationStep.ANALYSIS: request_data.analysis,
|
|
46
|
+
InvocationStep.FINALIZATION: request_data.finalization,
|
|
47
|
+
}
|
|
48
|
+
self.invoke_authentication = request_data.invoke_authentication
|
|
49
|
+
|
|
50
|
+
def submit_job(self, circuit_preparation_fn, post_processing_fn):
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
@param post_processing_fn: Post-processing function
|
|
54
|
+
@param circuit_preparation_fn: Circuit-preparation function
|
|
55
|
+
@return: Job result
|
|
56
|
+
"""
|
|
57
|
+
logger.debug("[Invocation] Invoke job")
|
|
58
|
+
|
|
59
|
+
circuit = self.__pre_execute(circuit_preparation_fn)
|
|
60
|
+
|
|
61
|
+
if circuit is None:
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
self.__execute(circuit, post_processing_fn)
|
|
65
|
+
|
|
66
|
+
def __pre_execute(self, circuit_preparation_fn):
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
@param circuit_preparation_fn: Circuit preparation function
|
|
70
|
+
"""
|
|
71
|
+
circuit = self.__prepare_circuit(circuit_preparation_fn)
|
|
72
|
+
|
|
73
|
+
if circuit is None:
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
self.__prepare_backend_data(circuit)
|
|
78
|
+
|
|
79
|
+
except Exception as exception:
|
|
80
|
+
job_response = JobResponse(
|
|
81
|
+
status_code=StatusCode.ERROR,
|
|
82
|
+
authentication=self.authentication,
|
|
83
|
+
project_header=self.project_header,
|
|
84
|
+
job_result={"error": str(exception)},
|
|
85
|
+
content_type=MediaType.APPLICATION_JSON,
|
|
86
|
+
job_status=JobStatus.ERROR.value,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
update_job_metadata(
|
|
90
|
+
job_response=job_response,
|
|
91
|
+
callback_url=self.callback_dict.get(
|
|
92
|
+
InvocationStep.PREPARATION
|
|
93
|
+
).on_error,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
self._export_circuit(circuit)
|
|
97
|
+
|
|
98
|
+
return circuit
|
|
99
|
+
|
|
100
|
+
def __execute(self, circuit, post_processing_fn):
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
@param circuit: Circuit was run
|
|
104
|
+
@param post_processing_fn: Post-processing function
|
|
105
|
+
@return: Job response
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
logger.debug("[Invocation] Execute job")
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
if self.backend_information is None:
|
|
112
|
+
raise Exception("Backend is not found")
|
|
113
|
+
|
|
114
|
+
device_name = self.backend_information.device_name
|
|
115
|
+
provider_tag = self.backend_information.provider_tag
|
|
116
|
+
backend_authentication = self.backend_information.authentication
|
|
117
|
+
|
|
118
|
+
logger.debug(
|
|
119
|
+
"[Invocation] Execute job with provider tag: {0}".format(
|
|
120
|
+
provider_tag.value
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
provider = self._create_provider()
|
|
125
|
+
|
|
126
|
+
logger.debug(
|
|
127
|
+
"[Invocation] Execute job with device name: {0}".format(device_name)
|
|
128
|
+
)
|
|
129
|
+
device = self._create_device(provider)
|
|
130
|
+
|
|
131
|
+
except Exception as exception:
|
|
132
|
+
job_response = JobResponse(
|
|
133
|
+
job_status=JobStatus.ERROR.value,
|
|
134
|
+
content_type=MediaType.APPLICATION_JSON,
|
|
135
|
+
status_code=StatusCode.ERROR,
|
|
136
|
+
authentication=self.authentication,
|
|
137
|
+
project_header=self.project_header,
|
|
138
|
+
job_result={"error": str(exception)},
|
|
139
|
+
)
|
|
140
|
+
update_job_metadata(
|
|
141
|
+
job_response=job_response,
|
|
142
|
+
callback_url=self.callback_dict.get(InvocationStep.EXECUTION).on_error,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
device.run_circuit(
|
|
148
|
+
circuit=circuit,
|
|
149
|
+
post_processing_fn=post_processing_fn,
|
|
150
|
+
options=self.options,
|
|
151
|
+
callback_dict=self.callback_dict,
|
|
152
|
+
authentication=self.authentication,
|
|
153
|
+
project_header=self.project_header,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def __prepare_circuit(self, circuit_preparation_fn):
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
@param circuit_preparation_fn: Circuit preparation function
|
|
160
|
+
@return: circuit
|
|
161
|
+
"""
|
|
162
|
+
logger.debug("[Invocation] Prepare circuit")
|
|
163
|
+
|
|
164
|
+
job_response = JobResponse(
|
|
165
|
+
status_code=StatusCode.DONE,
|
|
166
|
+
authentication=self.authentication,
|
|
167
|
+
project_header=self.project_header,
|
|
168
|
+
)
|
|
169
|
+
update_job_metadata(
|
|
170
|
+
job_response=job_response,
|
|
171
|
+
callback_url=self.callback_dict.get(InvocationStep.PREPARATION).on_start,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
circuit = circuit_preparation_fn(self.input)
|
|
176
|
+
|
|
177
|
+
if circuit is None:
|
|
178
|
+
raise Exception("Invalid circuit")
|
|
179
|
+
|
|
180
|
+
update_job_metadata(
|
|
181
|
+
job_response=job_response,
|
|
182
|
+
callback_url=self.callback_dict.get(InvocationStep.PREPARATION).on_done,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
return circuit
|
|
186
|
+
|
|
187
|
+
except Exception as exception:
|
|
188
|
+
job_response.job_status = JobStatus.ERROR.value
|
|
189
|
+
job_response.content_type = MediaType.APPLICATION_JSON
|
|
190
|
+
job_response.status_code = StatusCode.ERROR
|
|
191
|
+
job_response.job_result = {"error": str(exception)}
|
|
192
|
+
|
|
193
|
+
update_job_metadata(
|
|
194
|
+
job_response=job_response,
|
|
195
|
+
callback_url=self.callback_dict.get(
|
|
196
|
+
InvocationStep.PREPARATION
|
|
197
|
+
).on_error,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
def __prepare_backend_data(self, circuit):
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
@param circuit: Circuit was run
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
required_qubit_amount = self._get_qubit_amount(circuit)
|
|
209
|
+
|
|
210
|
+
device_selection = DeviceSelection(
|
|
211
|
+
required_qubit_amount,
|
|
212
|
+
self.device_id,
|
|
213
|
+
self.authentication.user_token,
|
|
214
|
+
self.device_selection_url,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
self.backend_information = device_selection.select()
|
|
218
|
+
logger.info("before")
|
|
219
|
+
logger.info(self.backend_information.authentication)
|
|
220
|
+
self.backend_information.authentication = self.invoke_authentication
|
|
221
|
+
logger.info("after")
|
|
222
|
+
logger.info(self.backend_information.authentication)
|
|
223
|
+
|
|
224
|
+
@abstractmethod
|
|
225
|
+
def _export_circuit(self, circuit):
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
@param circuit: Circuit was exported
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
raise NotImplemented('[Invocation] _export_circuit() method must be implemented')
|
|
232
|
+
|
|
233
|
+
@abstractmethod
|
|
234
|
+
def _create_provider(self, ):
|
|
235
|
+
"""
|
|
236
|
+
Create provider with ProviderFactory
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
raise NotImplemented('[Invocation] _create_provider() method must be implemented')
|
|
240
|
+
|
|
241
|
+
@abstractmethod
|
|
242
|
+
def _create_device(self, provider: Provider):
|
|
243
|
+
"""
|
|
244
|
+
Create device with DeviceFactory
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
raise NotImplemented('[Invocation] _create_device() method must be implemented')
|
|
248
|
+
|
|
249
|
+
@abstractmethod
|
|
250
|
+
def _get_qubit_amount(self, circuit):
|
|
251
|
+
"""
|
|
252
|
+
Get number qubit of circuit
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
raise NotImplemented('[Invocation] _get_qubit_amount() method must be implemented')
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
QApp Platform Project job_fetching.py Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
3
|
+
"""
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from ..callback.update_job_metadata import update_job_metadata
|
|
8
|
+
from ...async_tasks.post_processing_task import PostProcessingTask
|
|
9
|
+
from ...config.thread_config import circuit_running_pool
|
|
10
|
+
from ...data.callback.callback_url import CallbackUrl
|
|
11
|
+
from ...data.promise.post_processing_promise import PostProcessingPromise
|
|
12
|
+
from ...data.request.job_fetching_request import JobFetchingRequest
|
|
13
|
+
from ...data.response.authentication import Authentication
|
|
14
|
+
from ...data.response.job_response import JobResponse
|
|
15
|
+
from ...data.response.project_header import ProjectHeader
|
|
16
|
+
from ...enum.invocation_step import InvocationStep
|
|
17
|
+
from ...config.logging_config import logger
|
|
18
|
+
from ...enum.media_type import MediaType
|
|
19
|
+
from ...enum.status.job_status import JobStatus
|
|
20
|
+
from ...enum.status.status_code import StatusCode
|
|
21
|
+
from ...util.json_parser_utils import JsonParserUtils
|
|
22
|
+
from ...util.response_utils import ResponseUtils
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class JobFetching(ABC):
|
|
26
|
+
def __init__(self, request_data: JobFetchingRequest):
|
|
27
|
+
self.provider_authentication: dict = request_data.provider_authentication
|
|
28
|
+
self.provider_job_id: str = request_data.provider_job_id
|
|
29
|
+
self.backend_authentication: Authentication = request_data.authentication
|
|
30
|
+
self.project_header: ProjectHeader = request_data.project_header
|
|
31
|
+
self.callback_dict: dict = {
|
|
32
|
+
InvocationStep.EXECUTION: request_data.execution,
|
|
33
|
+
InvocationStep.ANALYSIS: request_data.analysis,
|
|
34
|
+
InvocationStep.FINALIZATION: request_data.finalization
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
def fetch(self, post_processing_fn):
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
@param post_processing_fn:
|
|
41
|
+
@return:
|
|
42
|
+
"""
|
|
43
|
+
logger.debug("[Fetching] Fetch job")
|
|
44
|
+
|
|
45
|
+
job_response = JobResponse(
|
|
46
|
+
provider_job_id=self.provider_job_id,
|
|
47
|
+
authentication=self.backend_authentication,
|
|
48
|
+
project_header=self.project_header,
|
|
49
|
+
status_code=StatusCode.DONE)
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
provider = self._collect_provider()
|
|
53
|
+
|
|
54
|
+
job = self._retrieve_job(provider)
|
|
55
|
+
job_status = self._get_job_status(job)
|
|
56
|
+
original_job_result = self._get_job_result(job)
|
|
57
|
+
|
|
58
|
+
job_response.job_status = job_status
|
|
59
|
+
|
|
60
|
+
if JobStatus.DONE.value.__eq__(job_response.job_status):
|
|
61
|
+
circuit_running_pool.submit(self.__handle_job_result,
|
|
62
|
+
original_job_result,
|
|
63
|
+
job_response,
|
|
64
|
+
self.callback_dict,
|
|
65
|
+
post_processing_fn)
|
|
66
|
+
|
|
67
|
+
update_job_metadata(
|
|
68
|
+
job_response=job_response,
|
|
69
|
+
callback_url=self.callback_dict.get(InvocationStep.EXECUTION).on_done)
|
|
70
|
+
|
|
71
|
+
elif JobStatus.ERROR.value.__eq__(job_response.job_status):
|
|
72
|
+
job_response.job_result = JsonParserUtils.parse(original_job_result)
|
|
73
|
+
|
|
74
|
+
update_job_metadata(
|
|
75
|
+
job_response=job_response,
|
|
76
|
+
callback_url=self.callback_dict.get(InvocationStep.EXECUTION).on_error)
|
|
77
|
+
|
|
78
|
+
else:
|
|
79
|
+
job_response.status_code = StatusCode.POLLING
|
|
80
|
+
|
|
81
|
+
except Exception as exception:
|
|
82
|
+
logger.debug("Exception when fetch job with provider_job_id {0}: {1}".format(
|
|
83
|
+
self.provider_job_id, str(exception)))
|
|
84
|
+
|
|
85
|
+
job_response.job_result = {
|
|
86
|
+
"error": "Exception when fetch job with provider_job_id {0}: {1}".format(
|
|
87
|
+
self.provider_job_id, str(exception)),
|
|
88
|
+
"exception": str(exception),
|
|
89
|
+
}
|
|
90
|
+
job_response.status_code = StatusCode.ERROR
|
|
91
|
+
job_response.job_status = JobStatus.ERROR.value
|
|
92
|
+
|
|
93
|
+
update_job_metadata(
|
|
94
|
+
job_response=job_response,
|
|
95
|
+
callback_url=self.callback_dict.get(InvocationStep.EXECUTION).on_error)
|
|
96
|
+
|
|
97
|
+
return ResponseUtils.generate_response(job_response)
|
|
98
|
+
|
|
99
|
+
def __handle_job_result(self,
|
|
100
|
+
original_job_result,
|
|
101
|
+
job_response: JobResponse,
|
|
102
|
+
callback_dict: dict,
|
|
103
|
+
post_processing_fn):
|
|
104
|
+
"""
|
|
105
|
+
Fetch job from IBM Quantum
|
|
106
|
+
|
|
107
|
+
@return: Job status
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
job_response = self.__on_analysis(
|
|
111
|
+
callback_url=callback_dict.get(InvocationStep.ANALYSIS),
|
|
112
|
+
job_response=job_response,
|
|
113
|
+
original_job_result=original_job_result)
|
|
114
|
+
|
|
115
|
+
if job_response is None:
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
self.__on_finalization(post_processing_fn=post_processing_fn,
|
|
119
|
+
callback_url=callback_dict.get(InvocationStep.FINALIZATION),
|
|
120
|
+
original_job_result=original_job_result)
|
|
121
|
+
|
|
122
|
+
def __on_analysis(self,
|
|
123
|
+
callback_url: CallbackUrl,
|
|
124
|
+
job_response: JobResponse,
|
|
125
|
+
original_job_result):
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
@param callback_url:
|
|
129
|
+
@param job_response:
|
|
130
|
+
@param original_job_result:
|
|
131
|
+
@return:
|
|
132
|
+
"""
|
|
133
|
+
logger.debug("[Fetching] On analysis")
|
|
134
|
+
|
|
135
|
+
update_job_metadata(job_response=job_response,
|
|
136
|
+
callback_url=callback_url.on_start)
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
job_response.content_type = MediaType.APPLICATION_JSON
|
|
140
|
+
|
|
141
|
+
job_response.job_histogram = self.__produce_histogram_data(original_job_result)
|
|
142
|
+
|
|
143
|
+
job_result_parse = JsonParserUtils.parse(original_job_result.result())
|
|
144
|
+
|
|
145
|
+
job_response.execution_time = self.__get_execution_time(original_job_result)
|
|
146
|
+
|
|
147
|
+
update_job_metadata(
|
|
148
|
+
job_response=job_response,
|
|
149
|
+
callback_url=callback_url.on_done)
|
|
150
|
+
|
|
151
|
+
return job_response
|
|
152
|
+
|
|
153
|
+
except Exception as exception:
|
|
154
|
+
logger.error("Exception when analyst job result with provider_job_id {0}: {1}".format(
|
|
155
|
+
self.provider_job_id, str(exception)))
|
|
156
|
+
|
|
157
|
+
job_response.status_code = StatusCode.ERROR
|
|
158
|
+
job_response.job_status = JobStatus.ERROR.value
|
|
159
|
+
job_response.job_result = {
|
|
160
|
+
"error": "Exception when analyst job result with provider_job_id {0}".format(
|
|
161
|
+
self.provider_job_id),
|
|
162
|
+
"exception": str(exception),
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
update_job_metadata(
|
|
166
|
+
job_response=job_response,
|
|
167
|
+
callback_url=callback_url.on_error)
|
|
168
|
+
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
def __on_finalization(self,
|
|
172
|
+
post_processing_fn,
|
|
173
|
+
callback_url: CallbackUrl,
|
|
174
|
+
original_job_result):
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
@param post_processing_fn:
|
|
178
|
+
@param callback_url:
|
|
179
|
+
@param original_job_result:
|
|
180
|
+
"""
|
|
181
|
+
logger.debug("[Fetching] On finalization")
|
|
182
|
+
|
|
183
|
+
promise = PostProcessingPromise(callback_url=callback_url,
|
|
184
|
+
authentication=self.backend_authentication,
|
|
185
|
+
job_result=original_job_result.result(),
|
|
186
|
+
project_header=self.project_header)
|
|
187
|
+
|
|
188
|
+
PostProcessingTask(post_processing_fn=post_processing_fn,
|
|
189
|
+
promise=promise).do()
|
|
190
|
+
|
|
191
|
+
@staticmethod
|
|
192
|
+
def __produce_histogram_data(job_result) -> Any | None:
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
@param job_result:
|
|
196
|
+
@return:
|
|
197
|
+
"""
|
|
198
|
+
logger.debug("[Fetching] Produce histogram")
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
histogram_data = job_result.result()[0].data.meas.get_counts()
|
|
202
|
+
|
|
203
|
+
except Exception as e:
|
|
204
|
+
logger.debug("[QiskitDevice] Can't produce histogram with error: {0}".format(str(e)))
|
|
205
|
+
histogram_data = None
|
|
206
|
+
|
|
207
|
+
return histogram_data
|
|
208
|
+
|
|
209
|
+
@staticmethod
|
|
210
|
+
def __get_execution_time(job_result):
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
@param job_result:
|
|
214
|
+
@return:
|
|
215
|
+
"""
|
|
216
|
+
return job_result.usage()
|
|
217
|
+
@abstractmethod
|
|
218
|
+
def _collect_provider(self, ):
|
|
219
|
+
"""
|
|
220
|
+
Create provider with ProviderFactory
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
raise NotImplemented('[Invocation] _create_provider() method must be implemented')
|
|
224
|
+
|
|
225
|
+
@abstractmethod
|
|
226
|
+
def _retrieve_job(self, provider):
|
|
227
|
+
"""
|
|
228
|
+
Retrieve job from provider
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
raise NotImplemented('[Invocation] _retrieve_job() method must be implemented')
|
|
232
|
+
|
|
233
|
+
@abstractmethod
|
|
234
|
+
def _get_job_status(self, job):
|
|
235
|
+
"""
|
|
236
|
+
Get job status
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
raise NotImplemented('[Invocation] _get_job_status() method must be implemented')
|
|
240
|
+
|
|
241
|
+
@abstractmethod
|
|
242
|
+
def _get_job_result(self, job):
|
|
243
|
+
"""
|
|
244
|
+
Get job result
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
raise NotImplemented('[Invocation] _get_job_result() method must be implemented')
|