GLDF 0.9.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.
- GLDF/__init__.py +2 -0
- GLDF/bridges/__init__.py +0 -0
- GLDF/bridges/causal_learn.py +185 -0
- GLDF/bridges/tigramite.py +143 -0
- GLDF/bridges/tigramite_plotting_modified.py +4764 -0
- GLDF/cit.py +274 -0
- GLDF/data_management.py +588 -0
- GLDF/data_processing.py +754 -0
- GLDF/frontend.py +537 -0
- GLDF/hccd.py +403 -0
- GLDF/hyperparams.py +205 -0
- GLDF/independence_atoms.py +78 -0
- GLDF/state_space_construction.py +288 -0
- GLDF/tutorials/01_preconfigured_quickstart.ipynb +302 -0
- GLDF/tutorials/02_detailed_configuration.ipynb +394 -0
- GLDF/tutorials/03_custom_patterns.ipynb +447 -0
- gldf-0.9.0.dist-info/METADATA +101 -0
- gldf-0.9.0.dist-info/RECORD +20 -0
- gldf-0.9.0.dist-info/WHEEL +4 -0
- gldf-0.9.0.dist-info/licenses/LICENSE +621 -0
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "code",
|
|
5
|
+
"execution_count": 46,
|
|
6
|
+
"id": "9d68999a",
|
|
7
|
+
"metadata": {},
|
|
8
|
+
"outputs": [],
|
|
9
|
+
"source": [
|
|
10
|
+
"import GLDF\n",
|
|
11
|
+
"import numpy as np\n",
|
|
12
|
+
"import matplotlib.pyplot as plt"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"cell_type": "markdown",
|
|
17
|
+
"id": "f0fdba52",
|
|
18
|
+
"metadata": {},
|
|
19
|
+
"source": [
|
|
20
|
+
"# Tutorial 03: Custom Patterns - Approximate Periodicity\n",
|
|
21
|
+
"\n",
|
|
22
|
+
"Our framework can deal with very general prior structures about patterns.\n",
|
|
23
|
+
"This tutorial demonstrates how to describe a custom pattern on the example of (approximate) periodic in time patterns.\n",
|
|
24
|
+
"To this end we\n",
|
|
25
|
+
"1. Describe our prior knowledge about the pattern in a formal way.\n",
|
|
26
|
+
"2. Configure HCCD to run with this custom pattern.\n",
|
|
27
|
+
"\n",
|
|
28
|
+
"For readability we will represent periodicity-patterns by strings, relevant properties can readily be extracted, for example:"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"cell_type": "code",
|
|
33
|
+
"execution_count": 47,
|
|
34
|
+
"id": "2637fe0b",
|
|
35
|
+
"metadata": {},
|
|
36
|
+
"outputs": [],
|
|
37
|
+
"source": [
|
|
38
|
+
"repeating_pattern = \"AACBCC\""
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"cell_type": "markdown",
|
|
43
|
+
"id": "3ee0d384",
|
|
44
|
+
"metadata": {},
|
|
45
|
+
"source": [
|
|
46
|
+
"## 1. The Pattern\n",
|
|
47
|
+
"\n",
|
|
48
|
+
"We first have to formalize what \"approximately periodic\" means.\n",
|
|
49
|
+
"We can do so by implementing a derived class of data_management.CIT_DataPatterned.\n",
|
|
50
|
+
"\n",
|
|
51
|
+
"As described in the documentation, we have to implement (at least) three methods:\n",
|
|
52
|
+
"\n",
|
|
53
|
+
"* view_blocks: This is the core functionality. It encodes our prior idea about patterns\n",
|
|
54
|
+
" by supplying \"blocks\" (of a given size) of \"similar\" data-points.\n",
|
|
55
|
+
" Similar, in our case, are data-points that follow the periodic pattern.\n",
|
|
56
|
+
" For example if the approximate pattern is 'AAB' we expect regimes in data to \"usually\" (but not always)\n",
|
|
57
|
+
" follow the structure AAB AAB AAB AAB AAB AAB AAB AAB (spaces for readability).\n",
|
|
58
|
+
" Now, if we want blocks of \"similar\" points of, say, size 8 from this sequence, we should consider\n",
|
|
59
|
+
" for example\n",
|
|
60
|
+
"\n",
|
|
61
|
+
" * block No1: XX. XX. XX. XX. ... ... ... ... (8 data-points, all from 'A')\n",
|
|
62
|
+
" * block No2: ... ... ... ... XX. XX. XX. XX. (8 data-points, all from 'A')\n",
|
|
63
|
+
" * block No3: ..X ..X ..X ..X ..X ..X ..X ..X (8 data-points, all from 'B')\n",
|
|
64
|
+
" * for the remaineder of the data, repeat this logic, collecting further blocks (possibly discarding some data-points in the end if rounding-issues occur).\n",
|
|
65
|
+
"\n",
|
|
66
|
+
" The code below is a simple (albeit likely not particular efficient) implementation of this strategy.\n",
|
|
67
|
+
"\n",
|
|
68
|
+
"* static reproject_blocks: This describes how results on blocks can be \"reprojected\" onto the time-axis for plotting. (This can be a static function on the patterned data,\n",
|
|
69
|
+
" or an arbitrary method of a factory-object as shown below on 'Pattern_PeriodicPersistent'.)\n",
|
|
70
|
+
"* get_actual_block_format: Blocks could be two-dimensional, in which case their format is not (for plotting purposes)\n",
|
|
71
|
+
" fully described by their size. In our case, we simply copy through the requested size."
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"cell_type": "markdown",
|
|
76
|
+
"id": "392096c4",
|
|
77
|
+
"metadata": {},
|
|
78
|
+
"source": [
|
|
79
|
+
"### 1.1 Analyizing Pattern-Strings\n",
|
|
80
|
+
"\n",
|
|
81
|
+
"Consider the following example:"
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"cell_type": "code",
|
|
86
|
+
"execution_count": 48,
|
|
87
|
+
"id": "7f3551d3",
|
|
88
|
+
"metadata": {},
|
|
89
|
+
"outputs": [
|
|
90
|
+
{
|
|
91
|
+
"name": "stdout",
|
|
92
|
+
"output_type": "stream",
|
|
93
|
+
"text": [
|
|
94
|
+
"AACBCC\n",
|
|
95
|
+
"6\n",
|
|
96
|
+
"{'B', 'A', 'C'}\n",
|
|
97
|
+
"{'B': 1, 'A': 2, 'C': 3}\n",
|
|
98
|
+
"{'B': array([False, False, False, True, False, False]), 'A': array([ True, True, False, False, False, False]), 'C': array([False, False, True, False, True, True])}\n"
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
"source": [
|
|
103
|
+
"repeating_pattern = \"AACBCC\"\n",
|
|
104
|
+
"\n",
|
|
105
|
+
"pattern_len = len(repeating_pattern)\n",
|
|
106
|
+
"repeating_elements = set(repeating_pattern) # letters in pattern, eg {A, B, C} for pattern AABCCC\n",
|
|
107
|
+
"repetitions = {elem: repeating_pattern.count(elem) for elem in repeating_elements} # number of occurences of letters per period, eg {A:2, B:1, C:3}\n",
|
|
108
|
+
"masks = {elem: np.array(list(repeating_pattern))==elem for elem in repeating_elements}\n",
|
|
109
|
+
"\n",
|
|
110
|
+
"print(repeating_pattern)\n",
|
|
111
|
+
"print(pattern_len)\n",
|
|
112
|
+
"print(repeating_elements)\n",
|
|
113
|
+
"print(repetitions)\n",
|
|
114
|
+
"print(masks)"
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"cell_type": "markdown",
|
|
119
|
+
"id": "9e3429cc",
|
|
120
|
+
"metadata": {},
|
|
121
|
+
"source": [
|
|
122
|
+
"Generally, the following helper provides this kind of quick analysis:"
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"cell_type": "code",
|
|
127
|
+
"execution_count": 49,
|
|
128
|
+
"id": "6f14a2cf",
|
|
129
|
+
"metadata": {},
|
|
130
|
+
"outputs": [],
|
|
131
|
+
"source": [
|
|
132
|
+
"class Pattern_PeriodicPersistent_Analyize:\n",
|
|
133
|
+
" def __init__(self, repeating_pattern: str, N: int):\n",
|
|
134
|
+
" self.N = N\n",
|
|
135
|
+
" self.repeating_pattern = repeating_pattern\n",
|
|
136
|
+
" self.pattern_len = len(repeating_pattern)\n",
|
|
137
|
+
" self.repeating_elements = set(repeating_pattern) # letters in pattern, eg {A, B, C} for pattern AABCCC\n",
|
|
138
|
+
" self.repetitions = {elem: repeating_pattern.count(elem) for elem in self.repeating_elements} # number of occurences of letters per period, eg {A:2, B:1, C:3}\n",
|
|
139
|
+
" self.masks = {elem: np.array(list(repeating_pattern))==elem for elem in self.repeating_elements}\n",
|
|
140
|
+
" self.full_masks = {elem: self.repeat_mask(mask) for elem, mask in self.masks.items()}\n",
|
|
141
|
+
" \n",
|
|
142
|
+
" def repeat_mask(self, mask):\n",
|
|
143
|
+
" result = np.empty(self.N, dtype=bool)\n",
|
|
144
|
+
" full_repetitions = int(len(result)/self.pattern_len)\n",
|
|
145
|
+
" result[:full_repetitions*self.pattern_len] = np.tile(mask, full_repetitions)\n",
|
|
146
|
+
" result[full_repetitions*self.pattern_len+1:] = mask[:len(result)-full_repetitions*self.pattern_len]\n",
|
|
147
|
+
" return result\n",
|
|
148
|
+
" \n",
|
|
149
|
+
" def count_blocks_for_element(self, element, block_size) -> int:\n",
|
|
150
|
+
" return int(np.count_nonzero(self.full_masks[element])/block_size)"
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"cell_type": "markdown",
|
|
155
|
+
"id": "9e22b70f",
|
|
156
|
+
"metadata": {},
|
|
157
|
+
"source": [
|
|
158
|
+
"### 1.2 The Patterned Data\n",
|
|
159
|
+
"\n",
|
|
160
|
+
"The view_blocks method (and its per-element analogue _view_blocks_element) provides the main functionality, as described above."
|
|
161
|
+
]
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"cell_type": "code",
|
|
165
|
+
"execution_count": 50,
|
|
166
|
+
"id": "b439432d",
|
|
167
|
+
"metadata": {},
|
|
168
|
+
"outputs": [],
|
|
169
|
+
"source": [
|
|
170
|
+
"class CIT_DataPatterned_PeriodicPersistent(GLDF.data_management.CIT_DataPatterned, Pattern_PeriodicPersistent_Analyize):\n",
|
|
171
|
+
" def __init__(self, repeating_pattern: str, **args):\n",
|
|
172
|
+
" GLDF.data_management.CIT_DataPatterned.__init__(self, **args)\n",
|
|
173
|
+
" Pattern_PeriodicPersistent_Analyize.__init__(self, repeating_pattern, self.sample_count())\n",
|
|
174
|
+
"\n",
|
|
175
|
+
"\n",
|
|
176
|
+
" def _view_blocks_element(self, element, block_size) -> np.ndarray:\n",
|
|
177
|
+
" block_count = self.count_blocks_for_element(element, block_size)\n",
|
|
178
|
+
" aligned_N = block_size * block_count\n",
|
|
179
|
+
"\n",
|
|
180
|
+
" data_masked_x = self.x_data[self.full_masks[element]]\n",
|
|
181
|
+
" blocks_x = data_masked_x[:aligned_N].reshape((block_count, block_size))\n",
|
|
182
|
+
" data_masked_y = self.y_data[self.full_masks[element]]\n",
|
|
183
|
+
" blocks_y = data_masked_y[:aligned_N].reshape((block_count, block_size))\n",
|
|
184
|
+
" if self.z_dim() > 0:\n",
|
|
185
|
+
" data_masked_z = self.z_data[self.full_masks[element],:]\n",
|
|
186
|
+
" blocks_z = data_masked_z[:aligned_N,:].reshape((block_count, block_size,-1))\n",
|
|
187
|
+
" else:\n",
|
|
188
|
+
" blocks_z = None\n",
|
|
189
|
+
"\n",
|
|
190
|
+
" return blocks_x, blocks_y, blocks_z\n",
|
|
191
|
+
"\n",
|
|
192
|
+
"\n",
|
|
193
|
+
" def view_blocks(self, block_size:int) -> GLDF.data_management.BlockView:\n",
|
|
194
|
+
" all_blocks_x = []\n",
|
|
195
|
+
" all_blocks_y = []\n",
|
|
196
|
+
" all_blocks_z = []\n",
|
|
197
|
+
" for elem in self.repeating_elements:\n",
|
|
198
|
+
" blocks_x, blocks_y, blocks_z = self._view_blocks_element(elem, block_size)\n",
|
|
199
|
+
" all_blocks_x.append(blocks_x)\n",
|
|
200
|
+
" all_blocks_y.append(blocks_y)\n",
|
|
201
|
+
" all_blocks_z.append(blocks_z)\n",
|
|
202
|
+
" return GLDF.data_management.BlockView(\n",
|
|
203
|
+
" pattern_provider=self,\n",
|
|
204
|
+
" cache_id=None if self.cache_id is None else (*self.cache_id, block_size),\n",
|
|
205
|
+
" x_blocks=np.vstack(all_blocks_x),\n",
|
|
206
|
+
" y_blocks=np.vstack(all_blocks_y),\n",
|
|
207
|
+
" z_blocks=np.vstack(all_blocks_z) if self.z_dim() > 0 else None\n",
|
|
208
|
+
" )\n",
|
|
209
|
+
" \n",
|
|
210
|
+
" @staticmethod\n",
|
|
211
|
+
" def get_actual_block_format(requested_size: int) -> int:\n",
|
|
212
|
+
" return requested_size"
|
|
213
|
+
]
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"cell_type": "markdown",
|
|
217
|
+
"id": "bd1ad9a9",
|
|
218
|
+
"metadata": {},
|
|
219
|
+
"source": [
|
|
220
|
+
"### 1.3 Factory Object\n",
|
|
221
|
+
"Finally, we wrap the above pattern into a factory-object and provide reprojection-logic for plotting."
|
|
222
|
+
]
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"cell_type": "code",
|
|
226
|
+
"execution_count": 51,
|
|
227
|
+
"id": "c9a4bb8d",
|
|
228
|
+
"metadata": {},
|
|
229
|
+
"outputs": [],
|
|
230
|
+
"source": [
|
|
231
|
+
"class Pattern_PeriodicPersistent:\n",
|
|
232
|
+
" def __init__(self, repeating_pattern: str):\n",
|
|
233
|
+
" self.repeating_pattern = repeating_pattern\n",
|
|
234
|
+
"\n",
|
|
235
|
+
" def __call__(self, **args):\n",
|
|
236
|
+
" return CIT_DataPatterned_PeriodicPersistent(repeating_pattern=self.repeating_pattern, **args)\n",
|
|
237
|
+
" \n",
|
|
238
|
+
" def reproject_blocks(self, value_per_block: np.ndarray, block_configuration: GLDF.data_management.BlockView, data_configuration: tuple[int,...]) -> dict[str, np.ndarray]:\n",
|
|
239
|
+
" analyize = Pattern_PeriodicPersistent_Analyize(self.repeating_pattern, N=block_configuration.pattern_provider.sample_count()) # patterned-data sample-size accounts for tau-max (window-count < N)\n",
|
|
240
|
+
" \n",
|
|
241
|
+
" block_size = block_configuration.block_size()\n",
|
|
242
|
+
" read_offset = 0\n",
|
|
243
|
+
" results = {}\n",
|
|
244
|
+
"\n",
|
|
245
|
+
" for elem in analyize.repeating_elements:\n",
|
|
246
|
+
" block_count = analyize.count_blocks_for_element(elem, block_size)\n",
|
|
247
|
+
" elem_values = value_per_block[read_offset:read_offset+block_count]\n",
|
|
248
|
+
" read_offset += block_count\n",
|
|
249
|
+
" result = np.full( data_configuration, float(\"nan\") )\n",
|
|
250
|
+
" renorm_block_size = int(block_size*analyize.pattern_len/analyize.repetitions[elem])\n",
|
|
251
|
+
" result[:block_count*renorm_block_size] = elem_values.repeat(renorm_block_size)\n",
|
|
252
|
+
" results[elem] = result\n",
|
|
253
|
+
"\n",
|
|
254
|
+
" return results"
|
|
255
|
+
]
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"cell_type": "markdown",
|
|
259
|
+
"id": "9636c7d3",
|
|
260
|
+
"metadata": {},
|
|
261
|
+
"source": [
|
|
262
|
+
"## 2. Modify the Configuration\n",
|
|
263
|
+
"\n",
|
|
264
|
+
"As a simple application of what is also explained in the second tutorial, we add a configuration using the above pattern in HCCD.\n",
|
|
265
|
+
"The pattern is actually associated to the data-manager, indeed we can use the default (time-series) data-manager\n",
|
|
266
|
+
"and provide the pattern in its constructor:"
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"cell_type": "code",
|
|
271
|
+
"execution_count": 52,
|
|
272
|
+
"id": "760021f5",
|
|
273
|
+
"metadata": {},
|
|
274
|
+
"outputs": [],
|
|
275
|
+
"source": [
|
|
276
|
+
"class ConfigureHCCD_PeriodicPersistent(GLDF.frontend.ConfigureHCCD):\n",
|
|
277
|
+
" def __init__(self, regimes_are_large: bool=True, alpha: float=0.01, alpha_pc1: float=0.1, repeating_pattern: str=\"AB\"):\n",
|
|
278
|
+
" ts_config = GLDF.frontend.configure_hccd_temporal_regimes(regimes_are_large=regimes_are_large, alpha=alpha, alpha_pc1=alpha_pc1)\n",
|
|
279
|
+
" from dataclasses import asdict\n",
|
|
280
|
+
" super().__init__(**asdict(ts_config))\n",
|
|
281
|
+
" self.repeating_pattern = repeating_pattern\n",
|
|
282
|
+
"\n",
|
|
283
|
+
" def get_data_manager(self):\n",
|
|
284
|
+
" return GLDF.data_management.DataManager_NumpyArray_Timeseries(self._data, pattern=Pattern_PeriodicPersistent(self.repeating_pattern))"
|
|
285
|
+
]
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"cell_type": "code",
|
|
289
|
+
"execution_count": 53,
|
|
290
|
+
"id": "5c534929",
|
|
291
|
+
"metadata": {},
|
|
292
|
+
"outputs": [],
|
|
293
|
+
"source": [
|
|
294
|
+
"import numpy as np\n",
|
|
295
|
+
"\n",
|
|
296
|
+
"N = 1000\n",
|
|
297
|
+
"\n",
|
|
298
|
+
"# At odd times, in last quarter [second half]\n",
|
|
299
|
+
"R1 = np.mod( np.arange(N), 2 ) == 1\n",
|
|
300
|
+
"R1[:int(3*N/4)] = False\n",
|
|
301
|
+
"\n",
|
|
302
|
+
"# and even times always [outside first and last quarter]\n",
|
|
303
|
+
"R2 = np.mod( np.arange(N), 2 ) == 0\n",
|
|
304
|
+
"#R2[:int(N/4)] = False\n",
|
|
305
|
+
"#R2[int(3*N/4):] = False\n",
|
|
306
|
+
"\n",
|
|
307
|
+
"# the context is \"on\":\n",
|
|
308
|
+
"R = np.logical_or(R1, R2)\n",
|
|
309
|
+
"\n",
|
|
310
|
+
"\n",
|
|
311
|
+
"rng = np.random.default_rng()\n",
|
|
312
|
+
"X_noise = rng.standard_normal(N)\n",
|
|
313
|
+
"Y_noise = rng.standard_normal(N)\n",
|
|
314
|
+
"Z_noise = rng.standard_normal(N)\n",
|
|
315
|
+
"\n",
|
|
316
|
+
"X = np.empty_like(X_noise)\n",
|
|
317
|
+
"Y = np.empty_like(Y_noise)\n",
|
|
318
|
+
"Z = np.empty_like(Z_noise)\n",
|
|
319
|
+
"\n",
|
|
320
|
+
"def lag_one_or_zero(values, t):\n",
|
|
321
|
+
" return values[t-1] if t > 0 else 0.0\n",
|
|
322
|
+
"\n",
|
|
323
|
+
"for t in range(N):\n",
|
|
324
|
+
" X[t] = X_noise[t] + 0.2 * lag_one_or_zero(X, t)\n",
|
|
325
|
+
" Z[t] = Z_noise[t]\n",
|
|
326
|
+
" Y[t] = Y_noise[t] + R[t] * lag_one_or_zero(X, t) + Z[t]\n",
|
|
327
|
+
"\n",
|
|
328
|
+
"data = np.array([X,Y,Z]).T\n",
|
|
329
|
+
"var_names = [\"X\", \"Y\", \"Z\"]"
|
|
330
|
+
]
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"cell_type": "markdown",
|
|
334
|
+
"id": "b7333338",
|
|
335
|
+
"metadata": {},
|
|
336
|
+
"source": [
|
|
337
|
+
"Commented out lines: If only using eg half the data at odd times, only about a quarter of data remains, which especially for \"small\" N, may not suffice.\n",
|
|
338
|
+
"In principle even times can have regimes independently, these are currently commented out, because otherwise persistent regimes also work (in principle/for infinite N at least)."
|
|
339
|
+
]
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"cell_type": "code",
|
|
343
|
+
"execution_count": 54,
|
|
344
|
+
"id": "db5d4477",
|
|
345
|
+
"metadata": {},
|
|
346
|
+
"outputs": [
|
|
347
|
+
{
|
|
348
|
+
"data": {
|
|
349
|
+
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAqgAAADFCAYAAACYTQDxAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjUsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvWftoOwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAMVVJREFUeJzt3Qd4VFXaB/A3BFIIJKGkEFooAVSagEEUBQUp67q0pa3wAQL5QFAURRJXiYhLIqIiLCusKwSxhA9FWAWpUhYIRUABkSpCKCGAkATSSHK+5z3ZO8wkM8lMMjO3zP/3PJdMOWfuvTPvXN4595xzvYQQggAAAAAANKKK2hsAAAAAAGAOCSoAAAAAaAoSVAAAAADQFCSoAAAAAKApSFABAAAAQFOQoAIAAACApiBBBQAAAABNqUoGUFRURJcuXaKaNWuSl5eX2psDGsLT/GZlZVFERARVqeLa32OIQ7AFcQhagDgEPcWgIRJU/hI0bNhQ7c0ADUtNTaUGDRq4dB2IQygP4hC0AHEIeohBQySo/AtN2eHAwEC1Nwc0JDMzUx4klRhxJcQh2II4BC1AHIKeYtAQCapy+oC/BPgigDXuOMWEOITyIA5BCxCHoIcYxCApABdLz8yl9zedlH9RR/91KrIOLdDq+6nlOlrdrorW0QItvz9qx4et59JVruPs9dvL4QR1x44d9NRTT8kOrpwBr169utw627Ztow4dOpCvry81b96ckpKSSpVZuHAhRUZGkp+fH3Xu3Jn27dvn6KaBh6hIDP7nP/9RLQbTs/Logy2n5F/U0X8dpfy6Td8jDg1eR6vbZV4HcaheHWevw9Zz6SrXcfb6XZag3r59m9q1ayeD1x5nz56lJ598kh577DH68ccf6YUXXqBx48bRhg0bTGVWrFhBU6dOpfj4eDp48KB8/d69e1N6ejpVdrRYdn4BFoMt129m0r2t29B7H8yXn3PenULTc/yZWzNkyBBVYhCMKyc726FjIUMcgrMhDsGoHO6D2rdvX7nYa9GiRdSkSRN699135f177rmHdu7cSe+//74MePbee+/R+PHjacyYMaY6a9eupSVLllBsbGyp18zLy5OLeadba3LuFNK9M+5+6cD17vc6Ra9W+8zm80sK+tJ3RZ3l7Xu8ztGb1ZbaLPtZQU9aXdRV3m7mdZESq30kb/O4vz9XI6Ijm+T98yun05s/7qbkwsfp2Ju9qbpP6bBu3LixU2OwvDjk0xrKL8ejFzMs/rLQmr4UGuhn8Xqoo9061srXahlNjzz+BDkCcaj9OlrdLlt1EIfurePsdfx+O5+b06h2gK/Fc7/fzpdL7YBqdOlmrtvrOHv9yntj+c6oPEgqJSWFevbsafEYfwn4VxvLz8+nAwcOUFxcnOl5nhuL63BdaxISEmjmzJku3nKoiECvbHqgykmbz3/r1cV0uwbllFn2e68Optv+lGezbFSVS+Trda3M7erevbtTY7C8OPxs73l5esNc7KojpttTekTRi0+0QB2d1CmvvL0Qh9qvo9XtsqeOvRCHFa/jjs/N1nOxKtep7GvxezO2czhpJkFNS0ujsLAwi8f4Pv+6ysnJoRs3blBhYaHVMsePH7f6mvzF4dMPJactKMm/mrdsUQM3utWe8i7cY/Pp2LDWNL1Wk+I72Z0o73xTm2WfD2lFz9WJKr6Tc5PyzlmZM23mQLrz4PM0fvAIGhfSSn7m1oSEhDg1BsuLw6c7N6In7g0z/YLkL2niwDbUun6Q6ZdkSaij3TrllW/zNtkFcaj9OlrdLnvqIA71Fx8lWyOV5yKC/S1aI91dx9nrv/vecF376HKaKe7YzUt5uMO4tdO94EK16xPVHmBfWZ8womB7y9Ylamu9bLXIB8m/fmvSUhzyKZ6Sp3n4S6p8UVFHX3Uqsg53QRyq/1lruY67eEocumMdJZ87+t/T5GrVceZrZWZqKEENDw+nK1euWDzG93leNH9/f/L29paLtTJcF3Tk6gmiM1uJghsStXqStOTq1asW9xGDoAbEIWgB4hD0wOXzoHbp0oW2bNli8dimTZvk48zHx4c6duxoUYav4cv3lTKgExcPEK2fTvSD7YFPatm+fbtqMcinNbjvjbVTP6ijvzoVWYcCcaivOlrdrorWUSAOnVPH2euw9VyoynWcvX67CQdlZWWJQ4cOyYWrv/fee/L2uXPn5POxsbFi5MiRpvK//vqrqF69upg2bZr45ZdfxMKFC4W3t7dYv369qUxycrLw9fUVSUlJ4tixYyImJkYEBweLtLQ0u7YpIyNDbgv/BRUd/FSI+EAhlg9y6WociUElNlwdg+brQhx6BsQhaAHiEPTEkbhwOEHdunWrfPGSy6hRo+Tz/Ldbt26l6rRv3174+PiIpk2biqVLl5Z63QULFohGjRrJMtHR0WLPnj12bxO+CJ6VoDoSg0psfPvtty6NQfN1IQ49A+IQtABxCHriSFx48T+kczxaMCgoiDIyMnDNXzUd+pRozSSiqF5ET68kT4sNxCHYgjgELUAcgtociQuX90EFAAAAAHAEElRwHlNjvJfKGwIAAAB6hgQVAAAAADQFs9iD8zR7nGjEV0TV66i9JQAAAKBjSFDBeYLqFy8AAAAAlYBT/AAAAACgKWhBBee5dorofApRUIPi0/0AAAAAFYAWVHCec7uJ/v0c0b6P1N4SAAAA0DEkqAAAAACgKUhQwYl0f1EyAAAA0AAkqOACmKgfAAAAKg4JKrjgSlIAAAAAFYcEFZzPCy2oAAAAUHFIUAEAAABAUzAPKjhPs8eIhiwnqhmu9pYAAACAjiFBBeepFVm8AAAAAFQCTvEDAAAAgKagBRWc5/oZokuHiALrEzXuovbWAAAAgE6hBRWc59etRF+NJdrzD7W3BAAAAHQMCSoAAAAAaAoSVHAeTNQPAAAAToAEFZwPE/UDAABAJSBBBQAAAABNQYIKLoAWVAAAAHBzgrpw4UKKjIwkPz8/6ty5M+3bt89m2e7du5OXl1ep5cknnzSVGT16dKnn+/TpU7E9Ao/hSBxyvCEOQc0YZEFBQYhBcDrEIRiRw/OgrlixgqZOnUqLFi2SX4R58+ZR79696cSJExQaGlqq/KpVqyg/P990//r169SuXTsaPHiwRTkO/qVLl5ru+/r6Or43oK6m3YkGLCYKauDyVTkah8uXL5cHbwXiENwdg+zkyZNUs2ZNeRsxCM6AOATDEg6Kjo4WkyZNMt0vLCwUERERIiEhwa7677//vqhZs6a4deuW6bFRo0aJfv362b0Nubm5IiMjw7Skpqby8HF5GzyDvXHIMWEtNhCHUFmOHAutxaEzYpAhDj0b4hD0xNb/ydY4dIqfW0IPHDhAPXv2ND1WpUoVeT8lJcWu1/j4449p2LBhFBAQYPH4tm3b5K+9li1b0sSJE+WvOlsSEhLkKQpladiwoSO7ATqHOAS1aSUGGeLQcyEOwcgcSlCvXbtGhYWFFBYWZvE4309LSyu3PveLOXr0KI0bN67UqYRPPvmEtmzZQm+//TZt376d+vbtK9dlTVxcHGVkZJiW1NRUR3YDXOX3s0TH1xVf7tSFEIegNq3EIEMcei7EIRiZw31QK4N/qbVp04aio6MtHudfbwp+vm3bttSsWTP5C65Hjx6lXof7wqA/jAad3EC0fjpR60FEf15CWoU4BKPEIEMcQkUhDsEwLah169Ylb29vunLlisXjfD88PLzMurdv36bk5GQaO3Zsuetp2rSpXNfp06cd2TzwEIhDUBtiELQAcQhG5lCC6uPjQx07dpTN/oqioiJ5v0uXLmXWXblyJeXl5dGIESPKXc+FCxdkf5d69eo5snmgOvdc6hRxCGpDDIIWIA7B0BwdgZWcnCx8fX1FUlKSOHbsmIiJiRHBwcEiLS1NPj9y5EgRGxtbql7Xrl3F0KFDSz2elZUlXn75ZZGSkiLOnj0rNm/eLDp06CCioqLkqEBnjwoDF0r5hxDxgUKsfMblq7I3DkvGBuIQnMWRY6F5bLgyBkuuC4wPcQh64khcONwHdejQoXT16lWaMWOG7ITdvn17Wr9+vamT9vnz5+UoQnM8H9vOnTtp48aNpV6PT08cPnyYli1bRjdv3qSIiAjq1asXzZo1C/1Z9Ea4pwWVIQ5BbRWJwVOnTiEGwakQh2BUXpylks5lZmbKaS145GBgYKDam+O5Uv5BtCGOqM1gokH/Ik+LDcQh2II4BC1AHILaHImLCl3qFAAAAADAENNMgcE1eZToj+8T1W6q9pYAAACAjiFBBecJb128AAAAAFSCIRNUvtrFnTt31N4McONUKyUHAQAAAIB+GSpB5fFely9fliMPQQVFBcWLlzeRdzW3rZaT0yZNmshEFQAAAPTPUAnqjRs3KCcnh0JDQ6l69erk5eWl9iZ5ltvXiG6nE/kGEAXVd8sqeVLqS5cuyR8mjRo1wmcOAABgAIZJUAMCAuSl2/jybnXq1FF7czxTQTWiPC8in6pEfn5uW21ISIhMUgsKCqhaNfe13AIAAIBrGKbjnpKUcsspeBbl1D73PQYAAAD9M0yCqpzaxSlez4PPHAAAwFgMk6ACAAAAgDEgQQUAAAAATUGCCg6LjIykefPmqb0ZAAAAYFBIUMGmpKQkCg4OLvX4/v37KSYmpnQFnxpEgfWJ/Gu5ZwMBAADAkAwzzRSQW6d1ssonoHgBAAAAqARDt6DylaWy8wtUWXjdjkw2n5CQIK+G5O/vT+3ataMvv/zS9FyDBg3oww8/tKhz6NAheQWlc+fOyft89axx48bJ5DEwMJAef/xx+umnn0zl33jjDWrfvj0tX75cnqIPCgqiYcOGUVZWltVt2rZtG40ZM4YyMjLkKHle+DWsneLn5xYvXkx//OMf5TRf99xzD6WkpNDp06epe/fuco7ahx56iM6cOWOxjjVr1lCHDh3Iz8+PmjZtSjNnzpRzmSqfHa+PJ9/39fWliIgIev755+1+TwEAAEC/DN2CmnOnkO6dsUGVdR97szdV5wnr7cDJ6aeffkqLFi2iqKgo2rFjB40YMUImm926daPhw4fT559/ThMnTjTV+eyzz+jhhx+mxo0by/uDBw+Wye13330nk09OGHv06EEnT56k2rVryzKcIK5evZq+/fZbedWtIUOGUGJiIv3tb38rtU2cUHISOmPGDDpx4oR8rEaNGjb3YdasWfTe3Dn0XuJbNP21ePrLX/4ik864uDiZZD7zzDM0efJkuX3sP//5D/3P//wPzZ8/nx555BG5bUq3gfj4ePrqq6/o/fffp+TkZLrvvvsoLS3NIuEGAAAA4zJ0C6oe5OXl0ezZs2nJkiXUu3dvmdSNHj1aJqicZLKnn36adu3aRefPnze1qnLixo+znTt30r59+2jlypXUqVMnmeTOnTtX9h9VWmKVetyvtHXr1jIpHDlyJG3ZssXm5Pec6HLrKF+di5eyElRubR3y5OPUorag6c+Opt9++01uH+8Tt6hOmTJFtsoquLU0NjaWRo0aJff5iSeekEmuss+8r7zOnj17ygQ3Ojqaxo8f76R3HQAAALTM0C2o/tW8ZUumWuu2B58Gz87Olgmaufz8fLr//vvlbT41z0ket6JyUrd9+3ZKT0+XraaMWxZv3bpV6hKvOTk5FqfV+dR8zZo1Tffr1asnX8cZ2rZta7odFlrcR7VNmzZ3HwsLo9zcXMrMzJRdEHibOek2b73lK0FxGX4/eN+4BZeT1z59+tAf/vAHeuqpp6hqVUOHLAAAABg9QeXWP3tPs6uFE0u2du1aql+/vsVz3PdSwa2RSoLKfzlpUxJSfg1ONs1bKBXmo/BLXqee3x9uVXUG89f2qmLlsf9e7UlZH28zt6IOHDiw1Gtxn9SGDRvKrgWbN2+mTZs20bPPPkvvvPOOTM5L7gcAAAAYi7azNw9w7733ykSUT2lzf1NbuE/na6+9RgcOHJCn7bm/qoIHGnEfTW5d5FZSZ+HT/K66vj1vMyegzZs3t1mG+9RyqykvkyZNolatWtGRI0dkXQAAADAuJKgq41PuL7/8Mr344ouydbFr165y5Dyf/uZT4dxHk3HiyQOXxo4dK5PGP/3pT6bX4H6aXbp0of79+9OcOXOoRYsWdOnSJdkqO2DAANkvtSJ4ndzSyf1UeWYBHqHPS/mKW0vLwoOveNQ/9y/985//LGck4NP+R48epbfeekv2leX97Ny5s1wnDyLjhFUZFAYAAADGhUFSGsCDg15//XU5mp/7mvLpe04uedopc3yan5M4Tjo5WTM/fb5u3Tp69NFH5WAlTlB5Cimegor7flYUJ8QTJkygoUOHyhkFOPl1Fh48xbMJbNy4kR544AF68MEH5ah9JQHlrgkfffSRnKmA+7fyqf5vvvmmVD9bAAAAMB4v4ciEnRrFA284ieGkrlmzZrIPI6ggK40o6zJR9TpEwY3ctloeWHX27FmZ0Jf87Dk2eDYCbpXmFmlXcue6QF8Qh6AFiENQmyNxgVP84Dx8qdOa9Yiq3W3dBQAAAHDLKf6FCxfK/oncWsV9BHkOTlu4L6FyJSJlKdnKxY243CeRR6LzqWvuU3nq1KmKbBqoyZcT1HAivyC3rE6JQ+4OwN0Q9u/fb7MsX9gAcQjO5sixkClzCyMGwZkQh2BEDieoK1asoKlTp8qr/Rw8eFAOnuH+hGXNp8nNuJcvXzYtyuU5Fdy3ka8oxCPT9+7dKy+Nya/Jp24ByotDvqwqj/DngWOIQ3AXHAtBCxCHYFjCQdHR0WLSpEmm+4WFhSIiIkIkJCRYLb906VIRFBRk8/WKiopEeHi4eOedd0yP3bx5U/j6+oovvvjCap3c3FyRkZFhWlJTU0Xjxo3F0aNHRU5OjqO7BM5SkC9EfrYQd/JcvirzOOTPnD/7evXqlYpDjg8O83/84x9uiUNeF98G43PkWKjEobNjkCEOPRviEPREiUF74sKhFlS+uhHPw8nN/QqeHojvcyuWLTxVEY/O5snX+/XrRz///LPpOR7cwnN4mr8mn37g0xS2XpNHu3MZZeHXBQ3Ivk509TjRrTSXrsZWHD7++OOIQ3ALrRwLGeLQcyEOwcgcSlCvXbsm56YsOXUR3+eAtqZly5byOvNr1qyRc1nyXJ88fdGFCxfk80o9R14zLi5OjgBTltTUVEd2A3TOVhyGhobajJmoqCjEIThNRY6FSl9BZ8YgQxx6LsQhGJnLR/HzBPK8KPiLwHN9Ll68WM7/WRF85SXzy4CCVmh3xrLo6GiLKS0Qh6CG4cOHm+LQGTHIEIfgKMQhGK4FtW7duuTt7U1XrlyxeJzvh4eH2/UafB31+++/n06fPi3vK/Uq85rgWWzFIQ8KQByCO+BYCFqAOAQjq+Lotdk7duwoL32p4NMDfN+8lbQsfDqCr6fO01cwnlydg978NXkiVx45aO9reqJt27bJ6UFu3rxZ5hRfPAWTo0aPHi0vm1px5V/qtDJsxeHWrVsRh+AWOBaCFiAOwdAcHYGVnJwsR/MlJSWJY8eOiZiYGBEcHCzS0tLk8yNHjhSxsbGm8jNnzhQbNmwQZ86cEQcOHBDDhg0Tfn5+4ueffzaVSUxMlK+xZs0acfjwYdGvXz/RpEkTu0fk82gwTxvFv3XrVjkS7saNGzbLlDeDwtmzZ+VrHDp0yOJxHrFZ1uvalHlJiIsHhbhxXriaeRzy9g8ePNhqHCojBl999VW3xCFGrXoOR46FSmysWrXKpTFovi7EoWdAHIKeOBIXDvdB5QnRr169Kifx5Q7T7du3p/Xr15s6VJ8/f16OIlTcuHGDxo8fL8vWqlVL/trbvXs33XvvvaYyr7zyCt2+fZtiYmJki2DXrl3la+KSpergEZhaVzIOeTAed/q3FYccV4hDcCZHj4VsypQp8lQpYhCcBXEIhiUMQO8tqDx/3HPPPSdCQkLkL+GHH35Y7Nu3z6LM2rVrRVRUlPyl2717d9k6WrIFlR9r2LCh8Pf3F/379xdz584tswX1v6OaTEu3bt3k46NGjZK/mBX8+OTJk8WUKVPkr+rQ0FDxz3/+U9y6dUuMHj1a1KhRQzRr1kysW/OlEBkXhcgu3qYjR46IPn36iICAAFlnxIgR4urVq6bXXblypWjdurXcp9q1a4sePXrI13QUf+bccmDts3fnr3i0GIAtiEPQAsQhGHYeVN3Kv217uZPrQNkc+8o6iH+tfvXVV7Rs2TJ5JZDmzZvLq3b8/vvv8nmermPgwIH01FNP0Y8//kjjxo2j2NhYi9fg/kFjx46lyZMnyzKPPfYYvfXWW2WuV7kc3ubNm+XVRFatWmWzLG8bd8jnOs899xxNnDiRBg8eLEeA8jb36tWLRj7zv5RdNZjIP1j+6uZ5Sbnz/Q8//CB/ffMv9iFDhsjX4/XxSNJnnnmGfvnlF9mnlvexOG8GAAAAT+byaaY0YXaE7eeiehE9vfLu/XeaE93Jtl62cVeiMWvv3p/Xpnhy+pLeyLB70/g0yocffigHNPXt21c+9tFHH9GmTZvo448/pmnTpsnnmzVrRu+++658nk9nc6f2t99+2/Q6H3zwAfXp00cmu6xFixbytA0nhraEhITIv3Xq1Cl3dCZfPu+1114zzXeXmJgoE1Y+bc749BJv5+HDh+nBBx+kv//97zI5nT17tuk1eB5Snrz55MmTcqLogoICmZTyhNGsTZs2dr9vAAAAYFye0YKqYWfOnKE7d+7Qww8/bDHtB8/byS2LjP/yVTzMlRxNaU+Zymjbtq3pNk9rwkmteUKp9HdKv3yRqPAO/fTTT3JUfY0aNUxLq1atTPvMCW+PHj3ka3BLLCfl3F8ZAAAAwDNaUF+9ZPs5L2/L+9NOl1G2RD7/whHyFJw0m+Mprswf4/us6MY5oqw02ULKXRLMW3kVPJ0JJ7ncSsytvBs3bqQFCxbQX//6V9lVgac5AQAAAM/lGS2oPgG2l2p+DpT1t6+sA/jUPc9lt2vXLtNj3KK6f/9+06hKvsqH0l9UsWfPHov7XIaTu7LKlHpbfHxM8+A5W4cOHeT1nSMjI2WfWvMlICDAlNRyy/HMmTPp0KFDcnu+/vprp28LAAAA6ItnJKgaxskaDzjivqbcX/TYsWOyX2d2drYc9MQmTJhAp06dkmVOnDhBn3/+ueyzau7555+X9efOnSvLch/QsvqfKteu9/f3Nw1g4usnO4UX0aRJk+QgLx4Ixck2n9bfsGEDjRkzRibEnExz/1QeQMXToPAALZ4qhRNtAAAA8GxIUDWABxwNGjSIRo4cKVse+ZJznMzxHHWsUaNGcpT/6tWrZd/NRYsWWQw+Yjwwiftx8mApLsOnzZVBTbZUrVqV5s+fL6/BHBERQf369XPaPvHrcaswJ6M8wp/7mr7wwgvyylY8Jx9fB3rHjh30hz/8QQ7o4m3lQWDKQDEAAADwXF481xTpHF+GjQfxrF27Vp4yx2TCKsm8RHTrClFACFFQA7etNjc3l86ePSv7rpb87Dk2+MID3DrMSbEruXNdoC+IQ9ACxCGozZG4QAsqAAAAAGgKElQAAAAA0BQkqOA8PIMBn973qaH2lgAAAICOecY8qOAefkHFCwAAAEAloAUVAAAAADTFMC2oRUVFFn9BBUWFRKKo+IpbVUpcocuFDDARBQAAABgxQb18+bK8MtGlS5coJCREXpVIufwmuMmtdKLsa0T+tYlqhrstOeUJ/kteehUAAAD0yzAJakFBAYWHh1NWVpZMUkEFuTeJcjOJfHOJ/HPctlpOThs0aEDe3u5rtQUAAADXMUyCqlwZia+6xMmqK64vD+XYtYDo0DKidk8TPfKi21bLLadITgEAAIzDUAkqU0714nSvCopuEd1KJSrMIsLVvAAAAKCCMIofnA99fwEAAKASkKCCE2E0PQAAAFQeElQAAAAA0BQkqOA8EfcT3T+CqH4HtbcEAAAAdMxwg6RARfcNKF4AAAAAKgEtqAAAAACg/wR14cKFFBkZSX5+ftS5c2fat2+fzbIfffQRPfLII1SrVi259OzZs1T50aNHy+mhzJc+ffpUZNNATXdyiyfq579u4EgcJiUlIQ5B1RhkHE+IQXA2xCEYkcMJ6ooVK2jq1KkUHx9PBw8epHbt2lHv3r0pPT3davlt27bR8OHDaevWrZSSkkINGzakXr160cWLFy3KcfDz5UqV5Ysvvqj4XoE6vp9FlNiQaOvfXL4qR+Nw586diENQNQbZoEGDEIPgVIhDMCzhoOjoaDFp0iTT/cLCQhERESESEhLsql9QUCBq1qwpli1bZnps1KhRol+/fnZvQ25ursjIyDAtqampPL+RvA0qWv+qEPGBQmx4zeWrsjcOOSasxQbiECrLkWOhtTh0RgwyxKFnQxyCntj6P9kah1pQ8/Pz6cCBA/KUgKJKlSryPv8Ss0d2djbduXOHateuXaqlNTQ0lFq2bEkTJ06k69ev23yNhIQECgoKMi38CxA8Z6J+xCGoTSsxyBCHngtxCEbmUIJ67do1eY37sLAwi8f5flpaml2vMX36dIqIiLD4QvGphE8++YS2bNlCb7/9Nm3fvp369u0r12VNXFwcZWRkmJbU1FRHdgN0DnEIatNKDDLEoedCHIKRuXWaqcTEREpOTpa/zLgzt2LYsGGm223atKG2bdtSs2bNZLkePXqUeh1fX1+5gFZp+1KniEMwSgwyxCFUFOIQDNOCWrduXfL29qYrV65YPM73w8PDy6w7d+5c+WXYuHGjDPayNG3aVK7r9OnTjmweqE2451KniENQG2IQtABxCEbmUILq4+NDHTt2lM3+iqKiInm/S5cuNuvNmTOHZs2aRevXr6dOnTqVu54LFy7I/i716tVzZPPAQ/qgIg5BbRWNwXnz5iEGwWkQh2Bojo7ASk5OFr6+viIpKUkcO3ZMxMTEiODgYJGWliafHzlypIiNjTWVT0xMFD4+PuLLL78Uly9fNi1ZWVnyef778ssvi5SUFHH27FmxefNm0aFDBxEVFSVHBTp7VBi40I9fCLFyjBCHV7p8VfbGoRIbb7zxBuIQnMqRY6ESG66OQfN1IQ49A+IQ9MSRuHA4QWULFiwQjRo1kkHOU1zs2bPH9Fy3bt3kFBWKxo0by40pucTHx8vns7OzRa9evURISIioVq2aLD9+/HjTl8se+CJ4JnviUIkNLoc4BGez91ioxIarY9B8XYhDz4E4BL1wJC68+B/SuczMTDmtBY8cDAwMVHtzwENjA3EItiAOQQsQh6A2R+LCraP4weCKiv77Y9yLJ+NTe2sAAABAp5BFgPNsiCN6szbR1rfU3hIAAADQMSSoAAAAAKApSFDBeUzdmbU9UT8AAABoGxJUAAAAANAUJKigu4n6AQAAwNiQoIIT6X7GMgAAANAAJKjgAmhBBQAAgIpDggrOE3oPUYu+RHWj1N4SAAAA0DFM1A/O0+mZ4gUAAACgEtCCCgAAAACaggQVAAAAADQFCSo4z7pXiGaFEG2fo/aWAAAAgI4hQQXnKSogKswnKipUe0sAAABAx5CggvPnQcVE/QAAAFAJSFABAAAAQFOQoIILoAUVAAAAKg4JKjiPwKVOAQAAoPKQoILzoQ8qAAAAVAISVHAevsRpk0eJghupvSUAAACgY7jUKThPl0nFCwAAAEAloAUVAAAAADQFCSoAAAAAaAoSVHCe72KJEhsT7V6g9pboRnpmLr2/6aT8q6U6Wt0uI9bRAsQH6qhNy++Np3wGhkhQFy5cSJGRkeTn50edO3emffv2lVl+5cqV1KpVK1m+TZs2tG7dOovnhRA0Y8YMqlevHvn7+1PPnj3p1KlTFdk0UNOd20S5N4kK3POFNEIcpmfl0QdbTsm/Wqqj1e3SWp2SMbhlx+4y63z99deai0GG+NB3HSPEoZbeT7XqQCUT1BUrVtDUqVMpPj6eDh48SO3ataPevXtTenq61fK7d++m4cOH09ixY+nQoUPUv39/uRw9etRUZs6cOTR//nxatGgR7d27lwICAuRr5ubilwc4Jw45rhCH4OoY/N8RA6nw9k2bdTj+EIPgTIhDMCqHR/G/9957NH78eBozZoy8zwG8du1aWrJkCcXGxpYq/8EHH1CfPn1o2rRp8v6sWbNo06ZN9Pe//13W5V9q8+bNo9dee4369esny3zyyScUFhZGq1evpmHDhpV6zby8PLkoMjMzbW/w8bVEu+bbfr5nPFHjh4pvn95MtP0d22W7Tydq9njx7d92Em2ZZbts1xeIWvYtvn3hB6INf7Vd9sEJRPcNKL6ddpRo7Uu2y3YaQ9Tuv+/JtdNEa8oYNc/luDy7mUr01TjbZXn9vB3s1lWiFSNsl+X94v1juZlEnw0uvv37mf8WcP08qI7G4YcffqhuHJrhUz7Kr+qjFzMs/rLQmr4UGujn9jpa3S6t1lFi8MlBw2Wdya+/TV+u/jfdOrKJjl58xKKOshZuiXJmDLozDrX4GaCOvuNQi++nu+uAkxLU/Px8OnDgAMXFxZkeq1Kligz2lJQUq3X4cf51Z45/iXGgs7Nnz1JaWpp8DUVQUJA8TcF1rX0ZEhISaObMmfZt9K0rRKl7bD+fc+Pu7dvXyy6b/btlvbLK3jJryePT3mWVva//3dt5WWWXbdHL8pR6WWUju969zafdyypbv8Pd24X5ZZcNbXX3tigsXTYwglypInG4f/9+eumll9SLQzOf7T0vT/2Yi111xHR7So8oevGJFm6vo9Xt0mId8xg0r5Mfdh8VXTxeqs7YzuHydvfu3Z0ag+6MQ619Bqij/zjU2vupRh1wUoJ67do1KiwslL+kzPH948ePW63DgW6tPD+uPK88ZqtMSfxlNE96+Zdaw4YNrW80t3gOWW57pyLMEjNuSS2rrHkSV79T2WXrtb17O7xt2WXD7rt7u26LssuG3nP3dnDjssvyxPmKmuFll63d9O7t6rXLLms+EX+1AMuyfkGWibELVCQOr1y5om4cmnm6cyN64t4w069rPoAlDmxDresHmX5lq1FHq9ulxTrmMfjQfXfrxF38hjZu2WqlTn7x7dBQp8agO+NQa58B6ug/DrX2fqpRBww2Ub+vr69c7FIrsnixR3DD4sUegfWI7v2TfWVrhNpfNqCO/WX9g+0v61vT/rLV/O0vW9XH/rIG41AcmuFTPCVP8/ABTDmIqVVHq9ulpzohNXyt1snMLE4M9ByHevkMUEc/caiX99OVdcBJg6Tq1q1L3t7esjXKHN8PDy8+dVASP15WeeWvI68Jnq0icci//hGH4OoYvH4tnbwDatmsV3IQH2IQKgNxCEbmUILq4+NDHTt2pC1btpgeKyoqkve7dOlitQ4/bl6ecYdspXyTJk1k0JuX4VMDPHLQ1muCZ6tIHD7wwAOajEM+5cP9khw59eOOOlrdLq3UsRWD+3fvoMcf7WpzPdu3b9dcDDLEhz7rGCkOtfB+ql0HShAOSk5OFr6+viIpKUkcO3ZMxMTEiODgYJGWliafHzlypIiNjTWV37Vrl6hataqYO3eu+OWXX0R8fLyoVq2aOHLkiKlMYmKifI01a9aIw4cPi379+okmTZqInJwcu7YpIyND8K7wX/AM9sahEhsbN25EHIJTOXIsVGLD1TFovi7EoWdAHIKeOBIXDvdBHTp0KF29elVO4ssdptu3b0/r1683dag+f/68HFGteOihh+jzzz+XU1a8+uqrFBUVJUcLtm7d2lTmlVdeodu3b1NMTAzdvHmTunbtKl+TJxG2B0+L4cj0KqB/ffv2pbfeeotef/11eeqJJ5v+6quv5KTSHAc8EpUHDygxER0djTgE1WJQiYl//etfNHv2bJfFIEMcehbEIeiJEg9KfJTFi7NU0rkLFy7YNWoVPFdqaio1aNDApetAHEJ5EIegBYhD0EMMGiJB5T43ly5dopo1a5KXl+Uk8cpUF/xmBAYGkh5hHyqOwzsrK4siIiIsWvbdGYdG+Pwqy9PfA8ShNnj6e4A4VJ+n779wIAZ1Oc1USbyT5WXiHAh6DwbsQ8XwJNNaiEMjfH6V5cnvAeJQOzz5PUAcaoMn73+QnTHo2p9QAAAAAAAOQoIKAAAAAJpi+ASVr2wRHx9foSutaAX2Qd88ed8VeA/Uh88A74EWePpn4On77whDDJICAAAAAOMwfAsqAAAAAOgLElQAAAAA0BQkqAAAAACgKUhQAQAAAEBTkKACAAAAgKYYOkFduHAhRUZGkp+fH3Xu3Jn27dtHWpGQkEAPPPCAvAxcaGgo9e/fn06cOGFRJjc3lyZNmkR16tShGjVq0KBBg+jKlSsWZc6fP09PPvkkVa9eXb7OtGnTqKCgwM17Q5SYmCgvZ/fCCy/ocvtd8fkpeKKMvn37yvdn9erVhth/e9+D7t27y/02XyZMmGCY90BrrH0Pz5w5QwMGDKCQkBB55ZohQ4aU+h7+/vvv9PTTT8vng4ODaezYsXTr1i3SgzfeeKNUjLVq1cojjkNawf/PlvwMeOH33ROOheXtP46DFWPYBHXFihU0depUOd/YwYMHqV27dtS7d29KT08nLdi+fbsM3j179tCmTZvozp071KtXL7p9+7apzIsvvkjffPMNrVy5Upbn6xoPHDjQ9HxhYaEM6Pz8fNq9ezctW7aMkpKSaMaMGW7dl/3799PixYupbdu2Fo/rZftd9fkp5s2bZ3EtaiPsvyPvwfjx4+ny5cumZc6cOYZ5D7TE2veQPwv+TDj+vv/+e9q1a5d8r5966il5rXQFJ6c///yz/By//fZb2rFjB8XExJBe3HfffRYxtnPnTo84Dmkp9szff44jNnjwYI84Ftqz/zgOVoAwqOjoaDFp0iTT/cLCQhERESESEhKEFqWnp/N8tGL79u3y/s2bN0W1atXEypUrTWV++eUXWSYlJUXeX7dunahSpYpIS0szlfnwww9FYGCgyMvLc8t2Z2VliaioKLFp0ybRrVs3MWXKFF1tv6s+P8WhQ4dE/fr1xeXLl+XzX3/9tek5I+2/rffAPCasMdp7oBZb38MNGzbI9zcjI8NUlr+bXl5esiw7duyY/Nz2799vKvPdd9/JMhcvXhRaFx8fL9q1a2f1OU87DmkFx1+zZs1EUVGRRx4LS+4/joMVY8gWVP4VcuDAAerZs6fpsSpVqsj7KSkppEUZGRnyb+3ateVf3n5ukTLfBz5t1ahRI9M+8N82bdpQWFiYqQy3EmdmZsrWEHfgFjT+5We+nXrafld9fiw7O5v+8pe/yK4m4eHhpeoYaf9tvQfss88+o7p161Lr1q0pLi5Ovi9GfQ/UYut7mJeXJ1uszK9aw12e+HiotDLyZ8Cn9Tt16mQqw6/DZfbu3Ut6cOrUKYqIiKCmTZvK1mA+XeqJxyGt/P/76aef0jPPPGNqLfWkY6G1/Wc4DjquKhnQtWvXZJO5+YfN+P7x48dJa/hUG/cZe/jhh2XwsrS0NPLx8ZH/cZTcB35OKWNtH5XnXC05OVl2n+DTGyXpYftd+fkppxYfeugh6tevn9V6Rtn/st4D/k+pcePGMnk4fPgwTZ8+XfZTXbVqleHeA7WU9T188MEHKSAgQL7vs2fPln0AY2Nj5fGRTzMq7zP3eTNXtWpV+UNDD58Bjy/g06EtW7aU+zRz5kx65JFH6OjRox51HNIK7lt68+ZNGj16tEceC63tP46DFWPIBFWPrR98MDXvN6V1qampNGXKFNnXhltkPJm1z+/f//637PN36NAh8uQYNu/HyC0E9erVox49esiBO82aNVNhS42lvO8hD4zivpcTJ06k+fPny1bR4cOHU4cOHeRtI+BBNwruf8sJKycD//d//0f+/v6qbpsn+vjjj+VnwsmYJx4LS+4/w3GwYoxxhCqBm9G9vb1LjdTk+9ZOL6hp8uTJclDC1q1bqUGDBqbHeTv5VAH/ErO1D/zX2j4qz7kSnzrjAWf8Hx23tvDCAxD4P0G+zb/+tLz9rv78+IDMBx9uuVHeH8YjiHlEp1H2v6z3wBpOHtjp06cN9R6opbzvIbeU8iApjkUux2eXli9fThcvXpSnw5X3ueTgUR49zCP79fgZ8HeuRYsWMsa0fhw1mnPnztHmzZtp3LhxHnkstLb/1uA4aCdh4EFSkydPthgkxR20tTJIijtP8yAuHrh18uRJm537v/zyS9Njx48ft9q5/8qVK6Yyixcvlh2rc3NzXbr9mZmZ4siRIxZLp06dxIgRI+RtrW+/qz8/HghQ8v3hff/ggw/Er7/+qvv9t+c9sGbnzp3yffjpp58M8R6orbzvoTVbtmyRA6D4+2g+SOqHH34wleHBVXoZJGVtwFitWrXkd83oxyEtDlgLDw8Xd+7c8ahjYVn7bw2Og/YxbIKanJwsfH19RVJSkjwAx8TEiODgYItRcmqaOHGiCAoKEtu2bZNfYGXJzs42lZkwYYJo1KiR+P777+V/Hl26dJGLoqCgQLRu3Vr06tVL/Pjjj2L9+vUiJCRExMXFqbJPJUcq6m37nf35lVRy5Kqe99+e9+D06dPizTfflJ/92bNnxZo1a0TTpk3Fo48+apj3QItKfg+XLFkikzH+PJYvXy5q164tpk6dalGnT58+4v777xd79+6V/3nyjADDhw8XevDSSy/JGOQY27Vrl+jZs6eoW7eunFXC6MchLeFGIH6fp0+fXm5Zox0Ly9p/HAcrzrAJKluwYIEMGB8fH9miumfPHqEV/AW1tixdutRUJicnRzz77LOyNaB69epiwIABMgEw99tvv4m+ffsKf39/eVDmg3V5v97c9R+j3rbf2Z9feQdlPe+/Pe/B+fPn5UGYEyL+sdi8eXMxbdo0iymP9P4eaFHJ7yH/hxkWFiZbEjnxfPfddy2m/2HXr1+XCWmNGjVkq82YMWNkS6QeDB06VNSrV08e5/ksGd/npMATjkNawq3u/P0/ceJEuWWNdiwsa/9xHKw4L/7H3u4AAAAAAACuZshBUgAAAACgX0hQAQAAAEBTkKACAAAAgKYgQQUAAAAATUGCCgAAAACaggQVAAAAADQFCSoAAAAAaAoSVAAAAADQFCSoAAAAAKApSFABAAAAQFOQoAIAAAAAacn/A/QsgO5bzLvsAAAAAElFTkSuQmCC",
|
|
350
|
+
"text/plain": [
|
|
351
|
+
"<Figure size 800x200 with 4 Axes>"
|
|
352
|
+
]
|
|
353
|
+
},
|
|
354
|
+
"metadata": {},
|
|
355
|
+
"output_type": "display_data"
|
|
356
|
+
}
|
|
357
|
+
],
|
|
358
|
+
"source": [
|
|
359
|
+
"fig, ax = plt.subplots(nrows=1, ncols=4, figsize=(8,2))\n",
|
|
360
|
+
"\n",
|
|
361
|
+
"for ax_i in ax:\n",
|
|
362
|
+
" ax_i.set_ylim(-0.05, 1.05)\n",
|
|
363
|
+
"\n",
|
|
364
|
+
"ax[0].plot(R[::2], label=\"even times\")\n",
|
|
365
|
+
"ax[0].plot(R[1::2], label=\"odd times\", linestyle=\"dashed\")\n",
|
|
366
|
+
"ax[0].legend()\n",
|
|
367
|
+
"\n",
|
|
368
|
+
"ax[1].plot(np.arange(20)+240, R[240:260], '+')\n",
|
|
369
|
+
"ax[2].plot(np.arange(20)+490, R[490:510], '+')\n",
|
|
370
|
+
"ax[3].plot(np.arange(20)+740, R[740:760], '+')\n",
|
|
371
|
+
"\n",
|
|
372
|
+
"plt.show()"
|
|
373
|
+
]
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
"cell_type": "code",
|
|
377
|
+
"execution_count": 55,
|
|
378
|
+
"id": "7c18e304",
|
|
379
|
+
"metadata": {},
|
|
380
|
+
"outputs": [
|
|
381
|
+
{
|
|
382
|
+
"data": {
|
|
383
|
+
"image/png": "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",
|
|
384
|
+
"text/plain": [
|
|
385
|
+
"<Figure size 400x400 with 1 Axes>"
|
|
386
|
+
]
|
|
387
|
+
},
|
|
388
|
+
"metadata": {},
|
|
389
|
+
"output_type": "display_data"
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
"data": {
|
|
393
|
+
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAjIAAAGdCAYAAAAIbpn/AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjUsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvWftoOwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAMvFJREFUeJzt3Ql4VOWhxvGXLYQtYd+jUUQWWWUTEJcrCkpVqrUULSBFWjdK5WqFqlCvCy7Vh3pBUcStLqDWFRWuIKhcowgILhdRFIECYRFJIECAZO7znelkEjITzsGZnDnn/H/PM2TOzDfJl5Mw5823VgmFQiEBAAB4UFW3KwAAAHCsCDIAAMCzCDIAAMCzCDIAAMCzCDIAAMCzCDIAAMCzCDIAAMCzCDIAAMCzqssDiouLtWXLFtWrV09VqlRxuzoAAMAGs+bunj171LJlS1WtWjW4QcaEmKysLLerAQAAjsGmTZvUunVrBTbImJaYyInIyMhwuzoAAMCG/Px8qyEich0PbJCJdCeZEEOQAQDAW6okcVgIg30BAIBnEWQAAIBnEWQAAIBneWKMDAAAXpx6fPjwYRUVFcmvqlWrpurVq7u6NApBBgCABDt48KC2bt2qffv2ye9q166tFi1aKC0tzZWvT5ABACDBi7iuX7/eaq0wC8GZC7wfF3MNhUJWYNuxY4f1/bZt2zZpi95VhCADAEACmYu7CTNm/RTTWuFntWrVUo0aNbRhwwbr+05PT6/0OjDYFwCAJHCjdSKI32cwzjIAAPAlggwAAPAsggwAAPAsggwAACgjJyfHmnU1ZMgQpTpmLQGAR+TkSC++aKa9ul0TbzjvPOmCC9yuhTfNnj1b48aNsz5u2bLFmkaeqggyAOAR11wjrV7tdi2848knpbw8pQQTPt1YG692bbPztLPX7N27V3PnztXy5cuVm5urp556Sn/5y1+UqggyAOARe/aEP44cKbVu7XZtUtfevdJDD0XPVyowIaZuXXfORZ06zl7z4osvqn379mrXrp1++9vf6k9/+pMmTZqUsov6EWQAwCMiXUqmZea009yuTerati0cZOiCOzamO8kEGGPw4MHKy8vT+++/r7POOkupiCADAB4RuTCn6B/GKaP0+mzmnKXC+TJdPKZ1xI2v68TatWu1bNkyvfrqq9ax2RBy2LBhVrghyAAAfhaCjD2lz0+qBBlTB6ddPG6YPXu2tWN36cG9Zk+lmjVravr06crMzFSqYfo1AHgEQebYggzsMQHmmWee0QMPPKBVq1aV3FavXm0FmxdeeEGpiBYZAPAIgow9BJljM2/ePP30008aM2ZMuZaXSy+91Gqtufrqq5VqaJEBAI8gyDgfI1Nc7GZNvGX27NkaOHBgzO4jE2TMdOzPP/9cqYYWGQDwCIKMPbTIHJs333wz7nO9e/e2xsqkomNqkZkxY4ays7OVnp6uPn36WCOcKzJt2jRrPnqtWrWUlZWlG264QQcOHDjWOgNAIBFk7CHIBIvjIGNW+5swYYKmTJmilStXqmvXrho0aJC2b98es/zzzz+viRMnWuXXrFljNV2Zz5HKqwQCQCqKdJMQZCpGkAkWx0HmwQcf1NixYzV69Gh17NhRM2fOVO3atfXEE0/ELP/RRx+pf//+uvzyy61WnPPOO0/Dhw8/aisOAKCsyEW59BgQlMcYmWBx9N/h4MGDWrFihTUYqOQTVK1qHZudMmPp16+f9ZpIcPn+++/19ttv64IKdvIqLCxUfn5+mRsABB1dS/bQIhMsjgb77ty5U0VFRWrWrFmZx83x119/HfM1piXGvO7000+3BgqZeepm+lZFXUtTp07V7bff7qRqAOB7BBl7CDLBkvQGyiVLlujuu+/Www8/bI2peeWVV/TWW2/pjjvuiPsaszmV2dshctu0aVOyqwkAKY8gY0/p80PXkv85apFp3LixqlWrpm1mR65SzHHz5s1jvua2227TiBEjdNVVV1nHnTt3VkFBgX7/+9/rlltusbqmjmSWQjY3AEAUQebY9lqCvzlqkUlLS1OPHj20aNGikseKi4ut4759+8Z8zb59+8qFFROGjFSdkw4AqYggYw9dS8HieEE8M/V61KhR6tmzp7VAjlkjxrSwmFlMxsiRI9WqVStrnItx4YUXWjOdunfvbq05s27dOquVxjweCTQAgKMjyNhDkAkWx0HGbOe9Y8cOTZ48Wbm5uerWrZvmz59fMgB448aNZVpgbr31VlWpUsX6uHnzZjVp0sQKMXfddVdivxMA8DmCjD1Mvz52V155pZ5++umS44YNG6pXr16677771KVLF6WiKiEP9O+Y6ddm7wcz8DcjI8Pt6gCAK+rXl/LypLVrpZNPdrs2qctc1SJhxqzV2qRJ5X59s3L9+vXrdcIJJ1gr4HstyGzbtk1PPvmkdWwaLExDhNljyTRUOP1+K+P6zbJKAOARtMjYQ9fSz2Mm25gJPOZmel3M6vxm9rDpjUlFbBoJAB5BkHEu1YJMQUH858yw0dINGhWVNS1OtWpVXLZOHf1se/fu1bPPPquTTjpJjRo1UioiyACARxBk7DMXejM+JtXGyNStG/85s+D9W29Fj5s2NTN/Y5c980yzTlv0ODvbLFqbmBA3b9481f13Rc1knhYtWliPxVouJRWkZq0AAOUQZOyLnKNUa5HxgrPPPlurVq2ybmZ7IbMx9Pnnn68NGzYoFdEiAwAeQZDxfpDZuzf+c0euSGIGKsdzZOPIDz8oYerUqWN1JUU8/vjj1oDdWbNm6c4771SqIcgAgEcQZLwfZJyMW0lWWafMEiqmW2n//v1KRQQZAPAIgox9kRaLVBsj4wWFhYXWtGvjp59+0vTp061Bv2YNuFREkAEAjyDIeL9Fxgvmz59vDfA16tWrp/bt2+ull17SWWedpVREkAEAjyDI2EeQOTZPPfWUdfMSZi0BgEdEukkIMkdHkAkOggwAeETkopyiy3mkFMbIBAf/HQDAI+haso8WmeAgyACARxBk7CPIBAdBBgA8giBjH0EmOAgyAOAxBBlvjJEJBSRFhVz+PgkyAOABpa8VBJnUbpGpUaOG9XFfvB0ffWbfv7/PyPdd2VhHBgA8gCDjnSBTrVo11a9fX9v/vVlS7dq1rWX+/dgSs2/fPuv7NN+v+b7dQJABAA8gyHhrjEzz5s2tj5Ew42f169cv+X7dQJABAA8gyHhrjIxpgTHL/Ddt2lSHDh2SX9WoUcO1lpgIggwAeABBxlstMhHmIu/2hd7vGOwLAB5AkPFmkEHyEWQAwAMIMs5EzhFbFPgfQQYAPIAgc2xjZGiR8T+CDAB4AEHGGbqWgoMgAwAeQJBxhiATHAQZAPAAgowzjJEJDoIMAHgAQcYZxsgEB0EGADyAIOMMXUvBQZABAA8o3UVCkDk6gkxwEGQAwANKX5Aj3SZI3S0KUHn47wAAHkDXkjO0yAQHQQYAPIAg4wxBJjgIMgDgAQQZZwgywUGQAQAPIMg4wxiZ4CDIAIAHEGScoUUmOAgyAOABBBlnCDLBQZABAA/gguwMQSY4CDIA4AGRCzKtMfYwRiY4qivAbr9dWrfO7Vp4x+mnS3/4g9u1AIKJIOMMLTLBEeggs2CBlJPjdi2847nnpOHDpYwMt2sCBA9BxhmCTHAEOsiMGyf96ldu18Ibbrop3ES7dy9BBnADQcYZgkxwBDrImNYF2DN5slRQIB044HZNgGAiyDjDGJngYLAvbKlZM/yRIAO4gyDjDC0ywUGQgS3p6eGPhYVu1wQIJoKMMwSZ4CDIwFGQoUUGcAdBxhmCTHAQZGALXUuAuwgyzjBGJjgIMrCFriXAXQQZZ2iRCQ6CDGyhawlwV6RlIdLSgIoRZIKD/xKwhSADuIsWGWci54muJf8jyMDRGBm6lgB3EGScibRc0SLjfwQZ2EKLDOAugowzdC0FB0EGthBkAHcRZJwhyAQHQQa2MGsJcBdBxhnGyARHoPdagvMxMvv2SUVFbtfGG/3zXHCQSAQZZxgjExwEGThqkbn99vANFWvRQvr0U6lVK7drAr8gyDhD11Jw0LUEW844Q6pO7LVt61bpk0/crgX8hCDjDEEmOLg0wZZLLpF272aMjB2XXSa99560d6/bNYGfEGScYYuC4CDIwLY6dcI3VKx+/fDHggK3awI/Icg4Q4tMcNC1BCRY3brhj7TIIJEIMs4QZIKDIAMkGEEGyUCQcYYgExwEGSDBCDJIBoKMM4yRCQ6CDJBgBBkkA0HGGVpkguOYgsyMGTOUnZ2t9PR09enTR8uWLauw/O7du3XdddepRYsWqlmzpk4++WS9/fbbx1pnIKURZJAMBBlnCDLB4XjW0ty5czVhwgTNnDnTCjHTpk3ToEGDtHbtWjVt2rRc+YMHD+rcc8+1nnv55ZfVqlUrbdiwQfUjUzsAnyHIIBkIMs4QZILDcZB58MEHNXbsWI0ePdo6NoHmrbfe0hNPPKGJEyeWK28e37Vrlz766CPVqFHDesy05gB+DzILF0odOrhdm9RXr5706KNS9+5u1yS1EWScYYxMcDgKMqZ1ZcWKFZo0aVLJY1WrVtXAgQOVk5MT8zVvvPGG+vbta3Utvf7662rSpIkuv/xy3XzzzapWrVrM1xQWFlq3iPz8fCfVBFwVCS9mX6qvv3a7Nt7w4osEmaOJXJAJMvbQIhMcjoLMzp07VVRUpGbNmpV53Bx/Hecd+/vvv9d7772nK664whoXs27dOl177bU6dOiQpkyZEvM1U6dO1e1s6AOP6tZN+vZbacsWt2uS+mbOlF54QTp0yO2apL7IBTnS0oCKEWSCI+kr+xYXF1vjYx577DGrBaZHjx7avHmz7r///rhBxrT4mHE4pVtksrKykl1VIGFOOil8Q8UWLAh/PHzY7ZqkPrqWnCHIBIejINO4cWMrjGzbtq3M4+a4efPmMV9jZiqZsTGlu5E6dOig3Nxcq6sqLS2t3GvMzCZzA+BvkY1ICTJHR5BxhjEyweGokdKEDtOismjRojItLubYjIOJpX///lZ3kikX8c0331gBJ1aIARAcBBn7CDLO0CITHI57W02Xz6xZs/T0009rzZo1uuaaa1RQUFAyi2nkyJFlBgOb582spfHjx1sBxsxwuvvuu63BvwCCjSBjH0HGGYJMcDgeIzNs2DDt2LFDkydPtrqHunXrpvnz55cMAN64caM1kynCjG1ZsGCBbrjhBnXp0sVaR8aEGjNrCUCwEWTsI8g4Q5AJjmMa7Hv99ddbt1iWLFlS7jHT7fTxxx8fy5cCEIAgw6yloyPIOMMYmeBgIh8A19AiYx9BxhlaZIIj6dOvASAegox9BBlnIufJtMgQZuzz4u8XLTIAXEOQsY8g40zkPN10U7ibiZuOevvkE3kSQQaAawgy9hFknBkwgHMVFHQtAXDNv/eRJcjYQJBxZuxYM8vW7BHodk28IzNTnkSQAeAaWmTsI8g4l5Hhdg1QGQgyAFxDkLGPIOPMp59KkydLBw6Uf86MBym1QL1uu01aurTiPcEiC9HfdZe0cGH8sm+8IdWrF77/wAPSvHkV7/repEn4/owZ0ssvxy/79NPScceF78+eLT37bPyyjz0mtW0bvv/cc9Ljj8cv+9BDUufO8jSCDADXEGTsI8g48+ij0vz5sZ8rtfWf5csvzRpo8T9X6VlPa9ZUXLb07/I331RctrAwen/duorL7t8fvb9+fcVl9+6N3t+4seKyeXnyPIIMANcQZOwjyDjz619LtWpJtWtLPXqUfe7Ic3jjjdLw4Uf/PTXMWrAXXRS/bJ06ZcfpnHNO/LING0bvjxxpFo+NX7ZFi+h9M/anS5f4ZbOzo/eHDpXatIlftl07eV6VUCj1Z9jn5+crMzNTeXl5yqDTE/AN8xfz+edL3btLK1e6XZvUZro3Bg+WunWTPvvM7doAqXP9Zvo1ANfQImMfLTJAbHQtAXANQca+yJ5BBBl71q6VduyQTjxRatnS7dogmWiRAeAagozzFpnIZoio2F//Gl4U76WX3K4Jko3/EgBcQ5Cxj64lZzhfwUGQAeAaVva1jwuzM5yv4CDIAHANLTL2cWF2hvMVHAQZAK4hyNjHhdkZxhQFBz9iAK4hyNhHkHGGWV7BQZAB4BqCjH0EGWc4X8HBOjIAXA8yhw65XZPUx4XZmSuukHr2lHr3drsmSDaCDADXg4zZPO/WW92uTWr7+uvwR4KMPZde6nYNUFkIMgBcU7dueDCmGc9w111u18Y75wxAFEEGgGsyM6Vnn5VyctyuiXdasK680u1aeMN335kNC6XjjpMaNXK7Nkgmdr8GAPjOL34hvfWW9Pjj0pgxbtcmuPLZ/RoAAOcYHB0cBBkAgO8QZIKDIAMA8B2CTHAQZAAAvsMWBcHBjxgA4DtsURAcBBkAgO/QtRQcrCMDAPCdyy8Pb0/QqZPbNUGyEWQAAL4zerTbNUBloWsJAAB4Fi0yAADf2bhR2r9fatlSqlfP7dogmWiRAQD4zogRUvv20jvvuF0TJBtBBgDgO8xaCg6CDADAdwgywUGQAQD4DkEmOAgyAADfYYuC4OBHDADwHVpkgoMgAwDwHYJMcLCODADAd37zG6lPH+nEE92uCZKNIAMA8J3x492uASoLXUsAAMCzaJEBAPjOtm3SoUNS48ZSerrbtUEy0SIDAPCdCy6QsrKkxYvdrgmSjSADAPAdZi0FB0EGAOA7BJngIMgAAHyHIBMcBBkAgO8QZIKDIAMA8B32WgoOfsQAAN8pLg5/pEXG/1hHBgDgO5ddJvXrJ7Vs6XZNkGwEGQCA70yZ4nYNUFnoWgIAAJ5FiwwAwHfy8sLjZOrVk6pzpfM1WmQAAL7Tq5fUsKH08cdu1wTJRpABAPgO68gEB0EGAOA7BJngIMgAAHyHIBMcBBkAgO8QZIKDIAMA8B22KAiOY/oRz5gxQ9nZ2UpPT1efPn20bNkyW6+bM2eOqlSpoqFDhx7LlwUAwBa2KAgOx0Fm7ty5mjBhgqZMmaKVK1eqa9euGjRokLZv317h63744QfdeOONGjBgwM+pLwAAR2X+Xr78cqlRI7drgmSrEgpFGuDsMS0wvXr10vTp063j4uJiZWVlady4cZo4cWLM1xQVFemMM87Q7373O3344YfavXu3XnvtNdtfMz8/X5mZmcrLy1NGRoaT6gIAAJdUxvXbUYvMwYMHtWLFCg0cODD6CapWtY5zcnLivu6//uu/1LRpU40ZM8bW1yksLLS++dI3AACAnxVkdu7cabWuNGvWrMzj5jg3Nzfma5YuXarZs2dr1qxZtr/O1KlTrQQXuZkWHwAA7CosDN8iY2XgX0kdz71nzx6NGDHCCjGNGze2/bpJkyZZzVCR26ZNm5JZTQCAz2RnS+np0uefu10TJJujrbRMGKlWrZq2bdtW5nFz3Lx583Llv/vuO2uQ74UXXljymBlTY33h6tW1du1atWnTptzratasad0AADgWrCMTHI5aZNLS0tSjRw8tWrSoTDAxx3379i1Xvn379vriiy+0atWqkttFF12ks88+27pPlxEAIBkIMsHheHNzM/V61KhR6tmzp3r37q1p06apoKBAo0ePtp4fOXKkWrVqZY1zMevMdOrUqczr69evb3088nEAABKFIBMcjoPMsGHDtGPHDk2ePNka4NutWzfNnz+/ZADwxo0brZlMAAC4hSATHI7XkXED68gAAJww80t+/FH66iupY0e3axNc+am2jgwAAF7AFgXB4bhrCQCAVDdkiFkCRKpXz+2aINkIMgAA3/nHP9yuASoLXUsAAMCzCDIAAMCzCDIAAN9p0MCsIG9WmHe7Jkg2ggwAwHcOHZKKipi1FAQEGQCA77AgXnAQZAAAvkOQCQ6CDADAdwgywUGQAQD4DkEmOAgyAADfBhn2MPY/VvYFAPjOwIHhmUvp6W7XBMlGkAEA+M5bb7ldA1QWGt0AAIBnEWQAAIBnEWQAAL6TmRm+7djhdk2QbIyRAQD4bsZSfr7btUBloUUGAODLqdcG68j4H0EGAOArBJlgIcgAAHyFIBMsBBkAgK8QZIKFIAMA8G2QYYsC/2PWEgDAd/r3Dwea6lzlfI8fMQDAV9LSpKVL3a4FKguNbgAAwLMIMgAAwLMIMgAAXykokFq0kFq2lPbvd7s2SDbGyAAAfKWoSMrNdbsWqCy0yAAAfIXp18HCjxgA4CssiBcsBBkAgK8QZIKFIAMA8BWCTLAQZAAAvkKQCRZmLQEAfMUM8O3WLRxoCDL+R5ABAPhKgwbSZ5+5XQtUFrqWAACAZ9EiAwAes3attHp1/OfPPFNq1ix8/7vvpBUr4pc9/fTwCrjGDz9Iy5bFL3vaadJxx4Xv/+tf0kcfxS/bq5d0wgnh+1u3Sh9+GL9s9+5S27bh+zt2SIsXxy/bubPUoUP4/q5d0sKFscs1aiSdc078zwP/IMgAgMfMmyfdeGP85xctigaZd9+Vrrkmftk33ogGmQ8+kEaNil/2hReiQeaTT6Rhw+KXnT07GmRM6Kqo7EMPRYPMmjUVl506NRpk1q+vuOwpp0hffhn/efgDQQYAPCYrSzrrrPjP168fvW9CSkVlTctFhAk/FZVt2jR6v0mTiss2bx6937BhxWVbtYrez8ysuGwkSBl161Zc9qKL4j8H/6gSCpWeqJaa8vPzlZmZqby8PGVkZLhdHQBwxTvvhDdEPPvssgEECPL1mxYZAPCIa68Nj2PJySHIABHMWgIAj4i0n7M2ChBFkAEAjyDIAOURZADAYwgyQBRBBgA8IvWnZgCVjyADAB5B1xJQHkEGADyCIAOUx/RrAPCIBx4IryNz/PFu1wRIHQQZAPCIipbjB4KKriUAAOBZtMgAgEeYzSAPHgzvWF2vntu1AVIDLTIA4BGXXy5dcEF412cAYQQZAPAIZi0B5RFkAMBjCDJAFEEGADyClX2B8ggyAOARdC0B5RFkAMAjCDJAeQQZAPAIggxQHuvIAIBH3HuvdOCA1KyZ2zUBUgdBBgA8YuxYt2sApB66lgAAQLCCzIwZM5Sdna309HT16dNHy5Yti1t21qxZGjBggBo0aGDdBg4cWGF5AEBsS5dK778v7d/vdk0ADweZuXPnasKECZoyZYpWrlyprl27atCgQdq+fXvM8kuWLNHw4cO1ePFi5eTkKCsrS+edd542b96ciPoDQGCcf7501lkSb59AVJVQyNkSS6YFplevXpo+fbp1XFxcbIWTcePGaeLEiUd9fVFRkdUyY14/cuRIW18zPz9fmZmZysvLU0ZGhpPqAoBv1K0rFRRI69ZJbdq4XRsgNa7fjlpkDh48qBUrVljdQyWfoGpV69i0ttixb98+HTp0SA0bNoxbprCw0PrmS98AAGFMvwaOMcjs3LnTalFpdsTcP3Ocm5tr63PcfPPNatmyZZkwdKSpU6daCS5yMy0+ABB0bFEAuDxr6Z577tGcOXP06quvWgOF45k0aZLVDBW5bdq0qTKrCQApiQXxgJ+5jkzjxo1VrVo1bdu2rczj5rh58+YVvvZvf/ubFWQWLlyoLl26VFi2Zs2a1g0AEEWQAX5mi0xaWpp69OihRYsWlTxmBvua4759+8Z93X333ac77rhD8+fPV8+ePZ18SQDAvxFkgASs7GumXo8aNcoKJL1799a0adNUUFCg0aNHW8+bmUitWrWyxrkY9957ryZPnqznn3/eWnsmMpambt261g0AYM+dd5qZn1L9+m7XBPBwkBk2bJh27NhhhRMTSrp162a1tEQGAG/cuNGayRTxyCOPWLOdfvWrX5X5PGYdmr/+9a+J+B4AIBBuvNHtGgA+WEfGDawjAwCA9+RXwvWbTSMBwCOWLw9/NPMl0tLcrg2QGggyAOARffqYCRbhLQpatnS7NkBqYPdrAPAIZi0B5RFkAMBjCDJAFEEGADyCFhmgPIIMAHgMQQaIIsgAgAeUXiiDIANEEWQAwAMIMkBsTL8GAI+YPDkcaGrXdrsmQOogyACAB5idX26/3e1aAKmHriUAAOBZtMgAgAeYLqWvvw7fP/lkqVo1t2sEpAaCDAB4wMGDUseO4fu7d0uZmW7XCEgNdC0BgMcwawmIIsgAgMemXwOIIsgAgAewjgwQG0EGADyAIAPERpABAA8gyACxEWQAwAMIMkBsTL8GAA+oUUOaMCF6H0AYQQYAPKBmTemBB9yuBZB6CDJw7JVXpAMHYj/XsKE0eHD0+PXXpYKC2GUzMqRf/CJ6/NZbUl5e7LJmk7yhQ6PHCxZIP/4Y/w3/0kujx4sWSdu2xS5rVkcdNix6vGSJtGWL4ho+PNqsv3SptHFj7HKtW0sDBtAFAADJViUUSv3VCfLz85WZmam8vDxlmKsfXNWsmbR9e+znevSQli+PHp94orR+feyy7dtLa9ZEjzt1kr76KnbZrKyyoaFPH2nZsthlGzWSdu6MHv/Hf0iLF8cum54u7d8fPTbBygSqeIqKwpv3GSYAvfhi/LKffir17Bn/ecCJ4mJp06ZwODb/HwjJ8IL8Srh+0yIDx844I7xEeixt25Y97t9fatMmdlnzZlxa375SixaxyzZpUva4d+9wi04sRz5uwlW8fWnS0soed+smFRbKls6dpV27yj++cKF0660sIY/EMq2V2dnR7QoYJwOE0SIDW/75z3C3y6BBZbuDAFSOn34Kd90ahw5J1fkzFB6QXwnXb6Zfw5YPPpCmT5dyctyuCRBMqf8nJ+AOMj0cvYlGxocgvnXrpD17wt0ADRq4XRv4BevIALFxWYLtgYYGQeborrpKOvXU8FgZIFEIMkBsXJZgC0HGvshFJnLOgEQgyACxcVmCLQQZ+yLniDENSCSCDBAbY2TgKMjwBmo/yNAig0SqVUu6+mq3awGkHoIMbGGwr30EGSRDvXrSI4+4XQsg9RBkYMvdd0sTJzILxw6CDABUHoIMbDEr6x65ui5iI8ggGczvk1lJ2nTvmm04AIQRZIAEM3swma0OzBYGQKLk5kqtWoVX9DUr+wIII8jAlrlzpc8+k4YMCe/qjPhGjnS7BvAjZsEBsTF0E7a8+aZ0771ld7YGUPlBhpmDQFm0yMAW1pGxb+vW8BYFTZtK9eu7XRv4BUEGiI3LEmwhyNg3bpzUrp30wgtu1wR+QpABYuOyBFsIMvaxRQGSgSADxMZlCbYQZOxj+jWSgSADxMYYGdjCFgX2EWSQDHXqSCNGSGlpbtcESC0EGdjCFgX2EWSQDGZBymeecbsWQOrhsgRbpk2TVq+WLr3U7ZqkPoIMAFQeWmRgy/HHu10D74h0v7GAGRLJBOPCwuhO2ADCCDJAgg0aFN4L59RT3a4J/OT776W2baWMDCkvz+3aAKmDIANbnnsu/EZ68cVSly5u1ya1XXFF+AYkEi18QGwEGdjyj39ICxZI2dkEGcANTL8GYiPIwBbWkbFv926poECqVy/cDQAkAkEGiI3LEmxhHRn7Jk6UWrcOz/QCEoUgA8RGkIEttMjYx/RrJANBBoiNyxJsIcjYFzlHDM5EIhFkgNgYIwNbeBO1j00jkQxmzNUll4Q/AogiyMAWWmTso2sJyZCVJf3zn27XAkg9BBnY8uijUn5+eEEuVIwgAwCVhyADWzp2dLsG3kGQAYDKQ0cBkGCnnSZddZXUu7fbNYGffP55ePxVy5Zu1wRILbTIwJZnn5W2b5eGDpVOPNHt2qS2yy4L34BkDLhnNhxQFkEGtpjF3VaskNq3J8gAbmDmIBAbQQa2MGvJvgMHwre0NKl2bbdrA78gyACxcVmCLQQZ++68U2rQILxVAZAoBBkgNi5LsIUgYx8r+yIZCDJAbMd0WZoxY4ays7OVnp6uPn36aNmyZRWWf+mll9S+fXurfOfOnfX2228fy5eFi9g00j6mXyMZCDJAgoLM3LlzNWHCBE2ZMkUrV65U165dNWjQIG03U1pi+OijjzR8+HCNGTNGn332mYYOHWrdvvzyS6dfGinwJkqLzNERZJAMmZnS4MHSWWe5XRMgtTi+LD344IMaO3asRo8erY4dO2rmzJmqXbu2nnjiiZjl//73v2vw4MG66aab1KFDB91xxx069dRTNX369ETUH5WEriX7CDJIBrOq9jvvSM8843ZNgNTi6LJ08OBBrVixQgMHDox+gqpVreOcnJyYrzGPly5vmBaceOWNwsJC5efnl7nBXU8+KS1cKHXp4nZNUh+bRgJAigaZnTt3qqioSM2aNSvzuDnOzc2N+RrzuJPyxtSpU5WZmVlyyzK7pcH11WrPOSc8GwcVo0UGACpPSnYUTJo0SXl5eSW3TZs2uV0lwLZTTpEuvzwc/oBE+fRTqW5dWkWBn7UgXuPGjVWtWjVt27atzOPmuHnz5jFfYx53Ut6oWbOmdUNqbVGwd6/0y1+aFjW3a5PaLroofAMS6fBhqaAgfANwjC0yaWlp6tGjhxYtWlTyWHFxsXXct2/fmK8xj5cub7z77rtxyyM13XqrdM010oYNbtcECCamXwOxOd6iwEy9HjVqlHr27KnevXtr2rRpKigosGYxGSNHjlSrVq2scS7G+PHjdeaZZ+qBBx7QkCFDNGfOHC1fvlyPPfaY0y8NFzFrydm5KioK369Rw+3awC8IMkBsji9Lw4YN09/+9jdNnjxZ3bp106pVqzR//vySAb0bN27U1q1bS8r369dPzz//vBVczJozL7/8sl577TV16tTJ6ZeGiwgy9j30UHifpVGj3K4J/IQgAyRw08jrr7/eusWyZMmSco9ddtll1g3exZuofWxRgGTg/yAQG39fwxZaZOxj+jWSgSADJLBFBsFDkLGPIINkbVEwYIB03HFu1wRILQQZ2MJeS/YRZJAMXbtKH3zgdi2A1EOQgS3/+IfZOoK/Bu1giwIAqDwEGdgyaJDbNfAOWmQAoPLQUQAkWHa2dPHFUu/ebtcEfrJ0qdS0qXTmmW7XBEgttMjAluefDy/yNnSoVK+e27VJbeeeG74BiWS6dnfsYIsQ4EgEGdjyhz+E91r67juCDOAGpl8DsdG1BFuYfg24iyADxMZlCbYQZJx1w5k9lgYPdrsm8BOCDBAblyU4CjK8idpz+HD4BiQKQQaIjSADW1gQzz6mXyMZCDJAbAz2hS10LdlHkEEyZGRIp54qtW/vdk2A1EKQgS0EGfsIMkiGfv2kFSvcrgWQergswZYXXgjfzMZ1sBdkPvxQevjh6OOLF4e7BeLd7r8/WnbZsorL3n57tOxXX1Vc9s9/jpZdv77istddFy27fXvFZUePjpYtKKi47K9/XbaLpPRzZjr///xPgn8IAAIj8C0yGzaE34Tj6dgxen/jxvBaKvF06BDtv/7Xv6T8/Phl27WTqlUL39+yRdq9O37Ztm3Ds2CM3Fxp1674Zdu0kWrWjF6Idu6MX/bEE6X09PB9s9CWucVz4YVS7drxn0fZzf3MxXnPHrdr4g3m/9SCBdJ557ldEwBeFPggc9VV0sKFsZ8z4eHgwejxuHHSG2/E/1ymbCRwmL+CTQtGPCa4RFo3pkyRHn88ftnNm6WWLcP3p06VHnooftlvvgkHH2PatHD5eFatCl90jccek269NX7Z//3fcNM2js6ESRMiTZCpWzf6eP/+4cfjqVMner9794rLlg6VJhRXVLZWreh9s+lnRWUjwdZo3LjispHAHKmP3bJGpOyDD0r33EPos+O996QxY6Ru3aRXX3W7NkDqCHyQMWHCvGHHEgklpQfbxSt7JPMXeUVlS888MBe7isqWHpdiLnYVlY208kQuLokqWz3wvynOmEBQOhQYaWlSkyb2Xm9+9+yWNT8bu2XNz9xuWfN7Z7es+X0+lrKR5fYJMkdnWo5/+CG83xKAqCqhUGRSX+rKz89XZmam8vLylGHSBABfmD073Co6ZIg0b57btUltpjXYbEbap4/08cdu1wZInes3f2cDcI1Z/fjdd6Ndp4iPdWSA2AgyAFzTqlX4hqNjdW0gNqZfA4AH0CIDxEaLDADXmNl7c+eGWxuuucbt2qQ2ggwQG0EGgGt+/FG6+urwjK61a8s+d8IJ0vjx0ePJk+OvzWS6p266KXp8553x11AyM6ZuuSV6bBYiNEscxJvVWHrxQbOkgZk5FG+ae+nlDsxiiGY5hHizxx54oOyg5y++UFymrJkJabYnOP74+OWAIGLWEgDX5OVJjRpJRUXlnzPrFpn1iyLMgOCtW2N/HrMeklkXKcKspbRuXeyy5rnSAcOsy7J6deyy5muWDjmmTjk58UNP6YUtzz3X/hpVQ4dKr78uW2tUAV6Sz6wlAH5mLv7//Gd4S4YjmcX7SvvjH+OvN9O8edlj001lWntiMcGpNLPInFkxO5Yj33dHjZLOPjt22SPXDRo+XOrd++hrOBm/+pV0yimKiz3OgPhokQEAAJ69fpPzAQCAZxFkAACAZxFkAACAZxFkAACAZxFkAACAZxFkAACAZxFkAACAZxFkAACAZxFkAACAZxFkAACAZxFkAACAZxFkAACAZxFkAACAZ1WXB0Q26Da7aAIAAG/I//d1O3IdD2yQ2bNnj/UxKyvL7aoAAIBjuI5nZmYqGaqEkhmTEqS4uFhbtmxRvXr1VKVKlYQmRROONm3apIyMjIR9XlSM8+4Ozrs7OO/u4Lynxnk3EcOEmJYtW6pq1arBbZEx33zr1q2T9vnNyeYXvfJx3t3BeXcH590dnHf3z3uyWmIiGOwLAAA8iyADAAA8K9BBpmbNmpoyZYr1EZWH8+4Ozrs7OO/u4LwH57x7YrAvAABALIFukQEAAN5GkAEAAJ5FkAEAAJ5FkAEAAJ4V6CAzY8YMZWdnKz09XX369NGyZcvcrpJnTZ06Vb169bJWX27atKmGDh2qtWvXlilz4MABXXfddWrUqJHq1q2rSy+9VNu2bStTZuPGjRoyZIhq165tfZ6bbrpJhw8fruTvxrvuuecea/XrP/3pTyWPcd6TY/Pmzfrtb39rnddatWqpc+fOWr58ecnzZh7F5MmT1aJFC+v5gQMH6ttvvy3zOXbt2qUrrrjCWjisfv36GjNmjPbu3evCd+MNRUVFuu2223TCCSdY57RNmza64447yuzjw3n/+T744ANdeOGF1mq85v3ktddeK/N8os7x559/rgEDBljXYLMa8H333XdsFQ4F1Jw5c0JpaWmhJ554IvTVV1+Fxo4dG6pfv35o27ZtblfNkwYNGhR68sknQ19++WVo1apVoQsuuCB03HHHhfbu3VtS5uqrrw5lZWWFFi1aFFq+fHnotNNOC/Xr16/k+cOHD4c6deoUGjhwYOizzz4Lvf3226HGjRuHJk2a5NJ35S3Lli0LZWdnh7p06RIaP358yeOc98TbtWtX6Pjjjw9deeWVoU8++ST0/fffhxYsWBBat25dSZl77rknlJmZGXrttddCq1evDl100UWhE044IbR///6SMoMHDw517do19PHHH4c+/PDD0EknnRQaPny4S99V6rvrrrtCjRo1Cs2bNy+0fv360EsvvRSqW7du6O9//3tJGc77z2feA2655ZbQK6+8YhJi6NVXXy3zfCLOcV5eXqhZs2ahK664wrpuvPDCC6FatWqFHn30Ucf1DWyQ6d27d+i6664rOS4qKgq1bNkyNHXqVFfr5Rfbt2+3/gO8//771vHu3btDNWrUsN54ItasWWOVycnJKfnPU7Vq1VBubm5JmUceeSSUkZERKiwsdOG78I49e/aE2rZtG3r33XdDZ555ZkmQ4bwnx8033xw6/fTT4z5fXFwcat68eej+++8vecz8LGrWrGm9YRv/93//Z/0cPv3005Iy77zzTqhKlSqhzZs3J/k78KYhQ4aEfve735V57JJLLrEuhgbnPfGODDKJOscPP/xwqEGDBmXeY8z/q3bt2jmuYyC7lg4ePKgVK1ZYzWGl93Myxzk5Oa7WzS/y8vKsjw0bNrQ+mvN96NChMue8ffv2Ou6440rOuflomuebNWtWUmbQoEHWJmRfffVVpX8PXmK6jkzXUOnza3Dek+ONN95Qz549ddlll1ldcd27d9esWbNKnl+/fr1yc3PLnHez34zpwi593k2Tu/k8Eaa8eS/65JNPKvk78oZ+/fpp0aJF+uabb6zj1atXa+nSpTr//POtY8578iXqHJsyZ5xxhtLS0sq875ghCT/99JP/No1MtJ07d1p9raXfuA1z/PXXX7tWL78wu5WbMRr9+/dXp06drMfML775hTW/3Eeec/NcpEysn0nkOcQ2Z84crVy5Up9++mm55zjvyfH999/rkUce0YQJE/SXv/zFOvd//OMfrXM9atSokvMW67yWPu8mBJVWvXp1K/xz3mObOHGiFbBNGK9WrZr1Pn7XXXdZYzEMznvyJeocm49mrNORnyPyXIMGDWzXKZBBBslvHfjyyy+tv5SQXJs2bdL48eP17rvvWgPmUHlh3fy1effdd1vHpkXG/M7PnDnTCjJIjhdffFHPPfecnn/+eZ1yyilatWqV9UeTGZTKeQ+uQHYtNW7c2ErzR87cMMfNmzd3rV5+cP3112vevHlavHixWrduXfK4Oa+mS2/37t1xz7n5GOtnEnkO5Zmuo+3bt+vUU0+1/uIxt/fff18PPfSQdd/8hcN5TzwzW6Njx45lHuvQoYM1+6v0eavoPcZ8ND+70sxMMTPbg/Mem5lNZ1plfvOb31jdoSNGjNANN9xgzZo0OO/Jl6hznMj3nUAGGdP826NHD6uvtfRfWOa4b9++rtbNq8yYMBNiXn31Vb333nvlmgzN+a5Ro0aZc276Qs0bf+Scm49ffPFFmf8ApqXBTN878qKBsHPOOcc6Z+Yv08jNtBSYpvbIfc574plu0yOXFzDjNo4//njrvvn9N2/Gpc+76RIx4wNKn3cTME0YjTD/d8x7kRlvgPL27dtnjbMozfxRas6ZwXlPvkSdY1PGTPM2Y/hKv++0a9fOUbeSJRTg6ddmlPVTTz1ljbD+/e9/b02/Lj1zA/Zdc8011nS8JUuWhLZu3Vpy27dvX5lpwGZK9nvvvWdNA+7bt691O3Ia8HnnnWdN4Z4/f36oSZMmTAN2qPSsJYPznpyp7tWrV7emA3/77beh5557LlS7du3Qs88+W2aKqnlPef3110Off/556OKLL445RbV79+7WFO6lS5daM8+YBhzfqFGjQq1atSqZfm2mB5ulAv785z+XlOG8J2YWpFmKwdxMTHjwwQet+xs2bEjYOTYzncz06xEjRljTr8012fwfYvq1Q//93/9tvcGb9WTMdGwz3x3Hxvyyx7qZtWUizC/5tddea025M7+wv/zlL62wU9oPP/wQOv/88631BMwb1H/+53+GDh065MJ35J8gw3lPjjfffNMKgOYPovbt24cee+yxMs+baaq33Xab9WZtypxzzjmhtWvXlinz448/Wm/uZi0UM9199OjR1kUEseXn51u/2+Z9Oz09PXTiiSda652UnsLLef/5Fi9eHPP93ATJRJ5jswaNWcbAfA4TUE1AOhZVzD8/v7EJAACg8gVyjAwAAPAHggwAAPAsggwAAPAsggwAAPAsggwAAPAsggwAAPAsggwAAPAsggwAAPAsggwAAPAsggwAAPAsggwAAPAsggwAAJBX/T99jh+1nPeiqwAAAABJRU5ErkJggg==",
|
|
394
|
+
"text/plain": [
|
|
395
|
+
"<Figure size 640x480 with 1 Axes>"
|
|
396
|
+
]
|
|
397
|
+
},
|
|
398
|
+
"metadata": {},
|
|
399
|
+
"output_type": "display_data"
|
|
400
|
+
}
|
|
401
|
+
],
|
|
402
|
+
"source": [
|
|
403
|
+
"config = ConfigureHCCD_PeriodicPersistent(repeating_pattern=\"AB\")\n",
|
|
404
|
+
"config.indicator_resolution_granularity = 80\n",
|
|
405
|
+
"\n",
|
|
406
|
+
"result = config.run(data)\n",
|
|
407
|
+
"result.var_names = var_names\n",
|
|
408
|
+
"import matplotlib.pyplot as plt\n",
|
|
409
|
+
"result.plot_labeled_union_graph()\n",
|
|
410
|
+
"plt.show()\n",
|
|
411
|
+
"for mi in result.model_indicators():\n",
|
|
412
|
+
" mi.plot_resolution()\n",
|
|
413
|
+
" plt.legend()\n",
|
|
414
|
+
" plt.show()"
|
|
415
|
+
]
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
"cell_type": "markdown",
|
|
419
|
+
"id": "81498128",
|
|
420
|
+
"metadata": {},
|
|
421
|
+
"source": [
|
|
422
|
+
"Indicator-resolution can be poor, especially when indicator_resolution_granularity is chosen poorly, considering that given N=1000 there are 998 (max-length, including PC1-parents for MCI tests) windows, eg using indicator_resolution_granularity=100 (the default) leaves a remainder of 98 data-points ..., effectively (per odd/even sub-dataset) blocks have temporal extension 2*indicator_resolution_granularity, which reinforces this issue."
|
|
423
|
+
]
|
|
424
|
+
}
|
|
425
|
+
],
|
|
426
|
+
"metadata": {
|
|
427
|
+
"kernelspec": {
|
|
428
|
+
"display_name": ".venv",
|
|
429
|
+
"language": "python",
|
|
430
|
+
"name": "python3"
|
|
431
|
+
},
|
|
432
|
+
"language_info": {
|
|
433
|
+
"codemirror_mode": {
|
|
434
|
+
"name": "ipython",
|
|
435
|
+
"version": 3
|
|
436
|
+
},
|
|
437
|
+
"file_extension": ".py",
|
|
438
|
+
"mimetype": "text/x-python",
|
|
439
|
+
"name": "python",
|
|
440
|
+
"nbconvert_exporter": "python",
|
|
441
|
+
"pygments_lexer": "ipython3",
|
|
442
|
+
"version": "3.13.5"
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
"nbformat": 4,
|
|
446
|
+
"nbformat_minor": 5
|
|
447
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: GLDF
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Summary: GLDF: Non-Homogeneous Causal Graph Discovery
|
|
5
|
+
Project-URL: Homepage, https://martin-rabel.github.io/Causal_GLDF/
|
|
6
|
+
Project-URL: Documentation, https://martin-rabel.github.io/Causal_GLDF/
|
|
7
|
+
Project-URL: Repository, https://github.com/martin-rabel/Causal_GLDF/
|
|
8
|
+
Project-URL: Issues, https://github.com/martin-rabel/Causal_GLDF/issues
|
|
9
|
+
Author: Martin Rabel
|
|
10
|
+
Author-email: Martin Rabel <martin.rabel@uni-potsdam.de>
|
|
11
|
+
License-Expression: GPL-3.0-or-later
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: CPD,causal discovery,causality,clustering,graph discovery,non-stationary
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: numpy
|
|
18
|
+
Requires-Dist: scipy
|
|
19
|
+
Provides-Extra: full
|
|
20
|
+
Requires-Dist: causal-learn>=0.1.4.3; extra == 'full'
|
|
21
|
+
Requires-Dist: matplotlib>=3.7.0; extra == 'full'
|
|
22
|
+
Requires-Dist: networkx>=3.0; extra == 'full'
|
|
23
|
+
Requires-Dist: tigramite>=5.2.0.0; extra == 'full'
|
|
24
|
+
Provides-Extra: minimal
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# GLDF: Non-Homogeneous Causal Graph Discovery
|
|
28
|
+
|
|
29
|
+
This is the reference implementation of the framework described by [1]
|
|
30
|
+
for causal graph discovery on non-homogeneous data.
|
|
31
|
+
|
|
32
|
+
## Introduction
|
|
33
|
+
|
|
34
|
+
Algorithms for causal graph discovery (CD) on IID or stationary data are readily
|
|
35
|
+
available. However, real-world data rarely is IID or stationary.
|
|
36
|
+
This framework particularly aims to find ways in which a causal graph
|
|
37
|
+
*changes* over time, space or patterns in other extraneous information attached
|
|
38
|
+
to data. It does so by focusing on an approach that is local in the graph (gL)
|
|
39
|
+
and testing qualitative questions directly (D), hence the name gLD-framework
|
|
40
|
+
(GLDF).
|
|
41
|
+
|
|
42
|
+
Besides statistical efficiency, another major strength of this approach
|
|
43
|
+
is its modularity. This allows the present framework to directly
|
|
44
|
+
build on existing CD-algorithm implementations.
|
|
45
|
+
At the same time, almost arbitrary "patterns" generalizing persistence
|
|
46
|
+
in time or space can be leveraged to account for regime-structure.
|
|
47
|
+
The framework extensively builds on modified conditional independence tests
|
|
48
|
+
(CITs), where individual (or all) modifications can easily be customized
|
|
49
|
+
if the need arises.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## Getting Started
|
|
53
|
+
|
|
54
|
+
The best place to get started is probably the extensive [documentation](https://martin-rabel.github.io/Causal_GLDF/).
|
|
55
|
+
Further, there are tutorials in the form of jupyter-notebooks in the sub-directory "tutorials".
|
|
56
|
+
This package is designed to be easily extensible and to integrate well out-of-the box
|
|
57
|
+
with [tigramite](https://github.com/jakobrunge/tigramite).
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
## Requirements
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
Minimal:
|
|
64
|
+
|
|
65
|
+
* python (version 3.10 or new recommended, tested on 3.13.5) and its standard-libary
|
|
66
|
+
* numpy (version 2.0.0 or newer, tested on 2.3.2)
|
|
67
|
+
* scipy (version 1.10.0 or newer, tested on 1.16.1)
|
|
68
|
+
|
|
69
|
+
Recommended (additionally):
|
|
70
|
+
|
|
71
|
+
* matplotlib (version 3.7.0 or newer, tested on 3.10.5)
|
|
72
|
+
* tigramite (version 5.2.0.0 or newer, tested on 5.2.8.2)
|
|
73
|
+
* networkx (version 3.0 or newer, tested on 3.5), used via tigramite's graph plotting
|
|
74
|
+
* causal-learn (tested on 0.1.4.3)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
## References
|
|
79
|
+
|
|
80
|
+
[1] M. Rabel, J. Runge.
|
|
81
|
+
Context-Specific Causal Graph Discovery with Unobserved Contexts: Non-Stationarity, Regimes and Spatio-Temporal Patterns.
|
|
82
|
+
*archive preprint* [arXiv:2511.21537](https://arxiv.org/abs/2511.21537), 2025.
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
## User Agreement
|
|
86
|
+
|
|
87
|
+
By downloading this software you agree with the following points:
|
|
88
|
+
This software is provided without any warranty or conditions of any kind. We assume no responsibility for errors or omissions in the results and interpretations following from application of this software.
|
|
89
|
+
|
|
90
|
+
You commit to cite above papers in your reports or publications.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
Copyright (C) 2025-2026 Martin Rabel
|
|
96
|
+
|
|
97
|
+
GNU General Public License v3.0
|
|
98
|
+
|
|
99
|
+
See the file LICENSE for full text.
|
|
100
|
+
|
|
101
|
+
This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|