quapp-common 0.0.11.dev3__tar.gz → 0.0.11.dev5__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.
Files changed (94) hide show
  1. {quapp_common-0.0.11.dev3/quapp_common.egg-info → quapp_common-0.0.11.dev5}/PKG-INFO +41 -6
  2. quapp_common-0.0.11.dev5/README.md +57 -0
  3. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/pyproject.toml +1 -1
  4. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/async_tasks/async_task.py +2 -2
  5. quapp_common-0.0.11.dev5/quapp_common/async_tasks/export_circuit_task.py +161 -0
  6. quapp_common-0.0.11.dev5/quapp_common/async_tasks/post_processing_task.py +49 -0
  7. quapp_common-0.0.11.dev5/quapp_common/component/backend/invocation.py +253 -0
  8. quapp_common-0.0.11.dev5/quapp_common/component/backend/job_fetcher.py +229 -0
  9. quapp_common-0.0.11.dev5/quapp_common/component/backend/job_fetching.py +248 -0
  10. quapp_common-0.0.11.dev5/quapp_common/component/callback/update_job_metadata.py +41 -0
  11. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/component/device/device_selection.py +20 -23
  12. quapp_common-0.0.11.dev5/quapp_common/config/logging_config.py +33 -0
  13. quapp_common-0.0.11.dev5/quapp_common/data/promise/post_processing_promise.py +18 -0
  14. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/request/invocation_request.py +24 -18
  15. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/request/job_fetching_request.py +10 -10
  16. quapp_common-0.0.11.dev5/quapp_common/data/request/request.py +31 -0
  17. quapp_common-0.0.11.dev5/quapp_common/data/response/custom_header.py +15 -0
  18. quapp_common-0.0.11.dev5/quapp_common/data/response/job_response.py +31 -0
  19. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/factory/device_factory.py +4 -2
  20. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/factory/handler_factory.py +3 -2
  21. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/factory/provider_factory.py +4 -2
  22. quapp_common-0.0.11.dev5/quapp_common/handler/handler.py +25 -0
  23. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/model/device/custom_device.py +32 -31
  24. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/model/device/device.py +94 -64
  25. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/model/provider/provider.py +2 -2
  26. quapp_common-0.0.11.dev5/quapp_common/util/http_utils.py +93 -0
  27. quapp_common-0.0.11.dev5/quapp_common/util/json_parser_utils.py +75 -0
  28. quapp_common-0.0.11.dev5/quapp_common/util/response_utils.py +97 -0
  29. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5/quapp_common.egg-info}/PKG-INFO +41 -6
  30. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common.egg-info/SOURCES.txt +1 -1
  31. quapp_common-0.0.11.dev3/README.md +0 -22
  32. quapp_common-0.0.11.dev3/quapp_common/async_tasks/export_circuit_task.py +0 -121
  33. quapp_common-0.0.11.dev3/quapp_common/async_tasks/post_processing_task.py +0 -61
  34. quapp_common-0.0.11.dev3/quapp_common/component/backend/invocation.py +0 -255
  35. quapp_common-0.0.11.dev3/quapp_common/component/backend/job_fetcher.py +0 -231
  36. quapp_common-0.0.11.dev3/quapp_common/component/backend/job_fetching.py +0 -250
  37. quapp_common-0.0.11.dev3/quapp_common/component/callback/update_job_metadata.py +0 -40
  38. quapp_common-0.0.11.dev3/quapp_common/config/logging_config.py +0 -34
  39. quapp_common-0.0.11.dev3/quapp_common/data/promise/post_processing_promise.py +0 -18
  40. quapp_common-0.0.11.dev3/quapp_common/data/request/request.py +0 -23
  41. quapp_common-0.0.11.dev3/quapp_common/data/response/job_response.py +0 -32
  42. quapp_common-0.0.11.dev3/quapp_common/data/response/project_header.py +0 -9
  43. quapp_common-0.0.11.dev3/quapp_common/handler/handler.py +0 -20
  44. quapp_common-0.0.11.dev3/quapp_common/util/http_utils.py +0 -37
  45. quapp_common-0.0.11.dev3/quapp_common/util/json_parser_utils.py +0 -75
  46. quapp_common-0.0.11.dev3/quapp_common/util/response_utils.py +0 -36
  47. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/LICENSE +0 -0
  48. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/__init__.py +0 -0
  49. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/async_tasks/__init__.py +0 -0
  50. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/component/__init__.py +0 -0
  51. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/component/backend/__init__.py +0 -0
  52. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/component/callback/__init__.py +0 -0
  53. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/component/device/__init__.py +0 -0
  54. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/config/__init__.py +0 -0
  55. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/config/thread_config.py +0 -0
  56. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/__init__.py +0 -0
  57. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/async_task/__init__.py +0 -0
  58. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/async_task/circuit_export/__init__.py +0 -0
  59. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/async_task/circuit_export/backend_holder.py +0 -0
  60. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/async_task/circuit_export/circuit_holder.py +0 -0
  61. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/backend/__init__.py +0 -0
  62. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/backend/backend_information.py +0 -0
  63. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/callback/__init__.py +0 -0
  64. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/callback/callback_url.py +0 -0
  65. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/device/__init__.py +0 -0
  66. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/device/circuit_running_option.py +0 -0
  67. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/promise/__init__.py +0 -0
  68. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/promise/promise.py +0 -0
  69. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/request/__init__.py +0 -0
  70. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/response/__init__.py +0 -0
  71. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/data/response/authentication.py +0 -0
  72. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/enum/__init__.py +0 -0
  73. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/enum/base_enum.py +0 -0
  74. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/enum/http_header.py +0 -0
  75. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/enum/invocation_step.py +0 -0
  76. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/enum/media_type.py +0 -0
  77. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/enum/processing_unit.py +0 -0
  78. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/enum/provider_tag.py +0 -0
  79. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/enum/sdk.py +0 -0
  80. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/enum/status/__init__.py +0 -0
  81. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/enum/status/job_status.py +0 -0
  82. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/enum/status/status_code.py +0 -0
  83. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/enum/token_type.py +0 -0
  84. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/factory/__init__.py +0 -0
  85. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/handler/__init__.py +0 -0
  86. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/model/__init__.py +0 -0
  87. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/model/device/__init__.py +0 -0
  88. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/model/provider/__init__.py +0 -0
  89. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/util/__init__.py +0 -0
  90. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common/util/file_utils.py +0 -0
  91. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common.egg-info/dependency_links.txt +0 -0
  92. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common.egg-info/requires.txt +0 -0
  93. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/quapp_common.egg-info/top_level.txt +0 -0
  94. {quapp_common-0.0.11.dev3 → quapp_common-0.0.11.dev5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quapp-common
3
- Version: 0.0.11.dev3
3
+ Version: 0.0.11.dev5
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 Quantum Computing by
42
- providing common utilities, configurations, and abstractions for working with quantum providers and
43
- devices.
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 quantum simulators.
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.dev3"
7
+ version = "0.0.11.dev5"
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" }]
@@ -1,10 +1,10 @@
1
1
  """
2
2
  QApp Platform Project async_task.py Copyright © CITYNOW Co. Ltd. All rights reserved.
3
3
  """
4
- from abc import abstractmethod
4
+ from abc import ABC, abstractmethod
5
5
 
6
6
 
7
- class AsyncTask:
7
+ class AsyncTask(ABC):
8
8
 
9
9
  @abstractmethod
10
10
  def do(self):
@@ -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')