pymodaq 4.1.5__py3-none-any.whl → 4.2.1__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.
Potentially problematic release.
This version of pymodaq might be problematic. Click here for more details.
- pymodaq/__init__.py +23 -4
- pymodaq/control_modules/daq_move.py +32 -73
- pymodaq/control_modules/daq_viewer.py +73 -98
- pymodaq/control_modules/daq_viewer_ui.py +2 -1
- pymodaq/control_modules/move_utility_classes.py +17 -7
- pymodaq/control_modules/utils.py +153 -5
- pymodaq/control_modules/viewer_utility_classes.py +31 -20
- pymodaq/dashboard.py +23 -5
- pymodaq/examples/tcp_client.py +97 -0
- pymodaq/extensions/__init__.py +4 -0
- pymodaq/extensions/bayesian/__init__.py +2 -0
- pymodaq/extensions/bayesian/bayesian_optimisation.py +673 -0
- pymodaq/extensions/bayesian/utils.py +403 -0
- pymodaq/extensions/daq_scan.py +4 -4
- pymodaq/extensions/daq_scan_ui.py +2 -1
- pymodaq/extensions/pid/pid_controller.py +12 -7
- pymodaq/extensions/pid/utils.py +9 -26
- pymodaq/extensions/utils.py +3 -0
- pymodaq/post_treatment/load_and_plot.py +42 -19
- pymodaq/resources/VERSION +1 -1
- pymodaq/resources/config_template.toml +9 -24
- pymodaq/resources/setup_plugin.py +1 -1
- pymodaq/utils/config.py +103 -5
- pymodaq/utils/daq_utils.py +35 -134
- pymodaq/utils/data.py +614 -95
- pymodaq/utils/enums.py +17 -1
- pymodaq/utils/factory.py +2 -2
- pymodaq/utils/gui_utils/custom_app.py +5 -2
- pymodaq/utils/gui_utils/dock.py +33 -4
- pymodaq/utils/gui_utils/utils.py +14 -1
- pymodaq/utils/h5modules/backends.py +9 -1
- pymodaq/utils/h5modules/data_saving.py +254 -57
- pymodaq/utils/h5modules/saving.py +1 -0
- pymodaq/utils/leco/__init__.py +25 -0
- pymodaq/utils/leco/daq_move_LECODirector.py +172 -0
- pymodaq/utils/leco/daq_xDviewer_LECODirector.py +170 -0
- pymodaq/utils/leco/desktop.ini +2 -0
- pymodaq/utils/leco/director_utils.py +58 -0
- pymodaq/utils/leco/leco_director.py +88 -0
- pymodaq/utils/leco/pymodaq_listener.py +279 -0
- pymodaq/utils/leco/utils.py +41 -0
- pymodaq/utils/managers/action_manager.py +20 -6
- pymodaq/utils/managers/parameter_manager.py +6 -4
- pymodaq/utils/managers/roi_manager.py +63 -54
- pymodaq/utils/math_utils.py +1 -1
- pymodaq/utils/plotting/data_viewers/__init__.py +3 -1
- pymodaq/utils/plotting/data_viewers/base.py +286 -0
- pymodaq/utils/plotting/data_viewers/viewer.py +29 -202
- pymodaq/utils/plotting/data_viewers/viewer0D.py +94 -47
- pymodaq/utils/plotting/data_viewers/viewer1D.py +341 -174
- pymodaq/utils/plotting/data_viewers/viewer1Dbasic.py +1 -1
- pymodaq/utils/plotting/data_viewers/viewer2D.py +271 -181
- pymodaq/utils/plotting/data_viewers/viewerND.py +26 -22
- pymodaq/utils/plotting/items/crosshair.py +3 -3
- pymodaq/utils/plotting/items/image.py +2 -1
- pymodaq/utils/plotting/plotter/plotter.py +94 -0
- pymodaq/utils/plotting/plotter/plotters/__init__.py +0 -0
- pymodaq/utils/plotting/plotter/plotters/matplotlib_plotters.py +134 -0
- pymodaq/utils/plotting/plotter/plotters/qt_plotters.py +78 -0
- pymodaq/utils/plotting/utils/axes_viewer.py +1 -1
- pymodaq/utils/plotting/utils/filter.py +194 -147
- pymodaq/utils/plotting/utils/lineout.py +13 -11
- pymodaq/utils/plotting/utils/plot_utils.py +89 -12
- pymodaq/utils/scanner/__init__.py +0 -3
- pymodaq/utils/scanner/scan_config.py +1 -9
- pymodaq/utils/scanner/scan_factory.py +10 -36
- pymodaq/utils/scanner/scanner.py +3 -2
- pymodaq/utils/scanner/scanners/_1d_scanners.py +7 -5
- pymodaq/utils/scanner/scanners/_2d_scanners.py +36 -49
- pymodaq/utils/scanner/scanners/sequential.py +10 -4
- pymodaq/utils/scanner/scanners/tabular.py +10 -5
- pymodaq/utils/slicing.py +1 -1
- pymodaq/utils/tcp_ip/serializer.py +38 -5
- pymodaq/utils/tcp_ip/tcp_server_client.py +25 -17
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/METADATA +4 -2
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/RECORD +79 -63
- pymodaq/resources/config_scan_template.toml +0 -42
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/WHEEL +0 -0
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/entry_points.txt +0 -0
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created the 31/08/2023
|
|
4
|
+
|
|
5
|
+
@author: Sebastien Weber
|
|
6
|
+
"""
|
|
7
|
+
from abc import ABC, abstractproperty, abstractmethod
|
|
8
|
+
from typing import List, TYPE_CHECKING, Union, Dict, Tuple, Iterable
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import importlib
|
|
11
|
+
import pkgutil
|
|
12
|
+
import inspect
|
|
13
|
+
import numpy as np
|
|
14
|
+
from qtpy import QtWidgets
|
|
15
|
+
import tempfile
|
|
16
|
+
from collections import namedtuple
|
|
17
|
+
|
|
18
|
+
from bayes_opt import BayesianOptimization
|
|
19
|
+
from bayes_opt import UtilityFunction
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
from pymodaq.utils.h5modules.saving import H5Saver
|
|
23
|
+
from pymodaq.utils.data import (DataToExport, DataActuator, DataToActuators, DataCalculated,
|
|
24
|
+
DataRaw, Axis)
|
|
25
|
+
from pymodaq.utils.managers.modules_manager import ModulesManager
|
|
26
|
+
from pymodaq.utils.daq_utils import find_dict_in_list_from_key_val, get_entrypoints
|
|
27
|
+
from pymodaq.utils.logger import set_logger, get_module_name
|
|
28
|
+
from pymodaq.utils.plotting.data_viewers.viewer import ViewersEnum
|
|
29
|
+
from pymodaq.utils.enums import BaseEnum
|
|
30
|
+
from pymodaq.utils.parameter import Parameter
|
|
31
|
+
from pymodaq.utils.config import BaseConfig
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if TYPE_CHECKING:
|
|
35
|
+
from pymodaq.extensions.bayesian.bayesian_optimisation import BayesianOptimisation
|
|
36
|
+
|
|
37
|
+
logger = set_logger(get_module_name(__file__))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class StopType(BaseEnum):
|
|
41
|
+
Predict = 0
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class UtilityKind(BaseEnum):
|
|
45
|
+
ucb = 'Upper Confidence Bound'
|
|
46
|
+
ei = 'Expected Improvement'
|
|
47
|
+
poi = 'Probability of Improvement'
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
UtilityParameters = namedtuple('UtilityParameters',
|
|
51
|
+
['kind', 'kappa', 'xi', 'kappa_decay', 'kappa_decay_delay'])
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
StoppingParameters = namedtuple('StoppingParameters',
|
|
55
|
+
['niter', 'stop_type', 'tolerance', 'npoints'])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class BayesianAlgorithm:
|
|
59
|
+
|
|
60
|
+
def __init__(self, ini_random: int, bounds: dict, **kwargs):
|
|
61
|
+
|
|
62
|
+
self._algo = BayesianOptimization(f=None,
|
|
63
|
+
pbounds=bounds,
|
|
64
|
+
**kwargs
|
|
65
|
+
)
|
|
66
|
+
self._next_point: np.ndarray = None
|
|
67
|
+
self._suggested_coordinates: List[np.ndarray] = []
|
|
68
|
+
self.ini_random_points = ini_random
|
|
69
|
+
self.kappa = 2.5
|
|
70
|
+
|
|
71
|
+
self._utility = UtilityFunction(kind="ucb", kappa=self.kappa, xi=0.0)
|
|
72
|
+
|
|
73
|
+
def set_utility_function(self, kind: str, **kwargs):
|
|
74
|
+
if kind in UtilityKind.names():
|
|
75
|
+
self._utility = UtilityFunction(kind, **kwargs)
|
|
76
|
+
|
|
77
|
+
def update_utility_function(self):
|
|
78
|
+
""" Update the parameters of the Utility function (kappa decay for instance)"""
|
|
79
|
+
self._utility.update_params()
|
|
80
|
+
self.kappa = self._utility.kappa
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def bounds(self) -> List[np.ndarray]:
|
|
84
|
+
return [bound for bound in self._algo._space.bounds]
|
|
85
|
+
|
|
86
|
+
@bounds.setter
|
|
87
|
+
def bounds(self, bounds: Union[Dict[str, Tuple[float, float]], Iterable[np.ndarray]]):
|
|
88
|
+
if isinstance(bounds, dict):
|
|
89
|
+
self._algo.set_bounds(bounds)
|
|
90
|
+
else:
|
|
91
|
+
self._algo.set_bounds(self._algo._space.array_to_params(np.array(bounds)))
|
|
92
|
+
|
|
93
|
+
def get_random_point(self) -> np.ndarray:
|
|
94
|
+
""" Get a random point coordinates in the defined bounds"""
|
|
95
|
+
point = []
|
|
96
|
+
for bound in self.bounds:
|
|
97
|
+
point.append((np.max(bound) - np.min(bound)) * np.random.random_sample() +
|
|
98
|
+
np.min(bound))
|
|
99
|
+
return np.array(point)
|
|
100
|
+
|
|
101
|
+
def ask(self) -> np.ndarray:
|
|
102
|
+
if self.ini_random_points > 0:
|
|
103
|
+
self.ini_random_points -= 1
|
|
104
|
+
self._next_point = self.get_random_point()
|
|
105
|
+
else:
|
|
106
|
+
self._next_point = self._algo.space.params_to_array(self._algo.suggest(self._utility))
|
|
107
|
+
self._suggested_coordinates.append(self._next_point)
|
|
108
|
+
return self._next_point
|
|
109
|
+
|
|
110
|
+
def tell(self, function_value: float):
|
|
111
|
+
self._algo.register(params=self._next_point, target=function_value)
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def best_fitness(self) -> float:
|
|
115
|
+
return self._algo.max['target']
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def best_individual(self) -> Union[np.ndarray, None]:
|
|
119
|
+
max_param = self._algo.max.get('params', None)
|
|
120
|
+
if max_param is None:
|
|
121
|
+
return None
|
|
122
|
+
return self._algo.space.params_to_array(max_param)
|
|
123
|
+
|
|
124
|
+
def stopping(self, ind_iter: int, stopping_parameters: StoppingParameters):
|
|
125
|
+
if ind_iter >= stopping_parameters.niter:
|
|
126
|
+
return True
|
|
127
|
+
if ind_iter > stopping_parameters.npoints and stopping_parameters.stop_type == 'Predict':
|
|
128
|
+
coordinates = np.array(self._suggested_coordinates[-stopping_parameters.npoints:]).T
|
|
129
|
+
return np.all(np.std(coordinates, axis=1)
|
|
130
|
+
< stopping_parameters.tolerance)
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
def _posterior(self, x_obs, y_obs, grid):
|
|
134
|
+
|
|
135
|
+
if len(x_obs.shape) == 1:
|
|
136
|
+
x_obs = x_obs.reshape(-1, 1)
|
|
137
|
+
y_obs = y_obs.reshape(-1, 1)
|
|
138
|
+
grid = grid.reshape(-1, 1)
|
|
139
|
+
|
|
140
|
+
self._algo._gp.fit(x_obs, y_obs)
|
|
141
|
+
|
|
142
|
+
mu, sigma = self._algo._gp.predict(grid, return_std=True)
|
|
143
|
+
return mu, sigma
|
|
144
|
+
|
|
145
|
+
def get_dwa_obervations(self, actuators_name):
|
|
146
|
+
try:
|
|
147
|
+
axes = [Axis(act, data=np.array([res['params'][act] for res in self._algo.res])) for
|
|
148
|
+
act in actuators_name]
|
|
149
|
+
data_arrays = [np.array([res['target'] for res in self._algo.res])]
|
|
150
|
+
|
|
151
|
+
return DataRaw('Observations', data=data_arrays, labels=actuators_name,
|
|
152
|
+
axes=axes)
|
|
153
|
+
|
|
154
|
+
except Exception as e:
|
|
155
|
+
pass
|
|
156
|
+
|
|
157
|
+
def get_1D_dwa_gp(self, x: np.ndarray, actuator_name: str):
|
|
158
|
+
""" Get Measurements and predictions as DataWithAxes
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
x: np.ndarray
|
|
163
|
+
linear grid to get the Bayesian Optimisation On
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
dwa_obervation = self.get_dwa_obervations([actuator_name])
|
|
167
|
+
|
|
168
|
+
mu, sigma = self._posterior(dwa_obervation.axes[0].get_data(),
|
|
169
|
+
dwa_obervation.data[0], x)
|
|
170
|
+
|
|
171
|
+
dwa_measured = DataCalculated('Measurements', data=[dwa_obervation.data[0]],
|
|
172
|
+
axes=[Axis('measured_axis',
|
|
173
|
+
data=dwa_obervation.axes[0].get_data())],
|
|
174
|
+
labels=['Sampled'])
|
|
175
|
+
dwa_prediction = DataCalculated('Prediction', data=[mu],
|
|
176
|
+
axes=[Axis('tested_pos', data=x)],
|
|
177
|
+
errors=[1.96 * sigma])
|
|
178
|
+
return dwa_measured, dwa_prediction
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class BayesianModelGeneric(ABC):
|
|
182
|
+
|
|
183
|
+
optimisation_algorithm: BayesianAlgorithm = BayesianAlgorithm
|
|
184
|
+
|
|
185
|
+
actuators_name: List[str] = []
|
|
186
|
+
detectors_name: List[str] = []
|
|
187
|
+
|
|
188
|
+
observables_dim: List[ViewersEnum] = []
|
|
189
|
+
|
|
190
|
+
params = [] # to be subclassed
|
|
191
|
+
|
|
192
|
+
def __init__(self, optimisation_controller: 'BayesianOptimisation'):
|
|
193
|
+
self.optimisation_controller = optimisation_controller # instance of the pid_controller using this model
|
|
194
|
+
self.modules_manager: ModulesManager = optimisation_controller.modules_manager
|
|
195
|
+
|
|
196
|
+
self.settings = self.optimisation_controller.settings.child('models', 'model_params') # set of parameters
|
|
197
|
+
self.check_modules(self.modules_manager)
|
|
198
|
+
|
|
199
|
+
def check_modules(self, modules_manager):
|
|
200
|
+
for act in self.actuators_name:
|
|
201
|
+
if act not in modules_manager.actuators_name:
|
|
202
|
+
logger.warning(f'The actuator {act} defined in the model is'
|
|
203
|
+
f' not present in the Dashboard')
|
|
204
|
+
return False
|
|
205
|
+
for det in self.detectors_name:
|
|
206
|
+
if det not in modules_manager.detectors_name:
|
|
207
|
+
logger.warning(f'The detector {det} defined in the model is'
|
|
208
|
+
f' not present in the Dashboard')
|
|
209
|
+
|
|
210
|
+
def update_detector_names(self):
|
|
211
|
+
names = self.optimisation_controller.settings.child(
|
|
212
|
+
'main_settings', 'detector_modules').value()['selected']
|
|
213
|
+
self.data_names = []
|
|
214
|
+
for name in names:
|
|
215
|
+
name = name.split('//')
|
|
216
|
+
self.data_names.append(name)
|
|
217
|
+
|
|
218
|
+
def update_settings(self, param: Parameter):
|
|
219
|
+
"""
|
|
220
|
+
Get a parameter instance whose value has been modified by a user on the UI
|
|
221
|
+
To be overwritten in child class
|
|
222
|
+
"""
|
|
223
|
+
...
|
|
224
|
+
|
|
225
|
+
def update_plots(self):
|
|
226
|
+
""" Called when updating the live plots """
|
|
227
|
+
pass
|
|
228
|
+
|
|
229
|
+
def ini_model_base(self):
|
|
230
|
+
self.modules_manager.selected_actuators_name = self.actuators_name
|
|
231
|
+
self.modules_manager.selected_detectors_name = self.detectors_name
|
|
232
|
+
|
|
233
|
+
self.ini_model()
|
|
234
|
+
|
|
235
|
+
def ini_model(self):
|
|
236
|
+
""" To be subclassed
|
|
237
|
+
|
|
238
|
+
Initialize whatever is needed by your custom model
|
|
239
|
+
"""
|
|
240
|
+
raise NotImplementedError
|
|
241
|
+
|
|
242
|
+
def runner_initialized(self):
|
|
243
|
+
""" To be subclassed
|
|
244
|
+
|
|
245
|
+
Initialize whatever is needed by your custom model after the optimization runner is
|
|
246
|
+
initialized
|
|
247
|
+
"""
|
|
248
|
+
pass
|
|
249
|
+
|
|
250
|
+
def convert_input(self, measurements: DataToExport) -> float:
|
|
251
|
+
"""
|
|
252
|
+
Convert the measurements in the units to be fed to the Optimisation Controller
|
|
253
|
+
Parameters
|
|
254
|
+
----------
|
|
255
|
+
measurements: DataToExport
|
|
256
|
+
data object exported from the detectors from which the model extract a float value
|
|
257
|
+
(fitness) to be fed to the algorithm
|
|
258
|
+
|
|
259
|
+
Returns
|
|
260
|
+
-------
|
|
261
|
+
float
|
|
262
|
+
|
|
263
|
+
"""
|
|
264
|
+
raise NotImplementedError
|
|
265
|
+
|
|
266
|
+
def convert_output(self, outputs: List[np.ndarray], best_individual=None) -> DataToActuators:
|
|
267
|
+
""" Convert the output of the Optimisation Controller in units to be fed into the actuators
|
|
268
|
+
Parameters
|
|
269
|
+
----------
|
|
270
|
+
outputs: list of numpy ndarray
|
|
271
|
+
output value from the controller from which the model extract a value of the same units as the actuators
|
|
272
|
+
best_individual: np.ndarray
|
|
273
|
+
the coordinates of the best individual so far
|
|
274
|
+
Returns
|
|
275
|
+
-------
|
|
276
|
+
DataToActuatorOpti: derived from DataToExport. Contains value to be fed to the actuators with a a mode
|
|
277
|
+
attribute, either 'rel' for relative or 'abs' for absolute.
|
|
278
|
+
|
|
279
|
+
"""
|
|
280
|
+
raise NotImplementedError
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class BayesianModelDefault(BayesianModelGeneric):
|
|
284
|
+
|
|
285
|
+
actuators_name: List[str] = [] # to be populated dynamically at instantiation
|
|
286
|
+
detectors_name: List[str] = [] # to be populated dynamically at instantiation
|
|
287
|
+
|
|
288
|
+
params = [{'title': 'Optimizing signal', 'name': 'optimizing_signal', 'type': 'group',
|
|
289
|
+
'children': [
|
|
290
|
+
{'title': 'Get data', 'name': 'data_probe', 'type': 'action'},
|
|
291
|
+
{'title': 'Optimize 0Ds:', 'name': 'optimize_0d', 'type': 'itemselect',
|
|
292
|
+
'checkbox': True},
|
|
293
|
+
]},]
|
|
294
|
+
|
|
295
|
+
def __init__(self, optimisation_controller: 'BayesianOptimisation'):
|
|
296
|
+
self.actuators_name = optimisation_controller.modules_manager.actuators_name
|
|
297
|
+
self.detectors_name = optimisation_controller.modules_manager.detectors_name
|
|
298
|
+
super().__init__(optimisation_controller)
|
|
299
|
+
|
|
300
|
+
self.settings.child('optimizing_signal', 'data_probe').sigActivated.connect(
|
|
301
|
+
self.optimize_from)
|
|
302
|
+
|
|
303
|
+
def ini_model(self):
|
|
304
|
+
pass
|
|
305
|
+
|
|
306
|
+
def optimize_from(self):
|
|
307
|
+
self.modules_manager.get_det_data_list()
|
|
308
|
+
data0D = self.modules_manager.settings['data_dimensions', 'det_data_list0D']
|
|
309
|
+
data0D['selected'] = data0D['all_items']
|
|
310
|
+
self.settings.child('optimizing_signal', 'optimize_0d').setValue(data0D)
|
|
311
|
+
|
|
312
|
+
def update_settings(self, param: Parameter):
|
|
313
|
+
pass
|
|
314
|
+
|
|
315
|
+
def convert_input(self, measurements: DataToExport) -> float:
|
|
316
|
+
""" Convert the measurements in the units to be fed to the Optimisation Controller
|
|
317
|
+
|
|
318
|
+
Parameters
|
|
319
|
+
----------
|
|
320
|
+
measurements: DataToExport
|
|
321
|
+
data object exported from the detectors from which the model extract a float value
|
|
322
|
+
(fitness) to be fed to the algorithm
|
|
323
|
+
|
|
324
|
+
Returns
|
|
325
|
+
-------
|
|
326
|
+
float
|
|
327
|
+
|
|
328
|
+
"""
|
|
329
|
+
data_name: str = self.settings['optimizing_signal', 'optimize_0d']['selected'][0]
|
|
330
|
+
origin, name = data_name.split('/')
|
|
331
|
+
return float(measurements.get_data_from_name_origin(name, origin).data[0][0])
|
|
332
|
+
|
|
333
|
+
def convert_output(self, outputs: List[np.ndarray], best_individual=None) -> DataToActuators:
|
|
334
|
+
""" Convert the output of the Optimisation Controller in units to be fed into the actuators
|
|
335
|
+
Parameters
|
|
336
|
+
----------
|
|
337
|
+
outputs: list of numpy ndarray
|
|
338
|
+
output value from the controller from which the model extract a value of the same units as the actuators
|
|
339
|
+
best_individual: np.ndarray
|
|
340
|
+
the coordinates of the best individual so far
|
|
341
|
+
|
|
342
|
+
Returns
|
|
343
|
+
-------
|
|
344
|
+
DataToActuators: derived from DataToExport. Contains value to be fed to the actuators
|
|
345
|
+
with a mode attribute, either 'rel' for relative or 'abs' for absolute.
|
|
346
|
+
|
|
347
|
+
"""
|
|
348
|
+
return DataToActuators('outputs', mode='abs',
|
|
349
|
+
data=[DataActuator(self.modules_manager.actuators_name[ind],
|
|
350
|
+
data=float(outputs[ind])) for ind in
|
|
351
|
+
range(len(outputs))])
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def get_bayesian_models(model_name=None):
|
|
355
|
+
"""
|
|
356
|
+
Get PID Models as a list to instantiate Control Actuators per degree of liberty in the model
|
|
357
|
+
|
|
358
|
+
Returns
|
|
359
|
+
-------
|
|
360
|
+
list: list of disct containting the name and python module of the found models
|
|
361
|
+
"""
|
|
362
|
+
models_import = []
|
|
363
|
+
discovered_models = get_entrypoints(group='pymodaq.models')
|
|
364
|
+
if len(discovered_models) > 0:
|
|
365
|
+
for pkg in discovered_models:
|
|
366
|
+
try:
|
|
367
|
+
module = importlib.import_module(pkg.value)
|
|
368
|
+
module_name = pkg.value
|
|
369
|
+
|
|
370
|
+
for mod in pkgutil.iter_modules([
|
|
371
|
+
str(Path(module.__file__).parent.joinpath('models'))]):
|
|
372
|
+
try:
|
|
373
|
+
model_module = importlib.import_module(f'{module_name}.models.{mod.name}',
|
|
374
|
+
module)
|
|
375
|
+
classes = inspect.getmembers(model_module, inspect.isclass)
|
|
376
|
+
for name, klass in classes:
|
|
377
|
+
if issubclass(klass, BayesianModelGeneric):
|
|
378
|
+
if find_dict_in_list_from_key_val(models_import, 'name', mod.name)\
|
|
379
|
+
is None:
|
|
380
|
+
models_import.append({'name': klass.__name__,
|
|
381
|
+
'module': model_module,
|
|
382
|
+
'class': klass})
|
|
383
|
+
|
|
384
|
+
except Exception as e:
|
|
385
|
+
logger.warning(str(e))
|
|
386
|
+
|
|
387
|
+
except Exception as e:
|
|
388
|
+
logger.warning(f'Impossible to import the {pkg.value} bayesian model: {str(e)}')
|
|
389
|
+
if find_dict_in_list_from_key_val(models_import, 'name', 'BayesianModelDefault') \
|
|
390
|
+
is None:
|
|
391
|
+
models_import.append({'name': 'BayesianModelDefault',
|
|
392
|
+
'module': inspect.getmodule(BayesianModelDefault),
|
|
393
|
+
'class': BayesianModelDefault})
|
|
394
|
+
if model_name is None:
|
|
395
|
+
return models_import
|
|
396
|
+
else:
|
|
397
|
+
return find_dict_in_list_from_key_val(models_import, 'name', model_name)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
class BayesianConfig(BaseConfig):
|
|
401
|
+
"""Main class to deal with configuration values for this plugin"""
|
|
402
|
+
config_template_path = None
|
|
403
|
+
config_name = f"bayesian_settings"
|
pymodaq/extensions/daq_scan.py
CHANGED
|
@@ -22,7 +22,7 @@ from pymodaq.utils import data as data_mod
|
|
|
22
22
|
from pymodaq.utils.logger import set_logger, get_module_name
|
|
23
23
|
from pymodaq.utils.config import Config, get_set_preset_path
|
|
24
24
|
from pymodaq.utils.parameter import ioxml
|
|
25
|
-
from pymodaq.utils.plotting.data_viewers
|
|
25
|
+
from pymodaq.utils.plotting.data_viewers import ViewersEnum
|
|
26
26
|
from pymodaq.utils.managers.parameter_manager import ParameterManager, Parameter, ParameterTree
|
|
27
27
|
|
|
28
28
|
from pymodaq.utils import exceptions
|
|
@@ -384,7 +384,8 @@ class DAQScan(QObject, ParameterManager):
|
|
|
384
384
|
|
|
385
385
|
def show_file_content(self):
|
|
386
386
|
try:
|
|
387
|
-
self.h5saver.init_file(addhoc_file_path=
|
|
387
|
+
self.h5saver.init_file(addhoc_file_path=
|
|
388
|
+
self.h5saver.settings.child('current_h5_file').value())
|
|
388
389
|
self.h5saver.show_file_content()
|
|
389
390
|
except Exception as e:
|
|
390
391
|
logger.exception(str(e))
|
|
@@ -1122,8 +1123,7 @@ class DAQScanAcquisition(QObject):
|
|
|
1122
1123
|
if self.Naverage > 1:
|
|
1123
1124
|
for nav_axis in nav_axes:
|
|
1124
1125
|
nav_axis.index += 1
|
|
1125
|
-
nav_axes.append(data_mod.Axis('Average', data=np.linspace(0, self.Naverage - 1,
|
|
1126
|
-
self.Naverage),
|
|
1126
|
+
nav_axes.append(data_mod.Axis('Average', data=np.linspace(0, self.Naverage - 1, self.Naverage),
|
|
1127
1127
|
index=0))
|
|
1128
1128
|
self.module_and_data_saver.add_nav_axes(nav_axes)
|
|
1129
1129
|
|
|
@@ -16,7 +16,8 @@ from pymodaq.utils.config import Config, get_set_preset_path
|
|
|
16
16
|
from pymodaq.utils.gui_utils import DockArea, Dock
|
|
17
17
|
from pymodaq.utils.gui_utils.widgets.spinbox import QSpinBox_ro
|
|
18
18
|
from pymodaq.utils.parameter.pymodaq_ptypes.led import QLED
|
|
19
|
-
from pymodaq.utils.plotting.data_viewers.viewer import ViewerDispatcher
|
|
19
|
+
from pymodaq.utils.plotting.data_viewers.viewer import ViewerDispatcher
|
|
20
|
+
from pymodaq.utils.plotting.data_viewers import ViewersEnum
|
|
20
21
|
from pymodaq.utils.daq_utils import ThreadCommand
|
|
21
22
|
|
|
22
23
|
if TYPE_CHECKING:
|
|
@@ -15,11 +15,11 @@ from pymodaq.utils.daq_utils import ThreadCommand, find_dict_in_list_from_key_va
|
|
|
15
15
|
from pymodaq.utils.managers.modules_manager import ModulesManager
|
|
16
16
|
from pymodaq.utils.plotting.data_viewers.viewer0D import Viewer0D
|
|
17
17
|
from pymodaq.utils.gui_utils.widgets import QLED
|
|
18
|
-
from pymodaq.extensions.pid.utils import
|
|
18
|
+
from pymodaq.extensions.pid.utils import get_models
|
|
19
19
|
from pymodaq.utils.gui_utils.dock import DockArea, Dock
|
|
20
20
|
from pymodaq.utils.gui_utils.custom_app import CustomApp
|
|
21
21
|
from pymodaq.utils.gui_utils.widgets.label import LabelWithFont
|
|
22
|
-
from pymodaq.utils.data import DataToExport, DataCalculated, DataActuator, DataRaw
|
|
22
|
+
from pymodaq.utils.data import DataToExport, DataCalculated, DataActuator, DataRaw, DataToActuators
|
|
23
23
|
from pymodaq.utils.config import Config
|
|
24
24
|
|
|
25
25
|
config = Config()
|
|
@@ -479,7 +479,7 @@ class PIDRunner(QObject):
|
|
|
479
479
|
data=[np.array([setpoints[ind]])])
|
|
480
480
|
for ind in range(Nsetpoints)])
|
|
481
481
|
self.outputs = [0. for _ in range(Nsetpoints)]
|
|
482
|
-
self.outputs_to_actuators =
|
|
482
|
+
self.outputs_to_actuators = DataToActuators('pid',
|
|
483
483
|
mode='rel',
|
|
484
484
|
data=[DataActuator(self.model_class.actuators_name[ind],
|
|
485
485
|
data=self.outputs[ind])
|
|
@@ -505,8 +505,13 @@ class PIDRunner(QObject):
|
|
|
505
505
|
# self.timeout_timer.timeout.connect(self.timeout)
|
|
506
506
|
#
|
|
507
507
|
def timerEvent(self, event):
|
|
508
|
-
|
|
509
|
-
|
|
508
|
+
outputs_dwa = self.outputs_to_actuators.merge_as_dwa('Data0D', name='outputs')
|
|
509
|
+
outputs_dwa.labels = self.modules_manager.selected_actuators_name
|
|
510
|
+
dte = DataToExport('toplot', data=[outputs_dwa])
|
|
511
|
+
|
|
512
|
+
inputs_dwa = self.inputs_from_dets.merge_as_dwa('Data0D', name='inputs')
|
|
513
|
+
inputs_dwa.labels = self.modules_manager.selected_actuators_name
|
|
514
|
+
dte.append(inputs_dwa)
|
|
510
515
|
self.pid_output_signal.emit(dte)
|
|
511
516
|
|
|
512
517
|
@Slot(ThreadCommand)
|
|
@@ -582,8 +587,8 @@ class PIDRunner(QObject):
|
|
|
582
587
|
self.outputs = [pid.setpoint for pid in self.pids]
|
|
583
588
|
|
|
584
589
|
dt = time.perf_counter() - self.current_time
|
|
585
|
-
self.outputs_to_actuators:
|
|
586
|
-
|
|
590
|
+
self.outputs_to_actuators: DataToActuators = (
|
|
591
|
+
self.model_class.convert_output(self.outputs, dt, stab=True))
|
|
587
592
|
|
|
588
593
|
if not self.paused:
|
|
589
594
|
self.modules_manager.move_actuators(self.outputs_to_actuators,
|
pymodaq/extensions/pid/utils.py
CHANGED
|
@@ -9,10 +9,12 @@ from pymodaq.utils.gui_utils.dock import DockArea
|
|
|
9
9
|
from pymodaq.utils.daq_utils import get_plugins
|
|
10
10
|
from pymodaq.utils.logger import get_module_name, set_logger
|
|
11
11
|
from pymodaq.utils.daq_utils import find_dict_in_list_from_key_val, get_entrypoints
|
|
12
|
-
from pymodaq.utils.data import DataToExport, DataCalculated, DataActuator
|
|
12
|
+
from pymodaq.utils.data import DataToExport, DataCalculated, DataActuator, DataToActuators
|
|
13
|
+
from pymodaq.utils.messenger import deprecation_msg
|
|
13
14
|
|
|
14
15
|
logger = set_logger(get_module_name(__file__))
|
|
15
16
|
|
|
17
|
+
|
|
16
18
|
DAQ_Move_Stage_type = get_plugins('daq_move')
|
|
17
19
|
DAQ_0DViewer_Det_types = get_plugins('daq_0Dviewer')
|
|
18
20
|
DAQ_1DViewer_Det_types = get_plugins('daq_1Dviewer')
|
|
@@ -20,31 +22,11 @@ DAQ_2DViewer_Det_types = get_plugins('daq_2Dviewer')
|
|
|
20
22
|
DAQ_NDViewer_Det_types = get_plugins('daq_NDviewer')
|
|
21
23
|
|
|
22
24
|
|
|
23
|
-
class DataToActuatorPID(
|
|
24
|
-
""" Particular case of a DataToExport adding one named parameter to indicate what kind of change should be applied
|
|
25
|
-
to the actuators, absolute or relative
|
|
26
|
-
|
|
27
|
-
Attributes
|
|
28
|
-
----------
|
|
29
|
-
mode: str
|
|
30
|
-
Adds an attribute called mode holding a string describing the type of change: relative or absolute
|
|
31
|
-
|
|
32
|
-
Parameters
|
|
33
|
-
---------
|
|
34
|
-
mode: str
|
|
35
|
-
either 'rel' or 'abs' for a relative or absolute change of the actuator's values
|
|
36
|
-
"""
|
|
25
|
+
class DataToActuatorPID(DataToActuators):
|
|
37
26
|
|
|
38
|
-
def __init__(self, *args,
|
|
39
|
-
if mode not in ['rel', 'abs']:
|
|
40
|
-
warnings.warn('Incorrect mode for the actuators, switching to default relative mode: rel')
|
|
41
|
-
mode = 'rel'
|
|
42
|
-
kwargs.update({'mode': mode})
|
|
27
|
+
def __init__(self, *args, **kwargs):
|
|
43
28
|
super().__init__(*args, **kwargs)
|
|
44
|
-
|
|
45
|
-
def __repr__(self):
|
|
46
|
-
return f'{super().__repr__()}: {self.mode}'
|
|
47
|
-
|
|
29
|
+
deprecation_msg('DataToActuatorPID object is deprecated use: pymodaq.utils.data:DataToActuators')
|
|
48
30
|
|
|
49
31
|
class PIDModelGeneric:
|
|
50
32
|
limits = dict(max=dict(state=False, value=1),
|
|
@@ -130,7 +112,7 @@ class PIDModelGeneric:
|
|
|
130
112
|
"""
|
|
131
113
|
raise NotImplementedError
|
|
132
114
|
|
|
133
|
-
def convert_output(self, outputs: List[float], dt, stab=True) ->
|
|
115
|
+
def convert_output(self, outputs: List[float], dt, stab=True) -> DataToActuators:
|
|
134
116
|
"""
|
|
135
117
|
Convert the output of the PID in units to be fed into the actuator
|
|
136
118
|
Parameters
|
|
@@ -143,7 +125,7 @@ class PIDModelGeneric:
|
|
|
143
125
|
|
|
144
126
|
"""
|
|
145
127
|
self.curr_output = outputs
|
|
146
|
-
return
|
|
128
|
+
return DataToActuators('pid', mode='rel',
|
|
147
129
|
data=[DataActuator(self.actuators_name[ind], data=outputs[ind])
|
|
148
130
|
for ind in range(len(outputs))])
|
|
149
131
|
|
|
@@ -195,6 +177,7 @@ def get_models(model_name=None):
|
|
|
195
177
|
from pymodaq.extensions.pid.utils import PIDModelGeneric
|
|
196
178
|
models_import = []
|
|
197
179
|
discovered_models = get_entrypoints(group='pymodaq.pid_models')
|
|
180
|
+
discovered_models = get_entrypoints(group='pymodaq.models')
|
|
198
181
|
if len(discovered_models) > 0:
|
|
199
182
|
for pkg in discovered_models:
|
|
200
183
|
try:
|
pymodaq/extensions/utils.py
CHANGED
|
@@ -7,9 +7,11 @@ Created the 27/10/2022
|
|
|
7
7
|
import importlib
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
import pkgutil
|
|
10
|
+
import warnings
|
|
10
11
|
|
|
11
12
|
from pymodaq.utils import logger as logger_module
|
|
12
13
|
from pymodaq.utils.daq_utils import get_entrypoints
|
|
14
|
+
from pymodaq.utils.data import DataToExport
|
|
13
15
|
logger = logger_module.set_logger(logger_module.get_module_name(__file__))
|
|
14
16
|
|
|
15
17
|
|
|
@@ -52,3 +54,4 @@ def get_extensions():
|
|
|
52
54
|
logger.warning(f'Impossible to import the {pkg.value}.extensions.{mod} extension: {str(e)}')
|
|
53
55
|
|
|
54
56
|
return extension_import
|
|
57
|
+
|