quapp-dwave-ocean 0.0.1.dev2__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.
- quapp_dwave_ocean/__init__.py +3 -0
- quapp_dwave_ocean/async_tasks/__init__.py +0 -0
- quapp_dwave_ocean/async_tasks/d_wave_ocean_circuit_export_task.py +228 -0
- quapp_dwave_ocean/component/__init__.py +0 -0
- quapp_dwave_ocean/component/backend/__init__.py +0 -0
- quapp_dwave_ocean/component/backend/d_wave_ocean_invocation.py +157 -0
- quapp_dwave_ocean/component/backend/d_wave_ocean_job_fetching.py +28 -0
- quapp_dwave_ocean/factory/__init__.py +0 -0
- quapp_dwave_ocean/factory/d_wave_device_factory.py +73 -0
- quapp_dwave_ocean/factory/d_wave_handler_factory.py +45 -0
- quapp_dwave_ocean/factory/d_wave_provider_factory.py +74 -0
- quapp_dwave_ocean/handler/__init__.py +0 -0
- quapp_dwave_ocean/handler/invocation_handler.py +42 -0
- quapp_dwave_ocean/handler/job_fetching_handler.py +42 -0
- quapp_dwave_ocean/model/__init__.py +0 -0
- quapp_dwave_ocean/model/device/__init__.py +0 -0
- quapp_dwave_ocean/model/device/d_wave_device.py +31 -0
- quapp_dwave_ocean/model/device/d_wave_hybrid_device.py +38 -0
- quapp_dwave_ocean/model/device/d_wave_system_device.py +34 -0
- quapp_dwave_ocean/model/device/quapp_d_wave_device.py +191 -0
- quapp_dwave_ocean/model/provider/__init__.py +0 -0
- quapp_dwave_ocean/model/provider/d_wave_system_provider.py +49 -0
- quapp_dwave_ocean/model/provider/quapp_d_wave_provider.py +26 -0
- quapp_dwave_ocean-0.0.1.dev2.dist-info/METADATA +88 -0
- quapp_dwave_ocean-0.0.1.dev2.dist-info/RECORD +28 -0
- quapp_dwave_ocean-0.0.1.dev2.dist-info/WHEEL +5 -0
- quapp_dwave_ocean-0.0.1.dev2.dist-info/licenses/LICENSE +8 -0
- quapp_dwave_ocean-0.0.1.dev2.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# d_wave_ocean_circuit_export_task.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
from io import BytesIO
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import requests
|
|
8
|
+
from dimod import BinaryQuadraticModel
|
|
9
|
+
from matplotlib import pyplot as plt
|
|
10
|
+
from quapp_common.async_tasks.export_circuit_task import CircuitExportTask
|
|
11
|
+
from quapp_common.config.logging_config import job_logger
|
|
12
|
+
from quapp_common.data.async_task.circuit_export.backend_holder import \
|
|
13
|
+
BackendDataHolder
|
|
14
|
+
from quapp_common.data.async_task.circuit_export.circuit_holder import \
|
|
15
|
+
CircuitDataHolder
|
|
16
|
+
from quapp_common.data.response.custom_header import CustomHeader
|
|
17
|
+
from quapp_common.enum.media_type import MediaType
|
|
18
|
+
from quapp_common.util.file_utils import FileUtils
|
|
19
|
+
from quapp_common.util.http_utils import create_bearer_header, \
|
|
20
|
+
get_job_id_from_url
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DWaveOceanCircuitExportTask:
|
|
24
|
+
MAX_CIRCUIT_IMAGE_SIZE = 5 * (1024 ** 2)
|
|
25
|
+
|
|
26
|
+
def __init__(self, circuit_data_holder: CircuitDataHolder,
|
|
27
|
+
backend_data_holder: BackendDataHolder,
|
|
28
|
+
project_header: CustomHeader, workspace_header: CustomHeader):
|
|
29
|
+
super().__init__()
|
|
30
|
+
self.project_header = project_header
|
|
31
|
+
self.workspace_header = workspace_header
|
|
32
|
+
self.circuit_data_holder = circuit_data_holder
|
|
33
|
+
self.backend_data_holder = backend_data_holder
|
|
34
|
+
self.logger = job_logger(
|
|
35
|
+
get_job_id_from_url(self.circuit_data_holder.export_url))
|
|
36
|
+
|
|
37
|
+
def do(self):
|
|
38
|
+
"""
|
|
39
|
+
Export circuit to svg file, then send it to QuaO server for saving
|
|
40
|
+
"""
|
|
41
|
+
self.logger.info("Starting circuit export task...")
|
|
42
|
+
|
|
43
|
+
circuit_export_url = self.circuit_data_holder.export_url
|
|
44
|
+
|
|
45
|
+
if circuit_export_url is None or len(circuit_export_url) < 1:
|
|
46
|
+
self.logger.warning("Export URL is missing. Task will exit.")
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
self.logger.debug('Converting circuit to SVG')
|
|
51
|
+
figure_buffer = self._render_circuit_svg(self._transpile_circuit())
|
|
52
|
+
except Exception as e:
|
|
53
|
+
self.logger.exception("Error converting circuit to SVG: %s", e,
|
|
54
|
+
exc_info=True)
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
self.logger.debug('Determining if circuit SVG should be zipped')
|
|
59
|
+
# Use protected helpers from base class (single underscore), not name-mangled privates
|
|
60
|
+
io_buffer_value, content_type = self.__determine_zip(
|
|
61
|
+
figure_buffer=figure_buffer)
|
|
62
|
+
size_bytes = (
|
|
63
|
+
io_buffer_value.getbuffer().nbytes
|
|
64
|
+
if isinstance(io_buffer_value, BytesIO)
|
|
65
|
+
else len(io_buffer_value)
|
|
66
|
+
)
|
|
67
|
+
self.logger.debug("Content type: %s", content_type)
|
|
68
|
+
self.logger.debug("Buffer size: %s bytes", size_bytes)
|
|
69
|
+
except Exception as e:
|
|
70
|
+
self.logger.exception(
|
|
71
|
+
"Error determining if circuit SVG should be zipped: %s", e,
|
|
72
|
+
exc_info=True)
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
self.logger.debug('Sending circuit to backend')
|
|
77
|
+
self.__send(io_buffer_value=io_buffer_value,
|
|
78
|
+
content_type=content_type)
|
|
79
|
+
self.logger.debug("Circuit sent to backend successfully.")
|
|
80
|
+
return
|
|
81
|
+
except Exception as e:
|
|
82
|
+
self.logger.exception(
|
|
83
|
+
"Error sending exported circuit to backend: %s", e,
|
|
84
|
+
exc_info=True)
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
def _render_circuit_svg(self,
|
|
88
|
+
transpiled_circuit: BinaryQuadraticModel) -> BytesIO:
|
|
89
|
+
"""
|
|
90
|
+
Render a BinaryQuadraticModel as an SVG figure and return it as a BytesIO buffer.
|
|
91
|
+
|
|
92
|
+
This matches the transpile output type (BinaryQuadraticModel) from _transpile_circuit.
|
|
93
|
+
"""
|
|
94
|
+
self.logger.info("Rendering BinaryQuadraticModel to SVG")
|
|
95
|
+
try:
|
|
96
|
+
linear_items = list(transpiled_circuit.linear.items())
|
|
97
|
+
quadratic_items = list(transpiled_circuit.quadratic.items())
|
|
98
|
+
|
|
99
|
+
# Prepare figure
|
|
100
|
+
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
|
|
101
|
+
fig.suptitle("D-Wave Ocean BQM Visualization", fontsize=12)
|
|
102
|
+
|
|
103
|
+
# Left: Linear biases (bar chart)
|
|
104
|
+
ax0 = axes[0]
|
|
105
|
+
if linear_items:
|
|
106
|
+
vars_, lin_vals = zip(*linear_items)
|
|
107
|
+
x = np.arange(len(vars_))
|
|
108
|
+
ax0.bar(x, lin_vals, color="#1f77b4")
|
|
109
|
+
ax0.set_xticks(x)
|
|
110
|
+
ax0.set_xticklabels([str(v) for v in vars_], rotation=45,
|
|
111
|
+
ha="right", fontsize=8)
|
|
112
|
+
ax0.set_title("Linear biases")
|
|
113
|
+
ax0.set_ylabel("bias")
|
|
114
|
+
else:
|
|
115
|
+
ax0.text(0.5, 0.5, "No linear biases", ha="center", va="center",
|
|
116
|
+
transform=ax0.transAxes)
|
|
117
|
+
ax0.set_axis_off()
|
|
118
|
+
|
|
119
|
+
# Right: Quadratic couplers (scatter)
|
|
120
|
+
ax1 = axes[1]
|
|
121
|
+
if quadratic_items:
|
|
122
|
+
uvs = [f"{u}-{v}" for (u, v), _ in quadratic_items]
|
|
123
|
+
q_vals = [b for _, b in quadratic_items]
|
|
124
|
+
x = np.arange(len(uvs))
|
|
125
|
+
ax1.scatter(x, q_vals, c=np.sign(q_vals), cmap="bwr",
|
|
126
|
+
edgecolor="k")
|
|
127
|
+
ax1.set_xticks(x)
|
|
128
|
+
ax1.set_xticklabels(uvs, rotation=45, ha="right", fontsize=8)
|
|
129
|
+
ax1.set_title("Quadratic couplers")
|
|
130
|
+
ax1.set_ylabel("coupling")
|
|
131
|
+
ax1.axhline(0, color="gray", linewidth=0.8)
|
|
132
|
+
else:
|
|
133
|
+
ax1.text(0.5, 0.5, "No quadratic couplers", ha="center",
|
|
134
|
+
va="center", transform=ax1.transAxes)
|
|
135
|
+
ax1.set_axis_off()
|
|
136
|
+
|
|
137
|
+
fig.tight_layout(rect=[0, 0.03, 1, 0.95])
|
|
138
|
+
|
|
139
|
+
self.logger.debug("Converting rendered figure to SVG buffer")
|
|
140
|
+
figure_buffer = BytesIO()
|
|
141
|
+
try:
|
|
142
|
+
fig.savefig(figure_buffer, format="svg", bbox_inches="tight")
|
|
143
|
+
figure_buffer.seek(0)
|
|
144
|
+
self.logger.debug("SVG export complete. Size: %s bytes",
|
|
145
|
+
figure_buffer.getbuffer().nbytes)
|
|
146
|
+
except Exception as exception:
|
|
147
|
+
self.logger.exception("Error saving BQM figure to SVG: %s",
|
|
148
|
+
exception, exc_info=True)
|
|
149
|
+
raise
|
|
150
|
+
finally:
|
|
151
|
+
plt.close(fig)
|
|
152
|
+
|
|
153
|
+
return figure_buffer
|
|
154
|
+
|
|
155
|
+
except Exception as exception:
|
|
156
|
+
self.logger.exception(
|
|
157
|
+
"Error rendering BinaryQuadraticModel to SVG: %s",
|
|
158
|
+
exception,
|
|
159
|
+
exc_info=True)
|
|
160
|
+
raise
|
|
161
|
+
|
|
162
|
+
def _transpile_circuit(self) -> BinaryQuadraticModel:
|
|
163
|
+
self.logger.info('Transpiling D-Wave Ocean circuit')
|
|
164
|
+
circuit = self.circuit_data_holder.circuit
|
|
165
|
+
self.logger.debug('Fetched circuit from holder: %s', circuit)
|
|
166
|
+
|
|
167
|
+
if circuit is None:
|
|
168
|
+
self.logger.error('Circuit must not be None')
|
|
169
|
+
raise ValueError('Circuit must not be None')
|
|
170
|
+
|
|
171
|
+
if isinstance(circuit, BinaryQuadraticModel):
|
|
172
|
+
self.logger.debug(
|
|
173
|
+
'Circuit is BinaryQuadraticModel; no transpilation needed')
|
|
174
|
+
return circuit
|
|
175
|
+
|
|
176
|
+
self.logger.error('Expected BinaryQuadraticModel, got %s',
|
|
177
|
+
type(circuit))
|
|
178
|
+
raise ValueError(f'Expected BinaryQuadraticModel, got {type(circuit)}')
|
|
179
|
+
|
|
180
|
+
def __determine_zip(self, figure_buffer):
|
|
181
|
+
"""
|
|
182
|
+
Determine if the buffer needs to be zipped; return (buffer, content_type).
|
|
183
|
+
"""
|
|
184
|
+
self.logger.debug("Checking if SVG file needs to be zipped.")
|
|
185
|
+
buffer_value = figure_buffer.getvalue()
|
|
186
|
+
content_type = MediaType.SVG_XML
|
|
187
|
+
|
|
188
|
+
self.logger.debug("Checking max file size")
|
|
189
|
+
estimated_file_size = len(buffer_value)
|
|
190
|
+
|
|
191
|
+
if estimated_file_size > CircuitExportTask.MAX_CIRCUIT_IMAGE_SIZE:
|
|
192
|
+
self.logger.debug("Zip file")
|
|
193
|
+
zip_file_buffer = FileUtils.zip(io_buffer_value=buffer_value,
|
|
194
|
+
file_name="circuit_image.svg")
|
|
195
|
+
|
|
196
|
+
buffer_value = zip_file_buffer.getvalue()
|
|
197
|
+
content_type = MediaType.APPLICATION_ZIP
|
|
198
|
+
|
|
199
|
+
return buffer_value, content_type
|
|
200
|
+
|
|
201
|
+
def __send(self, io_buffer_value, content_type: MediaType):
|
|
202
|
+
"""
|
|
203
|
+
Send circuit SVG (or zipped SVG) to the backend.
|
|
204
|
+
"""
|
|
205
|
+
url = self.circuit_data_holder.export_url
|
|
206
|
+
|
|
207
|
+
self.logger.debug(
|
|
208
|
+
f"Sending circuit svg image to [{url}] with POST method ...")
|
|
209
|
+
|
|
210
|
+
payload = {'circuit': ('circuit_image.svg', io_buffer_value,
|
|
211
|
+
content_type.value)}
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
response = requests.post(url=url, headers=create_bearer_header(
|
|
215
|
+
self.backend_data_holder.user_token, self.project_header,
|
|
216
|
+
self.workspace_header), files=payload)
|
|
217
|
+
except Exception as exception:
|
|
218
|
+
self.logger.exception(f"HTTP request failed: {exception}",
|
|
219
|
+
exc_info=True)
|
|
220
|
+
raise
|
|
221
|
+
|
|
222
|
+
if response.ok:
|
|
223
|
+
self.logger.info("Request sent to QuaO backend successfully.")
|
|
224
|
+
else:
|
|
225
|
+
self.logger.exception(
|
|
226
|
+
f"Sending request to QuaO backend failed with status {response.status_code}! Response: {response.content}")
|
|
227
|
+
|
|
228
|
+
self.logger.debug("HTTP request complete.")
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# d_wave_ocean_invocation.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
from dimod import BinaryQuadraticModel
|
|
6
|
+
from quapp_common.component.backend.invocation import Invocation
|
|
7
|
+
from quapp_common.config.thread_config import circuit_exporting_pool
|
|
8
|
+
from quapp_common.data.async_task.circuit_export.backend_holder import \
|
|
9
|
+
BackendDataHolder
|
|
10
|
+
from quapp_common.data.async_task.circuit_export.circuit_holder import \
|
|
11
|
+
CircuitDataHolder
|
|
12
|
+
from quapp_common.data.request.invocation_request import InvocationRequest
|
|
13
|
+
from quapp_common.model.provider.provider import Provider
|
|
14
|
+
|
|
15
|
+
from quapp_dwave_ocean.async_tasks.d_wave_ocean_circuit_export_task import \
|
|
16
|
+
DWaveOceanCircuitExportTask
|
|
17
|
+
from quapp_dwave_ocean.factory.d_wave_device_factory import DWaveDeviceFactory
|
|
18
|
+
from quapp_dwave_ocean.factory.d_wave_provider_factory import \
|
|
19
|
+
DWaveProviderFactory
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DWaveOceanInvocation(Invocation):
|
|
23
|
+
def __init__(self, request_data: InvocationRequest):
|
|
24
|
+
"""
|
|
25
|
+
Initialize DWaveOceanInvocation object
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
request_data: InvocationRequest object
|
|
29
|
+
"""
|
|
30
|
+
super().__init__(request_data)
|
|
31
|
+
try:
|
|
32
|
+
provider_tag = getattr(self.backend_information, "provider_tag",
|
|
33
|
+
None)
|
|
34
|
+
device_name = getattr(self.backend_information, "device_name", None)
|
|
35
|
+
self.logger.info("[DWaveOceanInvocation] Initialized")
|
|
36
|
+
self.logger.debug(
|
|
37
|
+
"[DWaveOceanInvocation] init details: provider_tag=%s, device_name=%s, sdk=%s, export_url_present=%s",
|
|
38
|
+
provider_tag, device_name, getattr(self, "sdk", None),
|
|
39
|
+
bool(getattr(self, "circuit_export_url", None)), )
|
|
40
|
+
except Exception as e:
|
|
41
|
+
self.logger.exception(
|
|
42
|
+
"[DWaveOceanInvocation] Initialization logging failed: %s",
|
|
43
|
+
e)
|
|
44
|
+
|
|
45
|
+
def _export_circuit(self, circuit):
|
|
46
|
+
"""
|
|
47
|
+
Export circuit to svg file then send to QuaO server for saving
|
|
48
|
+
|
|
49
|
+
@param circuit: Circuit was exported
|
|
50
|
+
"""
|
|
51
|
+
self.logger.info("[DWaveOceanInvocation] _export_circuit() started")
|
|
52
|
+
try:
|
|
53
|
+
circuit_export_task = DWaveOceanCircuitExportTask(
|
|
54
|
+
circuit_data_holder=CircuitDataHolder(circuit,
|
|
55
|
+
self.circuit_export_url),
|
|
56
|
+
backend_data_holder=BackendDataHolder(
|
|
57
|
+
self.backend_information,
|
|
58
|
+
self.authentication.user_token),
|
|
59
|
+
project_header=self.project_header,
|
|
60
|
+
workspace_header=self.workspace_header, )
|
|
61
|
+
self.logger.debug(
|
|
62
|
+
"[DWaveOceanInvocation] Prepared CircuitExportTask: export_url_present=%s, project_header=%s, workspace_header=%s",
|
|
63
|
+
bool(self.circuit_export_url),
|
|
64
|
+
bool(getattr(self, "project_header", None)),
|
|
65
|
+
bool(getattr(self, "workspace_header", None)), )
|
|
66
|
+
|
|
67
|
+
future = circuit_exporting_pool.submit(circuit_export_task.do)
|
|
68
|
+
self.logger.info(
|
|
69
|
+
"[DWaveOceanInvocation] Circuit export task submitted to thread pool")
|
|
70
|
+
|
|
71
|
+
def _done_callback(fut):
|
|
72
|
+
try:
|
|
73
|
+
fut.result()
|
|
74
|
+
self.logger.info(
|
|
75
|
+
"[DWaveOceanInvocation] Circuit export task completed successfully")
|
|
76
|
+
except Exception as export_err:
|
|
77
|
+
self.logger.exception(
|
|
78
|
+
"[DWaveOceanInvocation] Circuit export task failed: %s",
|
|
79
|
+
export_err)
|
|
80
|
+
|
|
81
|
+
future.add_done_callback(_done_callback)
|
|
82
|
+
except Exception as e:
|
|
83
|
+
self.logger.exception(
|
|
84
|
+
"[DWaveOceanInvocation] Failed to export circuit: %s", e)
|
|
85
|
+
raise
|
|
86
|
+
|
|
87
|
+
def _create_provider(self):
|
|
88
|
+
"""
|
|
89
|
+
Create a provider based on the provider type and SDK
|
|
90
|
+
|
|
91
|
+
Return: Provider object
|
|
92
|
+
"""
|
|
93
|
+
self.logger.info("[DWaveOceanInvocation] _create_provider()")
|
|
94
|
+
try:
|
|
95
|
+
self.logger.debug(
|
|
96
|
+
"[DWaveOceanInvocation] Creating provider with provider_tag=%s, sdk=%s",
|
|
97
|
+
getattr(self.backend_information, "provider_tag", None),
|
|
98
|
+
getattr(self, "sdk", None), )
|
|
99
|
+
return DWaveProviderFactory.create_provider(
|
|
100
|
+
provider_type=self.backend_information.provider_tag,
|
|
101
|
+
sdk=self.sdk,
|
|
102
|
+
authentication=self.backend_information.authentication, )
|
|
103
|
+
except Exception as e:
|
|
104
|
+
self.logger.exception(
|
|
105
|
+
"[DWaveOceanInvocation] _create_provider failed: %s", e)
|
|
106
|
+
raise
|
|
107
|
+
|
|
108
|
+
def _create_device(self, provider: Provider):
|
|
109
|
+
"""
|
|
110
|
+
Create a device based on the provider and device specification
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
provider: Provider instance that this device belongs to
|
|
114
|
+
Return:
|
|
115
|
+
A device instance corresponding to the specified provider and device type
|
|
116
|
+
"""
|
|
117
|
+
try:
|
|
118
|
+
self.logger.debug(
|
|
119
|
+
f"[DWaveOceanInvocation] Creating device device_name="
|
|
120
|
+
f"{getattr(self.backend_information, 'device_name', None)}"
|
|
121
|
+
f", sdk={getattr(self, 'sdk', None)}")
|
|
122
|
+
return DWaveDeviceFactory.create_device(provider=provider,
|
|
123
|
+
device_specification=self.backend_information.device_name,
|
|
124
|
+
authentication=self.backend_information.authentication,
|
|
125
|
+
sdk=self.sdk, )
|
|
126
|
+
except Exception as exception:
|
|
127
|
+
self.logger.exception(
|
|
128
|
+
f"[DWaveOceanInvocation] _create_device failed: {exception}")
|
|
129
|
+
raise
|
|
130
|
+
|
|
131
|
+
def _get_qubit_amount(self, circuit):
|
|
132
|
+
"""
|
|
133
|
+
Get the number of qubits in the given circuit.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
circuit: The quantum circuit for which the qubit count is needed.
|
|
137
|
+
Returns:
|
|
138
|
+
int: The number of qubits in the circuit.
|
|
139
|
+
"""
|
|
140
|
+
try:
|
|
141
|
+
if isinstance(circuit, BinaryQuadraticModel):
|
|
142
|
+
qubit_amount = circuit.binary.num_variables
|
|
143
|
+
self.logger.info(
|
|
144
|
+
f"[DWaveOceanInvocation] Circuit is a BinaryQuadraticModel with {qubit_amount} qubits.")
|
|
145
|
+
return qubit_amount
|
|
146
|
+
else:
|
|
147
|
+
qubit_amount = getattr(circuit, "num_qubits", None)
|
|
148
|
+
if qubit_amount is None:
|
|
149
|
+
raise AttributeError(
|
|
150
|
+
"circuit object has no attribute 'num_qubits'")
|
|
151
|
+
self.logger.info(f"[DWaveOceanInvocation] Circuit is not a "
|
|
152
|
+
f"BinaryQuadraticModel; it has {qubit_amount} qubits.")
|
|
153
|
+
return qubit_amount
|
|
154
|
+
except Exception as exception:
|
|
155
|
+
self.logger.exception(
|
|
156
|
+
f"[DWaveOceanInvocation] Failed to get qubit amount: {exception}")
|
|
157
|
+
raise
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from quapp_common.component.backend.job_fetcher import JobFetcher
|
|
2
|
+
from quapp_common.data.request.job_fetching_request import JobFetchingRequest
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DWaveOceanJobFetching(JobFetcher):
|
|
6
|
+
def __init__(self, request: JobFetchingRequest, ):
|
|
7
|
+
super().__init__(request)
|
|
8
|
+
|
|
9
|
+
def _collect_provider(self):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
def _retrieve_job(self, provider):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
def _get_job_status(self, job):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
def _get_job_result(self, job):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
def _produce_histogram_data(self, job_result) -> dict | None:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
def _get_execution_time(self, job_result):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
def _get_shots(self, job_result):
|
|
28
|
+
pass
|
|
File without changes
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# d_wave_device_factory.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
from quapp_common.config.logging_config import job_logger
|
|
6
|
+
from quapp_common.enum.provider_tag import ProviderTag
|
|
7
|
+
from quapp_common.enum.sdk import Sdk
|
|
8
|
+
from quapp_common.factory.device_factory import DeviceFactory
|
|
9
|
+
from quapp_common.model.provider.provider import Provider
|
|
10
|
+
|
|
11
|
+
from ..model.device.d_wave_hybrid_device import DWaveHybridDevice
|
|
12
|
+
from ..model.device.d_wave_system_device import DWaveSystemDevice
|
|
13
|
+
from ..model.device.quapp_d_wave_device import QuappDWaveOceanDevice
|
|
14
|
+
from ..model.provider.d_wave_system_provider import (system_devices,
|
|
15
|
+
hybrid_devices)
|
|
16
|
+
|
|
17
|
+
logger = job_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DWaveDeviceFactory(DeviceFactory):
|
|
21
|
+
@staticmethod
|
|
22
|
+
def create_device(provider: Provider, device_specification: str,
|
|
23
|
+
authentication: dict, sdk: Sdk, ):
|
|
24
|
+
"""
|
|
25
|
+
Creates a D-Wave device based on the provided specification and SDK.
|
|
26
|
+
|
|
27
|
+
This method selects and returns an appropriate D-Wave device instance
|
|
28
|
+
based on the provider type and device specification. It supports both
|
|
29
|
+
quantum simulators and actual D-Wave devices, either system or hybrid
|
|
30
|
+
devices.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
provider (Provider): The provider instance to create the device from.
|
|
34
|
+
device_specification (str): The specification of the device to be created.
|
|
35
|
+
authentication (dict): Authentication details required for the provider.
|
|
36
|
+
sdk (Sdk): The software development kit being used.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
A device instance corresponding to the specified provider and device type.
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
Exception: If the device specification or provider type is unsupported.
|
|
43
|
+
"""
|
|
44
|
+
provider_type = ProviderTag.resolve(provider.get_provider_type().value)
|
|
45
|
+
logger.debug(
|
|
46
|
+
f"[DWaveDeviceFactory] create_device called with provider_type={provider_type}, "
|
|
47
|
+
f"device_specification={device_specification}, sdk={sdk}")
|
|
48
|
+
|
|
49
|
+
if ProviderTag.QUAO_QUANTUM_SIMULATOR.__eq__(provider_type):
|
|
50
|
+
logger.debug(
|
|
51
|
+
f"[DWaveDeviceFactory] Detected QUAO_QUANTUM_SIMULATOR with sdk={sdk}")
|
|
52
|
+
if Sdk.D_WAVE_OCEAN.__eq__(sdk):
|
|
53
|
+
logger.info(
|
|
54
|
+
f"[DWaveDeviceFactory] Creating QuappDWaveOceanDevice for spec={device_specification}")
|
|
55
|
+
return QuappDWaveOceanDevice(provider, device_specification)
|
|
56
|
+
|
|
57
|
+
if ProviderTag.D_WAVE.__eq__(provider_type):
|
|
58
|
+
logger.debug(
|
|
59
|
+
f"[DWaveDeviceFactory] Detected D_WAVE provider; resolving device for spec={device_specification}")
|
|
60
|
+
if device_specification in system_devices:
|
|
61
|
+
logger.info(
|
|
62
|
+
f"[DWaveDeviceFactory] Creating DWaveSystemDevice for spec={device_specification}")
|
|
63
|
+
return DWaveSystemDevice(provider, device_specification)
|
|
64
|
+
|
|
65
|
+
if device_specification in hybrid_devices:
|
|
66
|
+
logger.info(
|
|
67
|
+
f"[DWaveDeviceFactory] Creating DWaveHybridDevice for spec={device_specification}")
|
|
68
|
+
return DWaveHybridDevice(provider, device_specification)
|
|
69
|
+
|
|
70
|
+
logger.error(
|
|
71
|
+
f"[DWaveDeviceFactory] Unsupported device: provider_type={provider_type}, "
|
|
72
|
+
f"device_specification={device_specification}, sdk={sdk}")
|
|
73
|
+
raise ValueError("Unsupported device!")
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# d_wave_handler_factory.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
from quapp_common.config.logging_config import logger
|
|
6
|
+
from quapp_common.factory.handler_factory import HandlerFactory
|
|
7
|
+
from quapp_common.handler.handler import Handler
|
|
8
|
+
|
|
9
|
+
from ..handler.invocation_handler import InvocationHandler
|
|
10
|
+
from ..handler.job_fetching_handler import JobFetchingHandler
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DWaveHandlerFactory(HandlerFactory):
|
|
14
|
+
@staticmethod
|
|
15
|
+
def create_handler(
|
|
16
|
+
event,
|
|
17
|
+
circuit_preparation_fn,
|
|
18
|
+
post_processing_fn,
|
|
19
|
+
) -> Handler:
|
|
20
|
+
"""
|
|
21
|
+
Create a handler based on event and functions.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
event (dict): Input event. Should contain "providerJobId" key.
|
|
25
|
+
circuit_preparation_fn (function): Function for preparing circuit.
|
|
26
|
+
post_processing_fn (function): Function for post-processing the result.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Handler: Handler based on event and functions.
|
|
30
|
+
"""
|
|
31
|
+
request_data = event.json()
|
|
32
|
+
provider_job_id = request_data.get("providerJobId")
|
|
33
|
+
|
|
34
|
+
if provider_job_id is None:
|
|
35
|
+
logger.debug("Create InvocationHandler")
|
|
36
|
+
return InvocationHandler(
|
|
37
|
+
request_data=request_data,
|
|
38
|
+
circuit_preparation_fn=circuit_preparation_fn,
|
|
39
|
+
post_processing_fn=post_processing_fn,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
logger.debug("Create JobFetchingHandler")
|
|
43
|
+
return JobFetchingHandler(
|
|
44
|
+
request_data=request_data, post_processing_fn=post_processing_fn
|
|
45
|
+
)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# d_wave_provider_factory.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
from quapp_common.config.logging_config import job_logger
|
|
5
|
+
from quapp_common.enum.provider_tag import ProviderTag
|
|
6
|
+
from quapp_common.enum.sdk import Sdk
|
|
7
|
+
from quapp_common.factory.provider_factory import ProviderFactory
|
|
8
|
+
|
|
9
|
+
from ..model.provider.d_wave_system_provider import DWaveSystemProvider
|
|
10
|
+
from ..model.provider.quapp_d_wave_provider import QuappDWaveProvider
|
|
11
|
+
|
|
12
|
+
logger = job_logger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DWaveProviderFactory(ProviderFactory):
|
|
16
|
+
@staticmethod
|
|
17
|
+
def create_provider(provider_type: ProviderTag, sdk: Sdk,
|
|
18
|
+
authentication: dict):
|
|
19
|
+
"""
|
|
20
|
+
Creates and returns a provider based on the specified provider type and SDK.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
provider_type: The type of provider to create, identified by a ProviderTag.
|
|
24
|
+
sdk: The SDK to use for the provider, identified by a Sdk.
|
|
25
|
+
authentication: A dictionary containing authentication details such as token and endpoint.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
An instance of DWaveSystemProvider or QuappDWaveProvider based on the matching criteria.
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
Exception: If the provider type or SDK is not supported.
|
|
32
|
+
"""
|
|
33
|
+
logger.debug(
|
|
34
|
+
"[DWaveProviderFactory] create_provider called with provider_type=%s, sdk=%s, auth_provided=%s",
|
|
35
|
+
provider_type, sdk, authentication is not None
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if ProviderTag.QUAO_QUANTUM_SIMULATOR.__eq__(provider_type):
|
|
39
|
+
logger.debug(
|
|
40
|
+
"[DWaveProviderFactory] Detected QUAO_QUANTUM_SIMULATOR")
|
|
41
|
+
if Sdk.D_WAVE_OCEAN.__eq__(sdk):
|
|
42
|
+
logger.info(
|
|
43
|
+
"[DWaveProviderFactory] Creating QuappDWaveProvider")
|
|
44
|
+
return QuappDWaveProvider()
|
|
45
|
+
|
|
46
|
+
if ProviderTag.D_WAVE.__eq__(provider_type):
|
|
47
|
+
logger.debug("[DWaveProviderFactory] Detected D_WAVE provider")
|
|
48
|
+
if authentication is None:
|
|
49
|
+
logger.error(
|
|
50
|
+
"[DWaveProviderFactory] Missing authentication for D-Wave provider")
|
|
51
|
+
raise ValueError(
|
|
52
|
+
"Authentication details are required for D-Wave provider.")
|
|
53
|
+
|
|
54
|
+
token = authentication.get("token")
|
|
55
|
+
endpoint = authentication.get("endpoint")
|
|
56
|
+
if token is None or endpoint is None:
|
|
57
|
+
missing_details = []
|
|
58
|
+
if token is None:
|
|
59
|
+
missing_details.append("token")
|
|
60
|
+
if endpoint is None:
|
|
61
|
+
missing_details.append("endpoint")
|
|
62
|
+
error_message = "Missing authentication details: {0}".format(
|
|
63
|
+
", ".join(missing_details))
|
|
64
|
+
logger.error("[DWaveProviderFactory] %s", error_message)
|
|
65
|
+
raise ValueError(error_message)
|
|
66
|
+
|
|
67
|
+
logger.info(
|
|
68
|
+
"[DWaveProviderFactory] Creating DWaveSystemProvider with provided endpoint")
|
|
69
|
+
return DWaveSystemProvider(token, endpoint)
|
|
70
|
+
|
|
71
|
+
logger.error(
|
|
72
|
+
"[DWaveProviderFactory] Unsupported provider: provider_type=%s, sdk=%s",
|
|
73
|
+
provider_type, sdk)
|
|
74
|
+
raise ValueError("Unsupported provider!")
|
|
File without changes
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# invocation_handler.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
from quapp_common.data.request.invocation_request import InvocationRequest
|
|
6
|
+
from quapp_common.handler.handler import Handler
|
|
7
|
+
|
|
8
|
+
from ..component.backend.d_wave_ocean_invocation import DWaveOceanInvocation
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InvocationHandler(Handler):
|
|
12
|
+
def __init__(self, request_data: dict, circuit_preparation_fn,
|
|
13
|
+
post_processing_fn):
|
|
14
|
+
super().__init__(request_data, post_processing_fn)
|
|
15
|
+
self.circuit_preparation_fn = circuit_preparation_fn
|
|
16
|
+
|
|
17
|
+
def handle(self):
|
|
18
|
+
"""
|
|
19
|
+
Handles the invocation and submission of a quantum job request to the
|
|
20
|
+
DWaveOceanInvocation backend. This method creates an invocation request
|
|
21
|
+
with the provided data, logs relevant debug information, and submits
|
|
22
|
+
the job to the backend using the specified preparation and post-processing
|
|
23
|
+
functions.
|
|
24
|
+
"""
|
|
25
|
+
self.logger.debug('Creating InvocationRequest')
|
|
26
|
+
try:
|
|
27
|
+
invocation_request = InvocationRequest(self.request_data)
|
|
28
|
+
self.logger.debug(
|
|
29
|
+
f'Invocation request keys: {list(invocation_request.__dict__.keys())}')
|
|
30
|
+
|
|
31
|
+
backend = DWaveOceanInvocation(invocation_request)
|
|
32
|
+
self.logger.debug('DWaveOceanInvocation backend instantiated')
|
|
33
|
+
|
|
34
|
+
self.logger.debug('Submitting job to backend')
|
|
35
|
+
backend.submit_job(
|
|
36
|
+
circuit_preparation_fn=self.circuit_preparation_fn,
|
|
37
|
+
post_processing_fn=self.post_processing_fn)
|
|
38
|
+
self.logger.info('Job submitted to backend')
|
|
39
|
+
except Exception as exception:
|
|
40
|
+
self.logger.exception(
|
|
41
|
+
f'Error submitting job to backend: {exception}')
|
|
42
|
+
raise ValueError(f'Error submitting job to backend: {exception}')
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# job_fetching_handler.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
from quapp_common.config.logging_config import logger
|
|
6
|
+
from quapp_common.data.request.job_fetching_request import JobFetchingRequest
|
|
7
|
+
from quapp_common.handler.handler import Handler
|
|
8
|
+
|
|
9
|
+
from ..component.backend.d_wave_ocean_job_fetching import DWaveOceanJobFetching
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class JobFetchingHandler(Handler):
|
|
13
|
+
def __init__(self,
|
|
14
|
+
request_data: dict,
|
|
15
|
+
post_processing_fn):
|
|
16
|
+
super().__init__(request_data, post_processing_fn)
|
|
17
|
+
|
|
18
|
+
def handle(self):
|
|
19
|
+
"""
|
|
20
|
+
Handles the job fetching request.
|
|
21
|
+
|
|
22
|
+
This method creates a JobFetchingRequest object using the provided
|
|
23
|
+
request data, then creates a JobFetching object to fetch the job.
|
|
24
|
+
The job fetching process uses a post-processing function
|
|
25
|
+
to process the fetched result.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
The result of the job fetching process after applying
|
|
29
|
+
post-processing.
|
|
30
|
+
"""
|
|
31
|
+
self.logger.debug("[JobFetchingHandler] Creating JobFetchingRequest with data: {0}".format(
|
|
32
|
+
self.request_data))
|
|
33
|
+
request = JobFetchingRequest(self.request_data)
|
|
34
|
+
|
|
35
|
+
self.logger.debug("[JobFetchingHandler] Initializing JobFetching with request.")
|
|
36
|
+
job_fetching = DWaveOceanJobFetching(request)
|
|
37
|
+
|
|
38
|
+
self.logger.debug("[JobFetchingHandler] Starting job fetching process.")
|
|
39
|
+
fetching_result = job_fetching.fetch(post_processing_fn=self.post_processing_fn)
|
|
40
|
+
|
|
41
|
+
self.logger.debug("[JobFetchingHandler] Job fetching result: {0}".format(fetching_result))
|
|
42
|
+
return fetching_result
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# d_wave_device.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
from abc import ABC
|
|
6
|
+
|
|
7
|
+
from quapp_common.enum.status.job_status import JobStatus
|
|
8
|
+
from quapp_common.model.device.custom_device import CustomDevice
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DWaveDevice(CustomDevice, ABC):
|
|
12
|
+
|
|
13
|
+
def _is_simulator(self) -> bool:
|
|
14
|
+
self.logger.debug("[DWave Device] Get device type")
|
|
15
|
+
|
|
16
|
+
return True
|
|
17
|
+
|
|
18
|
+
def _produce_histogram_data(self, job_result) -> dict | None:
|
|
19
|
+
self.logger.debug("[DWave Device] Produce histogram")
|
|
20
|
+
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
def _get_job_status(self, job) -> str:
|
|
24
|
+
self.logger.debug("[DWave Device] Get job status")
|
|
25
|
+
|
|
26
|
+
return JobStatus.DONE.value
|
|
27
|
+
|
|
28
|
+
def _get_shots(self, job_result) -> int | None:
|
|
29
|
+
self.logger.debug('[DWave Device] Get shots')
|
|
30
|
+
self.logger.debug(f'[DWave Device] Job result: {job_result}')
|
|
31
|
+
return None
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# d_wave_hybrid_device.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
from quapp_common.data.device.circuit_running_option import CircuitRunningOption
|
|
6
|
+
|
|
7
|
+
from ..device.d_wave_device import DWaveDevice
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DWaveHybridDevice(DWaveDevice):
|
|
11
|
+
|
|
12
|
+
def _create_job(self, circuit, options: CircuitRunningOption):
|
|
13
|
+
self.logger.debug("[DWave System] Create job")
|
|
14
|
+
|
|
15
|
+
return self.device.sample_bqm(circuit, time_limit=10)
|
|
16
|
+
|
|
17
|
+
def _get_provider_job_id(self, job) -> str:
|
|
18
|
+
self.logger.debug("[DWave System] Get provider job id")
|
|
19
|
+
|
|
20
|
+
return job.id
|
|
21
|
+
|
|
22
|
+
def _get_job_result(self, job):
|
|
23
|
+
self.logger.debug('[DWave System] Get job result')
|
|
24
|
+
|
|
25
|
+
return job.result().get('sampleset')
|
|
26
|
+
|
|
27
|
+
def _calculate_execution_time(self, job_result) -> None:
|
|
28
|
+
self.logger.debug("[DWave System] Calculate execution time")
|
|
29
|
+
|
|
30
|
+
self.execution_time = (
|
|
31
|
+
job_result.get("_info").get('run_time') / 1000
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
self.logger.debug(
|
|
35
|
+
"[DWave System] Execution time calculation was: {0} seconds".format(
|
|
36
|
+
self.execution_time
|
|
37
|
+
)
|
|
38
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# d_wave_system_device.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
from quapp_common.data.device.circuit_running_option import CircuitRunningOption
|
|
6
|
+
|
|
7
|
+
from ..device.d_wave_device import DWaveDevice
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DWaveSystemDevice(DWaveDevice):
|
|
11
|
+
|
|
12
|
+
def _create_job(self, circuit, options: CircuitRunningOption):
|
|
13
|
+
self.logger.debug("[DWave System] Create job")
|
|
14
|
+
|
|
15
|
+
return self.device.sample(circuit)
|
|
16
|
+
|
|
17
|
+
def _get_provider_job_id(self, job) -> str:
|
|
18
|
+
self.logger.debug("[DWave System] Get provider job id")
|
|
19
|
+
|
|
20
|
+
return job.info.get("problem_id")
|
|
21
|
+
|
|
22
|
+
def _calculate_execution_time(self, job_result) -> None:
|
|
23
|
+
self.logger.debug("[DWave System] Calculate execution time")
|
|
24
|
+
|
|
25
|
+
self.execution_time = (job_result.get("_info").get("timing").get(
|
|
26
|
+
"qpu_access_time") / 1000)
|
|
27
|
+
|
|
28
|
+
self.logger.debug(
|
|
29
|
+
f"[DWave System] Execution time calculation was: {self.execution_time} seconds")
|
|
30
|
+
|
|
31
|
+
def _get_job_result(self, job):
|
|
32
|
+
self.logger.debug('[DWave System] Get job result')
|
|
33
|
+
|
|
34
|
+
return job
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# quapp_d_wave_device.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
import uuid
|
|
7
|
+
from abc import ABC
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from dimod import SampleSet
|
|
11
|
+
from quapp_common.data.device.circuit_running_option import CircuitRunningOption
|
|
12
|
+
from quapp_common.enum.status.job_status import JobStatus
|
|
13
|
+
from quapp_common.model.device.custom_device import CustomDevice
|
|
14
|
+
from quapp_common.model.provider.provider import Provider
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class QuappDWaveOceanDevice(CustomDevice, ABC):
|
|
18
|
+
def __init__(self, provider: Provider, device_specification: str):
|
|
19
|
+
super().__init__(provider, device_specification)
|
|
20
|
+
|
|
21
|
+
def _create_job(self, circuit, options: CircuitRunningOption):
|
|
22
|
+
"""
|
|
23
|
+
Creates a D-Wave job for the provided BQM (Binary Quadratic Model) circuit.
|
|
24
|
+
|
|
25
|
+
This method creates a job by sampling the provided BQM circuit and records
|
|
26
|
+
the execution time for the job creation. The job is then returned.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
circuit (dimod.BinaryQuadraticModel): The BQM circuit to sample.
|
|
30
|
+
options (CircuitRunningOption): The options for running the circuit.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
dimod.SampleSet: The created job as a SampleSet.
|
|
34
|
+
"""
|
|
35
|
+
self.logger.debug(
|
|
36
|
+
'[QuappDWaveOceanDevice] Creating job with {0} shots'.format(
|
|
37
|
+
options.shots))
|
|
38
|
+
|
|
39
|
+
# Record the start time for execution time measurement
|
|
40
|
+
start_time = time.time()
|
|
41
|
+
|
|
42
|
+
# Create a job by sampling the provided BQM (Binary Quadratic Model) circuit
|
|
43
|
+
job = self.device.sample(bqm=circuit, num_reads=options.shots)
|
|
44
|
+
|
|
45
|
+
# Calculate the execution time by subtracting the start time from the current time
|
|
46
|
+
self.execution_time = time.time() - start_time
|
|
47
|
+
|
|
48
|
+
# Log the execution time for the job creation
|
|
49
|
+
self.logger.debug(
|
|
50
|
+
'[QuappDWaveOceanDevice] Job created in {0:.2f} seconds'.format(
|
|
51
|
+
self.execution_time))
|
|
52
|
+
|
|
53
|
+
# Return the created job
|
|
54
|
+
return job
|
|
55
|
+
|
|
56
|
+
def _produce_histogram_data(self, job_result) -> dict | None:
|
|
57
|
+
"""
|
|
58
|
+
Converts the samples of a SampleSet into a histogram data.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
job_result (SampleSet): The SampleSet object returned by the D-Wave Ocean Solver.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
dict | None: A dictionary containing the histogram data in the format {'0': int, '1': int}.
|
|
65
|
+
"""
|
|
66
|
+
self.logger.debug('[QuappDWaveOceanDevice] Produce histogram data')
|
|
67
|
+
|
|
68
|
+
# Check if job_result is an instance of SampleSet
|
|
69
|
+
if isinstance(job_result, SampleSet):
|
|
70
|
+
try:
|
|
71
|
+
# Retrieve the sample from the job_result
|
|
72
|
+
sample = job_result.record.sample
|
|
73
|
+
# Convert the first sample to a NumPy array
|
|
74
|
+
sample = np.array(sample[0])
|
|
75
|
+
|
|
76
|
+
# Count the occurrences of each value (0 and 1) in the sample
|
|
77
|
+
counts = np.bincount(sample, minlength=2)
|
|
78
|
+
|
|
79
|
+
# Return a dictionary with the counts of values 0 and 1
|
|
80
|
+
return {"0": int(counts[0]), "1": int(counts[1]), }
|
|
81
|
+
except Exception as e:
|
|
82
|
+
self.logger.error(
|
|
83
|
+
f"[QuappDWaveOceanDevice] Error producing histogram data: {e}")
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
self.logger.debug(
|
|
87
|
+
'[QuappDWaveOceanDevice] Job result is not an instance of SampleSet. Returning None.')
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
def _get_provider_job_id(self, job) -> str:
|
|
91
|
+
self.logger.debug('[QuappDWaveOceanDevice] Get provider job id')
|
|
92
|
+
|
|
93
|
+
provider_job_id = str(uuid.uuid4())
|
|
94
|
+
self.logger.debug(f'[QuappDWaveOceanDevice] Provider job ID:'
|
|
95
|
+
f' {provider_job_id}')
|
|
96
|
+
return provider_job_id
|
|
97
|
+
|
|
98
|
+
def _get_job_status(self, job) -> str:
|
|
99
|
+
self.logger.debug('[QuappDWaveOceanDevice] Get job status')
|
|
100
|
+
|
|
101
|
+
return JobStatus.DONE.value
|
|
102
|
+
|
|
103
|
+
def _get_job_result(self, job):
|
|
104
|
+
"""
|
|
105
|
+
Retrieves the result of the job from the provider.
|
|
106
|
+
|
|
107
|
+
If the job is a SampleSet, it is returned directly. Otherwise, the result of the job is retrieved by calling `job.result()`.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
job (SampleSet or Job): The job to retrieve the result from.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
SampleSet or Any: The result of the job if it is a SampleSet, otherwise the result of calling `job.result()`.
|
|
114
|
+
"""
|
|
115
|
+
self.logger.debug('[QuappDWaveOceanDevice] Get job result')
|
|
116
|
+
|
|
117
|
+
# Check if the job is an instance of SampleSet
|
|
118
|
+
if isinstance(job, SampleSet):
|
|
119
|
+
# Log that the job is a SampleSet and will be returned directly
|
|
120
|
+
self.logger.debug(
|
|
121
|
+
f'[QuappDWaveOceanDevice] Job is a SampleSet. Returning the job directly: {job}')
|
|
122
|
+
return job
|
|
123
|
+
|
|
124
|
+
# Log that the job is not a SampleSet and will return the result of the job
|
|
125
|
+
self.logger.debug(
|
|
126
|
+
f'[QuappDWaveOceanDevice] Job is not a SampleSet. Returning job.result(): {job.result()}')
|
|
127
|
+
return job.result()
|
|
128
|
+
|
|
129
|
+
def _get_shots(self, job_result) -> int | None:
|
|
130
|
+
"""
|
|
131
|
+
Get the number of shots of a job.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
job_result (SampleSet): The SampleSet object returned by the D-Wave Ocean Solver.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
int | None: The number of shots if the job_result is a SampleSet, otherwise None.
|
|
138
|
+
"""
|
|
139
|
+
self.logger.debug('[QuappDWaveOceanDevice] Get shots')
|
|
140
|
+
if isinstance(job_result, SampleSet):
|
|
141
|
+
# Log the size of the 'num_occurrences' data vector
|
|
142
|
+
occurrences_size = job_result.data_vectors['num_occurrences'].size
|
|
143
|
+
self.logger.debug(
|
|
144
|
+
f'[QuappDWaveOceanDevice] Number of occurrences size: {occurrences_size}')
|
|
145
|
+
return occurrences_size
|
|
146
|
+
|
|
147
|
+
# Log a warning if job_result is not a SampleSet
|
|
148
|
+
self.logger.warning(
|
|
149
|
+
f'[QuappDWaveOceanDevice] Job result is not a SampleSet, is {type(job_result)}. Returning None.')
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
def _is_simulator(self) -> bool:
|
|
153
|
+
self.logger.info('[QuappDWaveOceanDevice] Is simulator')
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
def _calculate_execution_time(self, job_result) -> None:
|
|
157
|
+
"""
|
|
158
|
+
Calculate the execution time of a job in seconds.
|
|
159
|
+
|
|
160
|
+
The execution time is calculated from the timing information in the job info,
|
|
161
|
+
which is extracted and summed directly. The result is stored in the
|
|
162
|
+
`execution_time` attribute.
|
|
163
|
+
|
|
164
|
+
If the `job_result` is not a SampleSet, the extract timing information from the job info skipped.
|
|
165
|
+
|
|
166
|
+
Parameters
|
|
167
|
+
----------
|
|
168
|
+
job_result : SampleSet
|
|
169
|
+
The result of the job.
|
|
170
|
+
|
|
171
|
+
Returns
|
|
172
|
+
-------
|
|
173
|
+
None
|
|
174
|
+
"""
|
|
175
|
+
timing = None
|
|
176
|
+
# Extract timing information from the job information
|
|
177
|
+
if isinstance(job_result, SampleSet):
|
|
178
|
+
timing = job_result.info.get('timing', {})
|
|
179
|
+
else:
|
|
180
|
+
if isinstance(job_result, dict) and '_info' in job_result:
|
|
181
|
+
timing = job_result['_info'].get('timing', {})
|
|
182
|
+
if timing is not None:
|
|
183
|
+
# Calculate total time in seconds by summing the values directly
|
|
184
|
+
self.execution_time = sum(timing.values()) / 1_000_000_000
|
|
185
|
+
|
|
186
|
+
# Log the execution time calculation
|
|
187
|
+
self.logger.debug(
|
|
188
|
+
f'[QuappDWaveOceanDevice] Execution time calculation was: {self.execution_time} seconds')
|
|
189
|
+
else:
|
|
190
|
+
self.logger.warning(
|
|
191
|
+
'[QuappDWaveOceanDevice] Extract timing information from the job info skipped.')
|
|
File without changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# d_wave_system_provider.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
from dwave.cloud import Client
|
|
6
|
+
from dwave.system import DWaveSampler, AutoEmbeddingComposite
|
|
7
|
+
|
|
8
|
+
from quapp_common.config.logging_config import job_logger
|
|
9
|
+
from quapp_common.enum.provider_tag import ProviderTag
|
|
10
|
+
from quapp_common.model.provider.provider import Provider
|
|
11
|
+
|
|
12
|
+
system_devices = ["Advantage_system4.1"]
|
|
13
|
+
hybrid_devices = ["hybrid_binary_quadratic_model_version2"]
|
|
14
|
+
|
|
15
|
+
logger = job_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DWaveSystemProvider(Provider):
|
|
19
|
+
|
|
20
|
+
def __init__(self, api_token, endpoint):
|
|
21
|
+
super().__init__(ProviderTag.D_WAVE)
|
|
22
|
+
self.api_token = api_token
|
|
23
|
+
self.endpoint = endpoint
|
|
24
|
+
|
|
25
|
+
def get_backend(self, device_specification: str):
|
|
26
|
+
logger.debug("[DWave system] Get backend")
|
|
27
|
+
|
|
28
|
+
if device_specification in system_devices:
|
|
29
|
+
provider = self.collect_provider()
|
|
30
|
+
logger.debug("[DWave system] Get auto embedding composite")
|
|
31
|
+
return AutoEmbeddingComposite(provider)
|
|
32
|
+
|
|
33
|
+
if device_specification in hybrid_devices:
|
|
34
|
+
client = Client(endpoint=self.endpoint, token=self.api_token)
|
|
35
|
+
logger.debug("[DWave system] Get solver")
|
|
36
|
+
return client.get_solver(device_specification)
|
|
37
|
+
|
|
38
|
+
raise ValueError(
|
|
39
|
+
"Unsupported DWave device: {0}".format(device_specification))
|
|
40
|
+
|
|
41
|
+
def collect_provider(self):
|
|
42
|
+
logger.debug("[DWave system] Connect to provider")
|
|
43
|
+
try:
|
|
44
|
+
return DWaveSampler(endpoint=self.endpoint, token=self.api_token)
|
|
45
|
+
except Exception as exception:
|
|
46
|
+
logger.exception(
|
|
47
|
+
f"Failed to connect to DWave provider: {exception}")
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"Failed to connect to DWave provider: {exception}")
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# quapp_d_wave_provider.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
from dwave.samplers import SimulatedAnnealingSampler
|
|
6
|
+
|
|
7
|
+
from quapp_common.config.logging_config import job_logger
|
|
8
|
+
from quapp_common.enum.provider_tag import ProviderTag
|
|
9
|
+
from quapp_common.model.provider.provider import Provider
|
|
10
|
+
|
|
11
|
+
logger = job_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class QuappDWaveProvider(Provider):
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
logger.debug('[Quapp D-Wave] Initiate QuappDWaveProvider')
|
|
18
|
+
super().__init__(ProviderTag.QUAO_QUANTUM_SIMULATOR)
|
|
19
|
+
|
|
20
|
+
def get_backend(self, device_specification):
|
|
21
|
+
logger.debug('[Quapp D-Wave] Get backend')
|
|
22
|
+
|
|
23
|
+
return SimulatedAnnealingSampler()
|
|
24
|
+
|
|
25
|
+
def collect_provider(self):
|
|
26
|
+
return None
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quapp-dwave-ocean
|
|
3
|
+
Version: 0.0.1.dev2
|
|
4
|
+
Summary: Quapp D-Wave Ocean library supporting Quapp Platform for Quantum Computing
|
|
5
|
+
Author-email: "CITYNOW Co. Ltd. " <corp@citynow.vn>
|
|
6
|
+
License: The MIT License (MIT)
|
|
7
|
+
Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
14
|
+
Project-URL: Homepage, https://citynow.asia/
|
|
15
|
+
Keywords: quapp,quapp-dwave-ocean,dwave-ocean,quantum
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Requires-Python: >=3.7
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: quapp-common==0.0.11.dev6
|
|
23
|
+
Requires-Dist: dwave-cloud-client==0.13.4
|
|
24
|
+
Requires-Dist: dwave-system==1.30.0
|
|
25
|
+
Requires-Dist: dwave-samplers==1.5.0
|
|
26
|
+
Requires-Dist: dimod==0.12.20
|
|
27
|
+
Requires-Dist: numpy==2.2.4
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: black; extra == "dev"
|
|
30
|
+
Requires-Dist: bumpver; extra == "dev"
|
|
31
|
+
Requires-Dist: isort; extra == "dev"
|
|
32
|
+
Requires-Dist: pip-tools; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# quapp-dwave-ocean
|
|
37
|
+
|
|
38
|
+
Quapp D-Wave Ocean library supporting Quapp Platform for Quantum Computing.
|
|
39
|
+
|
|
40
|
+
## Overview
|
|
41
|
+
|
|
42
|
+
`quapp-dwave-ocean` provides providers, devices, factories, and async tasks to
|
|
43
|
+
run
|
|
44
|
+
Binary Quadratic Models (BQMs) via D-Wave Ocean within the Quapp Platform. It
|
|
45
|
+
supports
|
|
46
|
+
D-Wave system and hybrid devices as well as a QuaO quantum simulator, featuring
|
|
47
|
+
consistent context-rich logging, robust error handling, and standardized
|
|
48
|
+
project/workspace
|
|
49
|
+
headers for clean integration with backend services.
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
- Provider and device factories for:
|
|
54
|
+
- D-Wave System devices
|
|
55
|
+
- D-Wave Hybrid devices
|
|
56
|
+
- Quapp D-Wave Ocean simulator
|
|
57
|
+
- Asynchronous circuit export to SVG (BQM visualization) with optional
|
|
58
|
+
compression and upload.
|
|
59
|
+
- Consistent, instance-level logging across provider/device creation, job
|
|
60
|
+
invocation, export, and fetching.
|
|
61
|
+
- Standardized project/workspace header handling for backend invocations.
|
|
62
|
+
- Improved error handling and diagnostics in critical paths (export,
|
|
63
|
+
provider/device creation, job processing).
|
|
64
|
+
- Job fetching workflow encapsulated for clarity and maintainability.
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
Install via pip:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install quapp-dwave-ocean
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Recently Changes Highlights
|
|
75
|
+
|
|
76
|
+
- chore: Bump a version to `0.0.1.dev2` and update `quapp-common` dependency to
|
|
77
|
+
`0.0.11.dev6`
|
|
78
|
+
- refactor: Replace global logger usage with instance-level logging and enhance
|
|
79
|
+
debug information across D-Wave Ocean modules
|
|
80
|
+
- feature: Create `DWaveOceanJobFetching` class for managing job fetching logic in
|
|
81
|
+
D-Wave Ocean backend
|
|
82
|
+
- feature: Add `DWaveOceanCircuitExportTask` for exporting and processing D-Wave
|
|
83
|
+
Ocean circuit visualizations
|
|
84
|
+
- refactor: Update import paths from `qapp_common` to `quapp_common` for consistency
|
|
85
|
+
across modules
|
|
86
|
+
|
|
87
|
+
For detailed usage and API references, please refer to the in-code documentation
|
|
88
|
+
or contact the maintainers.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
quapp_dwave_ocean/__init__.py,sha256=_0M_dDraklXjKk4RYnaWPUvefKf7AtD6-5F4X8wolFo,87
|
|
2
|
+
quapp_dwave_ocean/async_tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
quapp_dwave_ocean/async_tasks/d_wave_ocean_circuit_export_task.py,sha256=UJMcxWsYchhzS7h_BfKCholYrEcwjhhMDJ2baWfpniM,9442
|
|
4
|
+
quapp_dwave_ocean/component/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
quapp_dwave_ocean/component/backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
quapp_dwave_ocean/component/backend/d_wave_ocean_invocation.py,sha256=6N3SvCxKKr84USwaPWGTo2ZOp7o8bAfyrAwM2uPN5f8,7130
|
|
7
|
+
quapp_dwave_ocean/component/backend/d_wave_ocean_job_fetching.py,sha256=A6z2Z8_a0nU-4-aguVIV7tMSwVu7I2vJqgjVp0qhuLI,669
|
|
8
|
+
quapp_dwave_ocean/factory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
quapp_dwave_ocean/factory/d_wave_device_factory.py,sha256=mK5P3V_FlwsP3sccU74IwNj7asJ2NlVm1wEICFxpOrE,3515
|
|
10
|
+
quapp_dwave_ocean/factory/d_wave_handler_factory.py,sha256=hBkLw--fJh8rSH7KIs3Yx9kroZRIIkoLLKb-dXwH2a8,1588
|
|
11
|
+
quapp_dwave_ocean/factory/d_wave_provider_factory.py,sha256=LPFfbNdtnt7s2PWCoQzt-N3H4gimbMzE9Mq2UE9u-CM,3251
|
|
12
|
+
quapp_dwave_ocean/handler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
quapp_dwave_ocean/handler/invocation_handler.py,sha256=-MElH0kHBjBwCYtykNaDnSML-jdrpBU7E6EUr0yQYIg,1856
|
|
14
|
+
quapp_dwave_ocean/handler/job_fetching_handler.py,sha256=yscOF2K1EIIJ65j2h2cZbsRZgTnzF4GPtqKSRvZ6ze0,1654
|
|
15
|
+
quapp_dwave_ocean/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
quapp_dwave_ocean/model/device/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
quapp_dwave_ocean/model/device/d_wave_device.py,sha256=8jWvArw3gEgB7M0kptfJ2cfomLIgB5_mQSmHfenn8kM,891
|
|
18
|
+
quapp_dwave_ocean/model/device/d_wave_hybrid_device.py,sha256=UM0PLDKnrZurNktCRLPH4AIJ69FmHTjqmv3wQ0hNTiQ,1163
|
|
19
|
+
quapp_dwave_ocean/model/device/d_wave_system_device.py,sha256=7S9C4ahXyIt9oqBlcikcFa2u4t2UI-zd4mJYbHvzqH4,1090
|
|
20
|
+
quapp_dwave_ocean/model/device/quapp_d_wave_device.py,sha256=dSnubgpLZMLlFMygn_AP_3F9Vu8C3XYaSc5vdvh1RIU,7589
|
|
21
|
+
quapp_dwave_ocean/model/provider/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
quapp_dwave_ocean/model/provider/d_wave_system_provider.py,sha256=Z91-fwJpuIIQyFZSRrKo8uRGSMbdKJAju2oFcmdse2c,1799
|
|
23
|
+
quapp_dwave_ocean/model/provider/quapp_d_wave_provider.py,sha256=1JJhUaf_zdF_PUqkg2haUseZvl-9--aS8uvENyk5kVo,754
|
|
24
|
+
quapp_dwave_ocean-0.0.1.dev2.dist-info/licenses/LICENSE,sha256=LZ_WtCo2GQZm4PAl_QaQKdS37Pevj736S9jfCvzL8IA,1104
|
|
25
|
+
quapp_dwave_ocean-0.0.1.dev2.dist-info/METADATA,sha256=dJeOiM7xXaTxjNpE3rkQSauJ-txNtnD4ZIn3YYXzmhY,4024
|
|
26
|
+
quapp_dwave_ocean-0.0.1.dev2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
27
|
+
quapp_dwave_ocean-0.0.1.dev2.dist-info/top_level.txt,sha256=BSySVmOYNPU1vwej2xEUgP4_e0oup5GyEwRIu-4jHi8,18
|
|
28
|
+
quapp_dwave_ocean-0.0.1.dev2.dist-info/RECORD,,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
5
|
+
|
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
quapp_dwave_ocean
|