pymodaq 5.1.6__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.
Files changed (154) hide show
  1. pymodaq/__init__.py +98 -0
  2. pymodaq/control_modules/__init__.py +1 -0
  3. pymodaq/control_modules/daq_move.py +1238 -0
  4. pymodaq/control_modules/daq_move_ui/__init__.py +0 -0
  5. pymodaq/control_modules/daq_move_ui/factory.py +48 -0
  6. pymodaq/control_modules/daq_move_ui/ui_base.py +359 -0
  7. pymodaq/control_modules/daq_move_ui/uis/__init__.py +0 -0
  8. pymodaq/control_modules/daq_move_ui/uis/binary.py +139 -0
  9. pymodaq/control_modules/daq_move_ui/uis/original.py +120 -0
  10. pymodaq/control_modules/daq_move_ui/uis/relative.py +124 -0
  11. pymodaq/control_modules/daq_move_ui/uis/simple.py +126 -0
  12. pymodaq/control_modules/daq_viewer.py +1517 -0
  13. pymodaq/control_modules/daq_viewer_ui.py +407 -0
  14. pymodaq/control_modules/mocks.py +57 -0
  15. pymodaq/control_modules/move_utility_classes.py +1141 -0
  16. pymodaq/control_modules/thread_commands.py +137 -0
  17. pymodaq/control_modules/ui_utils.py +72 -0
  18. pymodaq/control_modules/utils.py +591 -0
  19. pymodaq/control_modules/viewer_utility_classes.py +670 -0
  20. pymodaq/daq_utils/__init__.py +0 -0
  21. pymodaq/daq_utils/daq_utils.py +6 -0
  22. pymodaq/dashboard.py +2396 -0
  23. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.aliases +3 -0
  24. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvlps +3 -0
  25. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvproj +32 -0
  26. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.vi +0 -0
  27. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_1Dgaussian.vi +0 -0
  28. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_2Dgaussian.vi +0 -0
  29. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_cmd.vi +0 -0
  30. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_float.vi +0 -0
  31. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_int.vi +0 -0
  32. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_data.vi +0 -0
  33. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_int.vi +0 -0
  34. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_scalar.vi +0 -0
  35. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_string.vi +0 -0
  36. pymodaq/examples/Labview_TCP_Client/client_state.ctl +0 -0
  37. pymodaq/examples/Labview_TCP_Client/cmd_types.ctl +0 -0
  38. pymodaq/examples/__init__.py +0 -0
  39. pymodaq/examples/function_plotter.py +160 -0
  40. pymodaq/examples/nonlinearscanner.py +126 -0
  41. pymodaq/examples/qt_less_standalone_module.py +165 -0
  42. pymodaq/examples/tcp_client.py +97 -0
  43. pymodaq/extensions/__init__.py +25 -0
  44. pymodaq/extensions/adaptive/__init__.py +2 -0
  45. pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
  46. pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
  47. pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
  48. pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
  49. pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
  50. pymodaq/extensions/adaptive/utils.py +123 -0
  51. pymodaq/extensions/bayesian/__init__.py +2 -0
  52. pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
  53. pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
  54. pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
  55. pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
  56. pymodaq/extensions/bayesian/utils.py +180 -0
  57. pymodaq/extensions/console.py +73 -0
  58. pymodaq/extensions/daq_logger/__init__.py +1 -0
  59. pymodaq/extensions/daq_logger/abstract.py +52 -0
  60. pymodaq/extensions/daq_logger/daq_logger.py +519 -0
  61. pymodaq/extensions/daq_logger/db/__init__.py +0 -0
  62. pymodaq/extensions/daq_logger/db/db_logger.py +300 -0
  63. pymodaq/extensions/daq_logger/db/db_logger_models.py +100 -0
  64. pymodaq/extensions/daq_logger/h5logging.py +84 -0
  65. pymodaq/extensions/daq_scan.py +1218 -0
  66. pymodaq/extensions/daq_scan_ui.py +241 -0
  67. pymodaq/extensions/data_mixer/__init__.py +0 -0
  68. pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
  69. pymodaq/extensions/data_mixer/data_mixer.py +262 -0
  70. pymodaq/extensions/data_mixer/model.py +108 -0
  71. pymodaq/extensions/data_mixer/models/__init__.py +0 -0
  72. pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
  73. pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
  74. pymodaq/extensions/data_mixer/parser.py +53 -0
  75. pymodaq/extensions/data_mixer/utils.py +23 -0
  76. pymodaq/extensions/h5browser.py +9 -0
  77. pymodaq/extensions/optimizers_base/__init__.py +0 -0
  78. pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
  79. pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
  80. pymodaq/extensions/optimizers_base/utils.py +427 -0
  81. pymodaq/extensions/pid/__init__.py +16 -0
  82. pymodaq/extensions/pid/actuator_controller.py +14 -0
  83. pymodaq/extensions/pid/daq_move_PID.py +154 -0
  84. pymodaq/extensions/pid/pid_controller.py +1016 -0
  85. pymodaq/extensions/pid/utils.py +189 -0
  86. pymodaq/extensions/utils.py +111 -0
  87. pymodaq/icon.ico +0 -0
  88. pymodaq/post_treatment/__init__.py +6 -0
  89. pymodaq/post_treatment/load_and_plot.py +352 -0
  90. pymodaq/resources/__init__.py +0 -0
  91. pymodaq/resources/config_template.toml +57 -0
  92. pymodaq/resources/preset_default.xml +1 -0
  93. pymodaq/resources/setup_plugin.py +73 -0
  94. pymodaq/splash.png +0 -0
  95. pymodaq/utils/__init__.py +0 -0
  96. pymodaq/utils/array_manipulation.py +6 -0
  97. pymodaq/utils/calibration_camera.py +180 -0
  98. pymodaq/utils/chrono_timer.py +203 -0
  99. pymodaq/utils/config.py +53 -0
  100. pymodaq/utils/conftests.py +5 -0
  101. pymodaq/utils/daq_utils.py +158 -0
  102. pymodaq/utils/data.py +128 -0
  103. pymodaq/utils/enums.py +6 -0
  104. pymodaq/utils/exceptions.py +38 -0
  105. pymodaq/utils/gui_utils/__init__.py +10 -0
  106. pymodaq/utils/gui_utils/loader_utils.py +75 -0
  107. pymodaq/utils/gui_utils/utils.py +18 -0
  108. pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
  109. pymodaq/utils/h5modules/__init__.py +2 -0
  110. pymodaq/utils/h5modules/module_saving.py +526 -0
  111. pymodaq/utils/leco/__init__.py +25 -0
  112. pymodaq/utils/leco/daq_move_LECODirector.py +217 -0
  113. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +163 -0
  114. pymodaq/utils/leco/director_utils.py +74 -0
  115. pymodaq/utils/leco/leco_director.py +166 -0
  116. pymodaq/utils/leco/pymodaq_listener.py +364 -0
  117. pymodaq/utils/leco/rpc_method_definitions.py +43 -0
  118. pymodaq/utils/leco/utils.py +74 -0
  119. pymodaq/utils/logger.py +6 -0
  120. pymodaq/utils/managers/__init__.py +0 -0
  121. pymodaq/utils/managers/batchscan_manager.py +346 -0
  122. pymodaq/utils/managers/modules_manager.py +589 -0
  123. pymodaq/utils/managers/overshoot_manager.py +242 -0
  124. pymodaq/utils/managers/preset_manager.py +229 -0
  125. pymodaq/utils/managers/preset_manager_utils.py +262 -0
  126. pymodaq/utils/managers/remote_manager.py +484 -0
  127. pymodaq/utils/math_utils.py +6 -0
  128. pymodaq/utils/messenger.py +6 -0
  129. pymodaq/utils/parameter/__init__.py +10 -0
  130. pymodaq/utils/parameter/utils.py +6 -0
  131. pymodaq/utils/scanner/__init__.py +5 -0
  132. pymodaq/utils/scanner/scan_config.py +16 -0
  133. pymodaq/utils/scanner/scan_factory.py +259 -0
  134. pymodaq/utils/scanner/scan_selector.py +477 -0
  135. pymodaq/utils/scanner/scanner.py +324 -0
  136. pymodaq/utils/scanner/scanners/_1d_scanners.py +174 -0
  137. pymodaq/utils/scanner/scanners/_2d_scanners.py +299 -0
  138. pymodaq/utils/scanner/scanners/__init__.py +1 -0
  139. pymodaq/utils/scanner/scanners/sequential.py +224 -0
  140. pymodaq/utils/scanner/scanners/tabular.py +319 -0
  141. pymodaq/utils/scanner/utils.py +110 -0
  142. pymodaq/utils/svg/__init__.py +6 -0
  143. pymodaq/utils/svg/svg_renderer.py +20 -0
  144. pymodaq/utils/svg/svg_view.py +35 -0
  145. pymodaq/utils/svg/svg_viewer2D.py +50 -0
  146. pymodaq/utils/tcp_ip/__init__.py +6 -0
  147. pymodaq/utils/tcp_ip/mysocket.py +12 -0
  148. pymodaq/utils/tcp_ip/serializer.py +13 -0
  149. pymodaq/utils/tcp_ip/tcp_server_client.py +772 -0
  150. pymodaq-5.1.6.dist-info/METADATA +238 -0
  151. pymodaq-5.1.6.dist-info/RECORD +154 -0
  152. pymodaq-5.1.6.dist-info/WHEEL +4 -0
  153. pymodaq-5.1.6.dist-info/entry_points.txt +7 -0
  154. pymodaq-5.1.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,22 @@
