Skip to content

Commit 448cc43

Browse files
authored
support EstimationResult and dictionary items (#529)
* support EstimationResult and dictionary items * feedback * unit tests
1 parent 4908b19 commit 448cc43

File tree

2 files changed

+124
-32
lines changed

2 files changed

+124
-32
lines changed

azure-quantum/azure/quantum/target/microsoft/result.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# Copyright (c) Microsoft Corporation. All rights reserved.
33
# Licensed under the MIT License.
44
##
5+
__all__ = ['MicrosoftEstimatorResult']
6+
57
from typing import Any, Dict, List, Optional, Union
68

79
import json
@@ -18,7 +20,6 @@ def __init__(self, content: str):
1820
def _repr_html_(self):
1921
return self.content
2022

21-
2223
class MicrosoftEstimatorResult(dict):
2324
"""
2425
Microsoft Resource Estimator result.
@@ -38,7 +39,7 @@ def __init__(self, data: Union[Dict, List]):
3839
super().__init__(data)
3940

4041
self._is_simple = True
41-
if self._is_succeeded():
42+
if MicrosoftEstimatorResult._is_succeeded(self):
4243
self._repr = self._item_result_table()
4344
self.summary = HTMLWrapper(self._item_result_summary_table())
4445
self.diagram = EstimatorResultDiagram(self.data().copy())
@@ -279,7 +280,7 @@ def _summary_data_frame(self, **kwargs):
279280
labels = labels[:len(self)]
280281

281282
def get_row(result):
282-
if self._is_succeeded():
283+
if MicrosoftEstimatorResult._is_succeeded(result):
283284
formatted = result["physicalCountsFormatted"]
284285

285286
return (
@@ -409,7 +410,7 @@ def _item_result_summary_table(self):
409410
return html
410411

411412
def _batch_result_table(self, indices):
412-
succeeded_item_indices = [i for i in indices if self[i]._is_succeeded()]
413+
succeeded_item_indices = [i for i in indices if MicrosoftEstimatorResult._is_succeeded(self[i])]
413414
if len(succeeded_item_indices) == 0:
414415
print("None of the jobs succeeded")
415416
return ""
@@ -471,7 +472,10 @@ def _batch_result_table(self, indices):
471472

472473
return html
473474

474-
475+
@staticmethod
476+
def _is_succeeded(obj):
477+
return 'status' in obj and obj['status'] == "success"
478+
475479
class EstimatorResultDiagram:
476480
def __init__(self, data):
477481
data.pop("reportData")

azure-quantum/tests/unit/test_microsoft_qc.py

Lines changed: 115 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def _ccnot_bitcode(self) -> bytes:
2929
bitcode_filename = path.join(path.dirname(__file__), "qir", "ccnot.bc")
3030
with open(bitcode_filename, "rb") as f:
3131
return f.read()
32-
32+
3333
def _mock_result_data(self) -> dict:
3434
"""
3535
A small result data for tests.
@@ -43,6 +43,34 @@ def _mock_result_data(self) -> dict:
4343
"reportData": {"groups": [], "assumptions": []}
4444
}
4545

46+
def _mock_result_data_full(self, status) -> dict:
47+
formatted = {
48+
"algorithmicLogicalQubits": 10,
49+
"logicalDepth": 100,
50+
"numTstates": 15,
51+
"numTfactories": 1000,
52+
"physicalQubitsForTfactoriesPercentage": 10,
53+
"physicalQubits": 30000,
54+
"rqops": 45678,
55+
"runtime": 23456789
56+
}
57+
58+
result = {
59+
"physicalCounts": {
60+
"physicalQubits": 655321,
61+
"runtime": 1729,
62+
"rqops": 314
63+
},
64+
"logicalQubit": {
65+
"codeDistance": 11
66+
},
67+
"reportData": {"groups": [], "assumptions": []}
68+
}
69+
70+
result["physicalCountsFormatted"] = formatted
71+
result["status"] = status
72+
return result
73+
4674
@pytest.mark.microsoft_qc
4775
@pytest.mark.live_test
4876
def test_estimator_non_batching_job(self):
@@ -88,7 +116,7 @@ def test_estimator_batching_job(self):
88116
params.items[0].qubit_params.t_gate_time = "10 ns"
89117
params.items[0].qubit_params.idle_error_rate = 0.00002
90118
params.items[0].qubit_params.two_qubit_joint_measurement_error_rate = \
91-
MeasurementErrorRate(process = 0.00005, readout = 0.00007)
119+
MeasurementErrorRate(process=0.00005, readout=0.00007)
92120

