quapp-merlin 0.0.1.dev1__tar.gz → 0.0.1.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {quapp_merlin-0.0.1.dev1/quapp_merlin.egg-info → quapp_merlin-0.0.1.dev2}/PKG-INFO +1 -1
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/pyproject.toml +1 -1
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/model/device/merlin_device.py +34 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/model/device/merlin_local_device.py +0 -19
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/model/device/merlin_remote_device.py +1 -16
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2/quapp_merlin.egg-info}/PKG-INFO +1 -1
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin.egg-info/SOURCES.txt +1 -0
- quapp_merlin-0.0.1.dev2/tests/test_measurement_kind.py +114 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/LICENSE +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/README.md +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/component/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/component/backend/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/component/backend/merlin_invocation.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/data/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/data/merlin_job_spec.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/error_rules.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/factory/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/factory/merlin_device_factory.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/factory/merlin_handler_factory.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/factory/merlin_provider_factory.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/handler/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/handler/invocation_handler.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/model/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/model/device/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/model/provider/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/model/provider/quandela_merlin_provider.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/model/provider/quapp_merlin_provider.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/util/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/util/single_machine.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/util/tensors.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin.egg-info/dependency_links.txt +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin.egg-info/requires.txt +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin.egg-info/top_level.txt +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/setup.cfg +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/tests/test_error_logging.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/tests/test_error_rules.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/tests/test_job_spec.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/tests/test_lazy_sdk_imports.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/tests/test_single_machine.py +0 -0
- {quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/tests/test_tensors.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quapp-merlin"
|
|
7
|
-
version = "0.0.1.
|
|
7
|
+
version = "0.0.1.dev2"
|
|
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" }]
|
{quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/model/device/merlin_device.py
RENAMED
|
@@ -110,6 +110,40 @@ class MerlinDevice(CustomDevice, ABC):
|
|
|
110
110
|
|
|
111
111
|
# ------------------------------------------------------------- helpers
|
|
112
112
|
|
|
113
|
+
# A MeasurementStrategy keeps its kind in `.type`, a MeasurementKind enum
|
|
114
|
+
# (PROBABILITIES / MODE_EXPECTATIONS / AMPLITUDES / PARTIAL). It has no
|
|
115
|
+
# `.name` of its own -- the first version of this read
|
|
116
|
+
# type(strategy).__name__ instead, which is always 'MeasurementStrategy',
|
|
117
|
+
# so nothing ever matched 'probs' and no job ever produced a histogram.
|
|
118
|
+
MEASUREMENT_KINDS = {'PROBABILITIES' : 'probs',
|
|
119
|
+
'MODE_EXPECTATIONS' : 'mode_expectations',
|
|
120
|
+
'AMPLITUDES' : 'amplitudes',
|
|
121
|
+
'PARTIAL' : 'partial'}
|
|
122
|
+
|
|
123
|
+
def _measurement_name(self, model) -> str | None:
|
|
124
|
+
"""Which measurement the quantum layer uses, as a short string.
|
|
125
|
+
|
|
126
|
+
Only used to decide whether a histogram makes sense, so anything
|
|
127
|
+
unrecognised degrades to None rather than raising.
|
|
128
|
+
"""
|
|
129
|
+
modules = model.modules() if hasattr(model, 'modules') else [model]
|
|
130
|
+
|
|
131
|
+
for module in modules:
|
|
132
|
+
strategy = getattr(module, 'measurement_strategy', None)
|
|
133
|
+
if strategy is None:
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
kind = getattr(strategy, 'type', None)
|
|
137
|
+
name = (getattr(kind, 'name', None) or getattr(kind, 'value', None)
|
|
138
|
+
or kind)
|
|
139
|
+
if name is None:
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
name = str(name).upper()
|
|
143
|
+
return self.MEASUREMENT_KINDS.get(name, name.lower())
|
|
144
|
+
|
|
145
|
+
return None
|
|
146
|
+
|
|
113
147
|
def _histogram_from(self, output, shots, batch_size, measurement) -> dict | None:
|
|
114
148
|
"""Bin a single-row probability output into a state -> count histogram.
|
|
115
149
|
|
{quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/model/device/merlin_local_device.py
RENAMED
|
@@ -174,22 +174,3 @@ class MerlinLocalDevice(MerlinDevice):
|
|
|
174
174
|
|
|
175
175
|
return output, {'epochs': epochs, 'loss': tensors.plain(history),
|
|
176
176
|
'final_loss': history[-1] if history else None}
|
|
177
|
-
|
|
178
|
-
@staticmethod
|
|
179
|
-
def _measurement_name(model) -> str | None:
|
|
180
|
-
"""Which MeasurementStrategy the quantum layer uses, as a short string.
|
|
181
|
-
|
|
182
|
-
Only used to decide whether a histogram makes sense, so an unknown shape
|
|
183
|
-
degrades to None instead of raising.
|
|
184
|
-
"""
|
|
185
|
-
for module in (model.modules() if hasattr(model, 'modules') else [model]):
|
|
186
|
-
strategy = getattr(module, 'measurement_strategy', None)
|
|
187
|
-
if strategy is None:
|
|
188
|
-
continue
|
|
189
|
-
name = getattr(strategy, 'name', None) or type(strategy).__name__
|
|
190
|
-
name = str(name).lower()
|
|
191
|
-
for known in ('probs', 'probabilit', 'expectation', 'amplitude'):
|
|
192
|
-
if known in name:
|
|
193
|
-
return 'probs' if known.startswith('prob') else known
|
|
194
|
-
return name
|
|
195
|
-
return None
|
|
@@ -29,8 +29,7 @@ from quapp_common.model.provider.provider import Provider
|
|
|
29
29
|
|
|
30
30
|
from .merlin_device import MerlinDevice
|
|
31
31
|
from ...util import tensors
|
|
32
|
-
from ...util.single_machine import (DEFAULT_MAX_BATCH,
|
|
33
|
-
quantum_leaves, reject_foreign_backend,
|
|
32
|
+
from ...util.single_machine import (DEFAULT_MAX_BATCH, reject_foreign_backend,
|
|
34
33
|
require_batch_fits, require_single_leaf)
|
|
35
34
|
|
|
36
35
|
# How long we are willing to hold the invocation open waiting for the cloud.
|
|
@@ -148,17 +147,3 @@ class MerlinRemoteDevice(MerlinDevice):
|
|
|
148
147
|
return ','.join(str(job_id) for job_id in job_ids)
|
|
149
148
|
|
|
150
149
|
return str(job_ids[0])
|
|
151
|
-
|
|
152
|
-
@staticmethod
|
|
153
|
-
def _measurement_name(model) -> str | None:
|
|
154
|
-
leaves = quantum_leaves(model)
|
|
155
|
-
for _, leaf in leaves:
|
|
156
|
-
strategy = getattr(leaf, 'measurement_strategy', None)
|
|
157
|
-
if strategy is None:
|
|
158
|
-
continue
|
|
159
|
-
name = str(getattr(strategy, 'name', None)
|
|
160
|
-
or type(strategy).__name__).lower()
|
|
161
|
-
if name.startswith('prob') or 'probabilit' in name:
|
|
162
|
-
return 'probs'
|
|
163
|
-
return name
|
|
164
|
-
return None
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# test_measurement_kind.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
"""Reading the measurement kind off a MerLin strategy.
|
|
6
|
+
|
|
7
|
+
This decides whether a job gets a histogram, and the first version got it
|
|
8
|
+
wrong in a way nothing would have noticed: MeasurementStrategy has no `.name`,
|
|
9
|
+
so `type(strategy).__name__` was read instead -- always the string
|
|
10
|
+
'MeasurementStrategy' -- nothing ever matched 'probs', and `_histogram_from`
|
|
11
|
+
returned None for every job ever run.
|
|
12
|
+
|
|
13
|
+
The stand-ins mirror merlinquantum 0.4.1 exactly: `MeasurementStrategy.type` is
|
|
14
|
+
a `MeasurementKind` enum whose members are PROBABILITIES, MODE_EXPECTATIONS,
|
|
15
|
+
AMPLITUDES and PARTIAL (merlin/measurement/strategies.py:244).
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import enum
|
|
19
|
+
|
|
20
|
+
import pytest
|
|
21
|
+
|
|
22
|
+
from quapp_merlin.model.device.merlin_device import MerlinDevice
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MeasurementKind(enum.Enum):
|
|
26
|
+
PROBABILITIES = 'PROBABILITIES'
|
|
27
|
+
MODE_EXPECTATIONS = 'MODE_EXPECTATIONS'
|
|
28
|
+
AMPLITUDES = 'AMPLITUDES'
|
|
29
|
+
PARTIAL = 'PARTIAL'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class MeasurementStrategy:
|
|
33
|
+
"""No `.name` -- exactly like the real one. The kind lives in `.type`."""
|
|
34
|
+
|
|
35
|
+
def __init__(self, kind):
|
|
36
|
+
self.type = kind
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Module:
|
|
40
|
+
def __init__(self, children=None, **attributes):
|
|
41
|
+
self._children = children or {}
|
|
42
|
+
self.__dict__.update(attributes)
|
|
43
|
+
|
|
44
|
+
def modules(self):
|
|
45
|
+
yield self
|
|
46
|
+
for child in self._children.values():
|
|
47
|
+
yield from child.modules()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def read(model):
|
|
51
|
+
"""_measurement_name does not touch instance state, so call it unbound."""
|
|
52
|
+
return MerlinDevice._measurement_name(MerlinDevice, model)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@pytest.mark.parametrize('kind, expected', [
|
|
56
|
+
(MeasurementKind.PROBABILITIES, 'probs'),
|
|
57
|
+
(MeasurementKind.MODE_EXPECTATIONS, 'mode_expectations'),
|
|
58
|
+
(MeasurementKind.AMPLITUDES, 'amplitudes'),
|
|
59
|
+
(MeasurementKind.PARTIAL, 'partial'),
|
|
60
|
+
])
|
|
61
|
+
def test_every_kind_is_recognised(kind, expected):
|
|
62
|
+
model = Module(measurement_strategy=MeasurementStrategy(kind))
|
|
63
|
+
|
|
64
|
+
assert read(model) == expected
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_probabilities_is_what_enables_a_histogram():
|
|
68
|
+
"""The whole point: 'probs' is the only value _histogram_from accepts."""
|
|
69
|
+
model = Module(measurement_strategy=MeasurementStrategy(
|
|
70
|
+
MeasurementKind.PROBABILITIES))
|
|
71
|
+
|
|
72
|
+
assert read(model) == 'probs'
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_the_strategy_is_found_on_a_nested_layer():
|
|
76
|
+
"""simple() returns a SimpleSequential wrapper; the strategy sits on the
|
|
77
|
+
QuantumLayer inside it."""
|
|
78
|
+
layer = Module(measurement_strategy=MeasurementStrategy(
|
|
79
|
+
MeasurementKind.PROBABILITIES))
|
|
80
|
+
model = Module(children={'quantum_layer': layer})
|
|
81
|
+
|
|
82
|
+
assert read(model) == 'probs'
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_class_name_is_never_used_as_the_kind():
|
|
86
|
+
"""The regression: reading type(strategy).__name__ gave
|
|
87
|
+
'measurementstrategy', which matches no kind and silently killed the
|
|
88
|
+
histogram."""
|
|
89
|
+
model = Module(measurement_strategy=MeasurementStrategy(
|
|
90
|
+
MeasurementKind.PROBABILITIES))
|
|
91
|
+
|
|
92
|
+
assert read(model) != 'measurementstrategy'
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_no_strategy_at_all():
|
|
96
|
+
assert read(Module()) is None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_unknown_kind_degrades_instead_of_raising():
|
|
100
|
+
class Odd(enum.Enum):
|
|
101
|
+
SOMETHING_NEW = 'SOMETHING_NEW'
|
|
102
|
+
|
|
103
|
+
model = Module(measurement_strategy=MeasurementStrategy(Odd.SOMETHING_NEW))
|
|
104
|
+
|
|
105
|
+
assert read(model) == 'something_new'
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_a_bare_module_without_modules_still_works():
|
|
109
|
+
"""A handler may return the layer itself rather than a wrapper."""
|
|
110
|
+
|
|
111
|
+
class Bare:
|
|
112
|
+
measurement_strategy = MeasurementStrategy(MeasurementKind.AMPLITUDES)
|
|
113
|
+
|
|
114
|
+
assert read(Bare()) == 'amplitudes'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/component/backend/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/factory/merlin_device_factory.py
RENAMED
|
File without changes
|
{quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/factory/merlin_handler_factory.py
RENAMED
|
File without changes
|
{quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/factory/merlin_provider_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
{quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin/handler/invocation_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_merlin-0.0.1.dev1 → quapp_merlin-0.0.1.dev2}/quapp_merlin.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|