azure-quantum 2.2.0.dev5__py3-none-any.whl → 2.3.0__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.
- azure/quantum/_client/_version.py +1 -1
- azure/quantum/argument_types/__init__.py +1 -1
- azure/quantum/cirq/service.py +0 -30
- azure/quantum/cirq/targets/ionq.py +0 -31
- azure/quantum/cirq/targets/quantinuum.py +0 -20
- azure/quantum/qiskit/backends/__init__.py +0 -5
- azure/quantum/qiskit/backends/backend.py +2 -19
- azure/quantum/qiskit/backends/ionq.py +0 -15
- azure/quantum/qiskit/backends/quantinuum.py +1 -32
- azure/quantum/qiskit/job.py +2 -8
- azure/quantum/target/ionq.py +0 -111
- azure/quantum/target/microsoft/elements/dft/job.py +108 -2
- azure/quantum/target/microsoft/elements/dft/target.py +157 -8
- azure/quantum/target/microsoft/target.py +0 -329
- azure/quantum/target/quantinuum.py +0 -123
- azure/quantum/target/rigetti/target.py +5 -0
- azure/quantum/target/target.py +1 -11
- azure/quantum/version.py +1 -1
- {azure_quantum-2.2.0.dev5.dist-info → azure_quantum-2.3.0.dist-info}/METADATA +3 -3
- {azure_quantum-2.2.0.dev5.dist-info → azure_quantum-2.3.0.dist-info}/RECORD +22 -28
- {azure_quantum-2.2.0.dev5.dist-info → azure_quantum-2.3.0.dist-info}/WHEEL +1 -1
- azure/quantum/qiskit/backends/microsoft.py +0 -161
- azure/quantum/qiskit/results/__init__.py +0 -0
- azure/quantum/qiskit/results/resource_estimator.py +0 -25
- azure/quantum/target/microsoft/__init__.py +0 -15
- azure/quantum/target/microsoft/job.py +0 -35
- azure/quantum/target/microsoft/result.py +0 -497
- {azure_quantum-2.2.0.dev5.dist-info → azure_quantum-2.3.0.dist-info}/top_level.txt +0 -0
|
@@ -1,497 +0,0 @@
|
|
|
1
|
-
##
|
|
2
|
-
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
-
# Licensed under the MIT License.
|
|
4
|
-
##
|
|
5
|
-
__all__ = ['MicrosoftEstimatorResult']
|
|
6
|
-
|
|
7
|
-
from typing import Any, Dict, List, Optional, Union
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
import markdown
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class HTMLWrapper:
|
|
14
|
-
"""
|
|
15
|
-
Simple HTML wrapper to expose _repr_html_ for Jupyter clients.
|
|
16
|
-
"""
|
|
17
|
-
def __init__(self, content: str):
|
|
18
|
-
self.content = content
|
|
19
|
-
|
|
20
|
-
def _repr_html_(self):
|
|
21
|
-
return self.content
|
|
22
|
-
|
|
23
|
-
class MicrosoftEstimatorResult(dict):
|
|
24
|
-
"""
|
|
25
|
-
Microsoft Resource Estimator result.
|
|
26
|
-
|
|
27
|
-
Job results from the `microsoft.estimator` target are represented by
|
|
28
|
-
instances of this class. The class represents simple resource estimation
|
|
29
|
-
results as well as batching resource estimation results. The latter can
|
|
30
|
-
be indexed by an integer index to access an individual result from the
|
|
31
|
-
batching result.
|
|
32
|
-
"""
|
|
33
|
-
MAX_DEFAULT_ITEMS_IN_TABLE = 5
|
|
34
|
-
|
|
35
|
-
def __init__(self, data: Union[Dict, List]):
|
|
36
|
-
self._data = data
|
|
37
|
-
|
|
38
|
-
if isinstance(data, dict):
|
|
39
|
-
super().__init__(data)
|
|
40
|
-
|
|
41
|
-
self._is_simple = True
|
|
42
|
-
if MicrosoftEstimatorResult._is_succeeded(self):
|
|
43
|
-
self._repr = self._item_result_table()
|
|
44
|
-
self.summary = HTMLWrapper(self._item_result_summary_table())
|
|
45
|
-
self.diagram = EstimatorResultDiagram(self.data().copy())
|
|
46
|
-
|
|
47
|
-
elif isinstance(data, list):
|
|
48
|
-
super().__init__({idx: MicrosoftEstimatorResult(item_data)
|
|
49
|
-
for idx, item_data in enumerate(data)})
|
|
50
|
-
|
|
51
|
-
self._data = data
|
|
52
|
-
self._is_simple = False
|
|
53
|
-
num_items = len(data)
|
|
54
|
-
self._repr = ""
|
|
55
|
-
if num_items > self.MAX_DEFAULT_ITEMS_IN_TABLE:
|
|
56
|
-
self._repr += "<p><b>Info:</b> <i>The overview table is " \
|
|
57
|
-
"cut off after " \
|
|
58
|
-
f"{self.MAX_DEFAULT_ITEMS_IN_TABLE} items. If " \
|
|
59
|
-
"you want to see all items, suffix the result " \
|
|
60
|
-
"variable with <code>[:]</code></i></p>"
|
|
61
|
-
num_items = self.MAX_DEFAULT_ITEMS_IN_TABLE
|
|
62
|
-
self._repr += self._batch_result_table(range(num_items))
|
|
63
|
-
|
|
64
|
-
# Add plot function for batching jobs
|
|
65
|
-
self.plot = self._plot
|
|
66
|
-
self.summary_data_frame = self._summary_data_frame
|
|
67
|
-
|
|
68
|
-
def _is_succeeded(self):
|
|
69
|
-
return 'status' in self and self['status'] == "success"
|
|
70
|
-
|
|
71
|
-
def data(self, idx: Optional[int] = None) -> Any:
|
|
72
|
-
"""
|
|
73
|
-
Returns raw data of the result object.
|
|
74
|
-
|
|
75
|
-
In case of a batching job, you can pass an index to access a specific
|
|
76
|
-
item.
|
|
77
|
-
"""
|
|
78
|
-
if idx is None:
|
|
79
|
-
return self._data
|
|
80
|
-
elif not self._is_simple:
|
|
81
|
-
return self._data[idx]
|
|
82
|
-
else:
|
|
83
|
-
msg = "Cannot pass parameter 'idx' to 'data' for non-batching job"
|
|
84
|
-
raise ValueError(msg)
|
|
85
|
-
|
|
86
|
-
def _repr_html_(self):
|
|
87
|
-
"""
|
|
88
|
-
HTML table representation of the result.
|
|
89
|
-
"""
|
|
90
|
-
return self._repr
|
|
91
|
-
|
|
92
|
-
def __getitem__(self, key):
|
|
93
|
-
"""
|
|
94
|
-
If the result represents a batching job and key is a slice, a
|
|
95
|
-
side-by-side table comparison is shown for the indexes represented by
|
|
96
|
-
the slice.
|
|
97
|
-
|
|
98
|
-
Otherwise, the key is used to access the raw data directly.
|
|
99
|
-
"""
|
|
100
|
-
if isinstance(key, slice):
|
|
101
|
-
if self._is_simple:
|
|
102
|
-
msg = "Cannot pass slice to '__getitem__' for non-batching job"
|
|
103
|
-
raise ValueError(msg)
|
|
104
|
-
return HTMLWrapper(self._batch_result_table(range(len(self))[key]))
|
|
105
|
-
else:
|
|
106
|
-
return super().__getitem__(key)
|
|
107
|
-
|
|
108
|
-
def _plot(self, **kwargs):
|
|
109
|
-
"""
|
|
110
|
-
Plots all result items in a space time plot, where the x-axis shows
|
|
111
|
-
total runtime, and the y-axis shows total number of physical qubits.
|
|
112
|
-
Both axes are in log-scale.
|
|
113
|
-
Attributes:
|
|
114
|
-
labels (list): List of labels for the legend.
|
|
115
|
-
"""
|
|
116
|
-
try:
|
|
117
|
-
import matplotlib.pyplot as plt
|
|
118
|
-
except ImportError:
|
|
119
|
-
raise ImportError(
|
|
120
|
-
"Missing optional 'matplotlib' dependency. To install run: "
|
|
121
|
-
"pip install matplotlib"
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
labels = kwargs.pop("labels", [])
|
|
125
|
-
|
|
126
|
-
[xs, ys] = zip(*[
|
|
127
|
-
(self.data(i)['physicalCounts']['runtime'],
|
|
128
|
-
self.data(i)['physicalCounts']['physicalQubits'])
|
|
129
|
-
for i in range(len(self))])
|
|
130
|
-
|
|
131
|
-
_ = plt.figure(figsize=(15, 8))
|
|
132
|
-
|
|
133
|
-
plt.ylabel('Physical qubits')
|
|
134
|
-
plt.xlabel('Runtime')
|
|
135
|
-
plt.loglog()
|
|
136
|
-
for i, (x, y) in enumerate(zip(xs, ys)):
|
|
137
|
-
if isinstance(labels, list) and i < len(labels):
|
|
138
|
-
label = labels[i]
|
|
139
|
-
else:
|
|
140
|
-
label = str(i)
|
|
141
|
-
plt.scatter(x=[x], y=[y], label=label, marker="os+x"[i % 4])
|
|
142
|
-
|
|
143
|
-
nsec = 1
|
|
144
|
-
usec = 1e3 * nsec
|
|
145
|
-
msec = 1e3 * usec
|
|
146
|
-
sec = 1e3 * msec
|
|
147
|
-
min = 60 * sec
|
|
148
|
-
hour = 60 * min
|
|
149
|
-
day = 24 * hour
|
|
150
|
-
week = 7 * day
|
|
151
|
-
month = 31 * day
|
|
152
|
-
year = 365 * month
|
|
153
|
-
decade = 10 * year
|
|
154
|
-
century = 10 * decade
|
|
155
|
-
|
|
156
|
-
time_units = [
|
|
157
|
-
nsec, usec, msec, sec, min, hour, day, week,
|
|
158
|
-
month, year, decade, century]
|
|
159
|
-
time_labels = [
|
|
160
|
-
"1 ns", "1 µs", "1 ms", "1 s", "1 min", "1 hour", "1 day",
|
|
161
|
-
"1 week", "1 month", "1 year", "1 decade", "1 century"]
|
|
162
|
-
|
|
163
|
-
cutoff = next(
|
|
164
|
-
(i for i, x in enumerate(time_units) if x > max(xs)),
|
|
165
|
-
len(time_units) - 1) + 1
|
|
166
|
-
|
|
167
|
-
plt.xticks(time_units[0:cutoff], time_labels[0:cutoff], rotation=90)
|
|
168
|
-
plt.legend(loc="upper left")
|
|
169
|
-
plt.show()
|
|
170
|
-
|
|
171
|
-
@property
|
|
172
|
-
def call_graph(self):
|
|
173
|
-
"""
|
|
174
|
-
Shows the call graph of a simple resource estimation result with
|
|
175
|
-
profiling information.
|
|
176
|
-
"""
|
|
177
|
-
try:
|
|
178
|
-
import graphviz
|
|
179
|
-
except ImportError:
|
|
180
|
-
raise ImportError(
|
|
181
|
-
"Missing optional 'graphviz' dependency. To install run: "
|
|
182
|
-
"pip install graphviz"
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
if not self._is_simple:
|
|
186
|
-
raise ValueError("The `call_graph` method cannot be called on a "
|
|
187
|
-
"batching result, try indexing into the result "
|
|
188
|
-
"first")
|
|
189
|
-
|
|
190
|
-
if not hasattr(self, "_call_graph"):
|
|
191
|
-
from itertools import groupby
|
|
192
|
-
|
|
193
|
-
data = self.data().get("callGraph", None)
|
|
194
|
-
|
|
195
|
-
if data is None:
|
|
196
|
-
raise ValueError("The result does not contain any profiling "
|
|
197
|
-
"information. Set "
|
|
198
|
-
"`profiling.call_stack_depth` to some value")
|
|
199
|
-
|
|
200
|
-
g = graphviz.Digraph()
|
|
201
|
-
g.attr('node', shape='box', style='rounded, filled',
|
|
202
|
-
fontname='Arial', fontsize='10', margin='0.05,0.05',
|
|
203
|
-
height='0', width='0', fillcolor='#f6f6f6', color='#e3e3e3')
|
|
204
|
-
g.attr('edge', color='#d0d0d0')
|
|
205
|
-
|
|
206
|
-
nodes_indexed = [{**node, 'index': index}
|
|
207
|
-
for index, node in enumerate(data['nodes'])]
|
|
208
|
-
|
|
209
|
-
def sorter(node): return node['depth']
|
|
210
|
-
nodes_indexed.sort(key=sorter)
|
|
211
|
-
for _, nodes in groupby(nodes_indexed, sorter):
|
|
212
|
-
with g.subgraph() as s:
|
|
213
|
-
s.attr(rank='same')
|
|
214
|
-
for node in nodes:
|
|
215
|
-
s.node(str(node['index']), node['name'])
|
|
216
|
-
|
|
217
|
-
for edge in data['edges']:
|
|
218
|
-
g.edge(str(edge[0]), str(edge[1]))
|
|
219
|
-
|
|
220
|
-
self._call_graph = g
|
|
221
|
-
|
|
222
|
-
return self._call_graph
|
|
223
|
-
|
|
224
|
-
@property
|
|
225
|
-
def profile(self):
|
|
226
|
-
"""
|
|
227
|
-
"""
|
|
228
|
-
if not self._is_simple:
|
|
229
|
-
raise ValueError("The `call_graph` method cannot be called on a "
|
|
230
|
-
"batching result, try indexing into the result "
|
|
231
|
-
"first")
|
|
232
|
-
|
|
233
|
-
if not hasattr(self, "_profile"):
|
|
234
|
-
import base64
|
|
235
|
-
import json
|
|
236
|
-
|
|
237
|
-
profile = self.data().get("profile", None)
|
|
238
|
-
|
|
239
|
-
if profile is None:
|
|
240
|
-
raise ValueError("The result does not contain any profiling "
|
|
241
|
-
"information. Set "
|
|
242
|
-
"`profiling.call_stack_depth` to some value")
|
|
243
|
-
|
|
244
|
-
profile_encoded = json.dumps(profile).encode('utf-8')
|
|
245
|
-
data64 = base64.b64encode(profile_encoded).decode('utf-8')
|
|
246
|
-
|
|
247
|
-
self._profile = f"""
|
|
248
|
-
<a href="data:text/json;base64,{data64}" download="profile.json">
|
|
249
|
-
Download the profile</a> to your computer. Then open the profile
|
|
250
|
-
by dragging it into <a href="https://speedscope.app"
|
|
251
|
-
target="_blank">speedscope</a>.
|
|
252
|
-
"""
|
|
253
|
-
|
|
254
|
-
return HTMLWrapper(self._profile)
|
|
255
|
-
|
|
256
|
-
@property
|
|
257
|
-
def json(self):
|
|
258
|
-
"""
|
|
259
|
-
Returns a JSON representation of the resource estimation result data.
|
|
260
|
-
"""
|
|
261
|
-
if not hasattr(self, "_json"):
|
|
262
|
-
import json
|
|
263
|
-
self._json = json.dumps(self._data)
|
|
264
|
-
|
|
265
|
-
return self._json
|
|
266
|
-
|
|
267
|
-
def _summary_data_frame(self, **kwargs):
|
|
268
|
-
try:
|
|
269
|
-
import pandas as pd
|
|
270
|
-
except ImportError:
|
|
271
|
-
raise ImportError(
|
|
272
|
-
"Missing optional 'pandas' dependency. To install run: "
|
|
273
|
-
"pip install pandas"
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
# get labels or use default value, then extend with missing elements,
|
|
277
|
-
# and truncate extra elements
|
|
278
|
-
labels = kwargs.pop("labels", [])
|
|
279
|
-
labels.extend(range(len(labels), len(self)))
|
|
280
|
-
labels = labels[:len(self)]
|
|
281
|
-
|
|
282
|
-
def get_row(result):
|
|
283
|
-
if MicrosoftEstimatorResult._is_succeeded(result):
|
|
284
|
-
formatted = result["physicalCountsFormatted"]
|
|
285
|
-
|
|
286
|
-
return (
|
|
287
|
-
formatted["algorithmicLogicalQubits"],
|
|
288
|
-
formatted["logicalDepth"],
|
|
289
|
-
formatted["numTstates"],
|
|
290
|
-
result["logicalQubit"]["codeDistance"],
|
|
291
|
-
formatted["numTfactories"],
|
|
292
|
-
formatted["physicalQubitsForTfactoriesPercentage"],
|
|
293
|
-
formatted["physicalQubits"],
|
|
294
|
-
formatted["rqops"],
|
|
295
|
-
formatted["runtime"]
|
|
296
|
-
)
|
|
297
|
-
else:
|
|
298
|
-
return ['No solution found'] * 9
|
|
299
|
-
|
|
300
|
-
data = [get_row(self.data(index)) for index in range(len(self))]
|
|
301
|
-
columns = ["Logical qubits", "Logical depth", "T states",
|
|
302
|
-
"Code distance", "T factories", "T factory fraction",
|
|
303
|
-
"Physical qubits", "rQOPS", "Physical runtime"]
|
|
304
|
-
return pd.DataFrame(data, columns=columns, index=labels)
|
|
305
|
-
|
|
306
|
-
def _item_result_table(self):
|
|
307
|
-
html = ""
|
|
308
|
-
|
|
309
|
-
md = markdown.Markdown(extensions=['mdx_math'])
|
|
310
|
-
for group in self['reportData']['groups']:
|
|
311
|
-
html += f"""
|
|
312
|
-
<details {"open" if group['alwaysVisible'] else ""}>
|
|
313
|
-
<summary style="display:list-item">
|
|
314
|
-
<strong>{group['title']}</strong>
|
|
315
|
-
</summary>
|
|
316
|
-
<table>"""
|
|
317
|
-
for entry in group['entries']:
|
|
318
|
-
val = self
|
|
319
|
-
for key in entry['path'].split("/"):
|
|
320
|
-
val = val[key]
|
|
321
|
-
explanation = md.convert(entry["explanation"])
|
|
322
|
-
html += f"""
|
|
323
|
-
<tr>
|
|
324
|
-
<td style="font-weight: bold; vertical-align: top; white-space: nowrap">{entry['label']}</td>
|
|
325
|
-
<td style="vertical-align: top; white-space: nowrap">{val}</td>
|
|
326
|
-
<td style="text-align: left">
|
|
327
|
-
<strong>{entry["description"]}</strong>
|
|
328
|
-
<hr style="margin-top: 2px; margin-bottom: 0px; border-top: solid 1px black" />
|
|
329
|
-
{explanation}
|
|
330
|
-
</td>
|
|
331
|
-
</tr>
|
|
332
|
-
"""
|
|
333
|
-
html += "</table></details>"
|
|
334
|
-
|
|
335
|
-
html += f"<details><summary style=\"display:list-item\"><strong>Assumptions</strong></summary><ul>"
|
|
336
|
-
for assumption in self['reportData']['assumptions']:
|
|
337
|
-
html += f"<li>{md.convert(assumption)}</li>"
|
|
338
|
-
html += "</ul></details>"
|
|
339
|
-
|
|
340
|
-
return html
|
|
341
|
-
|
|
342
|
-
def _item_result_summary_table(self):
|
|
343
|
-
html = """
|
|
344
|
-
<style>
|
|
345
|
-
.aqre-tooltip {
|
|
346
|
-
position: relative;
|
|
347
|
-
border-bottom: 1px dotted black;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
.aqre-tooltip .aqre-tooltiptext {
|
|
351
|
-
font-weight: normal;
|
|
352
|
-
visibility: hidden;
|
|
353
|
-
width: 600px;
|
|
354
|
-
background-color: #e0e0e0;
|
|
355
|
-
color: black;
|
|
356
|
-
text-align: center;
|
|
357
|
-
border-radius: 6px;
|
|
358
|
-
padding: 5px 5px;
|
|
359
|
-
position: absolute;
|
|
360
|
-
z-index: 1;
|
|
361
|
-
top: 150%;
|
|
362
|
-
left: 50%;
|
|
363
|
-
margin-left: -200px;
|
|
364
|
-
border: solid 1px black;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
.aqre-tooltip .aqre-tooltiptext::after {
|
|
368
|
-
content: "";
|
|
369
|
-
position: absolute;
|
|
370
|
-
bottom: 100%;
|
|
371
|
-
left: 50%;
|
|
372
|
-
margin-left: -5px;
|
|
373
|
-
border-width: 5px;
|
|
374
|
-
border-style: solid;
|
|
375
|
-
border-color: transparent transparent black transparent;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
.aqre-tooltip:hover .aqre-tooltiptext {
|
|
379
|
-
visibility: visible;
|
|
380
|
-
}
|
|
381
|
-
</style>"""
|
|
382
|
-
|
|
383
|
-
md = markdown.Markdown(extensions=['mdx_math'])
|
|
384
|
-
for group in self['reportData']['groups']:
|
|
385
|
-
html += f"""
|
|
386
|
-
<details {"open" if group['alwaysVisible'] else ""}>
|
|
387
|
-
<summary style="display:list-item">
|
|
388
|
-
<strong>{group['title']}</strong>
|
|
389
|
-
</summary>
|
|
390
|
-
<table>"""
|
|
391
|
-
for entry in group['entries']:
|
|
392
|
-
val = self
|
|
393
|
-
for key in entry['path'].split("/"):
|
|
394
|
-
val = val[key]
|
|
395
|
-
explanation = md.convert(entry["explanation"])
|
|
396
|
-
html += f"""
|
|
397
|
-
<tr class="aqre-tooltip">
|
|
398
|
-
<td style="font-weight: bold"><span class="aqre-tooltiptext">{explanation}</span>{entry['label']}</td>
|
|
399
|
-
<td>{val}</td>
|
|
400
|
-
<td style="text-align: left">{entry["description"]}</td>
|
|
401
|
-
</tr>
|
|
402
|
-
"""
|
|
403
|
-
html += "</table></details>"
|
|
404
|
-
|
|
405
|
-
html += f"<details><summary style=\'display:list-item\'><strong>Assumptions</strong></summary><ul>"
|
|
406
|
-
for assumption in self['reportData']['assumptions']:
|
|
407
|
-
html += f"<li>{md.convert(assumption)}</li>"
|
|
408
|
-
html += "</ul></details>"
|
|
409
|
-
|
|
410
|
-
return html
|
|
411
|
-
|
|
412
|
-
def _batch_result_table(self, indices):
|
|
413
|
-
succeeded_item_indices = [i for i in indices if MicrosoftEstimatorResult._is_succeeded(self[i])]
|
|
414
|
-
if len(succeeded_item_indices) == 0:
|
|
415
|
-
print("None of the jobs succeeded")
|
|
416
|
-
return ""
|
|
417
|
-
|
|
418
|
-
first_succeeded_item_index = succeeded_item_indices[0]
|
|
419
|
-
|
|
420
|
-
html = ""
|
|
421
|
-
|
|
422
|
-
md = markdown.Markdown(extensions=['mdx_math'])
|
|
423
|
-
|
|
424
|
-
item_headers = "".join(f"<th>{i}</th>" for i in indices)
|
|
425
|
-
|
|
426
|
-
for group_index, group in enumerate(self[first_succeeded_item_index]['reportData']['groups']):
|
|
427
|
-
html += f"""
|
|
428
|
-
<details {"open" if group['alwaysVisible'] else ""}>
|
|
429
|
-
<summary style="display:list-item">
|
|
430
|
-
<strong>{group['title']}</strong>
|
|
431
|
-
</summary>
|
|
432
|
-
<table>
|
|
433
|
-
<thead><tr><th>Item</th>{item_headers}</tr></thead>"""
|
|
434
|
-
|
|
435
|
-
visited_entries = set()
|
|
436
|
-
|
|
437
|
-
for entry in [entry for index in succeeded_item_indices for entry in self[index]['reportData']['groups'][group_index]['entries']]:
|
|
438
|
-
label = entry['label']
|
|
439
|
-
if label in visited_entries:
|
|
440
|
-
continue
|
|
441
|
-
visited_entries.add(label)
|
|
442
|
-
|
|
443
|
-
html += f"""
|
|
444
|
-
<tr>
|
|
445
|
-
<td style="font-weight: bold; vertical-align: top; white-space: nowrap">{label}</td>
|
|
446
|
-
"""
|
|
447
|
-
|
|
448
|
-
for index in indices:
|
|
449
|
-
val = self[index]
|
|
450
|
-
if index in succeeded_item_indices:
|
|
451
|
-
for key in entry['path'].split("/"):
|
|
452
|
-
if key in val:
|
|
453
|
-
val = val[key]
|
|
454
|
-
else:
|
|
455
|
-
val = "N/A"
|
|
456
|
-
break
|
|
457
|
-
else:
|
|
458
|
-
val = "N/A"
|
|
459
|
-
html += f"""
|
|
460
|
-
<td style="vertical-align: top; white-space: nowrap">{val}</td>
|
|
461
|
-
"""
|
|
462
|
-
|
|
463
|
-
html += """
|
|
464
|
-
</tr>
|
|
465
|
-
"""
|
|
466
|
-
html += "</table></details>"
|
|
467
|
-
|
|
468
|
-
html += f"<details><summary style=\"display:list-item\"><strong>Assumptions</strong></summary><ul>"
|
|
469
|
-
for assumption in self[first_succeeded_item_index]['reportData']['assumptions']:
|
|
470
|
-
html += f"<li>{md.convert(assumption)}</li>"
|
|
471
|
-
html += "</ul></details>"
|
|
472
|
-
|
|
473
|
-
return html
|
|
474
|
-
|
|
475
|
-
@staticmethod
|
|
476
|
-
def _is_succeeded(obj):
|
|
477
|
-
return 'status' in obj and obj['status'] == "success"
|
|
478
|
-
|
|
479
|
-
class EstimatorResultDiagram:
|
|
480
|
-
def __init__(self, data):
|
|
481
|
-
data.pop("reportData")
|
|
482
|
-
self.data_json = json.dumps(data).replace(" ", "")
|
|
483
|
-
self.vis_lib = "https://cdn-aquavisualization-prod.azureedge.net/resource-estimation/index.js"
|
|
484
|
-
self.space = HTMLWrapper(self._space_diagram())
|
|
485
|
-
self.time = HTMLWrapper(self._time_diagram())
|
|
486
|
-
|
|
487
|
-
def _space_diagram(self):
|
|
488
|
-
html = f"""
|
|
489
|
-
<script src={self.vis_lib}></script>
|
|
490
|
-
<re-space-diagram data={self.data_json}></re-space-diagram>"""
|
|
491
|
-
return html
|
|
492
|
-
|
|
493
|
-
def _time_diagram(self):
|
|
494
|
-
html = f"""
|
|
495
|
-
<script src={self.vis_lib}></script>
|
|
496
|
-
<re-time-diagram data={self.data_json}></re-time-diagram>"""
|
|
497
|
-
return html
|
|
File without changes
|