93121
specification1 = DistillationUnitSpecification()
94122
specification1.display_name = "S"
@@ -110,10 +138,11 @@ def test_estimator_batching_job(self):
110138
specification2 = DistillationUnitSpecification()
111139
specification2.name = "15-1 RM"
112140

113-
specification3= DistillationUnitSpecification()
141+
specification3 = DistillationUnitSpecification()
114142
specification3.name = "15-1 space-efficient"
115143

116-
params.items[0].distillation_unit_specifications = [specification1, specification2, specification3]
144+
params.items[0].distillation_unit_specifications = [
145+
specification1, specification2, specification3]
117146

118147
params.items[1].error_budget = 0.002
119148
params.items[1].constraints.max_duration = "20s"
@@ -318,21 +347,20 @@ def test_estimator_params_validation_measurement_error_rates_valid(self):
318347
params.qubit_params.idle_error_rate = 0.02
319348
params.qubit_params.one_qubit_measurement_error_rate = 0.01
320349
params.qubit_params.two_qubit_joint_measurement_error_rate = \
321-
MeasurementErrorRate(process = 0.02, readout = 0.03)
350+
MeasurementErrorRate(process=0.02, readout=0.03)
322351

323352
assert params.as_dict() == {
324-
"errorBudget": 0.1,
325-
"qubitParams": {"name": "qubit_gate_ns_e3",
326-
"instructionSet": "gate_based",
327-
"tGateErrorRate": 0.03,
328-
"tGateTime": "10 ns",
329-
"idleErrorRate": 0.02,
330-
"oneQubitMeasurementErrorRate": 0.01,
331-
"twoQubitJointMeasurementErrorRate":
353+
"errorBudget": 0.1,
354+
"qubitParams": {"name": "qubit_gate_ns_e3",
355+
"instructionSet": "gate_based",
356+
"tGateErrorRate": 0.03,
357+
"tGateTime": "10 ns",
358+
"idleErrorRate": 0.02,
359+
"oneQubitMeasurementErrorRate": 0.01,
360+
"twoQubitJointMeasurementErrorRate":
332361
{"process": 0.02, "readout": 0.03}}
333362
}
334363

335-
336364
def test_estimator_error_budget_float(self):
337365
params = MicrosoftEstimatorParams()
338366
params.error_budget = 0.001
@@ -398,9 +426,9 @@ def test_estimator_custom_distillation_units_name_and_custom_not_allowed_togethe
398426
params.distillation_unit_specifications.append(unit)
399427

400428
with raises(LookupError, match="If predefined name is provided, "
401-
"custom specification is not allowed. "
402-
"Either remove name or remove all other "
403-
"specification of the distillation unit"):
429+
"custom specification is not allowed. "
430+
"Either remove name or remove all other "
431+
"specification of the distillation unit"):
404432
params.as_dict()
405433

406434
def test_estimator_custom_distillation_units_by_specification_short(self):
@@ -414,9 +442,9 @@ def test_estimator_custom_distillation_units_by_specification_short(self):
414442
params.distillation_unit_specifications.append(unit)
415443

416444
assert params.as_dict() == {
417-
"distillationUnitSpecifications":
445+
"distillationUnitSpecifications":
418446
[{"displayName": "T", "failureProbabilityFormula": "c",
419-
"outputErrorRateFormula": "r", "numInputTs": 1, "numOutputTs": 2 }]
447+
"outputErrorRateFormula": "r", "numInputTs": 1, "numOutputTs": 2}]
420448
}
421449

