epyt-flow 0.9.0__py3-none-any.whl → 0.11.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/VERSION +1 -1
- epyt_flow/data/networks.py +27 -14
- epyt_flow/gym/control_gyms.py +8 -0
- epyt_flow/gym/scenario_control_env.py +17 -4
- epyt_flow/metrics.py +5 -0
- epyt_flow/models/event_detector.py +5 -0
- epyt_flow/models/sensor_interpolation_detector.py +5 -0
- epyt_flow/serialization.py +5 -0
- epyt_flow/simulation/__init__.py +0 -1
- epyt_flow/simulation/events/actuator_events.py +7 -1
- epyt_flow/simulation/events/sensor_reading_attack.py +16 -3
- epyt_flow/simulation/events/sensor_reading_event.py +18 -3
- epyt_flow/simulation/scada/__init__.py +3 -1
- epyt_flow/simulation/scada/advanced_control.py +6 -2
- epyt_flow/simulation/scada/complex_control.py +625 -0
- epyt_flow/simulation/scada/custom_control.py +134 -0
- epyt_flow/simulation/scada/scada_data.py +547 -8
- epyt_flow/simulation/scada/simple_control.py +317 -0
- epyt_flow/simulation/scenario_config.py +87 -26
- epyt_flow/simulation/scenario_simulator.py +865 -51
- epyt_flow/simulation/sensor_config.py +34 -2
- epyt_flow/topology.py +16 -0
- epyt_flow/uncertainty/model_uncertainty.py +80 -62
- epyt_flow/uncertainty/sensor_noise.py +15 -4
- epyt_flow/uncertainty/uncertainties.py +71 -18
- epyt_flow/uncertainty/utils.py +40 -13
- epyt_flow/utils.py +15 -1
- epyt_flow/visualization/__init__.py +2 -0
- epyt_flow/{simulation → visualization}/scenario_visualizer.py +429 -586
- epyt_flow/visualization/visualization_utils.py +611 -0
- {epyt_flow-0.9.0.dist-info → epyt_flow-0.11.0.dist-info}/LICENSE +1 -1
- {epyt_flow-0.9.0.dist-info → epyt_flow-0.11.0.dist-info}/METADATA +18 -6
- {epyt_flow-0.9.0.dist-info → epyt_flow-0.11.0.dist-info}/RECORD +35 -30
- {epyt_flow-0.9.0.dist-info → epyt_flow-0.11.0.dist-info}/WHEEL +1 -1
- {epyt_flow-0.9.0.dist-info → epyt_flow-0.11.0.dist-info}/top_level.txt +0 -0
epyt_flow/uncertainty/utils.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Module provides some helper functions regarding the implementation of uncertainty.
|
|
3
3
|
"""
|
|
4
4
|
import numpy as np
|
|
5
|
+
from numpy.random import Generator
|
|
5
6
|
from scipy.ndimage import gaussian_filter1d
|
|
6
7
|
|
|
7
8
|
|
|
@@ -54,7 +55,8 @@ def scale_to_range(pattern: np.ndarray, min_value: float, max_value: float) -> n
|
|
|
54
55
|
+ min_value for x in pattern]
|
|
55
56
|
|
|
56
57
|
|
|
57
|
-
def generate_random_gaussian_noise(n_samples: int)
|
|
58
|
+
def generate_random_gaussian_noise(n_samples: int, np_rand_gen: Generator = np.random.default_rng()
|
|
59
|
+
) -> np.ndarray:
|
|
58
60
|
"""
|
|
59
61
|
Generates Gaussian noise using a random mean ([0,1]) and random standard deviation ([0,1]).
|
|
60
62
|
|
|
@@ -62,16 +64,23 @@ def generate_random_gaussian_noise(n_samples: int):
|
|
|
62
64
|
----------
|
|
63
65
|
n_samples : `int`
|
|
64
66
|
Number of random samples.
|
|
67
|
+
np_rand_generator : `numpy.random.Generator <https://numpy.org/doc/stable/reference/random/generator.html#numpy.random.Generator>`_, optional
|
|
68
|
+
The random number generator that is going to be used.
|
|
69
|
+
|
|
70
|
+
The default is the default BitGenerator (PCG64) as constructed by
|
|
71
|
+
`numpy.random.default_rng() <https://numpy.org/doc/stable/reference/random/generator.html#numpy.random.default_rng>`_.
|
|
65
72
|
|
|
66
73
|
Returns
|
|
67
74
|
-------
|
|
68
75
|
`numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
69
76
|
Gaussian noise.
|
|
70
77
|
"""
|
|
71
|
-
return
|
|
78
|
+
return np_rand_gen.normal(np.random.rand(), np.random.rand(), size=n_samples)
|
|
72
79
|
|
|
73
80
|
|
|
74
|
-
def generate_deep_random_gaussian_noise(n_samples: int, mean: float = None
|
|
81
|
+
def generate_deep_random_gaussian_noise(n_samples: int, mean: float = None,
|
|
82
|
+
np_rand_gen: Generator = np.random.default_rng()
|
|
83
|
+
) -> np.ndarray:
|
|
75
84
|
"""
|
|
76
85
|
Generates random Gaussian noise where the standard deviations (and mean) are changing over time.
|
|
77
86
|
|
|
@@ -84,6 +93,11 @@ def generate_deep_random_gaussian_noise(n_samples: int, mean: float = None):
|
|
|
84
93
|
If None, random means are generated.
|
|
85
94
|
|
|
86
95
|
The default is None.
|
|
96
|
+
np_rand_generator : `numpy.random.Generator <https://numpy.org/doc/stable/reference/random/generator.html#numpy.random.Generator>`_, optional
|
|
97
|
+
The random number generator that is going to be used.
|
|
98
|
+
|
|
99
|
+
The default is the default BitGenerator (PCG64) as constructed by
|
|
100
|
+
`numpy.random.default_rng() <https://numpy.org/doc/stable/reference/random/generator.html#numpy.random.default_rng>`_.
|
|
87
101
|
|
|
88
102
|
Returns
|
|
89
103
|
-------
|
|
@@ -93,17 +107,19 @@ def generate_deep_random_gaussian_noise(n_samples: int, mean: float = None):
|
|
|
93
107
|
noise = []
|
|
94
108
|
|
|
95
109
|
if mean is None:
|
|
96
|
-
mean = create_deep_random_pattern(n_samples, min_value=-1., max_value=1
|
|
110
|
+
mean = create_deep_random_pattern(n_samples, min_value=-1., max_value=1.,
|
|
111
|
+
np_rand_gen=np_rand_gen)
|
|
97
112
|
else:
|
|
98
113
|
mean = [mean] * n_samples
|
|
99
|
-
rand_std = create_deep_random_pattern(n_samples)
|
|
100
|
-
noise = np.array([
|
|
114
|
+
rand_std = create_deep_random_pattern(n_samples, np_rand_gen=np_rand_gen)
|
|
115
|
+
noise = np.array([np_rand_gen.normal(m, s) for m, s in zip(mean, rand_std)])
|
|
101
116
|
|
|
102
117
|
return noise
|
|
103
118
|
|
|
104
119
|
|
|
105
120
|
def create_deep_random_pattern(n_samples: int, min_value: float = 0., max_value: float = 1.,
|
|
106
|
-
init_value: float = None
|
|
121
|
+
init_value: float = None,
|
|
122
|
+
np_rand_gen: Generator = np.random.default_rng()) -> np.ndarray:
|
|
107
123
|
"""
|
|
108
124
|
Generates a random pattern.
|
|
109
125
|
|
|
@@ -124,6 +140,11 @@ def create_deep_random_pattern(n_samples: int, min_value: float = 0., max_value:
|
|
|
124
140
|
If None, a random value is used.
|
|
125
141
|
|
|
126
142
|
The default is None.
|
|
143
|
+
np_rand_generator : `numpy.random.Generator <https://numpy.org/doc/stable/reference/random/generator.html#numpy.random.Generator>`_, optional
|
|
144
|
+
The random number generator that is going to be used.
|
|
145
|
+
|
|
146
|
+
The default is the default BitGenerator (PCG64) as constructed by
|
|
147
|
+
`numpy.random.default_rng() <https://numpy.org/doc/stable/reference/random/generator.html#numpy.random.default_rng>`_.
|
|
127
148
|
|
|
128
149
|
Returns
|
|
129
150
|
-------
|
|
@@ -138,7 +159,7 @@ def create_deep_random_pattern(n_samples: int, min_value: float = 0., max_value:
|
|
|
138
159
|
start_value = pattern[-1]
|
|
139
160
|
|
|
140
161
|
pattern += _create_deep_random_pattern(start_value, min_value=min_value,
|
|
141
|
-
max_value=max_value)
|
|
162
|
+
max_value=max_value, np_rand_gen=np_rand_gen)
|
|
142
163
|
|
|
143
164
|
pattern = pattern[:n_samples]
|
|
144
165
|
|
|
@@ -149,7 +170,8 @@ def create_deep_random_pattern(n_samples: int, min_value: float = 0., max_value:
|
|
|
149
170
|
|
|
150
171
|
|
|
151
172
|
def _create_deep_random_pattern(start_value: float = None, min_length: int = 2, max_length: int = 5,
|
|
152
|
-
min_value: float = None, max_value: float = None
|
|
173
|
+
min_value: float = None, max_value: float = None,
|
|
174
|
+
np_rand_gen: Generator = np.random.default_rng()) -> np.ndarray:
|
|
153
175
|
"""
|
|
154
176
|
Generates a random pattern of random length.
|
|
155
177
|
|
|
@@ -176,6 +198,11 @@ def _create_deep_random_pattern(start_value: float = None, min_length: int = 2,
|
|
|
176
198
|
Upper bound of the pattern.
|
|
177
199
|
|
|
178
200
|
The default is one.
|
|
201
|
+
np_rand_generator : `numpy.random.Generator <https://numpy.org/doc/stable/reference/random/generator.html#numpy.random.Generator>`_, optional
|
|
202
|
+
The random number generator that is going to be used.
|
|
203
|
+
|
|
204
|
+
The default is the default BitGenerator (PCG64) as constructed by
|
|
205
|
+
`numpy.random.default_rng() <https://numpy.org/doc/stable/reference/random/generator.html#numpy.random.default_rng>`_.
|
|
179
206
|
|
|
180
207
|
Returns
|
|
181
208
|
-------
|
|
@@ -186,16 +213,16 @@ def _create_deep_random_pattern(start_value: float = None, min_length: int = 2,
|
|
|
186
213
|
|
|
187
214
|
# Random parameters of pattern
|
|
188
215
|
if start_value is None:
|
|
189
|
-
start_value =
|
|
190
|
-
length =
|
|
191
|
-
vec =
|
|
216
|
+
start_value = np_rand_gen.random()
|
|
217
|
+
length = np_rand_gen.integers(low=min_length, high=max_length)
|
|
218
|
+
vec = np_rand_gen.choice([-.1, .1])
|
|
192
219
|
|
|
193
220
|
# Generate pattern
|
|
194
221
|
cur_value = start_value
|
|
195
222
|
pattern.append(start_value)
|
|
196
223
|
|
|
197
224
|
for _ in range(length):
|
|
198
|
-
cur_value = cur_value +
|
|
225
|
+
cur_value = cur_value + np_rand_gen.random() * vec
|
|
199
226
|
pattern.append(cur_value)
|
|
200
227
|
if min_value is not None and max_value is not None:
|
|
201
228
|
if cur_value < min_value:
|
epyt_flow/utils.py
CHANGED
|
@@ -314,7 +314,8 @@ def plot_timeseries_prediction(y: np.ndarray, y_pred: np.ndarray,
|
|
|
314
314
|
return ax
|
|
315
315
|
|
|
316
316
|
|
|
317
|
-
def download_if_necessary(download_path: str, url: str, verbose: bool = True
|
|
317
|
+
def download_if_necessary(download_path: str, url: str, verbose: bool = True,
|
|
318
|
+
backup_urls: list[str] = []) -> None:
|
|
318
319
|
"""
|
|
319
320
|
Downloads a file from a given URL if it does not already exist in a given path.
|
|
320
321
|
|
|
@@ -331,6 +332,10 @@ def download_if_necessary(download_path: str, url: str, verbose: bool = True) ->
|
|
|
331
332
|
If True, a progress bar is shown while downloading the file.
|
|
332
333
|
|
|
333
334
|
The default is True.
|
|
335
|
+
backup_urls : `list[str]`, optional
|
|
336
|
+
List of alternative URLs that are being tried in the case that downloading from 'url' fails.
|
|
337
|
+
|
|
338
|
+
The default is an empty list.
|
|
334
339
|
"""
|
|
335
340
|
folder_path = str(Path(download_path).parent.absolute())
|
|
336
341
|
create_path_if_not_exist(folder_path)
|
|
@@ -338,6 +343,15 @@ def download_if_necessary(download_path: str, url: str, verbose: bool = True) ->
|
|
|
338
343
|
if not os.path.isfile(download_path):
|
|
339
344
|
response = requests.get(url, stream=verbose, allow_redirects=True, timeout=1000)
|
|
340
345
|
|
|
346
|
+
if response.status_code != 200:
|
|
347
|
+
for backup_url in backup_urls:
|
|
348
|
+
response = requests.get(backup_url, stream=verbose, allow_redirects=True,
|
|
349
|
+
timeout=1000)
|
|
350
|
+
if response.status_code == 200:
|
|
351
|
+
break
|
|
352
|
+
if response.status_code != 200:
|
|
353
|
+
raise SystemError(f"Failed to download -- {response.status_code}")
|
|
354
|
+
|
|
341
355
|
if verbose is True:
|
|
342
356
|
content_length = int(response.headers.get('content-length', 0))
|
|
343
357
|
with open(download_path, "wb") as file, tqdm(desc=download_path,
|