quapp-merlin 0.0.1.dev2__tar.gz → 0.0.1.dev4__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.dev2/quapp_merlin.egg-info → quapp_merlin-0.0.1.dev4}/PKG-INFO +3 -3
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/README.md +1 -1
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/pyproject.toml +3 -3
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/model/device/merlin_remote_device.py +9 -2
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4/quapp_merlin.egg-info}/PKG-INFO +3 -3
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin.egg-info/SOURCES.txt +1 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin.egg-info/requires.txt +1 -1
- quapp_merlin-0.0.1.dev4/tests/test_remote_eval_mode.py +106 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/LICENSE +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/component/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/component/backend/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/component/backend/merlin_invocation.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/data/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/data/merlin_job_spec.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/error_rules.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/factory/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/factory/merlin_device_factory.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/factory/merlin_handler_factory.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/factory/merlin_provider_factory.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/handler/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/handler/invocation_handler.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/model/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/model/device/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/model/device/merlin_device.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/model/device/merlin_local_device.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/model/provider/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/model/provider/quandela_merlin_provider.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/model/provider/quapp_merlin_provider.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/util/__init__.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/util/single_machine.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/util/tensors.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin.egg-info/dependency_links.txt +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin.egg-info/top_level.txt +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/setup.cfg +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/tests/test_error_logging.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/tests/test_error_rules.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/tests/test_job_spec.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/tests/test_lazy_sdk_imports.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/tests/test_measurement_kind.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/tests/test_single_machine.py +0 -0
- {quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/tests/test_tensors.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quapp-merlin
|
|
3
|
-
Version: 0.0.1.
|
|
3
|
+
Version: 0.0.1.dev4
|
|
4
4
|
Summary: Quapp common library supporting Quapp Platform for Quantum Computing
|
|
5
5
|
Author-email: "CITYNOW Co. Ltd. " <corp@citynow.vn>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
19
19
|
Requires-Python: <3.15,>=3.10
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: quapp-common==0.0.
|
|
22
|
+
Requires-Dist: quapp-common==0.0.16.dev2
|
|
23
23
|
Requires-Dist: merlinquantum==0.4.1
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: black; extra == "dev"
|
|
@@ -129,7 +129,7 @@ rather than silently sending the first rows as if they were everything.
|
|
|
129
129
|
| | |
|
|
130
130
|
|---|---|
|
|
131
131
|
| `merlinquantum` | `==0.4.1` -- 0.4 renamed the `QuantumLayer` arguments 0.3 used (`output_mapping_strategy` -> `measurement_strategy`, `ansatz` -> `builder`), so `>=` would break every handler |
|
|
132
|
-
| `quapp-common` | `==0.0.
|
|
132
|
+
| `quapp-common` | `==0.0.16.dev2` -- `Sdk.MERLIN` exists from 0.0.15.dev5 on; 0.0.15 stable lacks it, so a stable pin needs 0.0.16 or later |
|
|
133
133
|
| Python | `>=3.10` (merlinquantum), `<3.15` (perceval-quandela) |
|
|
134
134
|
|
|
135
135
|
`import quapp_merlin` needs none of torch, merlin or perceval: every SDK import
|
|
@@ -98,7 +98,7 @@ rather than silently sending the first rows as if they were everything.
|
|
|
98
98
|
| | |
|
|
99
99
|
|---|---|
|
|
100
100
|
| `merlinquantum` | `==0.4.1` -- 0.4 renamed the `QuantumLayer` arguments 0.3 used (`output_mapping_strategy` -> `measurement_strategy`, `ansatz` -> `builder`), so `>=` would break every handler |
|
|
101
|
-
| `quapp-common` | `==0.0.
|
|
101
|
+
| `quapp-common` | `==0.0.16.dev2` -- `Sdk.MERLIN` exists from 0.0.15.dev5 on; 0.0.15 stable lacks it, so a stable pin needs 0.0.16 or later |
|
|
102
102
|
| Python | `>=3.10` (merlinquantum), `<3.15` (perceval-quandela) |
|
|
103
103
|
|
|
104
104
|
`import quapp_merlin` needs none of torch, merlin or perceval: every SDK import
|
|
@@ -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.dev4"
|
|
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" }]
|
|
@@ -26,8 +26,8 @@ keywords = ["quapp", "quapp-merlin", "quantum", "photonic", "pytorch"]
|
|
|
26
26
|
# constraint is still enforced -- it is just satisfied by what the image
|
|
27
27
|
# installed first.
|
|
28
28
|
dependencies = [
|
|
29
|
-
# 0.0.15.dev5
|
|
30
|
-
"quapp-common==0.0.
|
|
29
|
+
# Sdk.MERLIN exists from 0.0.15.dev5; 0.0.15 stable lacks it.
|
|
30
|
+
"quapp-common==0.0.16.dev2",
|
|
31
31
|
"merlinquantum==0.4.1"
|
|
32
32
|
]
|
|
33
33
|
# merlinquantum requires >=3.10; perceval-quandela caps at <3.15.
|
|
@@ -92,9 +92,16 @@ class MerlinRemoteDevice(MerlinDevice):
|
|
|
92
92
|
f'Submitting to {self.device_specification} | batch={batch_size} '
|
|
93
93
|
f'| shots={shots} | timeout={TIMEOUT_SECONDS}s')
|
|
94
94
|
|
|
95
|
+
# forward_async refuses a module still in training mode -- "no gradient
|
|
96
|
+
# can flow through the QPU runs" (merlin_processor.py:1109, it reads
|
|
97
|
+
# module.training). A torch Module is training=True from the moment it
|
|
98
|
+
# is built, so a handler that never calls .eval() is rejected every
|
|
99
|
+
# single time. The local device already switched to eval before running
|
|
100
|
+
# inference; this one did not.
|
|
101
|
+
model = spec.model.eval()
|
|
102
|
+
|
|
95
103
|
started = time.perf_counter()
|
|
96
|
-
future = processor.forward_async(
|
|
97
|
-
nsample=shots)
|
|
104
|
+
future = processor.forward_async(model, spec.inputs, nsample=shots)
|
|
98
105
|
|
|
99
106
|
job_ids = list(getattr(future, 'job_ids', None) or [])
|
|
100
107
|
self.logger.info(f'Remote job submitted: job_ids={job_ids}')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quapp-merlin
|
|
3
|
-
Version: 0.0.1.
|
|
3
|
+
Version: 0.0.1.dev4
|
|
4
4
|
Summary: Quapp common library supporting Quapp Platform for Quantum Computing
|
|
5
5
|
Author-email: "CITYNOW Co. Ltd. " <corp@citynow.vn>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
19
19
|
Requires-Python: <3.15,>=3.10
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: quapp-common==0.0.
|
|
22
|
+
Requires-Dist: quapp-common==0.0.16.dev2
|
|
23
23
|
Requires-Dist: merlinquantum==0.4.1
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: black; extra == "dev"
|
|
@@ -129,7 +129,7 @@ rather than silently sending the first rows as if they were everything.
|
|
|
129
129
|
| | |
|
|
130
130
|
|---|---|
|
|
131
131
|
| `merlinquantum` | `==0.4.1` -- 0.4 renamed the `QuantumLayer` arguments 0.3 used (`output_mapping_strategy` -> `measurement_strategy`, `ansatz` -> `builder`), so `>=` would break every handler |
|
|
132
|
-
| `quapp-common` | `==0.0.
|
|
132
|
+
| `quapp-common` | `==0.0.16.dev2` -- `Sdk.MERLIN` exists from 0.0.15.dev5 on; 0.0.15 stable lacks it, so a stable pin needs 0.0.16 or later |
|
|
133
133
|
| Python | `>=3.10` (merlinquantum), `<3.15` (perceval-quandela) |
|
|
134
134
|
|
|
135
135
|
`import quapp_merlin` needs none of torch, merlin or perceval: every SDK import
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# test_remote_eval_mode.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
"""A model sent to the cloud has to be in eval mode.
|
|
6
|
+
|
|
7
|
+
MerlinProcessor refuses anything else::
|
|
8
|
+
|
|
9
|
+
if module.training:
|
|
10
|
+
raise RuntimeError(
|
|
11
|
+
"Backend quantum execution requires `.eval()` mode because no
|
|
12
|
+
gradient can flow through the QPU runs...")
|
|
13
|
+
|
|
14
|
+
-- merlinquantum 0.4.1, merlin/core/merlin_processor.py:1109.
|
|
15
|
+
|
|
16
|
+
A torch Module is ``training=True`` from the moment it is constructed, so a
|
|
17
|
+
handler that never calls ``.eval()`` -- which is every handler, because nothing
|
|
18
|
+
in the contract tells them to -- was rejected on every single remote job. The
|
|
19
|
+
local device already switched to eval before inference; the remote one did not.
|
|
20
|
+
|
|
21
|
+
Checked by reading the source rather than by running a model: importing merlin
|
|
22
|
+
would drag torch into the test suite, and the rule being enforced is "the call
|
|
23
|
+
is there, before the submit", which the AST answers exactly.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import ast
|
|
27
|
+
import pathlib
|
|
28
|
+
|
|
29
|
+
import pytest
|
|
30
|
+
|
|
31
|
+
REMOTE_DEVICE = (pathlib.Path(__file__).resolve().parent.parent
|
|
32
|
+
/ 'quapp_merlin' / 'model' / 'device'
|
|
33
|
+
/ 'merlin_remote_device.py')
|
|
34
|
+
LOCAL_DEVICE = (pathlib.Path(__file__).resolve().parent.parent
|
|
35
|
+
/ 'quapp_merlin' / 'model' / 'device'
|
|
36
|
+
/ 'merlin_local_device.py')
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def create_job(path):
|
|
40
|
+
tree = ast.parse(path.read_text(encoding='utf-8'), filename=str(path))
|
|
41
|
+
for node in ast.walk(tree):
|
|
42
|
+
if isinstance(node, ast.FunctionDef) and node.name == '_create_job':
|
|
43
|
+
return node
|
|
44
|
+
raise AssertionError(f'_create_job() not found in {path.name}')
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def call_lines(node, attribute) -> list:
|
|
48
|
+
"""Line numbers of every ``x.<attribute>(...)`` call inside a function."""
|
|
49
|
+
return sorted(inner.lineno for inner in ast.walk(node)
|
|
50
|
+
if isinstance(inner, ast.Call)
|
|
51
|
+
and getattr(inner.func, 'attr', None) == attribute)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_remote_switches_the_model_to_eval():
|
|
55
|
+
assert call_lines(create_job(REMOTE_DEVICE), 'eval'), (
|
|
56
|
+
'merlin_remote_device._create_job never calls .eval(); MerlinProcessor '
|
|
57
|
+
'rejects a module in training mode and every remote job fails')
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_eval_happens_before_the_submit():
|
|
61
|
+
"""Calling it after forward_async would be too late to matter."""
|
|
62
|
+
node = create_job(REMOTE_DEVICE)
|
|
63
|
+
|
|
64
|
+
eval_at = call_lines(node, 'eval')
|
|
65
|
+
submit_at = call_lines(node, 'forward_async')
|
|
66
|
+
|
|
67
|
+
assert submit_at, 'no forward_async call in _create_job'
|
|
68
|
+
assert min(eval_at) < min(submit_at), (
|
|
69
|
+
f'.eval() is at line {min(eval_at)} but forward_async is at '
|
|
70
|
+
f'{min(submit_at)} -- the model is still in training mode when it is '
|
|
71
|
+
f'submitted')
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_the_model_that_is_submitted_is_the_one_that_was_evalled():
|
|
75
|
+
"""`spec.model.eval()` returns the module, so the submit has to use that
|
|
76
|
+
return value -- passing spec.model again would work only by side effect and
|
|
77
|
+
would break the moment eval() stops returning self."""
|
|
78
|
+
node = create_job(REMOTE_DEVICE)
|
|
79
|
+
|
|
80
|
+
submitted = None
|
|
81
|
+
for inner in ast.walk(node):
|
|
82
|
+
if isinstance(inner, ast.Call) and \
|
|
83
|
+
getattr(inner.func, 'attr', None) == 'forward_async':
|
|
84
|
+
submitted = inner.args[0] if inner.args else None
|
|
85
|
+
|
|
86
|
+
assert isinstance(submitted, ast.Name), (
|
|
87
|
+
'forward_async should be handed the local name bound from '
|
|
88
|
+
'spec.model.eval(), not an attribute read straight off the spec')
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_local_device_also_evals_before_inference():
|
|
92
|
+
"""Same rule on the simulator path, where it was already right -- a
|
|
93
|
+
regression there would be just as invisible."""
|
|
94
|
+
assert call_lines(create_job(LOCAL_DEVICE), 'eval')
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@pytest.mark.parametrize('path', [REMOTE_DEVICE, LOCAL_DEVICE],
|
|
98
|
+
ids=lambda p: p.name)
|
|
99
|
+
def test_no_train_mode_on_the_inference_path(path):
|
|
100
|
+
"""`.train()` belongs to the training loop only. On a device that submits to
|
|
101
|
+
a QPU it would put the module straight back into the state MerLin refuses."""
|
|
102
|
+
node = create_job(path)
|
|
103
|
+
|
|
104
|
+
assert not call_lines(node, 'train'), (
|
|
105
|
+
f'{path.name}: _create_job calls .train(); the training loop is the '
|
|
106
|
+
f'only place that may do that')
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/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.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/factory/merlin_device_factory.py
RENAMED
|
File without changes
|
{quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/factory/merlin_handler_factory.py
RENAMED
|
File without changes
|
{quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/factory/merlin_provider_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
{quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/handler/invocation_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/model/device/merlin_device.py
RENAMED
|
File without changes
|
{quapp_merlin-0.0.1.dev2 → quapp_merlin-0.0.1.dev4}/quapp_merlin/model/device/merlin_local_device.py
RENAMED
|
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.dev2 → quapp_merlin-0.0.1.dev4}/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
|