422450
def test_estimator_custom_distillation_units_by_specification_full(self):
@@ -449,13 +477,13 @@ def test_estimator_custom_distillation_units_by_specification_full(self):
449477

450478
print(params.as_dict())
451479
assert params.as_dict() == {
452-
"distillationUnitSpecifications":
453-
[{"displayName": "T", "numInputTs": 1, "numOutputTs": 2,
454-
"failureProbabilityFormula": "c", "outputErrorRateFormula": "r",
455-
"physicalQubitSpecification": {"numUnitQubits": 1, "durationInQubitCycleTime": 2},
456-
"logicalQubitSpecification": {"numUnitQubits":3, "durationInQubitCycleTime":4},
457-
"logicalQubitSpecificationFirstRoundOverride":
458-
{"numUnitQubits":5, "durationInQubitCycleTime":6}}]
480+
"distillationUnitSpecifications":
481+
[{"displayName": "T", "numInputTs": 1, "numOutputTs": 2,
482+
"failureProbabilityFormula": "c", "outputErrorRateFormula": "r",
483+
"physicalQubitSpecification": {"numUnitQubits": 1, "durationInQubitCycleTime": 2},
484+
"logicalQubitSpecification": {"numUnitQubits": 3, "durationInQubitCycleTime": 4},
485+
"logicalQubitSpecificationFirstRoundOverride":
486+
{"numUnitQubits": 5, "durationInQubitCycleTime": 6}}]
459487
}
460488

461489
def test_estimator_protocol_specific_distillation_unit_specification_empty_not_allowed(self):
@@ -496,7 +524,67 @@ def test_batch_result_as_json(self):
496524

497525
import json
498526
assert json.loads(result.json) == data
499-
527+
528+
def test_list_status_all_failed(self):
529+
data = [self._mock_result_data_full(
530+
"error"), self._mock_result_data_full("failure")]
531+
result = MicrosoftEstimatorResult(data)
532+
533+
import json
534+
assert json.loads(result.json) == data
535+
536+
data_frame = result.summary_data_frame()
537+
assert data_frame.values.real[0][0] == "No solution found"
538+
assert data_frame.values.real[1][5] == "No solution found"
539+
540+
assert not hasattr(result[0], "summary")
541+
assert not hasattr(result[1], "summary")
542+
543+
assert not hasattr(result[0], "diagram")
544+
assert not hasattr(result[1], "diagram")
545+
546+
def test_list_status_partial_success(self):
547+
data = [self._mock_result_data_full(
548+
"success"), self._mock_result_data_full("error")]
549+
result = MicrosoftEstimatorResult(data)
550+
551+
import json
552+
assert json.loads(result.json) == data
553+
554+
data_frame = result.summary_data_frame()
555+
assert data_frame.values.real[0][0] == 10
556+
assert data_frame.values.real[1][5] == "No solution found"
557+
558+
assert hasattr(result[0], "summary")
559+
assert not hasattr(result[1], "summary")
560+
561+
assert hasattr(result[0], "diagram")
562+
assert not hasattr(result[1], "diagram")
563+
564+
def test_dict_status_failed(self):
565+
data = self._mock_result_data_full("error")
566+
result = MicrosoftEstimatorResult(data)
567+
568+
import json
569+
assert json.loads(result.json) == data
570+
571+
assert not hasattr(result, "summary_data_frame")
572+
573+
assert not hasattr(result, "summary")
574+
assert not hasattr(result, "diagram")
575+
576+
def test_dict_status_success(self):
577+
data = self._mock_result_data_full("success")
578+
result = MicrosoftEstimatorResult(data)
579+
580+
import json
581+
assert json.loads(result.json) == data
582+
583+
assert not hasattr(result, "summary_data_frame")
584+
585+
assert hasattr(result, "summary")
586+
assert hasattr(result, "diagram")
587+
500588
def test_duration_and_physical_qubits_constraints_not_allowed_together(self):
501589
constraints = MicrosoftEstimatorConstraints()
502590
constraints.max_physical_qubits = 100

0 commit comments

Comments
 (0)