sqil-core 0.1.0__py3-none-any.whl → 1.1.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.
- sqil_core/__init__.py +1 -0
- sqil_core/config_log.py +42 -0
- sqil_core/experiment/__init__.py +11 -0
- sqil_core/experiment/_analysis.py +125 -0
- sqil_core/experiment/_events.py +25 -0
- sqil_core/experiment/_experiment.py +553 -0
- sqil_core/experiment/data/plottr.py +778 -0
- sqil_core/experiment/helpers/_function_override_handler.py +111 -0
- sqil_core/experiment/helpers/_labone_wrappers.py +12 -0
- sqil_core/experiment/instruments/__init__.py +2 -0
- sqil_core/experiment/instruments/_instrument.py +190 -0
- sqil_core/experiment/instruments/drivers/SignalCore_SC5511A.py +515 -0
- sqil_core/experiment/instruments/local_oscillator.py +205 -0
- sqil_core/experiment/instruments/server.py +175 -0
- sqil_core/experiment/instruments/setup.yaml +21 -0
- sqil_core/experiment/instruments/zurich_instruments.py +55 -0
- sqil_core/fit/__init__.py +23 -0
- sqil_core/fit/_core.py +179 -31
- sqil_core/fit/_fit.py +544 -94
- sqil_core/fit/_guess.py +304 -0
- sqil_core/fit/_models.py +50 -1
- sqil_core/fit/_quality.py +266 -0
- sqil_core/resonator/__init__.py +2 -0
- sqil_core/resonator/_resonator.py +256 -74
- sqil_core/utils/__init__.py +40 -13
- sqil_core/utils/_analysis.py +226 -0
- sqil_core/utils/_const.py +83 -18
- sqil_core/utils/_formatter.py +127 -55
- sqil_core/utils/_plot.py +272 -6
- sqil_core/utils/_read.py +178 -95
- sqil_core/utils/_utils.py +147 -0
- {sqil_core-0.1.0.dist-info → sqil_core-1.1.0.dist-info}/METADATA +9 -1
- sqil_core-1.1.0.dist-info/RECORD +36 -0
- {sqil_core-0.1.0.dist-info → sqil_core-1.1.0.dist-info}/WHEEL +1 -1
- sqil_core-0.1.0.dist-info/RECORD +0 -19
- {sqil_core-0.1.0.dist-info → sqil_core-1.1.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from contextlib import contextmanager
|
3
|
+
from types import MethodType
|
4
|
+
from typing import Callable
|
5
|
+
|
6
|
+
|
7
|
+
class FunctionOverrideHandler(ABC):
|
8
|
+
"""
|
9
|
+
A base class that allows functions to be overridden, restored, and temporarily replaced.
|
10
|
+
|
11
|
+
Attributes
|
12
|
+
----------
|
13
|
+
_default_functions : dict
|
14
|
+
A dictionary storing the default functions of the object.
|
15
|
+
_functions : dict
|
16
|
+
A dictionary storing the current functions, which may include overridden versions.
|
17
|
+
"""
|
18
|
+
|
19
|
+
def __init__(self):
|
20
|
+
"""
|
21
|
+
Initializes the handler with empty dictionaries for default and overridden functions.
|
22
|
+
"""
|
23
|
+
self._default_functions = {}
|
24
|
+
self._functions = {}
|
25
|
+
|
26
|
+
def override_function(self, func_name, new_func):
|
27
|
+
"""
|
28
|
+
Overrides a function with a new implementation.
|
29
|
+
|
30
|
+
Parameters
|
31
|
+
----------
|
32
|
+
func_name : str
|
33
|
+
The name of the function to override.
|
34
|
+
new_func : function
|
35
|
+
The new function implementation.
|
36
|
+
"""
|
37
|
+
if func_name in self._functions:
|
38
|
+
self._functions[func_name] = MethodType(new_func, self)
|
39
|
+
else:
|
40
|
+
raise AttributeError(f"Function '{func_name}' not found in the object.")
|
41
|
+
|
42
|
+
def restore_function(self, func_name):
|
43
|
+
"""
|
44
|
+
Restores a function to its default implementation.
|
45
|
+
|
46
|
+
Parameters
|
47
|
+
----------
|
48
|
+
func_name : str
|
49
|
+
The name of the function to restore.
|
50
|
+
"""
|
51
|
+
if func_name in self._default_functions:
|
52
|
+
self._functions[func_name] = self._default_functions[func_name]
|
53
|
+
else:
|
54
|
+
raise AttributeError(
|
55
|
+
f"Default for function '{func_name}' not found in the object."
|
56
|
+
)
|
57
|
+
|
58
|
+
@contextmanager
|
59
|
+
def temporary_override(self, func_name, temp_func):
|
60
|
+
"""
|
61
|
+
Temporarily overrides a function within a context manager.
|
62
|
+
|
63
|
+
Parameters
|
64
|
+
----------
|
65
|
+
func_name : str
|
66
|
+
The name of the function to override temporarily.
|
67
|
+
temp_func : function
|
68
|
+
The temporary function implementation.
|
69
|
+
|
70
|
+
Yields
|
71
|
+
------
|
72
|
+
None
|
73
|
+
"""
|
74
|
+
if func_name not in self._functions:
|
75
|
+
raise AttributeError(f"Function '{func_name}' not found in the object.")
|
76
|
+
|
77
|
+
original_func = self._functions[func_name]
|
78
|
+
try:
|
79
|
+
self._functions[func_name] = MethodType(temp_func, self)
|
80
|
+
yield
|
81
|
+
finally:
|
82
|
+
self._functions[func_name] = original_func # Restore the original
|
83
|
+
|
84
|
+
def restore_all_functions(self):
|
85
|
+
"""
|
86
|
+
Restores all overridden functions to their default implementations.
|
87
|
+
"""
|
88
|
+
self._functions = self._default_functions.copy()
|
89
|
+
|
90
|
+
def call(self, func_name, *args, **kwargs):
|
91
|
+
"""
|
92
|
+
Calls a function by its name, passing any provided arguments.
|
93
|
+
|
94
|
+
Parameters
|
95
|
+
----------
|
96
|
+
func_name : str
|
97
|
+
The name of the function to call.
|
98
|
+
*args : tuple
|
99
|
+
Positional arguments to pass to the function.
|
100
|
+
**kwargs : dict
|
101
|
+
Keyword arguments to pass to the function.
|
102
|
+
|
103
|
+
Returns
|
104
|
+
-------
|
105
|
+
Any
|
106
|
+
The return value of the called function.
|
107
|
+
"""
|
108
|
+
if func_name in self._functions:
|
109
|
+
return self._functions[func_name](*args, **kwargs)
|
110
|
+
else:
|
111
|
+
raise AttributeError(f"Function '{func_name}' not found in Instrument.")
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import json
|
2
|
+
|
3
|
+
from laboneq import serializers
|
4
|
+
|
5
|
+
|
6
|
+
def w_save(obj, path):
|
7
|
+
serializers.save(obj, path)
|
8
|
+
# Re-open the file, read the content, and re-save it pretty-printed
|
9
|
+
with open(path, "r", encoding="utf-8") as f:
|
10
|
+
data = json.load(f)
|
11
|
+
with open(path, "w", encoding="utf-8") as f:
|
12
|
+
json.dump(data, f, indent=4, ensure_ascii=False)
|
@@ -0,0 +1,190 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
|
3
|
+
import Pyro5.server
|
4
|
+
from blinker import NamedSignal
|
5
|
+
|
6
|
+
from sqil_core.config_log import logger
|
7
|
+
from sqil_core.experiment._events import (
|
8
|
+
after_experiment,
|
9
|
+
after_sequence,
|
10
|
+
before_experiment,
|
11
|
+
before_sequence,
|
12
|
+
one_time_listener,
|
13
|
+
)
|
14
|
+
from sqil_core.experiment.helpers._function_override_handler import (
|
15
|
+
FunctionOverrideHandler,
|
16
|
+
)
|
17
|
+
|
18
|
+
|
19
|
+
@Pyro5.server.expose
|
20
|
+
class Instrument(FunctionOverrideHandler, ABC):
|
21
|
+
"""
|
22
|
+
Base class for instruments with configurable behavior.
|
23
|
+
|
24
|
+
Supports overriding `connect`, `setup`, and `disconnect` methods
|
25
|
+
via a configuration dictionary.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self, id: str, config: dict):
|
29
|
+
"""
|
30
|
+
Initializes the instrument with an ID and configuration.
|
31
|
+
|
32
|
+
If `connect`, `setup`, or `disconnect` are provided in `config`,
|
33
|
+
they override the default implementations.
|
34
|
+
"""
|
35
|
+
super().__init__()
|
36
|
+
|
37
|
+
self._id = id
|
38
|
+
self._type = config.get("type", "")
|
39
|
+
self._model = config.get("model", "")
|
40
|
+
self._name = config.get("name", "")
|
41
|
+
self._address = config.get("address", "")
|
42
|
+
self._variables = config.get("variables", {})
|
43
|
+
self._config = config
|
44
|
+
self._device = None
|
45
|
+
|
46
|
+
self._default_functions = {
|
47
|
+
"connect": self._default_connect,
|
48
|
+
"setup": self._default_setup,
|
49
|
+
"disconnect": self._default_disconnect,
|
50
|
+
"on_before_experiment": self._default_on_before_experiment,
|
51
|
+
"on_after_experiment": self._default_on_after_experiment,
|
52
|
+
"on_before_sequence": self._default_on_before_sequence,
|
53
|
+
"on_after_sequence": self._default_on_after_sequence,
|
54
|
+
}
|
55
|
+
self._functions = self._default_functions.copy()
|
56
|
+
|
57
|
+
# Override functions if provided in config
|
58
|
+
for method_name in self._default_functions:
|
59
|
+
if method := config.get(method_name):
|
60
|
+
self.override_function(method_name, method)
|
61
|
+
|
62
|
+
self._default_functions = self._functions.copy()
|
63
|
+
self._device = self.connect() # Auto-connect on instantiation
|
64
|
+
|
65
|
+
# Subscribe to events
|
66
|
+
self._subscribe_to_events()
|
67
|
+
|
68
|
+
def _subscribe_to_events(self):
|
69
|
+
before_experiment.connect(
|
70
|
+
lambda *a, **kw: self.call("on_before_experiment", *a, **kw), weak=False
|
71
|
+
)
|
72
|
+
before_sequence.connect(
|
73
|
+
lambda *a, **kw: self.call("on_before_sequence", *a, **kw), weak=False
|
74
|
+
)
|
75
|
+
after_sequence.connect(
|
76
|
+
lambda *a, **kw: self.call("on_after_sequence", *a, **kw), weak=False
|
77
|
+
)
|
78
|
+
after_experiment.connect(
|
79
|
+
lambda *a, **kw: self.call("on_after_experiment", *a, **kw), weak=False
|
80
|
+
)
|
81
|
+
|
82
|
+
def connect(self, *args, **kwargs):
|
83
|
+
"""Calls the overridden or default `connect` method."""
|
84
|
+
return self.call("connect", *args, **kwargs)
|
85
|
+
|
86
|
+
@abstractmethod
|
87
|
+
def _default_connect(self, *args, **kwargs):
|
88
|
+
"""Default `connect` implementation (must be overridden)."""
|
89
|
+
pass
|
90
|
+
|
91
|
+
def setup(self, *args, **kwargs):
|
92
|
+
"""Calls the overridden or default `setup` method."""
|
93
|
+
return self.call("setup", *args, **kwargs)
|
94
|
+
|
95
|
+
@abstractmethod
|
96
|
+
def _default_setup(self, *args, **kwargs):
|
97
|
+
"""Default `setup` implementation (must be overridden)."""
|
98
|
+
pass
|
99
|
+
|
100
|
+
def disconnect(self, *args, **kwargs):
|
101
|
+
"""Calls the overridden or default `disconnect` method."""
|
102
|
+
return self.call("disconnect", *args, **kwargs)
|
103
|
+
|
104
|
+
@abstractmethod
|
105
|
+
def _default_disconnect(self, *args, **kwargs):
|
106
|
+
pass
|
107
|
+
|
108
|
+
def on_before_experiment(self, *args, **kwargs):
|
109
|
+
"""Calls the overridden or default `on_before_experiment` method."""
|
110
|
+
return self.call("on_before_experiment", *args, **kwargs)
|
111
|
+
|
112
|
+
def _default_on_before_experiment(self, *args, **kwargs):
|
113
|
+
pass
|
114
|
+
|
115
|
+
def on_before_sequence(self, *args, **kwargs):
|
116
|
+
"""Calls the overridden or default `on_before_sequence` method."""
|
117
|
+
return self.call("on_before_sequence", *args, **kwargs)
|
118
|
+
|
119
|
+
def _default_on_before_sequence(self, *args, **kwargs):
|
120
|
+
pass
|
121
|
+
|
122
|
+
def on_after_experiment(self, *args, **kwargs):
|
123
|
+
"""Calls the overridden or default `on_after_experiment` method."""
|
124
|
+
return self.call("on_after_experiment", *args, **kwargs)
|
125
|
+
|
126
|
+
def _default_on_after_experiment(self, *args, **kwargs):
|
127
|
+
pass
|
128
|
+
|
129
|
+
def on_after_sequence(self, *args, **kwargs):
|
130
|
+
"""Calls the overridden or default `on_after_sequence` method."""
|
131
|
+
return self.call("on_after_sequence", *args, **kwargs)
|
132
|
+
|
133
|
+
def _default_on_after_sequence(self, *args, **kwargs):
|
134
|
+
pass
|
135
|
+
|
136
|
+
def __getattr__(self, name):
|
137
|
+
"""
|
138
|
+
Dynamically expose all attributes to Pyro server.
|
139
|
+
"""
|
140
|
+
if name in self.__dict__:
|
141
|
+
return self.__dict__[name]
|
142
|
+
raise AttributeError(
|
143
|
+
f"'{self.__class__.__name__}' object has no attribute '{name}'"
|
144
|
+
)
|
145
|
+
|
146
|
+
def get_variable(self, key, *args, **kwargs):
|
147
|
+
var = self._variables.get(key, None)
|
148
|
+
if callable(var):
|
149
|
+
var = var(*args, **kwargs)
|
150
|
+
return var
|
151
|
+
|
152
|
+
@property
|
153
|
+
def id(self):
|
154
|
+
"""Instrument ID (read-only)."""
|
155
|
+
return self._id
|
156
|
+
|
157
|
+
@property
|
158
|
+
def type(self):
|
159
|
+
"""Instrument type (read-only)."""
|
160
|
+
return self._type
|
161
|
+
|
162
|
+
@property
|
163
|
+
def model(self):
|
164
|
+
"""Instrument model (read-only)."""
|
165
|
+
return self._model
|
166
|
+
|
167
|
+
@property
|
168
|
+
def name(self):
|
169
|
+
"""Instrument name (read-only)."""
|
170
|
+
return self._name
|
171
|
+
|
172
|
+
@property
|
173
|
+
def address(self):
|
174
|
+
"""Instrument address (read-only)."""
|
175
|
+
return self._address
|
176
|
+
|
177
|
+
@property
|
178
|
+
def variables(self):
|
179
|
+
"""Instrument variables (read-only)."""
|
180
|
+
return self._variables
|
181
|
+
|
182
|
+
@property
|
183
|
+
def config(self):
|
184
|
+
"""Instrument configuration dictionary (read-only)."""
|
185
|
+
return self._config
|
186
|
+
|
187
|
+
@property
|
188
|
+
def device(self):
|
189
|
+
"""Raw instrument instance (read-only)."""
|
190
|
+
return self._device
|