aesoptparam 0.3.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.
Potentially problematic release.
This version of aesoptparam might be problematic. Click here for more details.
- aesoptparam/__init__.py +13 -0
- aesoptparam/example.py +110 -0
- aesoptparam/parameterized.py +417 -0
- aesoptparam/parameters.py +641 -0
- aesoptparam/serializer.py +94 -0
- aesoptparam/test/dummy_instance.json +30 -0
- aesoptparam/test/dummy_schema.json +752 -0
- aesoptparam/test/test_parameterized.py +574 -0
- aesoptparam/test/test_parameters.py +519 -0
- aesoptparam/test/test_units.py +369 -0
- aesoptparam/test/test_utils.py +147 -0
- aesoptparam/utils/__init__.py +63 -0
- aesoptparam/utils/html_repr.py +533 -0
- aesoptparam/utils/json_utils.py +127 -0
- aesoptparam/utils/unit_library.ini +233 -0
- aesoptparam/utils/units.py +1060 -0
- aesoptparam-0.3.6.dist-info/METADATA +86 -0
- aesoptparam-0.3.6.dist-info/RECORD +21 -0
- aesoptparam-0.3.6.dist-info/WHEEL +5 -0
- aesoptparam-0.3.6.dist-info/licenses/LICENSE +21 -0
- aesoptparam-0.3.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from inspect import getsource, signature
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import param as pm
|
|
6
|
+
|
|
7
|
+
from .utils.html_repr import value_ref_default_repr_html
|
|
8
|
+
from .utils.units import valid_units
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SubParameterized(pm.ClassSelector):
|
|
12
|
+
@typing.overload
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
class_,
|
|
16
|
+
*,
|
|
17
|
+
default=None,
|
|
18
|
+
instantiate=True,
|
|
19
|
+
is_instance=True,
|
|
20
|
+
allow_None=False,
|
|
21
|
+
doc=None,
|
|
22
|
+
label=None,
|
|
23
|
+
precedence=None,
|
|
24
|
+
constant=False,
|
|
25
|
+
readonly=False,
|
|
26
|
+
pickle_default_value=True,
|
|
27
|
+
per_instance=True,
|
|
28
|
+
allow_refs=False,
|
|
29
|
+
nested_refs=False,
|
|
30
|
+
): ...
|
|
31
|
+
|
|
32
|
+
def __init__(self, class_, *, default=pm.Undefined, doc=pm.Undefined, **params):
|
|
33
|
+
"""Include a Parameterized class as a parameter. A direct child `param.ClassSelector` for including nested Parameterized model in a Parameterized model. Default to an empty instance of the class and adding the doc string from the class."""
|
|
34
|
+
if default is pm.Undefined:
|
|
35
|
+
default = lambda self: class_(parent_object=self)
|
|
36
|
+
if (doc is pm.Undefined) and (class_.__doc__):
|
|
37
|
+
doc = class_.__doc__
|
|
38
|
+
super().__init__(class_=class_, default=default, doc=doc, **params)
|
|
39
|
+
self._validate(self.default)
|
|
40
|
+
|
|
41
|
+
def __get__(self, obj, objtype):
|
|
42
|
+
out = super().__get__(obj, objtype)
|
|
43
|
+
if callable(out):
|
|
44
|
+
if len(signature(out).parameters) == 1:
|
|
45
|
+
obj._param__private.values[self.name] = out(obj)
|
|
46
|
+
else:
|
|
47
|
+
obj._param__private.values[self.name] = out()
|
|
48
|
+
return super().__get__(obj, objtype)
|
|
49
|
+
return out
|
|
50
|
+
|
|
51
|
+
def _validate_class_(self, val, class_, is_instance):
|
|
52
|
+
if val is None:
|
|
53
|
+
return
|
|
54
|
+
if not callable(val):
|
|
55
|
+
if not (isinstance(val, class_)):
|
|
56
|
+
class_name = class_.__name__
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"{pm.parameters._validate_error_prefix(self)} value must be an instance of {class_name}, not {val!r}."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ListOfParameterized(pm.List):
|
|
63
|
+
__slots__ = ["identifier", "default_call"]
|
|
64
|
+
_slot_defaults = dict(pm.List._slot_defaults, identifier="name", default_call=None)
|
|
65
|
+
|
|
66
|
+
@typing.overload
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
item_type,
|
|
70
|
+
identifier,
|
|
71
|
+
*,
|
|
72
|
+
default=[],
|
|
73
|
+
instantiate=True,
|
|
74
|
+
bounds=(0, None),
|
|
75
|
+
allow_None=False,
|
|
76
|
+
doc=None,
|
|
77
|
+
label=None,
|
|
78
|
+
precedence=None,
|
|
79
|
+
constant=False,
|
|
80
|
+
readonly=False,
|
|
81
|
+
pickle_default_value=True,
|
|
82
|
+
per_instance=True,
|
|
83
|
+
allow_refs=False,
|
|
84
|
+
nested_refs=False,
|
|
85
|
+
default_call=None,
|
|
86
|
+
): ...
|
|
87
|
+
|
|
88
|
+
def __init__(self, item_type, identifier="name", *, default_call=None, **params):
|
|
89
|
+
"""List of Parameterized objects. Adds a html render and unique object identifier. A direct child of `param.List`"""
|
|
90
|
+
self.identifier = identifier
|
|
91
|
+
if default_call is not None and not callable(default_call):
|
|
92
|
+
raise RuntimeError(
|
|
93
|
+
f"`default_call` need to be a callable or None (given: {default_call})"
|
|
94
|
+
)
|
|
95
|
+
self.default_call = default_call
|
|
96
|
+
super().__init__(item_type=item_type, **params)
|
|
97
|
+
if (self.doc is None) and (self.item_type.__doc__):
|
|
98
|
+
self.doc = self.item_type.__doc__
|
|
99
|
+
|
|
100
|
+
def __get__(self, obj, objtype):
|
|
101
|
+
out = super().__get__(obj, objtype)
|
|
102
|
+
if not out and self.default_call is not None:
|
|
103
|
+
val = self.default_call(obj)
|
|
104
|
+
if not isinstance(val, list):
|
|
105
|
+
raise ValueError(
|
|
106
|
+
f"`.default_call` need to return a list (gives: `type(default_call(obj))`={type(self.default_call(obj))})"
|
|
107
|
+
)
|
|
108
|
+
obj._param__private.values[self.name] = self.default_call(obj)
|
|
109
|
+
return super().__get__(obj, objtype)
|
|
110
|
+
return out
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class BaseRefFunc:
|
|
114
|
+
def __init__(
|
|
115
|
+
self,
|
|
116
|
+
default=pm.Undefined,
|
|
117
|
+
*,
|
|
118
|
+
default_ref=pm.Undefined,
|
|
119
|
+
**params,
|
|
120
|
+
):
|
|
121
|
+
if self._is_ref(default):
|
|
122
|
+
default = Reference(default)
|
|
123
|
+
elif self._is_func(default):
|
|
124
|
+
default = Function(default)
|
|
125
|
+
if not (default_ref is pm.Undefined or default_ref is None) and (
|
|
126
|
+
not isinstance(default_ref, (str, Reference))
|
|
127
|
+
):
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f"`default_ref` need to be of type string (given: type(default_ref)={type(default_ref)})"
|
|
130
|
+
)
|
|
131
|
+
self.default_ref = None
|
|
132
|
+
if not (default_ref is pm.Undefined or default_ref is None):
|
|
133
|
+
self.default_ref = (
|
|
134
|
+
default_ref
|
|
135
|
+
if isinstance(default_ref, Reference)
|
|
136
|
+
else Reference(default_ref)
|
|
137
|
+
)
|
|
138
|
+
super().__init__(default=default, **params)
|
|
139
|
+
|
|
140
|
+
def __get__(self, obj, objtype):
|
|
141
|
+
out = super().__get__(obj, objtype)
|
|
142
|
+
if isinstance(out, Function):
|
|
143
|
+
return out.method(obj)
|
|
144
|
+
elif isinstance(out, Reference):
|
|
145
|
+
return obj[out]
|
|
146
|
+
elif out is None:
|
|
147
|
+
if isinstance(self.default_ref, Reference):
|
|
148
|
+
return obj[self.default_ref]
|
|
149
|
+
return out
|
|
150
|
+
|
|
151
|
+
def __set__(self, obj, val):
|
|
152
|
+
if not isinstance(val, Function) and self._is_func(val):
|
|
153
|
+
val = Function(val)
|
|
154
|
+
elif not isinstance(val, Reference) and self._is_ref(val):
|
|
155
|
+
val = Reference(val)
|
|
156
|
+
return super().__set__(obj, val)
|
|
157
|
+
|
|
158
|
+
def _validate_value(self, val, allow_None):
|
|
159
|
+
if isinstance(val, (Function, Reference)):
|
|
160
|
+
return
|
|
161
|
+
return super()._validate_value(val, allow_None)
|
|
162
|
+
|
|
163
|
+
def _is_func(
|
|
164
|
+
self,
|
|
165
|
+
func,
|
|
166
|
+
):
|
|
167
|
+
return (
|
|
168
|
+
callable(func)
|
|
169
|
+
or isinstance(func, Function)
|
|
170
|
+
or (isinstance(func, str) and func.startswith("$function"))
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
def _is_ref(self, ref):
|
|
174
|
+
return isinstance(ref, Reference) or (
|
|
175
|
+
isinstance(ref, str)
|
|
176
|
+
and ((ref.startswith(".") and not ref[1] == "/") or ref.startswith("$ref"))
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
def repr_html(self, val, val_pp, max_arr_size=50):
|
|
180
|
+
return value_ref_default_repr_html(self, val, val_pp, max_arr_size)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class BaseRefFuncUnits(BaseRefFunc):
|
|
184
|
+
def __init__(
|
|
185
|
+
self,
|
|
186
|
+
default=pm.Undefined,
|
|
187
|
+
*,
|
|
188
|
+
units=pm.Undefined,
|
|
189
|
+
**params,
|
|
190
|
+
):
|
|
191
|
+
if (not units is pm.Undefined) and (not valid_units(units)):
|
|
192
|
+
raise ValueError(f"units is not valid (given: {units})")
|
|
193
|
+
self.units = units
|
|
194
|
+
super().__init__(default=default, **params)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class AESOptString(BaseRefFunc, pm.String):
|
|
198
|
+
__slots__ = ["default_ref"]
|
|
199
|
+
|
|
200
|
+
_slot_defaults = dict(
|
|
201
|
+
pm.String._slot_defaults,
|
|
202
|
+
default=None,
|
|
203
|
+
allow_None=True,
|
|
204
|
+
default_ref=None,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
@typing.overload
|
|
208
|
+
def __init__(
|
|
209
|
+
self,
|
|
210
|
+
default="",
|
|
211
|
+
*,
|
|
212
|
+
regex=None,
|
|
213
|
+
doc=None,
|
|
214
|
+
label=None,
|
|
215
|
+
precedence=None,
|
|
216
|
+
instantiate=False,
|
|
217
|
+
constant=False,
|
|
218
|
+
readonly=False,
|
|
219
|
+
pickle_default_value=True,
|
|
220
|
+
allow_None=False,
|
|
221
|
+
per_instance=True,
|
|
222
|
+
allow_refs=False,
|
|
223
|
+
nested_refs=False,
|
|
224
|
+
default_ref=None,
|
|
225
|
+
): ...
|
|
226
|
+
|
|
227
|
+
def __init__(self, default=None, **param):
|
|
228
|
+
super().__init__(default=default, **param)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class AESOptNumber(BaseRefFuncUnits, pm.Number):
|
|
232
|
+
__slots__ = ["units", "default_ref"]
|
|
233
|
+
|
|
234
|
+
_slot_defaults = dict(
|
|
235
|
+
pm.Number._slot_defaults,
|
|
236
|
+
default=None,
|
|
237
|
+
units=None,
|
|
238
|
+
allow_None=True,
|
|
239
|
+
default_ref=None,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
@typing.overload
|
|
243
|
+
def __init__(
|
|
244
|
+
self,
|
|
245
|
+
default=None,
|
|
246
|
+
*,
|
|
247
|
+
bounds=None,
|
|
248
|
+
softbounds=None,
|
|
249
|
+
inclusive_bounds=(True, True),
|
|
250
|
+
is_instance=True,
|
|
251
|
+
allow_None=False,
|
|
252
|
+
doc=None,
|
|
253
|
+
label=None,
|
|
254
|
+
precedence=None,
|
|
255
|
+
instantiate=True,
|
|
256
|
+
constant=False,
|
|
257
|
+
readonly=False,
|
|
258
|
+
pickle_default_value=True,
|
|
259
|
+
per_instance=True,
|
|
260
|
+
allow_refs=False,
|
|
261
|
+
nested_refs=False,
|
|
262
|
+
units=None,
|
|
263
|
+
default_ref=None,
|
|
264
|
+
): ...
|
|
265
|
+
|
|
266
|
+
def __init__(self, default=None, **param):
|
|
267
|
+
super().__init__(default=default, **param)
|
|
268
|
+
|
|
269
|
+
def _validate_value(self, val, allow_None):
|
|
270
|
+
if isinstance(val, np.ndarray):
|
|
271
|
+
raise ValueError(
|
|
272
|
+
f"{pm._utils._validate_error_prefix(self)} only takes numeric values, "
|
|
273
|
+
f"not {type(val)}."
|
|
274
|
+
)
|
|
275
|
+
return super()._validate_value(val, allow_None)
|
|
276
|
+
|
|
277
|
+
def _validate_bounds(self, val, bounds, inclusive_bounds):
|
|
278
|
+
if isinstance(val, (Reference, Function)):
|
|
279
|
+
return
|
|
280
|
+
return super()._validate_bounds(val, bounds, inclusive_bounds)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class AESOptInteger(AESOptNumber, pm.Integer):
|
|
284
|
+
_slot_defaults = dict(
|
|
285
|
+
pm.Integer._slot_defaults,
|
|
286
|
+
default=None,
|
|
287
|
+
units=None,
|
|
288
|
+
allow_None=True,
|
|
289
|
+
default_ref=None,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class AESOptArray(BaseRefFuncUnits, pm.Array):
|
|
294
|
+
__slots__ = [
|
|
295
|
+
"bounds",
|
|
296
|
+
"softbounds",
|
|
297
|
+
"inclusive_bounds",
|
|
298
|
+
"units",
|
|
299
|
+
"dtype",
|
|
300
|
+
"shape",
|
|
301
|
+
"default_full",
|
|
302
|
+
"default_interp",
|
|
303
|
+
"default_ref",
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
_slot_defaults = dict(
|
|
307
|
+
pm.Array._slot_defaults,
|
|
308
|
+
default=None,
|
|
309
|
+
bounds=None,
|
|
310
|
+
softbounds=None,
|
|
311
|
+
inclusive_bounds=(True, True),
|
|
312
|
+
units=None,
|
|
313
|
+
dtype=np.number,
|
|
314
|
+
shape=None,
|
|
315
|
+
default_full=None,
|
|
316
|
+
default_interp=None,
|
|
317
|
+
default_ref=None,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
@typing.overload
|
|
321
|
+
def __init__(
|
|
322
|
+
self,
|
|
323
|
+
default=None,
|
|
324
|
+
*,
|
|
325
|
+
bounds=None,
|
|
326
|
+
softbounds=None,
|
|
327
|
+
inclusive_bounds=(True, True),
|
|
328
|
+
is_instance=True,
|
|
329
|
+
allow_None=False,
|
|
330
|
+
doc=None,
|
|
331
|
+
label=None,
|
|
332
|
+
precedence=None,
|
|
333
|
+
instantiate=True,
|
|
334
|
+
constant=False,
|
|
335
|
+
readonly=False,
|
|
336
|
+
pickle_default_value=True,
|
|
337
|
+
per_instance=True,
|
|
338
|
+
allow_refs=False,
|
|
339
|
+
nested_refs=False,
|
|
340
|
+
units=None,
|
|
341
|
+
dtype=np.number,
|
|
342
|
+
shape=None,
|
|
343
|
+
default_full=None,
|
|
344
|
+
default_interp=None,
|
|
345
|
+
default_ref=None,
|
|
346
|
+
): ...
|
|
347
|
+
|
|
348
|
+
def __init__(
|
|
349
|
+
self,
|
|
350
|
+
default=pm.Undefined,
|
|
351
|
+
*,
|
|
352
|
+
bounds=pm.Undefined,
|
|
353
|
+
softbounds=pm.Undefined,
|
|
354
|
+
inclusive_bounds=pm.Undefined,
|
|
355
|
+
dtype=np.number,
|
|
356
|
+
shape=pm.Undefined,
|
|
357
|
+
default_full=pm.Undefined,
|
|
358
|
+
default_interp=pm.Undefined,
|
|
359
|
+
default_ref=pm.Undefined,
|
|
360
|
+
**params,
|
|
361
|
+
):
|
|
362
|
+
"""Numeric numpy array. Adds bound checking, shape, dtype, units, etc. A direct child of `param.Array`
|
|
363
|
+
For `bounds`, `softbounds` and `inclusive_bounds` see `param.Number`.
|
|
364
|
+
`default` can either be a string with the variable to default to or it can a a callable with one argument which is going to be the Parameterized instance (e.g. `lambda self: self._param__private.values["x"]` will make the variable default to the parameter "x")
|
|
365
|
+
`units` should be a `str` following [OpenMDAO: Specifying Units for Variables](https://openmdao.org/newdocs/versions/latest/features/core_features/working_with_components/units.html?highlight=units)
|
|
366
|
+
`dtype` should be a numpy dtype class. Default to `numpy.number`.
|
|
367
|
+
`shape` should be one of `int`: static shape, `str`: shape like another parameter (using object-path), `tuple`: multi-dim, where each dim can be either `int` or `str`.
|
|
368
|
+
`default_full` should be a tuple of length 2 with items: 1: shape-like input (`int`, `str` or `tuple`) see above, 2: value to fill with (`float`, `int`)
|
|
369
|
+
`default_interp` should be a tuple of length 3 with items (each can be either `numpy.ndarray` or `str`): 1: new grid (`x`), 2: old grid (`xp`), 3: old values (`yp`).
|
|
370
|
+
"""
|
|
371
|
+
self.bounds = bounds
|
|
372
|
+
self.softbounds = softbounds
|
|
373
|
+
self.inclusive_bounds = inclusive_bounds
|
|
374
|
+
self.dtype = dtype
|
|
375
|
+
if (not default_full is pm.Undefined) and (
|
|
376
|
+
not isinstance(default_full, tuple)
|
|
377
|
+
or not self._validate_shape(default_full[0])
|
|
378
|
+
or not isinstance(default_full[1], (float, int, str))
|
|
379
|
+
):
|
|
380
|
+
raise ValueError(
|
|
381
|
+
f"default_full must be a tuple of length 2, with the first element should be shape compliant (tuple, str, int) and the second the value to fill with (given: {default_full})"
|
|
382
|
+
)
|
|
383
|
+
if not default_full is pm.Undefined and isinstance(default_full, tuple):
|
|
384
|
+
self.default_full = [
|
|
385
|
+
Reference(el) if isinstance(el, str) else el for el in default_full
|
|
386
|
+
]
|
|
387
|
+
if isinstance(self.default_full[0], tuple):
|
|
388
|
+
self.default_full[0] = tuple(
|
|
389
|
+
Reference(el) if isinstance(el, str) else el
|
|
390
|
+
for el in default_full[0]
|
|
391
|
+
)
|
|
392
|
+
self.default_full = tuple(self.default_full)
|
|
393
|
+
else:
|
|
394
|
+
self.default_full = default_full
|
|
395
|
+
if (not default_interp is pm.Undefined) and (
|
|
396
|
+
not isinstance(default_interp, tuple)
|
|
397
|
+
or not all([isinstance(el, (str, np.ndarray)) for el in default_interp])
|
|
398
|
+
):
|
|
399
|
+
raise ValueError(
|
|
400
|
+
f"default_interp must be a tuple of length 3, with elements of either str, numpy.ndarray (given: {default_interp})"
|
|
401
|
+
)
|
|
402
|
+
if not default_interp is pm.Undefined and isinstance(default_interp, tuple):
|
|
403
|
+
self.default_interp = tuple(
|
|
404
|
+
Reference(el) if isinstance(el, str) else el for el in default_interp
|
|
405
|
+
)
|
|
406
|
+
else:
|
|
407
|
+
self.default_interp = default_interp
|
|
408
|
+
if shape is pm.Undefined:
|
|
409
|
+
if self._is_ref(default):
|
|
410
|
+
shape = default
|
|
411
|
+
elif isinstance(self.default_full, tuple):
|
|
412
|
+
shape = self.default_full[0]
|
|
413
|
+
elif isinstance(self.default_interp, tuple):
|
|
414
|
+
if isinstance(self.default_interp[0], Reference):
|
|
415
|
+
shape = self.default_interp[0]
|
|
416
|
+
else:
|
|
417
|
+
shape = self.default_interp[0].shape
|
|
418
|
+
elif not (default_ref is pm.Undefined or default_ref is None):
|
|
419
|
+
shape = default_ref
|
|
420
|
+
if isinstance(shape, str):
|
|
421
|
+
shape = Reference(shape)
|
|
422
|
+
elif isinstance(shape, tuple):
|
|
423
|
+
shape = tuple(Reference(el) if isinstance(el, str) else el for el in shape)
|
|
424
|
+
self._validate_shape(shape)
|
|
425
|
+
self.shape = shape
|
|
426
|
+
if self._is_ref(default) or self._is_func(default):
|
|
427
|
+
params["instantiate"] = False
|
|
428
|
+
super().__init__(default=default, default_ref=default_ref, **params)
|
|
429
|
+
|
|
430
|
+
def _validate_shape(self, shape):
|
|
431
|
+
if not (shape is pm.Undefined or shape is None):
|
|
432
|
+
if isinstance(shape, tuple):
|
|
433
|
+
if not all([isinstance(el, (Reference, str, int)) for el in shape]):
|
|
434
|
+
raise ValueError(
|
|
435
|
+
f"shape as a tuple need to have item-types of either str,int. ([type(el) for el in shape])={[type(el) for el in shape]})"
|
|
436
|
+
)
|
|
437
|
+
elif not isinstance(shape, (Reference, str, int)):
|
|
438
|
+
raise ValueError(
|
|
439
|
+
f"shape need to be of type tuple, str, int (given: type(shape)={type(shape)})"
|
|
440
|
+
)
|
|
441
|
+
return True
|
|
442
|
+
|
|
443
|
+
def _validate_bounds(self, val, bounds, inclusive_bounds):
|
|
444
|
+
if (
|
|
445
|
+
bounds is None
|
|
446
|
+
or (val is None and self.allow_None)
|
|
447
|
+
or callable(val)
|
|
448
|
+
or isinstance(val, str)
|
|
449
|
+
):
|
|
450
|
+
return
|
|
451
|
+
vmin, vmax = bounds
|
|
452
|
+
incmin, incmax = inclusive_bounds
|
|
453
|
+
if vmax is not None:
|
|
454
|
+
if incmax is True:
|
|
455
|
+
if np.any(val > vmax):
|
|
456
|
+
raise ValueError(
|
|
457
|
+
f"{pm.parameters._validate_error_prefix(self)} must be at most "
|
|
458
|
+
f"{vmax}, not {val}."
|
|
459
|
+
)
|
|
460
|
+
else:
|
|
461
|
+
if np.any(val >= vmax):
|
|
462
|
+
raise ValueError(
|
|
463
|
+
f"{pm.parameters._validate_error_prefix(self)} must be less than "
|
|
464
|
+
f"{vmax}, not {val}."
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
if vmin is not None:
|
|
468
|
+
if incmin is True:
|
|
469
|
+
if np.any(val < vmin):
|
|
470
|
+
raise ValueError(
|
|
471
|
+
f"{pm.parameters._validate_error_prefix(self)} must be at least "
|
|
472
|
+
f"{vmin}, not {val}."
|
|
473
|
+
)
|
|
474
|
+
else:
|
|
475
|
+
if np.any(val <= vmin):
|
|
476
|
+
raise ValueError(
|
|
477
|
+
f"{pm.parameters._validate_error_prefix(self)} must be greater than "
|
|
478
|
+
f"{vmin}, not {val}."
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
def _validate_dtype(self, val, dtype):
|
|
482
|
+
if isinstance(val, np.ndarray):
|
|
483
|
+
if not np.issubdtype(val.dtype, dtype):
|
|
484
|
+
raise ValueError(
|
|
485
|
+
f"Array dtype must be a subclass of {self.dtype} but it is {val.dtype} (is validated with `numpy.issubdtype`)"
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
def _validate_class_(self, val, class_, is_instance):
|
|
489
|
+
if val is None:
|
|
490
|
+
return
|
|
491
|
+
elif isinstance(val, (Reference, Function)):
|
|
492
|
+
return
|
|
493
|
+
else:
|
|
494
|
+
if not (isinstance(val, class_)):
|
|
495
|
+
class_name = class_.__name__
|
|
496
|
+
raise ValueError(
|
|
497
|
+
f"{pm.parameters._validate_error_prefix(self)} value must be an instance of {class_name}, not {val!r}."
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
def _validate(self, val):
|
|
501
|
+
super()._validate(val)
|
|
502
|
+
self._validate_dtype(val, self.dtype)
|
|
503
|
+
self._validate_bounds(val, self.bounds, self.inclusive_bounds)
|
|
504
|
+
|
|
505
|
+
def __get__(self, obj, objtype):
|
|
506
|
+
out = super().__get__(obj, objtype)
|
|
507
|
+
if out is None:
|
|
508
|
+
if not self.default_full is None:
|
|
509
|
+
shape_ref = obj.get_shape_ref(self)
|
|
510
|
+
if not shape_ref is None:
|
|
511
|
+
if isinstance(self.default_full[1], Reference):
|
|
512
|
+
val = obj[self.default_full[1]]
|
|
513
|
+
else:
|
|
514
|
+
val = self.default_full[1]
|
|
515
|
+
return np.full(shape_ref, val)
|
|
516
|
+
elif not self.default_interp is None:
|
|
517
|
+
return obj.interp(*self.default_interp)
|
|
518
|
+
return out
|
|
519
|
+
|
|
520
|
+
def __set__(self, obj, val):
|
|
521
|
+
if not isinstance(val, str) and np.isscalar(val):
|
|
522
|
+
if self.shape is not None:
|
|
523
|
+
val = np.full(obj.get_shape_ref(self), val)
|
|
524
|
+
else:
|
|
525
|
+
raise ValueError(
|
|
526
|
+
f"Scaler values can only be assigned to AESOptArray if it has a defined shape (given: val={val})"
|
|
527
|
+
)
|
|
528
|
+
return super().__set__(obj, val)
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
class AESOptBoolean(BaseRefFunc, pm.Boolean):
|
|
532
|
+
|
|
533
|
+
__slots__ = ["default_ref"]
|
|
534
|
+
|
|
535
|
+
_slot_defaults = dict(
|
|
536
|
+
pm.Boolean._slot_defaults,
|
|
537
|
+
default=None,
|
|
538
|
+
allow_None=True,
|
|
539
|
+
default_ref=None,
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
@typing.overload
|
|
543
|
+
def __init__(
|
|
544
|
+
self,
|
|
545
|
+
default=None,
|
|
546
|
+
*,
|
|
547
|
+
allow_None=False,
|
|
548
|
+
doc=None,
|
|
549
|
+
label=None,
|
|
550
|
+
precedence=None,
|
|
551
|
+
instantiate=False,
|
|
552
|
+
constant=False,
|
|
553
|
+
readonly=False,
|
|
554
|
+
pickle_default_value=True,
|
|
555
|
+
per_instance=True,
|
|
556
|
+
allow_refs=False,
|
|
557
|
+
nested_refs=False,
|
|
558
|
+
default_ref=None,
|
|
559
|
+
): ...
|
|
560
|
+
|
|
561
|
+
def __init__(self, default=None, **param):
|
|
562
|
+
super().__init__(default=default, **param)
|
|
563
|
+
|
|
564
|
+
def _validate_value(self, val, allow_None):
|
|
565
|
+
if (
|
|
566
|
+
not (
|
|
567
|
+
val is None
|
|
568
|
+
or isinstance(val, bool)
|
|
569
|
+
or self._is_ref(val)
|
|
570
|
+
or self._is_func(val)
|
|
571
|
+
)
|
|
572
|
+
and self.default_ref is None
|
|
573
|
+
):
|
|
574
|
+
raise ValueError(
|
|
575
|
+
f"Either `value`, `default` or `default_ref` need to be set (given: {val})"
|
|
576
|
+
)
|
|
577
|
+
if self._is_ref(val) or self._is_func(val) or self.default_ref is not None:
|
|
578
|
+
return
|
|
579
|
+
super()._validate_value(val, allow_None)
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
class Reference:
|
|
583
|
+
def __init__(self, path: str) -> None:
|
|
584
|
+
if path.startswith("$ref "):
|
|
585
|
+
path = path[5:]
|
|
586
|
+
self.is_valid_object_path(path)
|
|
587
|
+
self.path = path
|
|
588
|
+
|
|
589
|
+
def is_valid_object_path(self, path):
|
|
590
|
+
if not path.startswith("."):
|
|
591
|
+
raise ValueError(
|
|
592
|
+
f"Object path need to start with `.` or `$ref ` (given: path={path})"
|
|
593
|
+
)
|
|
594
|
+
if " " in path or "\t" in path:
|
|
595
|
+
raise ValueError(
|
|
596
|
+
f"Object path can not contain spaces or tabs (given: path={path})"
|
|
597
|
+
)
|
|
598
|
+
return True
|
|
599
|
+
|
|
600
|
+
def __str__(self):
|
|
601
|
+
return f"$ref {self.path}"
|
|
602
|
+
|
|
603
|
+
def __repr__(self):
|
|
604
|
+
return self.path
|
|
605
|
+
|
|
606
|
+
def __eq__(self, val):
|
|
607
|
+
return self.path == val
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
class Function:
|
|
611
|
+
def __init__(self, method) -> None:
|
|
612
|
+
if isinstance(method, str) and method.startswith("$function "):
|
|
613
|
+
method_source = method[10:]
|
|
614
|
+
if method_source.startswith("def "):
|
|
615
|
+
method_name = method_source[4:].split("(")[0]
|
|
616
|
+
exec(method_source)
|
|
617
|
+
method = eval(method_name)
|
|
618
|
+
else:
|
|
619
|
+
method = eval(method_source)
|
|
620
|
+
method.method_source = method_source
|
|
621
|
+
|
|
622
|
+
if not callable(method):
|
|
623
|
+
raise ValueError(
|
|
624
|
+
f"`method` need to be a `callable` or string starting with `$function ` (given: method={method})"
|
|
625
|
+
)
|
|
626
|
+
self.method = method
|
|
627
|
+
|
|
628
|
+
@property
|
|
629
|
+
def source_str(self):
|
|
630
|
+
if hasattr(self.method, "method_source"):
|
|
631
|
+
return self.method.method_source
|
|
632
|
+
method_source = getsource(self.method).strip()
|
|
633
|
+
if "lambda" in method_source:
|
|
634
|
+
method_source = "lambda" + "lambda".join(method_source.split("lambda")[1:])
|
|
635
|
+
return method_source
|
|
636
|
+
|
|
637
|
+
def __str__(self):
|
|
638
|
+
return f"$function {self.source_str}"
|
|
639
|
+
|
|
640
|
+
def __repr__(self) -> str:
|
|
641
|
+
return f"<$function id={id(self.method)}>"
|