epyt-flow 0.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.
- epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +28 -0
- epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +21 -0
- epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +18 -0
- epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +134 -0
- epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +5578 -0
- epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +865 -0
- epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +131 -0
- epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +73 -0
- epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +193 -0
- epyt_flow/EPANET/EPANET/SRC_engines/genmmd.c +1000 -0
- epyt_flow/EPANET/EPANET/SRC_engines/hash.c +177 -0
- epyt_flow/EPANET/EPANET/SRC_engines/hash.h +28 -0
- epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +1151 -0
- epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +1117 -0
- epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +720 -0
- epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +476 -0
- epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +431 -0
- epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +1786 -0
- epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +468 -0
- epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +810 -0
- epyt_flow/EPANET/EPANET/SRC_engines/input1.c +707 -0
- epyt_flow/EPANET/EPANET/SRC_engines/input2.c +864 -0
- epyt_flow/EPANET/EPANET/SRC_engines/input3.c +2170 -0
- epyt_flow/EPANET/EPANET/SRC_engines/main.c +93 -0
- epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +142 -0
- epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +24 -0
- epyt_flow/EPANET/EPANET/SRC_engines/output.c +852 -0
- epyt_flow/EPANET/EPANET/SRC_engines/project.c +1359 -0
- epyt_flow/EPANET/EPANET/SRC_engines/quality.c +685 -0
- epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +743 -0
- epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +694 -0
- epyt_flow/EPANET/EPANET/SRC_engines/report.c +1489 -0
- epyt_flow/EPANET/EPANET/SRC_engines/rules.c +1362 -0
- epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +871 -0
- epyt_flow/EPANET/EPANET/SRC_engines/text.h +497 -0
- epyt_flow/EPANET/EPANET/SRC_engines/types.h +874 -0
- epyt_flow/EPANET/EPANET-MSX/MSX_Updates.txt +53 -0
- epyt_flow/EPANET/EPANET-MSX/Src/dispersion.h +27 -0
- epyt_flow/EPANET/EPANET-MSX/Src/hash.c +107 -0
- epyt_flow/EPANET/EPANET-MSX/Src/hash.h +28 -0
- epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx.h +102 -0
- epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx_export.h +42 -0
- epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.c +937 -0
- epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.h +39 -0
- epyt_flow/EPANET/EPANET-MSX/Src/mempool.c +204 -0
- epyt_flow/EPANET/EPANET-MSX/Src/mempool.h +24 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxchem.c +1285 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxcompiler.c +368 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxdict.h +42 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxdispersion.c +586 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxerr.c +116 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxfile.c +260 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.c +175 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.h +35 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxinp.c +1504 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxout.c +401 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxproj.c +791 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxqual.c +2010 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxrpt.c +400 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxtank.c +422 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxtoolkit.c +1164 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxtypes.h +551 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxutils.c +524 -0
- epyt_flow/EPANET/EPANET-MSX/Src/msxutils.h +56 -0
- epyt_flow/EPANET/EPANET-MSX/Src/newton.c +158 -0
- epyt_flow/EPANET/EPANET-MSX/Src/newton.h +34 -0
- epyt_flow/EPANET/EPANET-MSX/Src/rk5.c +287 -0
- epyt_flow/EPANET/EPANET-MSX/Src/rk5.h +39 -0
- epyt_flow/EPANET/EPANET-MSX/Src/ros2.c +293 -0
- epyt_flow/EPANET/EPANET-MSX/Src/ros2.h +35 -0
- epyt_flow/EPANET/EPANET-MSX/Src/smatrix.c +816 -0
- epyt_flow/EPANET/EPANET-MSX/Src/smatrix.h +29 -0
- epyt_flow/EPANET/EPANET-MSX/readme.txt +14 -0
- epyt_flow/EPANET/compile.sh +4 -0
- epyt_flow/VERSION +1 -0
- epyt_flow/__init__.py +24 -0
- epyt_flow/data/__init__.py +0 -0
- epyt_flow/data/benchmarks/__init__.py +11 -0
- epyt_flow/data/benchmarks/batadal.py +257 -0
- epyt_flow/data/benchmarks/batadal_data.py +28 -0
- epyt_flow/data/benchmarks/battledim.py +473 -0
- epyt_flow/data/benchmarks/battledim_data.py +51 -0
- epyt_flow/data/benchmarks/gecco_water_quality.py +267 -0
- epyt_flow/data/benchmarks/leakdb.py +592 -0
- epyt_flow/data/benchmarks/leakdb_data.py +18923 -0
- epyt_flow/data/benchmarks/water_usage.py +123 -0
- epyt_flow/data/networks.py +650 -0
- epyt_flow/gym/__init__.py +4 -0
- epyt_flow/gym/control_gyms.py +47 -0
- epyt_flow/gym/scenario_control_env.py +101 -0
- epyt_flow/metrics.py +404 -0
- epyt_flow/models/__init__.py +2 -0
- epyt_flow/models/event_detector.py +31 -0
- epyt_flow/models/sensor_interpolation_detector.py +118 -0
- epyt_flow/rest_api/__init__.py +4 -0
- epyt_flow/rest_api/base_handler.py +70 -0
- epyt_flow/rest_api/res_manager.py +95 -0
- epyt_flow/rest_api/scada_data_handler.py +476 -0
- epyt_flow/rest_api/scenario_handler.py +352 -0
- epyt_flow/rest_api/server.py +106 -0
- epyt_flow/serialization.py +438 -0
- epyt_flow/simulation/__init__.py +5 -0
- epyt_flow/simulation/events/__init__.py +6 -0
- epyt_flow/simulation/events/actuator_events.py +259 -0
- epyt_flow/simulation/events/event.py +81 -0
- epyt_flow/simulation/events/leakages.py +404 -0
- epyt_flow/simulation/events/sensor_faults.py +267 -0
- epyt_flow/simulation/events/sensor_reading_attack.py +185 -0
- epyt_flow/simulation/events/sensor_reading_event.py +170 -0
- epyt_flow/simulation/events/system_event.py +88 -0
- epyt_flow/simulation/parallel_simulation.py +147 -0
- epyt_flow/simulation/scada/__init__.py +3 -0
- epyt_flow/simulation/scada/advanced_control.py +134 -0
- epyt_flow/simulation/scada/scada_data.py +1589 -0
- epyt_flow/simulation/scada/scada_data_export.py +255 -0
- epyt_flow/simulation/scenario_config.py +608 -0
- epyt_flow/simulation/scenario_simulator.py +1897 -0
- epyt_flow/simulation/scenario_visualizer.py +61 -0
- epyt_flow/simulation/sensor_config.py +1289 -0
- epyt_flow/topology.py +290 -0
- epyt_flow/uncertainty/__init__.py +3 -0
- epyt_flow/uncertainty/model_uncertainty.py +302 -0
- epyt_flow/uncertainty/sensor_noise.py +73 -0
- epyt_flow/uncertainty/uncertainties.py +555 -0
- epyt_flow/uncertainty/utils.py +206 -0
- epyt_flow/utils.py +306 -0
- epyt_flow-0.1.0.dist-info/LICENSE +21 -0
- epyt_flow-0.1.0.dist-info/METADATA +139 -0
- epyt_flow-0.1.0.dist-info/RECORD +131 -0
- epyt_flow-0.1.0.dist-info/WHEEL +5 -0
- epyt_flow-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module provides classes for implementing different types of uncertainties.
|
|
3
|
+
"""
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from .utils import generate_deep_random_gaussian_noise, create_deep_random_pattern
|
|
8
|
+
from ..serialization import serializable, JsonSerializable, ABSOLUTE_GAUSSIAN_UNCERTAINTY_ID, \
|
|
9
|
+
RELATIVE_GAUSSIAN_UNCERTAINTY_ID, ABSOLUTE_UNIFORM_UNCERTAINTY_ID, \
|
|
10
|
+
RELATIVE_UNIFORM_UNCERTAINTY_ID, ABSOLUTE_DEEP_UNIFORM_UNCERTAINTY_ID, \
|
|
11
|
+
RELATIVE_DEEP_UNIFORM_UNCERTAINTY_ID, ABSOLUTE_DEEP_GAUSSIAN_UNCERTAINTY_ID, \
|
|
12
|
+
RELATIVE_DEEP_GAUSSIAN_UNCERTAINTY_ID, ABSOLUTE_DEEP_UNCERTAINTY_ID, \
|
|
13
|
+
RELATIVE_DEEP_UNCERTAINTY_ID, PERCENTAGE_DEVIATON_UNCERTAINTY_ID
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Uncertainty(ABC):
|
|
17
|
+
"""
|
|
18
|
+
Base class for uncertainties -- i.e. perturbations of data/signals.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
min_value : `float`, optional
|
|
23
|
+
Lower bound on the data/signal that is perturbed by this uncertainty.
|
|
24
|
+
|
|
25
|
+
The default is None.
|
|
26
|
+
max_value : `float`, optional
|
|
27
|
+
Upper bound on the data/signal that is perturbed by this uncertainty.
|
|
28
|
+
|
|
29
|
+
The default is None.
|
|
30
|
+
"""
|
|
31
|
+
def __init__(self, min_value: float = None, max_value: float = None, **kwds):
|
|
32
|
+
super().__init__(**kwds)
|
|
33
|
+
|
|
34
|
+
self.__min_value = min_value
|
|
35
|
+
self.__max_value = max_value
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def min_value(self) -> float:
|
|
39
|
+
"""
|
|
40
|
+
Gets the lower bound on the data/signal.
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
`float`
|
|
45
|
+
Lower bound.
|
|
46
|
+
"""
|
|
47
|
+
return self.__min_value
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def max_value(self) -> float:
|
|
51
|
+
"""
|
|
52
|
+
Gets the upper bound on the data/signal.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
`float`
|
|
57
|
+
Upper bound.
|
|
58
|
+
"""
|
|
59
|
+
return self.__max_value
|
|
60
|
+
|
|
61
|
+
def get_attributes(self) -> dict:
|
|
62
|
+
"""
|
|
63
|
+
Gets all attributes to be serialized -- these attributes are passed to the
|
|
64
|
+
constructor when the object is deserialized.
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
`dict`
|
|
69
|
+
Dictionary of attributes -- i.e. pairs of attribute name + value.
|
|
70
|
+
"""
|
|
71
|
+
return {"min_value": self.__min_value, "max_value": self.__max_value}
|
|
72
|
+
|
|
73
|
+
def __eq__(self, other) -> bool:
|
|
74
|
+
if not isinstance(other, Uncertainty):
|
|
75
|
+
raise TypeError("Can not compare 'Uncertainty' instance " +
|
|
76
|
+
f"with '{type(other)}' instance")
|
|
77
|
+
|
|
78
|
+
return self.__min_value == other.min_value and self.__max_value == other.max_value
|
|
79
|
+
|
|
80
|
+
def __str__(self) -> str:
|
|
81
|
+
return f"min_value: {self.__min_value} max_value: {self.__max_value}"
|
|
82
|
+
|
|
83
|
+
def clip(self, data: np.ndarray) -> np.ndarray:
|
|
84
|
+
"""
|
|
85
|
+
Clips values in a given array -- i.e. every value must be in [min_value, max_value].
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
data : `numpy.ndarray`
|
|
90
|
+
Array to be clipped.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
`numpy.ndarray`
|
|
95
|
+
Clipped data.
|
|
96
|
+
"""
|
|
97
|
+
if self.__min_value is not None:
|
|
98
|
+
data = np.min([data, self.__min_value])
|
|
99
|
+
if self.__max_value is not None:
|
|
100
|
+
data = np.max([data, self.__max_value])
|
|
101
|
+
|
|
102
|
+
return data
|
|
103
|
+
|
|
104
|
+
@abstractmethod
|
|
105
|
+
def apply(self, data: float):
|
|
106
|
+
"""
|
|
107
|
+
Applies the uncertainty to a single value.
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
----------
|
|
111
|
+
data : `float`
|
|
112
|
+
The value to which the uncertainty is applied.
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
`float`
|
|
117
|
+
Uncertainty applied to 'data'.
|
|
118
|
+
"""
|
|
119
|
+
raise NotImplementedError()
|
|
120
|
+
|
|
121
|
+
def apply_batch(self, data: np.ndarray) -> np.ndarray:
|
|
122
|
+
"""
|
|
123
|
+
Applies the uncertainty to an array of values.
|
|
124
|
+
|
|
125
|
+
Parameters
|
|
126
|
+
----------
|
|
127
|
+
data : `numpy.ndarray`
|
|
128
|
+
Array of values to which the uncertainty is applied.
|
|
129
|
+
|
|
130
|
+
Returns
|
|
131
|
+
-------
|
|
132
|
+
`numpy.ndarray`
|
|
133
|
+
Uncertainty applied to `data`.
|
|
134
|
+
"""
|
|
135
|
+
for t in range(data.shape[0]):
|
|
136
|
+
data[t] = self.apply(data[t])
|
|
137
|
+
return data
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class GaussianUncertainty(Uncertainty):
|
|
141
|
+
"""
|
|
142
|
+
Base class implementing Gaussian uncertainty
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
mean : `float`, optional
|
|
147
|
+
Mean of the Gaussian noise.
|
|
148
|
+
|
|
149
|
+
If None, mean will be assigned a random value between 0 and 1.
|
|
150
|
+
|
|
151
|
+
The default is None.
|
|
152
|
+
scale : `float`, optional
|
|
153
|
+
Scale (i.e. standard deviation) of the Gaussian noise.
|
|
154
|
+
|
|
155
|
+
If None, scale will be assigned a random value between 0 and 1.
|
|
156
|
+
|
|
157
|
+
The default is None.
|
|
158
|
+
"""
|
|
159
|
+
def __init__(self, mean: float = None, scale: float = None, **kwds):
|
|
160
|
+
super().__init__(**kwds)
|
|
161
|
+
|
|
162
|
+
self.__mean = np.random.rand() if mean is None else mean
|
|
163
|
+
self.__scale = np.random.rand() if scale is None else scale
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def mean(self) -> float:
|
|
167
|
+
"""
|
|
168
|
+
Gets the mean of the Gaussian noise.
|
|
169
|
+
|
|
170
|
+
Returns
|
|
171
|
+
-------
|
|
172
|
+
`float`
|
|
173
|
+
Mean of the Gaussian noise.
|
|
174
|
+
"""
|
|
175
|
+
return self.__mean
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def scale(self) -> float:
|
|
179
|
+
"""
|
|
180
|
+
Gets the scale (i.e. standard deviation) of the Gaussian noise.
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
`float`
|
|
185
|
+
Scale (i.e. standard deviation) of the Gaussian noise.
|
|
186
|
+
"""
|
|
187
|
+
return self.__scale
|
|
188
|
+
|
|
189
|
+
def get_attributes(self) -> dict:
|
|
190
|
+
return super().get_attributes() | {"mean": self.__mean, "scale": self.__scale}
|
|
191
|
+
|
|
192
|
+
def __eq__(self, other) -> bool:
|
|
193
|
+
if not isinstance(other, GaussianUncertainty):
|
|
194
|
+
raise TypeError("Can not compare 'GaussianUncertainty' instance " +
|
|
195
|
+
f"with '{type(other)}' instance")
|
|
196
|
+
|
|
197
|
+
return super().__eq__(other) and self.__mean == other.mean and self.__scale == other.scale
|
|
198
|
+
|
|
199
|
+
def __str__(self) -> str:
|
|
200
|
+
return super().__str__() + f" mean: {self.__mean} scale: {self.__scale}"
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@serializable(ABSOLUTE_GAUSSIAN_UNCERTAINTY_ID, ".epytflow_uncertainty_absolute_gaussian")
|
|
204
|
+
class AbsoluteGaussianUncertainty(GaussianUncertainty, JsonSerializable):
|
|
205
|
+
"""
|
|
206
|
+
Class implementing absolute Gaussian uncertainty -- i.e. Gaussian noise is added to the data.
|
|
207
|
+
"""
|
|
208
|
+
def apply(self, data: float) -> float:
|
|
209
|
+
data += np.random.normal(loc=self.mean, scale=self.scale)
|
|
210
|
+
|
|
211
|
+
return self.clip(data)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@serializable(RELATIVE_GAUSSIAN_UNCERTAINTY_ID, ".epytflow_uncertainty_relative_gaussian")
|
|
215
|
+
class RelativeGaussianUncertainty(GaussianUncertainty, JsonSerializable):
|
|
216
|
+
"""
|
|
217
|
+
Class implementing relative Gaussian uncertainty -- i.e. data is perturbed by Gaussian noise
|
|
218
|
+
centered at zero.
|
|
219
|
+
|
|
220
|
+
Parameters
|
|
221
|
+
----------
|
|
222
|
+
scale : `float`, optional
|
|
223
|
+
Scale (i.e. standard deviation) of the Gaussian noise.
|
|
224
|
+
|
|
225
|
+
If None, scale will be assigned a random value between 0 and 1.
|
|
226
|
+
|
|
227
|
+
The default is None.
|
|
228
|
+
"""
|
|
229
|
+
def __init__(self, scale: float = None, **kwds):
|
|
230
|
+
super().__init__(mean=0., scale=scale, **kwds)
|
|
231
|
+
|
|
232
|
+
def apply(self, data: float) -> float:
|
|
233
|
+
data += np.random.normal(loc=0, scale=self.scale)
|
|
234
|
+
|
|
235
|
+
return self.clip(data)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class UniformUncertainty(Uncertainty):
|
|
239
|
+
"""
|
|
240
|
+
Base class implementing uniform uncertainty.
|
|
241
|
+
|
|
242
|
+
Parameters
|
|
243
|
+
----------
|
|
244
|
+
low : `float`, optional
|
|
245
|
+
Lower bound of the uniform noise.
|
|
246
|
+
|
|
247
|
+
The default is zero.
|
|
248
|
+
high : `float`, optional
|
|
249
|
+
Upper bound of the uniform noise.
|
|
250
|
+
|
|
251
|
+
The default is one.
|
|
252
|
+
"""
|
|
253
|
+
def __init__(self, low: float = 0., high: float = 1., **kwds):
|
|
254
|
+
super().__init__(**kwds)
|
|
255
|
+
|
|
256
|
+
self.__low = low
|
|
257
|
+
self.__high = high
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def low(self) -> float:
|
|
261
|
+
"""
|
|
262
|
+
Gets the lower bound of the uniform noise.
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
`float`
|
|
267
|
+
Lower bound of the uniform noise.
|
|
268
|
+
"""
|
|
269
|
+
return self.__low
|
|
270
|
+
|
|
271
|
+
@property
|
|
272
|
+
def high(self) -> float:
|
|
273
|
+
"""
|
|
274
|
+
Gets the upper bound of the uniform noise.
|
|
275
|
+
|
|
276
|
+
Returns
|
|
277
|
+
-------
|
|
278
|
+
`float`
|
|
279
|
+
Upper bound of the uniform noise.
|
|
280
|
+
"""
|
|
281
|
+
return self.__high
|
|
282
|
+
|
|
283
|
+
def get_attributes(self) -> dict:
|
|
284
|
+
return super().get_attributes() | {"low": self.__low, "high": self.__high}
|
|
285
|
+
|
|
286
|
+
def __eq__(self, other) -> bool:
|
|
287
|
+
if not isinstance(other, UniformUncertainty):
|
|
288
|
+
raise TypeError("Can not compare 'UniformUncertainty' instance " +
|
|
289
|
+
f"with '{type(other)}' instance")
|
|
290
|
+
|
|
291
|
+
return super().__eq__(other) and self.__low == other.low and self.__high == other.high
|
|
292
|
+
|
|
293
|
+
def __str__(self) -> str:
|
|
294
|
+
return super().__str__() + f" low: {self.__low} high: {self.__high}"
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@serializable(ABSOLUTE_UNIFORM_UNCERTAINTY_ID, ".epytflow_uncertainty_absolute_uniform")
|
|
298
|
+
class AbsoluteUniformUncertainty(UniformUncertainty, JsonSerializable):
|
|
299
|
+
"""
|
|
300
|
+
Class implementing absolute uniform uncertainty -- i.e. uniform noise is added to the data.
|
|
301
|
+
"""
|
|
302
|
+
def apply(self, data: float) -> float:
|
|
303
|
+
data += np.random.uniform(low=self.low, high=self.high)
|
|
304
|
+
|
|
305
|
+
return self.clip(data)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
@serializable(RELATIVE_UNIFORM_UNCERTAINTY_ID, ".epytflow_uncertainty_relative_uniform")
|
|
309
|
+
class RelativeUniformUncertainty(UniformUncertainty, JsonSerializable):
|
|
310
|
+
"""
|
|
311
|
+
Class implementing relative uniform uncertainty -- i.e. data is multiplied by uniform noise.
|
|
312
|
+
"""
|
|
313
|
+
def apply(self, data: float) -> float:
|
|
314
|
+
data *= np.random.uniform(low=self.low, high=self.high)
|
|
315
|
+
|
|
316
|
+
return self.clip(data)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
@serializable(PERCENTAGE_DEVIATON_UNCERTAINTY_ID, ".epytflow_uncertainty_percentage_deviation")
|
|
320
|
+
class PercentageDeviationUncertainty(UniformUncertainty, JsonSerializable):
|
|
321
|
+
"""
|
|
322
|
+
Class implementing a uniform data deviation -- i.e. the data can deviate up to some percentage
|
|
323
|
+
from its original value.
|
|
324
|
+
|
|
325
|
+
Parameters
|
|
326
|
+
----------
|
|
327
|
+
deviation_percentage : `float`
|
|
328
|
+
Percentage (0-1) the data can deviate from its original value.
|
|
329
|
+
"""
|
|
330
|
+
def __init__(self, deviation_percentage: float, **kwds):
|
|
331
|
+
if not isinstance(deviation_percentage, float):
|
|
332
|
+
raise TypeError("'deviation_percentage' must be an instance of 'float' " +
|
|
333
|
+
f"but not of {type(deviation_percentage)}")
|
|
334
|
+
if not 0 < deviation_percentage < 1:
|
|
335
|
+
raise ValueError("'deviation_percentage' must be in (0,1)")
|
|
336
|
+
|
|
337
|
+
super().__init__(low=1. - deviation_percentage, high=1. + deviation_percentage, **kwds)
|
|
338
|
+
|
|
339
|
+
def apply(self, data: float) -> float:
|
|
340
|
+
data *= np.random.uniform(low=self.low, high=self.high)
|
|
341
|
+
|
|
342
|
+
return self.clip(data)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
class DeepUniformUncertainty(Uncertainty):
|
|
346
|
+
"""
|
|
347
|
+
Base class implementing deep uniform uncertainty.
|
|
348
|
+
"""
|
|
349
|
+
def __init__(self, **kwds):
|
|
350
|
+
super().__init__(**kwds)
|
|
351
|
+
|
|
352
|
+
self.__create_uncertainties()
|
|
353
|
+
|
|
354
|
+
def __create_uncertainties(self, n_samples: int = 500):
|
|
355
|
+
self._uncertainties_idx = 0
|
|
356
|
+
rand_low = create_deep_random_pattern(n_samples)
|
|
357
|
+
rand_high = create_deep_random_pattern(n_samples)
|
|
358
|
+
rand_low = np.minimum(rand_low, rand_high)
|
|
359
|
+
rand_high = np.maximum(rand_low, rand_high)
|
|
360
|
+
self._uncertainties = [np.random.uniform(low, high)
|
|
361
|
+
for low, high in zip(rand_low, rand_high)]
|
|
362
|
+
|
|
363
|
+
@abstractmethod
|
|
364
|
+
def apply(self, data: float) -> float:
|
|
365
|
+
self._uncertainties_idx += 1
|
|
366
|
+
if self._uncertainties_idx >= len(self._uncertainties):
|
|
367
|
+
self.__create_uncertainties()
|
|
368
|
+
|
|
369
|
+
return self.clip(data)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
@serializable(ABSOLUTE_DEEP_UNIFORM_UNCERTAINTY_ID, ".epytflow_uncertainty_absolute_deep_uniform")
|
|
373
|
+
class AbsoluteDeepUniformUncertainty(DeepUniformUncertainty, JsonSerializable):
|
|
374
|
+
"""
|
|
375
|
+
Class implementing absolute deep uniform uncertainty -- i.e. random uniform noise
|
|
376
|
+
(shape of the noise is changing over time) is added to the data.
|
|
377
|
+
"""
|
|
378
|
+
def apply(self, data: float) -> float:
|
|
379
|
+
data += self._uncertainties[self._uncertainties_idx]
|
|
380
|
+
|
|
381
|
+
return super().apply(data)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
@serializable(RELATIVE_DEEP_UNIFORM_UNCERTAINTY_ID, ".epytflow_uncertainty_relative_deep_uniform")
|
|
385
|
+
class RelativeDeepUniformUncertainty(DeepUniformUncertainty, JsonSerializable):
|
|
386
|
+
"""
|
|
387
|
+
Class implementing relative deep uniform uncertainty -- i.e. data is multiplied by
|
|
388
|
+
random uniform noise (shape of the noise is changing over time).
|
|
389
|
+
"""
|
|
390
|
+
def apply(self, data: float) -> float:
|
|
391
|
+
data *= self._uncertainties[self._uncertainties_idx]
|
|
392
|
+
|
|
393
|
+
return super().apply(data)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
class DeepGaussianUncertainty(Uncertainty, JsonSerializable):
|
|
397
|
+
"""
|
|
398
|
+
Base class implementing deep Gaussian uncertainty.
|
|
399
|
+
|
|
400
|
+
Parameters
|
|
401
|
+
----------
|
|
402
|
+
mean : `float`, optional
|
|
403
|
+
Fixed mean of Gaussian noise.
|
|
404
|
+
If None, random means are generated.
|
|
405
|
+
|
|
406
|
+
The default is None.
|
|
407
|
+
"""
|
|
408
|
+
def __init__(self, mean: float = None, **kwds):
|
|
409
|
+
self.__mean = mean
|
|
410
|
+
|
|
411
|
+
super().__init__(**kwds)
|
|
412
|
+
|
|
413
|
+
self.__create_uncertainties()
|
|
414
|
+
|
|
415
|
+
def __create_uncertainties(self, n_samples: int = 500) -> None:
|
|
416
|
+
self._uncertainties_idx = 0
|
|
417
|
+
self._uncertainties = generate_deep_random_gaussian_noise(n_samples, self.__mean)
|
|
418
|
+
|
|
419
|
+
@abstractmethod
|
|
420
|
+
def apply(self, data: float) -> float:
|
|
421
|
+
self._uncertainties_idx += 1
|
|
422
|
+
if self._uncertainties_idx >= len(self._uncertainties):
|
|
423
|
+
self.__create_uncertainties()
|
|
424
|
+
|
|
425
|
+
return self.clip(data)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
@serializable(ABSOLUTE_DEEP_GAUSSIAN_UNCERTAINTY_ID, ".epytflow_uncertainty_absolute_deep_gaussian")
|
|
429
|
+
class AbsoluteDeepGaussianUncertainty(DeepGaussianUncertainty, JsonSerializable):
|
|
430
|
+
"""
|
|
431
|
+
Class implementing absolute deep Gaussian uncertainty -- i.e. random Gaussian noise
|
|
432
|
+
(mean and variance are changing over time) is added to the data.
|
|
433
|
+
"""
|
|
434
|
+
def apply(self, data: float) -> float:
|
|
435
|
+
data += self._uncertainties[self._uncertainties_idx]
|
|
436
|
+
|
|
437
|
+
return super().apply(data)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
@serializable(RELATIVE_DEEP_GAUSSIAN_UNCERTAINTY_ID, ".epytflow_uncertainty_relative_deep_gaussian")
|
|
441
|
+
class RelativeDeepGaussianUncertainty(DeepGaussianUncertainty, JsonSerializable):
|
|
442
|
+
"""
|
|
443
|
+
Class implementing realtive deep Gaussian uncertainty -- i.e. data is multiplied by
|
|
444
|
+
random Gaussian noise (mean and variance are changing over time).
|
|
445
|
+
"""
|
|
446
|
+
def __init__(self, **kwds):
|
|
447
|
+
super().__init__(mean=0., **kwds)
|
|
448
|
+
|
|
449
|
+
def apply(self, data: float) -> float:
|
|
450
|
+
data += self._uncertainties[self._uncertainties_idx]
|
|
451
|
+
|
|
452
|
+
return super().apply(data)
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
class DeepUncertainty(Uncertainty):
|
|
456
|
+
"""
|
|
457
|
+
Base class implementing deep uncertainty.
|
|
458
|
+
|
|
459
|
+
Parameters
|
|
460
|
+
----------
|
|
461
|
+
min_noise_value : `float`
|
|
462
|
+
Lower bound on the noise.
|
|
463
|
+
max_noise_value : `float`
|
|
464
|
+
Upper bound on the noise.
|
|
465
|
+
"""
|
|
466
|
+
def __init__(self, min_noise_value: float = 0., max_noise_value: float = 1., **kwds):
|
|
467
|
+
super().__init__(**kwds)
|
|
468
|
+
|
|
469
|
+
self.__min_noise_value = min_noise_value
|
|
470
|
+
self.__max_noise_value = max_noise_value
|
|
471
|
+
|
|
472
|
+
self._uncertainties_idx = None
|
|
473
|
+
self._uncertainties = None
|
|
474
|
+
self.__create_uncertainties()
|
|
475
|
+
|
|
476
|
+
@property
|
|
477
|
+
def min_noise_value(self) -> float:
|
|
478
|
+
"""
|
|
479
|
+
Gets the lower bound on the noise.
|
|
480
|
+
|
|
481
|
+
Returns
|
|
482
|
+
-------
|
|
483
|
+
`float`
|
|
484
|
+
Lower bound on the noise.
|
|
485
|
+
"""
|
|
486
|
+
return self.__min_noise_value
|
|
487
|
+
|
|
488
|
+
@property
|
|
489
|
+
def max_noise_value(self) -> float:
|
|
490
|
+
"""
|
|
491
|
+
Gets the upper bound on the noise.
|
|
492
|
+
|
|
493
|
+
Returns
|
|
494
|
+
-------
|
|
495
|
+
`float`
|
|
496
|
+
Upper bound on the noise.
|
|
497
|
+
"""
|
|
498
|
+
return self.__max_noise_value
|
|
499
|
+
|
|
500
|
+
def get_attributes(self) -> dict:
|
|
501
|
+
return super().get_attributes() | {"min_noise_value": self.__min_noise_value,
|
|
502
|
+
"max_noise_value": self.__max_noise_value}
|
|
503
|
+
|
|
504
|
+
def __eq__(self, other) -> bool:
|
|
505
|
+
if not isinstance(other, DeepUncertainty):
|
|
506
|
+
raise TypeError("Can not compare 'DeepUncertainty' instance " +
|
|
507
|
+
f"with '{type(other)}' instance")
|
|
508
|
+
|
|
509
|
+
return super().__eq__(other) and self.__min_noise_value == other.min_noise_value and \
|
|
510
|
+
self.__max_noise_value == other.max_noise_value
|
|
511
|
+
|
|
512
|
+
def __str__(self) -> str:
|
|
513
|
+
return super().__str__() + f" min_noise_value: {self.__min_noise_value} " +\
|
|
514
|
+
f"max_noise_value: {self.__max_noise_value}"
|
|
515
|
+
|
|
516
|
+
def __create_uncertainties(self, n_samples: int = 500) -> None:
|
|
517
|
+
init_value = None
|
|
518
|
+
if self._uncertainties_idx is not None:
|
|
519
|
+
init_value = self._uncertainties[-1]
|
|
520
|
+
|
|
521
|
+
self._uncertainties_idx = 0
|
|
522
|
+
self._uncertainties = create_deep_random_pattern(n_samples, self.__min_noise_value,
|
|
523
|
+
self.__max_noise_value, init_value)
|
|
524
|
+
|
|
525
|
+
@abstractmethod
|
|
526
|
+
def apply(self, data: float) -> float:
|
|
527
|
+
self._uncertainties_idx += 1
|
|
528
|
+
if self._uncertainties_idx >= len(self._uncertainties):
|
|
529
|
+
self.__create_uncertainties()
|
|
530
|
+
|
|
531
|
+
return self.clip(data)
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
@serializable(ABSOLUTE_DEEP_UNCERTAINTY_ID, ".epytflow_uncertainty_absolute_deep")
|
|
535
|
+
class AbsoluteDeepUncertainty(DeepUncertainty, JsonSerializable):
|
|
536
|
+
"""
|
|
537
|
+
Class implementing absolute deep uncertainty -- i.e. completely random noise
|
|
538
|
+
is added to the data.
|
|
539
|
+
"""
|
|
540
|
+
def apply(self, data: float) -> float:
|
|
541
|
+
data += self._uncertainties[self._uncertainties_idx]
|
|
542
|
+
|
|
543
|
+
return super().apply(data)
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
@serializable(RELATIVE_DEEP_UNCERTAINTY_ID, ".epytflow_uncertainty_relative_deep")
|
|
547
|
+
class RelativeDeepUncertainty(DeepUncertainty, JsonSerializable):
|
|
548
|
+
"""
|
|
549
|
+
Class implementing relative deep uncertainty -- i.e. data is multiplied by
|
|
550
|
+
completely random noise.
|
|
551
|
+
"""
|
|
552
|
+
def apply(self, data: float) -> float:
|
|
553
|
+
data *= self._uncertainties[self._uncertainties_idx]
|
|
554
|
+
|
|
555
|
+
return super().apply(data)
|