1
+ from pymodaq_utils.enums import StrEnum
2
+
3
+
4
+ class OptimizerToRunner(StrEnum):
5
+ """ Allowed Generic commands sent from an Optimizer to its thread running class
6
+
7
+ """
8
+ START = 'start'
9
+ RUN = 'run'
10
+ PAUSE = 'pause'
11
+ STOP = 'stop'
12
+ STOPPING = 'stopping'
13
+ BOUNDS = 'bounds'
14
+ RESTART = 'restart'
15
+ PREDICTION = 'prediction'
16
+
17
+
18
+ class OptimizerThreadStatus(StrEnum):
19
+
20
+ ADD_DATA = "add_data"
21
+ TRADE_OFF = "tradeoff"
22
+
@@ -0,0 +1,427 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created the 31/08/2023
4
+
5
+ @author: Sebastien Weber
6
+ """
7
+ import abc
8
+ from abc import ABC
9
+ from collections import OrderedDict
10
+ from typing import List, TYPE_CHECKING, Union, Dict, Tuple, Iterable, Optional
11
+ from pathlib import Path
12
+ import importlib
13
+ import pkgutil
14
+ import inspect
15
+ import numpy as np
16
+ from collections import namedtuple
17
+
18
+ from pymodaq_utils.abstract import abstract_attribute
19
+ from pymodaq_utils.utils import find_dict_in_list_from_key_val, get_entrypoints
20
+ from pymodaq_utils.logger import set_logger, get_module_name
21
+ from pymodaq_utils.enums import StrEnum
22
+ from pymodaq_utils.config import BaseConfig
23
+
24
+ from pymodaq_gui.plotting.data_viewers.viewer import ViewersEnum
25
+ from pymodaq_gui.managers.parameter_manager import Parameter
26
+
27
+
28
+ from pymodaq_data.data import (DataToExport, DataCalculated,
29
+ DataRaw, Axis)
30
+
31
+ from pymodaq.utils.data import DataActuator, DataToActuators
32
+ from pymodaq.utils.managers.modules_manager import ModulesManager
33
+
34
+ if TYPE_CHECKING:
35
+ from pymodaq.control_modules.daq_move import DAQ_Move
36
+
37
+ logger = set_logger(get_module_name(__file__))
38
+
39
+
40
+ class StopType(StrEnum):
41
+ NONE = 'None'
42
+ ITER = 'Iter'
43
+ PREDICT = 'Predict'
44
+ BEST = 'Best'
45
+
46
+ def tip(self):
47
+ if self == StopType.NONE:
48
+ return 'Stopping only after the number of iteration has been reached'
49
+ elif self == StopType.ITER:
50
+ return 'Stopping only after the number of iteration has been reached'
51
+ elif self == StopType.PREDICT:
52
+ return ('Stopping either after the number of iteration has been reached or the last N'
53
+ 'tested coordinates have a standard deviation less than tolerance')
54
+ elif self == StopType.BEST:
55
+ return ('Stopping either after the number of iteration has been reached or the N best '
56
+ 'coordinates have a standard deviation less than tolerance')
57
+
58
+
59
+ StoppingParameters = namedtuple('StoppingParameters',
60
+ ['niter', 'stop_type', 'tolerance', 'npoints'])
61
+
62
+ class PredictionError(Exception):
63
+ pass
64
+
65
+
66
+ def individual_as_dte(individual: dict[str, float], actuators: list['DAQ_Move'],
67
+ name: str = 'Individual') -> DataToExport:
68
+ """ Create a DataToExport from the individual coordinates and the list of selected actuators"""
69
+ return DataToExport(
70
+ name,
71
+ data=[DataCalculated(actuators[ind].title,
72
+ data=[np.atleast_1d(individual[actuators[ind].title])],
73
+ units=actuators[ind].units,
74
+ labels=[actuators[ind].title],
75
+ origin=name)
76
+ for ind in range(len(individual))],)
77
+
78
+
79
+ def individual_as_dta(individual: dict[str, float], actuators: list['DAQ_Move'],
80
+ name: str = 'Individual', mode='abs') -> DataToActuators:
81
+ """ Create a DataToActuators from the individual coordinates and the list of selected actuators"""
82
+ return DataToActuators(
83
+ name, mode=mode,
84
+ data=[DataActuator(actuators[ind].title,
85
+ data=[np.atleast_1d(individual[actuators[ind].title])],
86
+ units=actuators[ind].units,
87
+ labels=[actuators[ind].title],
88
+ origin=name)
89
+ for ind in range(len(individual))],)
90
+
91
+
92
+ class GenericAlgorithm(abc.ABC):
93
+
94
+ def __init__(self, ini_random: int, bounds: OrderedDict[str, tuple[float, float]], actuators: list[str]):
95
+
96
+ self._algo = abstract_attribute() #could be a Bayesian on Adaptive algorithm
97
+ self._prediction = abstract_attribute() # could be an acquisition function...
98
+
99
+ self.actuators = actuators
100
+ self.ini_bounds = bounds
101
+
102
+ self._next_point: Optional[dict[str, float]] = None
103
+ self._suggested_coordinates: list[dict[str, float]] = []
104
+ self.ini_random_points = ini_random
105
+
106
+ @abc.abstractmethod
107
+ def set_prediction_function(self, kind: str='', **kwargs):
108
+ """ Set/Load a given function/class to predict next probed points"""
109
+
110
+ @abc.abstractmethod
111
+ def update_prediction_function(self):
112
+ """ Update the parameters of the prediction function (kappa decay for instance)"""
113
+
114
+ def set_acquisition_function(self, kind: str, **kwargs):
115
+ """ Deprecated"""
116
+ self.set_prediction_function(kind, **kwargs)
117
+
118
+ def update_acquisition_function(self):
119
+ """ deprecated"""
120
+ self.update_prediction_function()
121
+
122
+ @property
123
+ def _acquisition(self):
124
+ """ deprecated """
125
+ return self._prediction
126
+
127
+ @property
128
+ def tradeoff(self):
129
+ return self._prediction.tradeoff
130
+
131
+ @property
132
+ @abc.abstractmethod
133
+ def bounds(self) -> dict[str, tuple[float, float]]:
134
+ ...
135
+
136
+ @bounds.setter
137
+ def bounds(self, bounds: dict[str, tuple[float, float]]):
138
+ ...
139
+
140
+ @abc.abstractmethod
141
+ def get_random_point(self) -> dict[str, float]:
142
+ """ Get a random point coordinates in the defined bounds"""
143
+ ...
144
+
145
+ def ask(self) -> dict[str, float]:
146
+ """ Predict next actuator values to probe
147
+
148
+ Return a DataToActuator, one DataWithAxes per actuator. In general these dwa are 0D
149
+ """
150
+ try:
151
+ self._next_point = self.prediction_ask()
152
+ except PredictionError:
153
+ self.ini_random_points -= 1
154
+ self._next_point = self.get_random_point()
155
+ self._suggested_coordinates.append(self._next_point)
156
+ return self._next_point
157
+
158
+ @abc.abstractmethod
159
+ def prediction_ask(self) -> dict[str, float]:
160
+ """ Ask the prediction function or algo to provide the next point to probe"""
161
+
162
+ @abc.abstractmethod
163
+ def tell(self, function_value: float):
164
+ """ Add next points and function value into the algo"""
165
+
166
+ @property
167
+ @abc.abstractmethod
168
+ def best_fitness(self) -> float:
169
+ pass
170
+
171
+ @property
172
+ @abc.abstractmethod
173
+ def best_individual(self) -> Union[dict[str, float], None]:
174
+ pass
175
+
176
+ @abc.abstractmethod
177
+ def stopping(self, ind_iter: int, stopping_parameters: StoppingParameters) -> bool:
178
+ pass
179
+
180
+
181
+ class OptimizerModelGeneric(ABC):
182
+
183
+ optimization_algorithm: GenericAlgorithm = None
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, optimization_controller):
193
+ self.optimization_controller = optimization_controller # instance of the pid_controller using this model
194
+ self.modules_manager: ModulesManager = optimization_controller.modules_manager
195
+
196
+ self.settings = self.optimization_controller.settings.child('models', 'model_params') # set of parameters
197
+ self.check_modules(self.modules_manager)
198
+
199
+ @abc.abstractmethod
200
+ def has_fitness_observable(self) -> bool:
201
+ """ Should return True if the model defined a 0D data to be used as fitness value"""
202
+ return False
203
+
204
+ def check_modules(self, modules_manager):
205
+ for act in self.actuators_name:
206
+ if act not in modules_manager.actuators_name:
207
+ logger.warning(f'The actuator {act} defined in the model is'
208
+ f' not present in the Dashboard')
209
+ return False
210
+ for det in self.detectors_name:
211
+ if det not in modules_manager.detectors_name:
212
+ logger.warning(f'The detector {det} defined in the model is'
213
+ f' not present in the Dashboard')
214
+
215
+ def update_detector_names(self):
216
+ names = self.optimization_controller.settings.child(
217
+ 'main_settings', 'detector_modules').value()['selected']
218
+ self.data_names = []
219
+ for name in names:
220
+ name = name.split('//')
221
+ self.data_names.append(name)
222
+
223
+ def update_settings(self, param: Parameter):
224
+ """
225
+ Get a parameter instance whose value has been modified by a user on the UI
226
+ To be overwritten in child class
227
+ """
228
+ ...
229
+
230
+ def update_plots(self):
231
+ """ Called when updating the live plots """
232
+ pass
233
+
234
+ def ini_model_base(self):
235
+ self.modules_manager.selected_actuators_name = self.actuators_name
236
+ self.modules_manager.selected_detectors_name = self.detectors_name
237
+
238
+ self.ini_model()
239
+
240
+ def ini_model(self):
241
+ """ To be subclassed
242
+
243
+ Initialize whatever is needed by your custom model
244
+ """
245
+ raise NotImplementedError
246
+
247
+ def runner_initialized(self):
248
+ """ To be subclassed
249
+
250
+ Initialize whatever is needed by your custom model after the optimization runner is
251
+ initialized
252
+ """
253
+ pass
254
+
255
+ def convert_input(self, measurements: DataToExport) -> float:
256
+ """
257
+ Convert the measurements in the units to be fed to the Optimisation Controller
258
+ Parameters
259
+ ----------
260
+ measurements: DataToExport
261
+ data object exported from the detectors from which the model extract a float value
262
+ (fitness) to be fed to the algorithm
263
+
264
+ Returns
265
+ -------
266
+ float
267
+
268
+ """
269
+ raise NotImplementedError
270
+
271
+ def convert_output(self, outputs: dict[str, Union[float, np.ndarray]],
272
+ best_individual: Optional[dict[str, float]] = None) -> DataToActuators:
273
+ """ Convert the output of the Optimisation Controller in units to be fed into the actuators
274
+ Parameters
275
+ ----------
276
+ outputs: dict with name of the actuator as key and the value to move to as a float (or ndarray)
277
+ output value from the controller from which the model extract a value of the same units as the actuators
278
+ best_individual: dict[str, float]
279
+ the coordinates of the best individual so far
280
+ Returns
281
+ -------
282
+ DataToActuatorOpti: derived from DataToExport. Contains value to be fed to the actuators with a 'mode'
283
+ attribute, either 'rel' for relative or 'abs' for absolute.
284
+
285
+ """
286
+ raise NotImplementedError
287
+
288
+
289
+ class OptimizerModelDefault(OptimizerModelGeneric):
290
+
291
+ actuators_name: List[str] = [] # to be populated dynamically at instantiation
292
+ detectors_name: List[str] = [] # to be populated dynamically at instantiation
293
+
294
+ params = [{'title': 'Optimizing signal', 'name': 'optimizing_signal', 'type': 'group',
295
+ 'children': [
296
+ {'title': 'Get data', 'name': 'data_probe', 'type': 'action'},
297
+ {'title': 'Optimize 0Ds:', 'name': 'optimize_0d', 'type': 'itemselect',
298
+ 'checkbox': True},
299
+ ]},]
300
+
301
+ def __init__(self, optimization_controller):
302
+ self.actuators_name = optimization_controller.modules_manager.selected_actuators_name
303
+ self.detectors_name = optimization_controller.modules_manager.selected_detectors_name
304
+ super().__init__(optimization_controller)
305
+
306
+ self.settings.child('optimizing_signal', 'data_probe').sigActivated.connect(
307
+ self.optimize_from)
308
+
309
+ def has_fitness_observable(self) -> bool:
310
+ """ Should return True if the model defined a 0D data to be used as fitness value"""
311
+ return len(self.settings.child('optimizing_signal', 'optimize_0d').value()['selected']) == 1
312
+
313
+ def ini_model(self):
314
+ pass
315
+
316
+ def update_settings(self, param: Parameter):
317
+ pass
318
+
319
+ def convert_input(self, measurements: DataToExport) -> float:
320
+ """ Convert the measurements in the units to be fed to the Optimisation Controller
321
+
322
+ Parameters
323
+ ----------
324
+ measurements: DataToExport
325
+ data object exported from the detectors from which the model extract a float value
326
+ (fitness) to be fed to the algorithm
327
+
328
+ Returns
329
+ -------
330
+ float
331
+
332
+ """
333
+ data_name: str = self.settings['optimizing_signal', 'optimize_0d']['selected'][0]
334
+ origin, name = data_name.split('/')
335
+ return float(measurements.get_data_from_name_origin(name, origin).data[0][0])
336
+
337
+ def convert_output(self, outputs: dict[str, Union[float, np.ndarray]],
338
+ best_individual: Optional[dict[str, float]] = None) -> DataToActuators:
339
+ """ Convert the output of the Optimisation Controller in units to be fed into the actuators
340
+ Parameters
341
+ ----------
342
+ outputs: dict with name of the actuator as key and the value to move to as a float (or ndarray)
343
+ output value from the controller from which the model extract a value of the same units as the actuators
344
+ best_individual: dict[str, float]
345
+ the coordinates of the best individual so far
346
+ Returns
347
+ -------
348
+ DataToActuatorOpti: derived from DataToExport. Contains value to be fed to the actuators with a 'mode'
349
+ attribute, either 'rel' for relative or 'abs' for absolute.
350
+
351
+ """
352
+ return individual_as_dta(outputs, self.modules_manager.actuators, 'outputs', mode='abs')
353
+
354
+ def optimize_from(self):
355
+ self.modules_manager.get_det_data_list()
356
+ data0D = self.modules_manager.settings['data_dimensions', 'det_data_list0D']
357
+ data0D['selected'] = data0D['all_items']
358
+ self.settings.child('optimizing_signal', 'optimize_0d').setValue(data0D)
359
+
360
+
361
+ def get_optimizer_models(model_name=None):
362
+ """
363
+ Get Optimizer Models as a list to instantiate Control Actuators per degree of liberty in the model
364
+
365
+ Returns
366
+ -------
367
+ list: list of disct containting the name and python module of the found models
368
+ """
369
+ models_import = []
370
+ discovered_models = get_entrypoints(group='pymodaq.models')
371
+ if len(discovered_models) > 0:
372
+ for pkg in discovered_models:
373
+ try:
374
+ module = importlib.import_module(pkg.value)
375
+ module_name = pkg.value
376
+
377
+ for mod in pkgutil.iter_modules([
378
+ str(Path(module.__file__).parent.joinpath('models'))]):
379
+ try:
380
+ model_module = importlib.import_module(f'{module_name}.models.{mod.name}',
381
+ module)
382
+ classes = inspect.getmembers(model_module, inspect.isclass)
383
+ for name, klass in classes:
384
+ if issubclass(klass, OptimizerModelGeneric):
385
+ if find_dict_in_list_from_key_val(models_import, 'name', mod.name)\
386
+ is None:
387
+ models_import.append({'name': klass.__name__,
388
+ 'module': model_module,
389
+ 'class': klass})
390
+
391
+ except Exception as e:
392
+ logger.warning(str(e))
393
+
394
+ except Exception as e:
395
+ logger.warning(f'Impossible to import the {pkg.value} optimizer model: {str(e)}')
396
+
397
+ if find_dict_in_list_from_key_val(models_import, 'name', 'OptimizerModelDefault') \
398
+ is None:
399
+ models_import.append({'name': 'OptimizerModelDefault',
400
+ 'module': inspect.getmodule(OptimizerModelDefault),
401
+ 'class': OptimizerModelDefault})
402
+ if model_name is None:
403
+ return models_import
404
+ else:
405
+ return find_dict_in_list_from_key_val(models_import, 'name', model_name)
406
+
407
+
408
+ class OptimizerConfig(BaseConfig):
409
+ """Main class to deal with configuration values for this plugin
410
+
411
+ To b subclassed for real implementation if needed, see Optimizer class attribute config_saver
412
+ """
413
+ config_template_path = None
414
+ config_name = f"optimizer_settings"
415
+
416
+
417
+ def find_key_in_nested_dict(dic, key):
418
+ stack = [dic]
419
+ while stack:
420
+ d = stack.pop()
421
+ if key in d:
422
+ return d[key]
423
+ for v in d.values():
424
+ if isinstance(v, dict):
425
+ stack.append(v)
426
+ if isinstance(v, list):
427
+ stack += v
@@ -0,0 +1,16 @@
1
+ import importlib
2
+ from pathlib import Path
3
+
4
+ from pymodaq_utils.logger import set_logger, get_module_name
5
+
6
+ logger = set_logger('move_plugins', add_to_console=False)
7
+
8
+
9
+ for path in Path(__file__).parent.iterdir():
10
+ try:
11
+ if '__init__' not in str(path):
12
+ importlib.import_module('.' + path.stem, __package__)
13
+ except Exception as e:
14
+ logger.warning("{:} plugin couldn't be loaded due to some missing packages or errors:"
15
+ " {:}".format(path.stem, str(e)))
16
+ pass
@@ -0,0 +1,14 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ if TYPE_CHECKING:
4
+ from .pid_controller import DAQ_PID
5
+
6
+
7
+ class PIDController:
8
+ """ Fake controller object for the DAQ_Move_PID"""
9
+
10
+ def __init__(self, daq_pid: 'DAQ_PID', set_point_name: str = 'setpoint'):
11
+ self.curr_point = daq_pid.curr_points_signal
12
+ self.setpoint = daq_pid.setpoints_signal
13
+ self.emit_curr_points = daq_pid.emit_curr_points_sig
14
+ self.queue_points = daq_pid.queue_points[set_point_name]
@@ -0,0 +1,154 @@
1
+ from collections import deque
2
+
3
+ import numpy as np
4
+ from pymodaq_utils.utils import ThreadCommand
5
+
6
+ from pymodaq.control_modules.move_utility_classes import (
7
+ DAQ_Move_base,
8
+ DataActuator,
9
+ DataActuatorType,
10
+ comon_parameters_fun,
11
+ )
12
+ from pymodaq.extensions.pid.actuator_controller import PIDController
13
+
14
+
15
+ class DAQ_Move_PID(DAQ_Move_base):
16
+ """ """
17
+
18
+ _controller_units = ""
19
+ data_actuator_type = DataActuatorType.DataActuator
20
+ is_multiaxes = False
21
+ stage_names = [
22
+ "",
23
+ ]
24
+ params = [ # elements to be added in order to control your custom stage
25
+ {
26
+ "title": "Check stability:",
27
+ "name": "check_stab",
28
+ "type": "bool",
29
+ "value": False,
30
+ "default": "False",
31
+ "tooltip": "Activate to only trigger move_done once standard deviation over queue length is below threshold",
32
+ "children": [
33
+ {
34
+ "title": "Stable:",
35
+ "name": "is_stab",
36
+ "type": "led",
37
+ "value": False,
38
+ "default": False,
39
+ "tooltip": "Red if the standard deviation of the last positions is above threshold, green if below",
40
+ },
41
+ {
42
+ "title": "Current stability:",
43
+ "name": "current_stab",
44
+ "type": "float",
45
+ "value": 0,
46
+ "default": 0,
47
+ "readonly": True,
48
+ "tooltip": "",
49
+ },
50
+ {
51
+ "title": "Threshold:",
52
+ "name": "threshold",
53
+ "type": "float",
54
+ "value": 0.1,
55
+ "default": 0.1,
56
+ "min": 0,
57
+ "tooltip": "Standard deviation threshold to consider the stage stable",
58
+ },
59
+ {
60
+ "title": "Queue length:",
61
+ "name": "queue_length",
62
+ "type": "int",
63
+ "default": 10,
64
+ "value": 50,
65
+ "min": 0,
66
+ "tooltip": "Length of the queue used to compute the standard deviation for stability check",
67
+ },
68
+ ],
69
+ },
70
+ ] + comon_parameters_fun(is_multiaxes, stage_names, master=False)
71
+ # params = comon_parameters_fun(is_multiaxes, stage_names, master=False)
72
+
73
+ def ini_attributes(self):
74
+ self.controller: PIDController = None
75
+ self.last_positions = deque(maxlen=self.settings["check_stab", "queue_length"])
76
+
77
+ def update_position(self, dict_val: dict):
78
+ self.current_value = dict_val[self.parent.title]
79
+
80
+ def get_actuator_value(self):
81
+ self.controller.emit_curr_points.emit()
82
+ pos = self.current_value
83
+ return pos
84
+
85
+ def close(self):
86
+ pass
87
+
88
+ def user_condition_to_reach_target(self):
89
+ cond = super().user_condition_to_reach_target()
90
+ parameter_stab = self.settings.child("check_stab")
91
+
92
+ if parameter_stab.value():
93
+ if len(self.controller.queue_points) >= self.settings["check_stab", "queue_length"]:
94
+ self.last_positions = deque(
95
+ self.controller.queue_points, maxlen=self.settings["check_stab", "queue_length"]
96
+ )
97
+ current_stab = np.std(self.last_positions)
98
+ parameter_stab.child("current_stab").setValue(current_stab)
99
+ cond = current_stab <= parameter_stab["threshold"]
100
+ else:
101
+ cond = False
102
+
103
+ parameter_stab.child("is_stab").setValue(cond)
104
+
105
+ return cond
106
+
107
+ def commit_settings(self, param):
108
+ if param.name() == "check_stab":
109
+ pass
110
+ elif param.name() == "queue_length":
111
+ self.last_positions = deque(
112
+ self.controller.queue_points, maxlen=self.settings["check_stab", "queue_length"]
113
+ )
114
+ param.setOpts(max=self.controller.queue_points.maxlen)
115
+
116
+ def ini_stage(self, controller: PIDController = None):
117
+ """ """
118
+ self.controller = controller
119
+ self.controller.curr_point.connect(self.update_position)
120
+
121
+ self.settings.child("check_stab", "queue_length").setValue(self.controller.queue_points.maxlen)
122
+
123
+ info = "PID stage"
124
+ initialized = True
125
+ return info, initialized
126
+
127
+ def move_abs(self, position: DataActuator):
128
+ """ """
129
+ position = self.check_bound(position)
130
+ self.target_value = position
131
+
132
+ self.controller.setpoint.emit({self.parent.title: self.target_value})
133
+
134
+ def move_rel(self, position: DataActuator):
135
+ """ """
136
+ position = self.check_bound(self.current_value + position) - self.current_value
137
+ self.target_value = position + self.current_value
138
+
139
+ self.controller.setpoint.emit({self.parent.title: self.target_value})
140
+ self.poll_moving()
141
+
142
+ def move_home(self):
143
+ """ """
144
+ self.emit_status(ThreadCommand("Update_Status", ["Move Home not implemented"]))
145
+
146
+ def stop_motion(self):
147
+ """
148
+ Call the specific move_done function (depending on the hardware).
149
+
150
+ See Also
151
+ --------
152
+ move_done
153
+ """
154
+ self.move_done()