quapp-common 0.0.11.dev3__tar.gz → 0.0.11.dev4__tar.gz
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.
- {quapp_common-0.0.11.dev3/quapp_common.egg-info → quapp_common-0.0.11.dev4}/PKG-INFO +41 -6
- quapp_common-0.0.11.dev4/README.md +57 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/pyproject.toml +1 -1
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/async_tasks/async_task.py +2 -2
- quapp_common-0.0.11.dev4/quapp_common/async_tasks/export_circuit_task.py +161 -0
- quapp_common-0.0.11.dev4/quapp_common/async_tasks/post_processing_task.py +49 -0
- quapp_common-0.0.11.dev4/quapp_common/component/backend/invocation.py +253 -0
- quapp_common-0.0.11.dev4/quapp_common/component/backend/job_fetcher.py +229 -0
- quapp_common-0.0.11.dev4/quapp_common/component/backend/job_fetching.py +248 -0
- quapp_common-0.0.11.dev4/quapp_common/component/callback/update_job_metadata.py +41 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/component/device/device_selection.py +20 -23
- quapp_common-0.0.11.dev4/quapp_common/config/logging_config.py +31 -0
- quapp_common-0.0.11.dev4/quapp_common/data/promise/post_processing_promise.py +18 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/request/invocation_request.py +24 -18
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/request/job_fetching_request.py +10 -10
- quapp_common-0.0.11.dev4/quapp_common/data/request/request.py +31 -0
- quapp_common-0.0.11.dev4/quapp_common/data/response/custom_header.py +15 -0
- quapp_common-0.0.11.dev4/quapp_common/data/response/job_response.py +31 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/factory/device_factory.py +4 -2
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/factory/handler_factory.py +3 -2
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/factory/provider_factory.py +4 -2
- quapp_common-0.0.11.dev4/quapp_common/handler/handler.py +25 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/model/device/custom_device.py +32 -31
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/model/device/device.py +94 -64
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/model/provider/provider.py +2 -2
- quapp_common-0.0.11.dev4/quapp_common/util/http_utils.py +93 -0
- quapp_common-0.0.11.dev4/quapp_common/util/json_parser_utils.py +75 -0
- quapp_common-0.0.11.dev4/quapp_common/util/response_utils.py +97 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4/quapp_common.egg-info}/PKG-INFO +41 -6
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common.egg-info/SOURCES.txt +1 -1
- quapp_common-0.0.11.dev3/README.md +0 -22
- quapp_common-0.0.11.dev3/quapp_common/async_tasks/export_circuit_task.py +0 -121
- quapp_common-0.0.11.dev3/quapp_common/async_tasks/post_processing_task.py +0 -61
- quapp_common-0.0.11.dev3/quapp_common/component/backend/invocation.py +0 -255
- quapp_common-0.0.11.dev3/quapp_common/component/backend/job_fetcher.py +0 -231
- quapp_common-0.0.11.dev3/quapp_common/component/backend/job_fetching.py +0 -250
- quapp_common-0.0.11.dev3/quapp_common/component/callback/update_job_metadata.py +0 -40
- quapp_common-0.0.11.dev3/quapp_common/config/logging_config.py +0 -34
- quapp_common-0.0.11.dev3/quapp_common/data/promise/post_processing_promise.py +0 -18
- quapp_common-0.0.11.dev3/quapp_common/data/request/request.py +0 -23
- quapp_common-0.0.11.dev3/quapp_common/data/response/job_response.py +0 -32
- quapp_common-0.0.11.dev3/quapp_common/data/response/project_header.py +0 -9
- quapp_common-0.0.11.dev3/quapp_common/handler/handler.py +0 -20
- quapp_common-0.0.11.dev3/quapp_common/util/http_utils.py +0 -37
- quapp_common-0.0.11.dev3/quapp_common/util/json_parser_utils.py +0 -75
- quapp_common-0.0.11.dev3/quapp_common/util/response_utils.py +0 -36
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/LICENSE +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/async_tasks/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/component/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/component/backend/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/component/callback/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/component/device/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/config/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/config/thread_config.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/async_task/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/async_task/circuit_export/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/async_task/circuit_export/backend_holder.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/async_task/circuit_export/circuit_holder.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/backend/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/backend/backend_information.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/callback/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/callback/callback_url.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/device/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/device/circuit_running_option.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/promise/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/promise/promise.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/request/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/response/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/data/response/authentication.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/enum/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/enum/base_enum.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/enum/http_header.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/enum/invocation_step.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/enum/media_type.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/enum/processing_unit.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/enum/provider_tag.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/enum/sdk.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/enum/status/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/enum/status/job_status.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/enum/status/status_code.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/enum/token_type.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/factory/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/handler/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/model/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/model/device/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/model/provider/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/util/__init__.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common/util/file_utils.py +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common.egg-info/dependency_links.txt +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common.egg-info/requires.txt +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/quapp_common.egg-info/top_level.txt +0 -0
- {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quapp-common
|
|
3
|
-
Version: 0.0.11.
|
|
3
|
+
Version: 0.0.11.dev4
|
|
4
4
|
Summary: Quapp common library supporting Quapp Platform for Quantum Computing
|
|
5
5
|
Author-email: "CITYNOW Co. Ltd. " <corp@citynow.vn>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -38,15 +38,29 @@ Quapp common library supporting Quapp Platform for Quantum Computing.
|
|
|
38
38
|
|
|
39
39
|
## Overview
|
|
40
40
|
|
|
41
|
-
`quapp-common` is a Python library designed to support the Quapp Platform for
|
|
42
|
-
providing common utilities, configurations, and
|
|
43
|
-
|
|
41
|
+
`quapp-common` is a Python library designed to support the Quapp Platform for
|
|
42
|
+
Quantum Computing by providing common utilities, configurations, and
|
|
43
|
+
abstractions for working with
|
|
44
|
+
quantum providers and devices.
|
|
45
|
+
|
|
46
|
+
Recent improvements focus on cleaner and more consistent logging, enhanced error
|
|
47
|
+
handling, and
|
|
48
|
+
standardizing header management by adding workspace-specific headers instead of
|
|
49
|
+
tenant-specific ones,
|
|
50
|
+
improving maintainability and clarity.
|
|
44
51
|
|
|
45
52
|
## Features
|
|
46
53
|
|
|
47
54
|
- Provider and device factory for quantum computing platforms.
|
|
48
|
-
- Logging and configuration utilities.
|
|
49
|
-
- Support for AWS Braket, OQC Cloud, Qiskit, PennyLane, DWave Ocean and Quapp
|
|
55
|
+
- Logging and configuration utilities with improved and detailed log messages.
|
|
56
|
+
- Support for AWS Braket, OQC Cloud, Qiskit, PennyLane, DWave Ocean, and Quapp
|
|
57
|
+
quantum simulators.
|
|
58
|
+
- Refactored classes and utilities to remove tenant-specific request, response,
|
|
59
|
+
and promise classes.
|
|
60
|
+
- Standardized naming by renaming `ProjectHeader` to `CustomHeader`.
|
|
61
|
+
- Enhanced error handling and job metadata update mechanisms.
|
|
62
|
+
- Simplified and cleaner HTTP request/response logging and URL parsing
|
|
63
|
+
utilities.
|
|
50
64
|
|
|
51
65
|
## Installation
|
|
52
66
|
|
|
@@ -54,3 +68,24 @@ Install via pip:
|
|
|
54
68
|
|
|
55
69
|
```bash
|
|
56
70
|
pip install quapp-common
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Recently Changes Highlights
|
|
74
|
+
|
|
75
|
+
- Added workspace-specific classes for request, response, and promise,
|
|
76
|
+
consolidating code to unify context handling.
|
|
77
|
+
- Refactored logging throughout the codebase, removing redundant debug logs and
|
|
78
|
+
adding context-rich log messages.
|
|
79
|
+
- Improved URL parsing and job ID extraction, especially in HTTP utilities, to
|
|
80
|
+
better track jobs.
|
|
81
|
+
- Cleaned up logging configuration files to standardize log formats and levels.
|
|
82
|
+
- Added robust error handling in job execution and analysis pipelines, ensuring
|
|
83
|
+
detailed failure reporting.
|
|
84
|
+
- Simplified header and request body logging, improving performance and
|
|
85
|
+
readability.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
For detailed usage and API references, please refer to the in-code documentation
|
|
90
|
+
or contact the maintainers.
|
|
91
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# quapp-common
|
|
2
|
+
|
|
3
|
+
Quapp common library supporting Quapp Platform for Quantum Computing.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`quapp-common` is a Python library designed to support the Quapp Platform for
|
|
8
|
+
Quantum Computing by providing common utilities, configurations, and
|
|
9
|
+
abstractions for working with
|
|
10
|
+
quantum providers and devices.
|
|
11
|
+
|
|
12
|
+
Recent improvements focus on cleaner and more consistent logging, enhanced error
|
|
13
|
+
handling, and
|
|
14
|
+
standardizing header management by adding workspace-specific headers instead of
|
|
15
|
+
tenant-specific ones,
|
|
16
|
+
improving maintainability and clarity.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- Provider and device factory for quantum computing platforms.
|
|
21
|
+
- Logging and configuration utilities with improved and detailed log messages.
|
|
22
|
+
- Support for AWS Braket, OQC Cloud, Qiskit, PennyLane, DWave Ocean, and Quapp
|
|
23
|
+
quantum simulators.
|
|
24
|
+
- Refactored classes and utilities to remove tenant-specific request, response,
|
|
25
|
+
and promise classes.
|
|
26
|
+
- Standardized naming by renaming `ProjectHeader` to `CustomHeader`.
|
|
27
|
+
- Enhanced error handling and job metadata update mechanisms.
|
|
28
|
+
- Simplified and cleaner HTTP request/response logging and URL parsing
|
|
29
|
+
utilities.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
Install via pip:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install quapp-common
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Recently Changes Highlights
|
|
40
|
+
|
|
41
|
+
- Added workspace-specific classes for request, response, and promise,
|
|
42
|
+
consolidating code to unify context handling.
|
|
43
|
+
- Refactored logging throughout the codebase, removing redundant debug logs and
|
|
44
|
+
adding context-rich log messages.
|
|
45
|
+
- Improved URL parsing and job ID extraction, especially in HTTP utilities, to
|
|
46
|
+
better track jobs.
|
|
47
|
+
- Cleaned up logging configuration files to standardize log formats and levels.
|
|
48
|
+
- Added robust error handling in job execution and analysis pipelines, ensuring
|
|
49
|
+
detailed failure reporting.
|
|
50
|
+
- Simplified header and request body logging, improving performance and
|
|
51
|
+
readability.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
For detailed usage and API references, please refer to the in-code documentation
|
|
56
|
+
or contact the maintainers.
|
|
57
|
+
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quapp-common"
|
|
7
|
-
version = "0.0.11.
|
|
7
|
+
version = "0.0.11.dev4"
|
|
8
8
|
description = "Quapp common library supporting Quapp Platform for Quantum Computing"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "CITYNOW Co. Ltd. ", email = "corp@citynow.vn" }]
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# export_circuit_task.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
from abc import abstractmethod
|
|
6
|
+
from io import BytesIO
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from .async_task import AsyncTask
|
|
11
|
+
from ..config.logging_config import job_logger
|
|
12
|
+
from ..data.async_task.circuit_export.backend_holder import BackendDataHolder
|
|
13
|
+
from ..data.async_task.circuit_export.circuit_holder import CircuitDataHolder
|
|
14
|
+
from ..enum.media_type import MediaType
|
|
15
|
+
from ..util.file_utils import FileUtils
|
|
16
|
+
from ..util.http_utils import create_bearer_header, get_job_id_from_url
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CircuitExportTask(AsyncTask):
|
|
20
|
+
MAX_CIRCUIT_IMAGE_SIZE = 5 * (1024 ** 2)
|
|
21
|
+
|
|
22
|
+
def __init__(self, circuit_data_holder: CircuitDataHolder,
|
|
23
|
+
backend_data_holder: BackendDataHolder):
|
|
24
|
+
super().__init__()
|
|
25
|
+
self.circuit_data_holder = circuit_data_holder
|
|
26
|
+
self.backend_data_holder = backend_data_holder
|
|
27
|
+
self.logger = job_logger(
|
|
28
|
+
get_job_id_from_url(self.circuit_data_holder.export_url))
|
|
29
|
+
|
|
30
|
+
def do(self):
|
|
31
|
+
"""
|
|
32
|
+
Export circuit to svg file, then send it to QuaO server for saving
|
|
33
|
+
"""
|
|
34
|
+
self.logger.info("Starting circuit export task...")
|
|
35
|
+
|
|
36
|
+
circuit_export_url = self.circuit_data_holder.export_url
|
|
37
|
+
|
|
38
|
+
if circuit_export_url is None or len(circuit_export_url) < 1:
|
|
39
|
+
self.logger.warning("Export URL is missing. Task will exit.")
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
self.logger.debug('Converting circuit to SVG')
|
|
44
|
+
figure_buffer = self.__convert()
|
|
45
|
+
except Exception as e:
|
|
46
|
+
self.logger.error(f"Error converting circuit to SVG: {e}",
|
|
47
|
+
exc_info=True)
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
self.logger.debug('Determining if circuit SVG should be zipped')
|
|
52
|
+
io_buffer_value, content_type = self.__determine_zip(
|
|
53
|
+
figure_buffer=figure_buffer)
|
|
54
|
+
self.logger.debug(f"Content type: {content_type}")
|
|
55
|
+
self.logger.debug(f"Buffer size: {len(io_buffer_value)} bytes")
|
|
56
|
+
except Exception as e:
|
|
57
|
+
self.logger.error(
|
|
58
|
+
f"Error determining if circuit SVG should be zipped: {e}",
|
|
59
|
+
exc_info=True)
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
self.logger.debug('Sending circuit to backend')
|
|
64
|
+
self.__send(io_buffer_value=io_buffer_value,
|
|
65
|
+
content_type=content_type)
|
|
66
|
+
self.logger.debug("Circuit sent to backend successfully.")
|
|
67
|
+
except Exception as e:
|
|
68
|
+
self.logger.error(f"Error sending exported circuit to backend: {e}",
|
|
69
|
+
exc_info=True)
|
|
70
|
+
|
|
71
|
+
self.logger.info("Circuit export task finished.")
|
|
72
|
+
|
|
73
|
+
def __convert(self):
|
|
74
|
+
"""
|
|
75
|
+
Convert circuit to SVG, return as BytesIO.
|
|
76
|
+
"""
|
|
77
|
+
self.logger.debug("Preparing circuit figure...")
|
|
78
|
+
transpiled_circuit = self._transpile_circuit()
|
|
79
|
+
self.logger.debug("Transpiled circuit successfully.")
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
circuit_figure = transpiled_circuit.draw(output='mpl', fold=-1)
|
|
83
|
+
except Exception as exception:
|
|
84
|
+
self.logger.error(f"Error drawing circuit: {exception}",
|
|
85
|
+
exc_info=True)
|
|
86
|
+
raise exception
|
|
87
|
+
|
|
88
|
+
self.logger.debug("Converting circuit figure to SVG file...")
|
|
89
|
+
figure_buffer = BytesIO()
|
|
90
|
+
try:
|
|
91
|
+
circuit_figure.savefig(figure_buffer, format='svg',
|
|
92
|
+
bbox_inches='tight')
|
|
93
|
+
self.logger.debug(
|
|
94
|
+
f"SVG export complete. Size: {figure_buffer.getbuffer().nbytes} bytes")
|
|
95
|
+
except Exception as exception:
|
|
96
|
+
self.logger.error(
|
|
97
|
+
f"Error saving circuit figure to SVG: {exception}",
|
|
98
|
+
exc_info=True)
|
|
99
|
+
raise exception
|
|
100
|
+
|
|
101
|
+
return figure_buffer
|
|
102
|
+
|
|
103
|
+
def __determine_zip(self, figure_buffer):
|
|
104
|
+
"""
|
|
105
|
+
Determine if the buffer needs to be zipped; return (buffer, content_type).
|
|
106
|
+
"""
|
|
107
|
+
self.logger.debug("Checking if SVG file needs to be zipped.")
|
|
108
|
+
buffer_value = figure_buffer.getvalue()
|
|
109
|
+
content_type = MediaType.SVG_XML
|
|
110
|
+
|
|
111
|
+
self.logger.debug("Checking max file size")
|
|
112
|
+
estimated_file_size = len(buffer_value)
|
|
113
|
+
|
|
114
|
+
if estimated_file_size > CircuitExportTask.MAX_CIRCUIT_IMAGE_SIZE:
|
|
115
|
+
self.logger.debug("Zip file")
|
|
116
|
+
zip_file_buffer = FileUtils.zip(io_buffer_value=buffer_value,
|
|
117
|
+
file_name="circuit_image.svg")
|
|
118
|
+
|
|
119
|
+
buffer_value = zip_file_buffer.getvalue()
|
|
120
|
+
content_type = MediaType.APPLICATION_ZIP
|
|
121
|
+
|
|
122
|
+
return buffer_value, content_type
|
|
123
|
+
|
|
124
|
+
def __send(self, io_buffer_value, content_type: MediaType):
|
|
125
|
+
"""
|
|
126
|
+
Send circuit SVG (or zipped SVG) to the backend.
|
|
127
|
+
"""
|
|
128
|
+
url = self.circuit_data_holder.export_url
|
|
129
|
+
|
|
130
|
+
self.logger.debug(
|
|
131
|
+
f"Sending circuit svg image to [{url}] with POST method ...")
|
|
132
|
+
|
|
133
|
+
payload = {'circuit': ('circuit_image.svg', io_buffer_value,
|
|
134
|
+
content_type.value)}
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
response = requests.post(url=url, headers=create_bearer_header(
|
|
138
|
+
self.backend_data_holder.user_token), files=payload)
|
|
139
|
+
except Exception as exception:
|
|
140
|
+
self.logger.error(f"HTTP request failed: {exception}",
|
|
141
|
+
exc_info=True)
|
|
142
|
+
raise
|
|
143
|
+
|
|
144
|
+
if response.ok:
|
|
145
|
+
self.logger.info("Request sent to QuaO backend successfully.")
|
|
146
|
+
else:
|
|
147
|
+
self.logger.error(
|
|
148
|
+
f"Sending request to QuaO backend failed with status {response.status_code}! Response: {response.content}")
|
|
149
|
+
|
|
150
|
+
self.logger.debug("HTTP request complete.")
|
|
151
|
+
|
|
152
|
+
@abstractmethod
|
|
153
|
+
def _transpile_circuit(self):
|
|
154
|
+
"""
|
|
155
|
+
Should be implemented in subclass to return transpiled circuit.
|
|
156
|
+
"""
|
|
157
|
+
self.logger.debug(
|
|
158
|
+
"Calling _transpile_circuit (must be implemented in subclass)")
|
|
159
|
+
|
|
160
|
+
raise NotImplementedError(
|
|
161
|
+
'[CircuitExportTask] __transpile_circuit() method must be implemented')
|
|
@@ -0,0 +1,49 @@
|
|
|
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 ..config.logging_config import job_logger
|
|
6
|
+
from ..data.promise.post_processing_promise import PostProcessingPromise
|
|
7
|
+
from ..enum.media_type import MediaType
|
|
8
|
+
from ..util.http_utils import get_job_id_from_url
|
|
9
|
+
from ..util.json_parser_utils import parse
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PostProcessingTask(AsyncTask):
|
|
13
|
+
def __init__(self, post_processing_fn, promise: PostProcessingPromise):
|
|
14
|
+
self.post_processing_fn = post_processing_fn
|
|
15
|
+
self.promise = promise
|
|
16
|
+
self.logger = job_logger(
|
|
17
|
+
get_job_id_from_url(self.promise.callback_url.on_done))
|
|
18
|
+
|
|
19
|
+
def do(self):
|
|
20
|
+
"""
|
|
21
|
+
Execute post_processing and send to backend
|
|
22
|
+
"""
|
|
23
|
+
from ..component.callback.update_job_metadata import update_job_metadata
|
|
24
|
+
from ..util.response_utils import build_done_job_response, \
|
|
25
|
+
build_error_job_response
|
|
26
|
+
|
|
27
|
+
job_response = build_done_job_response(post_promise=self.promise)
|
|
28
|
+
|
|
29
|
+
update_job_metadata(job_response, self.promise.callback_url.on_start)
|
|
30
|
+
|
|
31
|
+
job_response.content_type = MediaType.APPLICATION_JSON
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
self.logger.info("Execute post_processing ...")
|
|
35
|
+
job_result_post_processing = self.post_processing_fn(
|
|
36
|
+
self.promise.job_result)
|
|
37
|
+
|
|
38
|
+
self.logger.debug("Parsing job result....")
|
|
39
|
+
print(job_result_post_processing)
|
|
40
|
+
job_response.job_result = parse(job_result_post_processing)
|
|
41
|
+
|
|
42
|
+
update_job_metadata(job_response, self.promise.callback_url.on_done)
|
|
43
|
+
|
|
44
|
+
except Exception as exception:
|
|
45
|
+
job_response = build_error_job_response(exception, job_response,
|
|
46
|
+
message='Error when post processing job result')
|
|
47
|
+
|
|
48
|
+
update_job_metadata(job_response,
|
|
49
|
+
self.promise.callback_url.on_error)
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# invocation.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
|
|
7
|
+
from ..callback.update_job_metadata import update_job_metadata
|
|
8
|
+
from ...component.device.device_selection import DeviceSelection
|
|
9
|
+
from ...config.logging_config import job_logger
|
|
10
|
+
from ...config.thread_config import circuit_running_pool
|
|
11
|
+
from ...data.backend.backend_information import BackendInformation
|
|
12
|
+
from ...data.device.circuit_running_option import CircuitRunningOption
|
|
13
|
+
from ...data.request.invocation_request import InvocationRequest
|
|
14
|
+
from ...data.response.authentication import Authentication
|
|
15
|
+
from ...data.response.custom_header import CustomHeader
|
|
16
|
+
from ...data.response.job_response import JobResponse
|
|
17
|
+
from ...enum.invocation_step import InvocationStep
|
|
18
|
+
from ...enum.sdk import Sdk
|
|
19
|
+
from ...enum.status.status_code import StatusCode
|
|
20
|
+
from ...model.provider.provider import Provider
|
|
21
|
+
from ...util.response_utils import build_error_job_response
|
|
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: CustomHeader = request_data.project_header
|
|
34
|
+
self.workspace_header: CustomHeader = request_data.workspace_header
|
|
35
|
+
self.job_id = request_data.job_id
|
|
36
|
+
self.options = CircuitRunningOption(shots=request_data.shots,
|
|
37
|
+
processing_unit=request_data.processing_unit,
|
|
38
|
+
executor=circuit_running_pool,
|
|
39
|
+
max_job_size=1, )
|
|
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
|
+
self.invoke_authentication = request_data.invoke_authentication
|
|
48
|
+
self.logger = job_logger(self.job_id)
|
|
49
|
+
self.logger.info(
|
|
50
|
+
f"Initializing Invocation with job id: {self.job_id}, sdk: {self.sdk}, device id: {self.device_id}")
|
|
51
|
+
|
|
52
|
+
def submit_job(self, circuit_preparation_fn, post_processing_fn):
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
@param post_processing_fn: Post-processing function
|
|
56
|
+
@param circuit_preparation_fn: Circuit-preparation function
|
|
57
|
+
@return: Job result
|
|
58
|
+
"""
|
|
59
|
+
self.logger.debug("Invoke job started")
|
|
60
|
+
|
|
61
|
+
circuit = self.__pre_execute(circuit_preparation_fn)
|
|
62
|
+
|
|
63
|
+
if circuit is None:
|
|
64
|
+
self.logger.warning(
|
|
65
|
+
"Circuit preparation returned None, cancelling job submission")
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
self.__execute(circuit, post_processing_fn)
|
|
69
|
+
self.logger.info("Submit job completed")
|
|
70
|
+
|
|
71
|
+
def __pre_execute(self, circuit_preparation_fn):
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
@param circuit_preparation_fn: Circuit preparation function
|
|
75
|
+
"""
|
|
76
|
+
self.logger.debug("Pre-execute: preparing circuit")
|
|
77
|
+
circuit = self.__prepare_circuit(circuit_preparation_fn)
|
|
78
|
+
|
|
79
|
+
if circuit is None:
|
|
80
|
+
self.logger.error(
|
|
81
|
+
"Circuit preparation failed, returning None from pre-execute")
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
self.logger.debug("Preparing backend data")
|
|
86
|
+
self.__prepare_backend_data(circuit)
|
|
87
|
+
self.logger.info(
|
|
88
|
+
f"Backend data prepared: device {self.backend_information.device_name}, provider {self.backend_information.provider_tag.value}")
|
|
89
|
+
|
|
90
|
+
except Exception as exception:
|
|
91
|
+
self.logger.error(f"Error when prepare backend data: {exception}",
|
|
92
|
+
exc_info=True)
|
|
93
|
+
|
|
94
|
+
job_response = build_error_job_response(exception,
|
|
95
|
+
message='Error when prepare backend data')
|
|
96
|
+
|
|
97
|
+
update_job_metadata(job_response, self.callback_dict.get(
|
|
98
|
+
InvocationStep.PREPARATION).on_error)
|
|
99
|
+
|
|
100
|
+
self.logger.debug("Exporting circuit")
|
|
101
|
+
self._export_circuit(circuit)
|
|
102
|
+
|
|
103
|
+
return circuit
|
|
104
|
+
|
|
105
|
+
def __execute(self, circuit, post_processing_fn):
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
@param circuit: Circuit was run
|
|
109
|
+
@param post_processing_fn: Post-processing function
|
|
110
|
+
@return: Job response
|
|
111
|
+
"""
|
|
112
|
+
self.logger.debug("Execute: starting job execution")
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
if self.backend_information is None:
|
|
116
|
+
self.logger.error(
|
|
117
|
+
"Backend information is None before execution")
|
|
118
|
+
raise ValueError("Backend is not found")
|
|
119
|
+
|
|
120
|
+
device_name = self.backend_information.device_name
|
|
121
|
+
provider_tag = self.backend_information.provider_tag
|
|
122
|
+
|
|
123
|
+
self.logger.debug(
|
|
124
|
+
f"Executing job with provider tag: {provider_tag.value}")
|
|
125
|
+
|
|
126
|
+
provider = self._create_provider()
|
|
127
|
+
|
|
128
|
+
self.logger.debug(f"Executing job with device name: {device_name}")
|
|
129
|
+
device = self._create_device(provider)
|
|
130
|
+
|
|
131
|
+
except Exception as exception:
|
|
132
|
+
self.logger.error(
|
|
133
|
+
f"Exception when create provider or device: {exception}",
|
|
134
|
+
exc_info=True)
|
|
135
|
+
|
|
136
|
+
job_response = build_error_job_response(exception,
|
|
137
|
+
message='Error when create provider or device')
|
|
138
|
+
update_job_metadata(job_response, self.callback_dict.get(
|
|
139
|
+
InvocationStep.EXECUTION).on_error, )
|
|
140
|
+
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
self.logger.info(
|
|
144
|
+
f"Running circuit on device {device_name} with provider {provider_tag.value}")
|
|
145
|
+
|
|
146
|
+
device.run_circuit(circuit=circuit,
|
|
147
|
+
post_processing_fn=post_processing_fn,
|
|
148
|
+
options=self.options,
|
|
149
|
+
callback_dict=self.callback_dict,
|
|
150
|
+
authentication=self.authentication,
|
|
151
|
+
project_header=self.project_header,
|
|
152
|
+
workspace_header=self.workspace_header)
|
|
153
|
+
|
|
154
|
+
def __prepare_circuit(self, circuit_preparation_fn):
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
@param circuit_preparation_fn: Circuit preparation function
|
|
158
|
+
@return: circuit
|
|
159
|
+
"""
|
|
160
|
+
self.logger.debug('Prepare circuit started')
|
|
161
|
+
|
|
162
|
+
job_response = JobResponse(status_code=StatusCode.DONE,
|
|
163
|
+
authentication=self.authentication,
|
|
164
|
+
project_header=self.project_header,
|
|
165
|
+
workspace_header=self.workspace_header)
|
|
166
|
+
update_job_metadata(job_response, self.callback_dict.get(
|
|
167
|
+
InvocationStep.PREPARATION).on_start)
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
circuit = circuit_preparation_fn(self.input)
|
|
171
|
+
|
|
172
|
+
if circuit is None:
|
|
173
|
+
self.logger.warning(
|
|
174
|
+
"Circuit preparation function returned None")
|
|
175
|
+
raise ValueError('Error when prepare circuit')
|
|
176
|
+
|
|
177
|
+
self.logger.debug(f"Circuit prepared: {circuit}")
|
|
178
|
+
|
|
179
|
+
update_job_metadata(job_response, self.callback_dict.get(
|
|
180
|
+
InvocationStep.PREPARATION).on_done)
|
|
181
|
+
|
|
182
|
+
return circuit
|
|
183
|
+
|
|
184
|
+
except Exception as exception:
|
|
185
|
+
self.logger.error(f'Error when prepare circuit: {exception}',
|
|
186
|
+
exc_info=True)
|
|
187
|
+
|
|
188
|
+
job_response = build_error_job_response(exception, job_response,
|
|
189
|
+
message='Error when prepare circuit')
|
|
190
|
+
|
|
191
|
+
update_job_metadata(job_response, self.callback_dict.get(
|
|
192
|
+
InvocationStep.PREPARATION).on_error)
|
|
193
|
+
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
def __prepare_backend_data(self, circuit):
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
@param circuit: Circuit was run
|
|
200
|
+
"""
|
|
201
|
+
self.logger.debug("Prepare backend data started")
|
|
202
|
+
|
|
203
|
+
required_qubit_amount = self._get_qubit_amount(circuit)
|
|
204
|
+
self.logger.info(
|
|
205
|
+
f"Qubit amount required for circuit: {required_qubit_amount}")
|
|
206
|
+
|
|
207
|
+
device_selection = DeviceSelection(required_qubit_amount,
|
|
208
|
+
self.device_id,
|
|
209
|
+
self.authentication.user_token,
|
|
210
|
+
self.device_selection_url,
|
|
211
|
+
self.project_header,
|
|
212
|
+
self.workspace_header)
|
|
213
|
+
|
|
214
|
+
self.backend_information = device_selection.select()
|
|
215
|
+
self.backend_information.authentication = self.invoke_authentication
|
|
216
|
+
self.logger.debug(
|
|
217
|
+
f"Selected backend: deviceName={self.backend_information.device_name}, provider_tag={self.backend_information.provider_tag}")
|
|
218
|
+
|
|
219
|
+
@abstractmethod
|
|
220
|
+
def _export_circuit(self, circuit):
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
@param circuit: Circuit was exported
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
raise NotImplementedError(
|
|
227
|
+
'_export_circuit() method must be implemented')
|
|
228
|
+
|
|
229
|
+
@abstractmethod
|
|
230
|
+
def _create_provider(self, ):
|
|
231
|
+
"""
|
|
232
|
+
Create a provider with ProviderFactory
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
raise NotImplementedError(
|
|
236
|
+
'_create_provider() method must be implemented')
|
|
237
|
+
|
|
238
|
+
@abstractmethod
|
|
239
|
+
def _create_device(self, provider: Provider):
|
|
240
|
+
"""
|
|
241
|
+
Create a device with DeviceFactory
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
raise NotImplementedError('_create_device() method must be implemented')
|
|
245
|
+
|
|
246
|
+
@abstractmethod
|
|
247
|
+
def _get_qubit_amount(self, circuit):
|
|
248
|
+
"""
|
|
249
|
+
Get number qubit of circuit
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
raise NotImplementedError(
|
|
253
|
+
'_get_qubit_amount() method must be implemented')
|