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.
@@ -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": "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",
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.