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
GLDF/frontend.py
ADDED
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
from . import hyperparams
|
|
2
|
+
from . import data_management
|
|
3
|
+
from .cit import ParCorr
|
|
4
|
+
from .data_processing import Homogeneity_Binomial, WeakRegime_AcceptanceInterval, IndicatorImplication_AcceptanceInterval, mCIT, \
|
|
5
|
+
ITestCI, IProvideAnalyticQuantilesForCIT, IProvideVarianceForCIT, ITestHomogeneity, ITestWeakRegime, ITestIndicatorImplications, ITestMarkedCI
|
|
6
|
+
from .independence_atoms import IndependenceAtoms_Backend, IndependenceAtoms_TimeSeries, IProvideIndependenceAtoms
|
|
7
|
+
from .hccd import Controller, ControllerTimeseriesMCI, ControllerTimeseriesLPCMCI, IPresentResult, IResolveRegimeStructure, graph_t, abstract_cd_t, IConstructStateSpace
|
|
8
|
+
from . import state_space_construction
|
|
9
|
+
import numpy as np
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
class cache_layer:
|
|
13
|
+
"""
|
|
14
|
+
Flexible cache-layer.
|
|
15
|
+
"""
|
|
16
|
+
def __init__(self, where: object, on: str, only_latest: bool):
|
|
17
|
+
"""Inject a cache-layer caching :py:meth:`!where.on`.
|
|
18
|
+
|
|
19
|
+
:param where: stage where to inject the layer
|
|
20
|
+
:type where: object
|
|
21
|
+
:param on: name of the method to cache
|
|
22
|
+
:type on: str
|
|
23
|
+
:param only_latest: cache only the latest request (if true),
|
|
24
|
+
or keep a dictionary of all previously executed requests (if false).
|
|
25
|
+
:type only_latest: bool
|
|
26
|
+
"""
|
|
27
|
+
assert hasattr(where, on)
|
|
28
|
+
|
|
29
|
+
# if layer below is also a cache, should insert fname later
|
|
30
|
+
self.extract_cache_id = where._extract_cache_id
|
|
31
|
+
self.fname=on
|
|
32
|
+
|
|
33
|
+
self.call_lower_layer = getattr(where, on)
|
|
34
|
+
self.cache_object = (None, None) if only_latest else {}
|
|
35
|
+
|
|
36
|
+
if only_latest:
|
|
37
|
+
setattr(where, on, self._call_cached_latest)
|
|
38
|
+
else:
|
|
39
|
+
setattr(where, on, self._call_cached_full)
|
|
40
|
+
|
|
41
|
+
def _call_cached_latest(self, *args1, **args2):
|
|
42
|
+
request_id = self.extract_cache_id(self.fname, *args1, **args2)
|
|
43
|
+
latest_id, latest_result = self.cache_object
|
|
44
|
+
if (latest_id is not None) and (latest_id == request_id):
|
|
45
|
+
return latest_result
|
|
46
|
+
result = self.call_lower_layer(*args1, **args2)
|
|
47
|
+
if request_id is not None:
|
|
48
|
+
self.cache_object = (request_id, result)
|
|
49
|
+
return result
|
|
50
|
+
|
|
51
|
+
def _call_cached_full(self, *args1, **args2):
|
|
52
|
+
request_id = self.extract_cache_id(self.fname, *args1, **args2)
|
|
53
|
+
cached_result = self.cache_object.get(request_id)
|
|
54
|
+
if cached_result is not None:
|
|
55
|
+
return cached_result
|
|
56
|
+
else:
|
|
57
|
+
result = self.call_lower_layer(*args1, **args2)
|
|
58
|
+
if request_id is not None:
|
|
59
|
+
self.cache_object[request_id] = result
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
color_scheme = [ "blue", "orange", "cyan", "red", "green", "darkgray" ] #: These colors are used (in order, repeating) in labeled union graphs as "labels".
|
|
64
|
+
|
|
65
|
+
class LinkInfo:
|
|
66
|
+
"""
|
|
67
|
+
Information attached to links for plotting graphs.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, r_info) -> None:
|
|
71
|
+
if len(r_info) == 0 or isinstance( next(iter(r_info.keys()))[0], tuple ):
|
|
72
|
+
self.regimy_links = {self._consistent_indexing(key[0][0], key[1][0]): value for key, value in r_info.items()}
|
|
73
|
+
else:
|
|
74
|
+
self.regimy_links = {self._consistent_indexing(key[0], key[1]): value for key, value in r_info.items()}
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def _consistent_indexing(u,v):
|
|
78
|
+
return (u,0), (v,0)
|
|
79
|
+
|
|
80
|
+
def vanishing_link(self, u,v) -> bool:
|
|
81
|
+
"""Check for the vanishing (regime-dependence) of a union-graph link.
|
|
82
|
+
|
|
83
|
+
:return: regime-dependence
|
|
84
|
+
:rtype: bool
|
|
85
|
+
"""
|
|
86
|
+
u, v = self._consistent_indexing(u, v)
|
|
87
|
+
return (u,v) in self.regimy_links or (v,u) in self.regimy_links
|
|
88
|
+
def get_color(self, u,v):
|
|
89
|
+
"""
|
|
90
|
+
Get color-label for a link in the "labeled" union-graph.
|
|
91
|
+
"""
|
|
92
|
+
u, v = self._consistent_indexing(u, v)
|
|
93
|
+
c = self.regimy_links[(u,v)] if (u,v) in self.regimy_links else self.regimy_links[(v,u)]
|
|
94
|
+
return c
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ResolvableModelIndicator:
|
|
100
|
+
"""
|
|
101
|
+
Structured result presenting results about a particular model-indicator.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def __init__(self, indicator_resolution: IResolveRegimeStructure, model_indicator, assigned_color):
|
|
105
|
+
"""Construct a structured result.
|
|
106
|
+
|
|
107
|
+
:param indicator_resolution: Indicator resolution strategy.
|
|
108
|
+
:type indicator_resolution: IResolveRegimeStructure
|
|
109
|
+
:param model_indicator: underlying model-indicator
|
|
110
|
+
:type model_indicator: state-space construction specific
|
|
111
|
+
:param assigned_color: color "label"
|
|
112
|
+
:type assigned_color: str
|
|
113
|
+
"""
|
|
114
|
+
self._model_indicator = model_indicator
|
|
115
|
+
self._indicator_resolution = indicator_resolution
|
|
116
|
+
self.assigned_color = assigned_color
|
|
117
|
+
|
|
118
|
+
def undirected_link(self) -> tuple:
|
|
119
|
+
"""The underlying undirected link.
|
|
120
|
+
|
|
121
|
+
:return: the changing link in the model
|
|
122
|
+
:rtype: tuple[data_management.var_index,data_management.var_index]
|
|
123
|
+
"""
|
|
124
|
+
return self._model_indicator.undirected_link
|
|
125
|
+
|
|
126
|
+
def resolve(self) -> np.ndarray|dict[str, np.ndarray]:
|
|
127
|
+
"""Approximately resolve the indicator relative to index-space.
|
|
128
|
+
|
|
129
|
+
:return: resolved indicator (possibly as dictionary "label": values)
|
|
130
|
+
:rtype: np.ndarray|dict[str, np.ndarray]
|
|
131
|
+
"""
|
|
132
|
+
return self._indicator_resolution.resolve_model_indicator(self._model_indicator)
|
|
133
|
+
|
|
134
|
+
def _plot_resolution_2D(self, resolved_data: np.ndarray) -> None:
|
|
135
|
+
from matplotlib.colors import LinearSegmentedColormap
|
|
136
|
+
color_map = LinearSegmentedColormap.from_list("temp_indicator_colormap", colors=[self.assigned_color, "white"], N=256, gamma=1.0)
|
|
137
|
+
import matplotlib.pyplot as plt
|
|
138
|
+
plt.imshow(resolved_data, cmap=color_map)
|
|
139
|
+
|
|
140
|
+
def _plot_resolution_1D(self, resolved_data: np.ndarray, label=None, linestyle="solid") -> None:
|
|
141
|
+
import matplotlib.pyplot as plt
|
|
142
|
+
plt.plot(resolved_data, color=self.assigned_color, label=label, linestyle=linestyle)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _plot_resolution_auto(self, resolved_data, label=None, linestyle="solid")->None:
|
|
146
|
+
if len(resolved_data.shape) == 1:
|
|
147
|
+
self._plot_resolution_1D(resolved_data, label=label, linestyle=linestyle)
|
|
148
|
+
elif len(resolved_data.shape) == 2:
|
|
149
|
+
self._plot_resolution_2D(resolved_data)
|
|
150
|
+
else:
|
|
151
|
+
raise NotImplementedError("Currently auto-plotting is only implemented for 1D and 2D data. Use '.resolve()' and plot the data externally.")
|
|
152
|
+
|
|
153
|
+
def plot_resolution(self) -> None:
|
|
154
|
+
"""Generate and plot an approximate resolution of the indicator in index-space.
|
|
155
|
+
|
|
156
|
+
:raises NotImplementedError: Currently only supports 1D and 2D data.
|
|
157
|
+
"""
|
|
158
|
+
resolved_data = self.resolve()
|
|
159
|
+
if isinstance(resolved_data, dict):
|
|
160
|
+
for idx, label_data in enumerate(resolved_data.items()):
|
|
161
|
+
label, data = label_data
|
|
162
|
+
self._plot_resolution_auto(data, label=label, linestyle=["solid", "dashed", "dotted", "dashdot"][idx])
|
|
163
|
+
else:
|
|
164
|
+
self._plot_resolution_auto(resolved_data)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class Result:
|
|
170
|
+
"""
|
|
171
|
+
Structured result presenting output obtained from HCCD.
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
var_names : list[str] #: Write to this field before plotting to provide variable-names.
|
|
175
|
+
|
|
176
|
+
def __init__(self, hccd_result: IPresentResult, indicator_resolution: IResolveRegimeStructure|None = None):
|
|
177
|
+
"""Construct from backend-result.
|
|
178
|
+
|
|
179
|
+
:param hccd_result: backend hccd result
|
|
180
|
+
:type hccd_result: IPresentResult
|
|
181
|
+
:param indicator_resolution: indicator resolution strategy, defaults to None
|
|
182
|
+
:type indicator_resolution: IResolveRegimeStructure | None, optional
|
|
183
|
+
"""
|
|
184
|
+
self.hccd_result = hccd_result
|
|
185
|
+
self.indicator_resolution = indicator_resolution
|
|
186
|
+
self.var_names = None
|
|
187
|
+
|
|
188
|
+
def union_graph(self) -> graph_t:
|
|
189
|
+
"""Get the estimated union-graph.
|
|
190
|
+
|
|
191
|
+
:return: union-graph (tigramite format)
|
|
192
|
+
:rtype: graph_t
|
|
193
|
+
"""
|
|
194
|
+
return self.hccd_result.union_graph()
|
|
195
|
+
|
|
196
|
+
def state_graphs(self) -> list[graph_t]:
|
|
197
|
+
"""Get state-specific graphs.
|
|
198
|
+
|
|
199
|
+
:return: list of state-specific graphs (tigramite format)
|
|
200
|
+
:rtype: list[graph_t]
|
|
201
|
+
"""
|
|
202
|
+
return self.hccd_result.state_graphs()
|
|
203
|
+
|
|
204
|
+
def model_indicators(self) -> list[ResolvableModelIndicator]:
|
|
205
|
+
"""Get structured result representing discovered model-indicators (changing links).
|
|
206
|
+
|
|
207
|
+
:return: list of model indicators
|
|
208
|
+
:rtype: list[ResolvableModelIndicator]
|
|
209
|
+
"""
|
|
210
|
+
return [ResolvableModelIndicator(self.indicator_resolution, mi, color_scheme[idx%len(color_scheme)]) for idx, mi in enumerate(self.hccd_result.model_indicators())]
|
|
211
|
+
|
|
212
|
+
def plot_labeled_union_graph(self, **args):
|
|
213
|
+
"""Plot the (color-)labeled union-graph.
|
|
214
|
+
|
|
215
|
+
Keyword arguments are forwarded to (a modified version of) :py:meth:`!tigramite.plotting.plot_graph`.
|
|
216
|
+
|
|
217
|
+
.. seealso::
|
|
218
|
+
|
|
219
|
+
Uses colors from :py:data:`color_scheme`.
|
|
220
|
+
"""
|
|
221
|
+
link_info = LinkInfo( {mi.undirected_link(): mi.assigned_color for mi in self.model_indicators()} )
|
|
222
|
+
from .bridges.tigramite_plotting_modified import plot_graph
|
|
223
|
+
if self.var_names is not None and "var_names" not in args:
|
|
224
|
+
args["var_names"] = self.var_names
|
|
225
|
+
plot_graph(graph=self.union_graph(), figsize=(4,4), special_links=link_info, **args)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
# def unique_get(config_cls):
|
|
230
|
+
# """Type decorator, use as @unique_get class ...
|
|
231
|
+
|
|
232
|
+
# Make methods whose names start with "get_" instance-unique on this type, i.e.
|
|
233
|
+
# modify them such that they are executed only on the first invocation, and
|
|
234
|
+
# automatically cached for later requests.
|
|
235
|
+
# """
|
|
236
|
+
# # pythons for-loop-scoping bug still f***s lambdas as of v3.13, while partials are still
|
|
237
|
+
# # exposed inconsistently for use as methods, so we need some 'creative' approach ...
|
|
238
|
+
# def bind_captures(name, get_value):
|
|
239
|
+
# def unique_entry(self):
|
|
240
|
+
# if hasattr(self, f"_unique_{name}"):
|
|
241
|
+
# return getattr(self, f"_unique_{name}")
|
|
242
|
+
# else:
|
|
243
|
+
# value = get_value(self)
|
|
244
|
+
# setattr(self, f"_unique_{name}", value)
|
|
245
|
+
# return value
|
|
246
|
+
# return unique_entry
|
|
247
|
+
# for elem in [elem for elem in dir(config_cls) if elem.startswith("get_")]:
|
|
248
|
+
# setattr(config_cls, elem, bind_captures(name=elem, get_value=getattr(config_cls, elem)))
|
|
249
|
+
# return config_cls
|
|
250
|
+
class Config:
|
|
251
|
+
"""
|
|
252
|
+
Helper for setting up configuration-objects.
|
|
253
|
+
Calling :py:meth:`finalize` will return a copy which is modified such as to execute each method prefixed with 'get\\_'
|
|
254
|
+
once, caching the result.
|
|
255
|
+
"""
|
|
256
|
+
# pythons for-loop-scoping bug still f***s lambdas as of v3.13, while partials are still
|
|
257
|
+
# exposed inconsistently for use as methods, so we need some 'creative' approach ...
|
|
258
|
+
def _make_getters_unqiue(self):
|
|
259
|
+
def bind_captures(name, get_value):
|
|
260
|
+
def unique_entry():
|
|
261
|
+
if hasattr(self, f"_unique_{name}"):
|
|
262
|
+
return getattr(self, f"_unique_{name}")
|
|
263
|
+
else:
|
|
264
|
+
value = get_value()
|
|
265
|
+
setattr(self, f"_unique_{name}", value)
|
|
266
|
+
return value
|
|
267
|
+
return unique_entry
|
|
268
|
+
for elem in [elem for elem in dir(self) if elem.startswith("get_")]:
|
|
269
|
+
setattr(self, elem, bind_captures(name=elem, get_value=getattr(self, elem)))
|
|
270
|
+
|
|
271
|
+
def finalize(self):
|
|
272
|
+
"""
|
|
273
|
+
Modify methods prefixed by 'get\\_' to be executed at most once (on first invokation) and cached for further calls.
|
|
274
|
+
"""
|
|
275
|
+
from copy import copy
|
|
276
|
+
cpy = copy(self)
|
|
277
|
+
cpy._make_getters_unqiue()
|
|
278
|
+
return cpy
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
@dataclass
|
|
283
|
+
class ConfigureBackend(Config):
|
|
284
|
+
"""
|
|
285
|
+
Modular configuration of extended independence atom backend.
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
data_manager : data_management.IManageData
|
|
289
|
+
|
|
290
|
+
alpha : float = 0.01
|
|
291
|
+
alpha_homogeneity : float = 0.01
|
|
292
|
+
regimes_are_large : bool = True
|
|
293
|
+
|
|
294
|
+
min_regime_fraction : float = 0.15
|
|
295
|
+
|
|
296
|
+
enable_weak_test : bool = True
|
|
297
|
+
enable_implication_test : bool = True
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def get_cit(self) -> ITestCI:
|
|
301
|
+
cit = ParCorr(self.alpha)
|
|
302
|
+
# Prepare for robust ci by caching latest result (if robust testing is with same block-size as homogeneity)
|
|
303
|
+
self.cit_cache_layer = cache_layer(cit, "run_many", only_latest=True)
|
|
304
|
+
return cit
|
|
305
|
+
|
|
306
|
+
def cit_analytic_quantile_estimate(self) -> IProvideAnalyticQuantilesForCIT:
|
|
307
|
+
return self.get_cit()
|
|
308
|
+
def cit_variance_estimate(self) -> IProvideVarianceForCIT:
|
|
309
|
+
return self.get_cit()
|
|
310
|
+
|
|
311
|
+
def get_homogeneity_test(self) -> ITestHomogeneity:
|
|
312
|
+
return Homogeneity_Binomial(hyperparams=hyperparams.Hyperparams_HomogeneityBinomial_ParCorr(alpha_error_control=self.alpha_homogeneity, regimes_are_large=self.regimes_are_large),
|
|
313
|
+
cit=self.get_cit(), cit_analytic_quantile_estimate=self.cit_analytic_quantile_estimate())
|
|
314
|
+
|
|
315
|
+
def get_weak_test(self) -> ITestWeakRegime:
|
|
316
|
+
return WeakRegime_AcceptanceInterval(hyperparams=hyperparams.Hyperparams_WeakInterval_ParCorr(regimes_are_large=self.regimes_are_large),
|
|
317
|
+
cit=self.get_cit(), cit_variance_estimate=self.cit_variance_estimate(), min_regime_fraction=self.min_regime_fraction)
|
|
318
|
+
|
|
319
|
+
def get_mcit(self) -> ITestMarkedCI:
|
|
320
|
+
return mCIT(cit=self.get_cit(), homogeneity_test=self.get_homogeneity_test(), weak_test=self.get_weak_test() if self.enable_weak_test else None)
|
|
321
|
+
|
|
322
|
+
def get_implication_test(self) -> ITestIndicatorImplications:
|
|
323
|
+
return IndicatorImplication_AcceptanceInterval(cit=self.get_cit(), cit_variance_estimate=self.cit_variance_estimate(),
|
|
324
|
+
hyperparams=hyperparams.Hyperparams_WeakInterval_ParCorr(regimes_are_large=self.regimes_are_large))
|
|
325
|
+
|
|
326
|
+
def get_backend(self) -> IProvideIndependenceAtoms:
|
|
327
|
+
m_cit = self.get_mcit()
|
|
328
|
+
test_indicator_implication = self.get_implication_test() if self.enable_weak_test else None
|
|
329
|
+
independence_atoms = IndependenceAtoms_Backend(data_manager=self.data_manager, m_cit=m_cit, implication_test=test_indicator_implication)
|
|
330
|
+
cache_layer(independence_atoms, "marked_independence", only_latest=False)
|
|
331
|
+
cache_layer(independence_atoms, "regime_implication", only_latest=False)
|
|
332
|
+
return independence_atoms
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
#@unique_get applied late in run to ensure user-overwrites are treated consistently
|
|
336
|
+
@dataclass
|
|
337
|
+
class ConfigureHCCD(Config):
|
|
338
|
+
"""
|
|
339
|
+
Modular configuration of backend for HCCD.
|
|
340
|
+
"""
|
|
341
|
+
is_timeseries : bool #: is time-series data
|
|
342
|
+
alpha : float = 0.01 #: confidence-parameter :math:`\alpha` for hypothesis-tests
|
|
343
|
+
min_regime_fraction : float = 0.15 #: minimum regime-fraction to consider
|
|
344
|
+
indicator_resolution_granularity : int =100 #: granularity used for post-process approximate indicator resultion
|
|
345
|
+
regimes_are_large : bool = True #: use hyper-parameter sets optimized for large regimes (True is recommended, especially for time-series data)
|
|
346
|
+
tau_max : int = 1 #: maximum lag to use for time-series algorithms (ignored for IID algorithms)
|
|
347
|
+
|
|
348
|
+
# ts-specific
|
|
349
|
+
alpha_pc1 : float = 0.1 #: confidence-parameter :math:`\alpha` for independence-tests in PC1-phase of PCMCI-family algorithms.
|
|
350
|
+
|
|
351
|
+
_data : np.ndarray = None
|
|
352
|
+
|
|
353
|
+
def get_data_manager(self) -> data_management.IManageData:
|
|
354
|
+
if self.is_timeseries:
|
|
355
|
+
return data_management.DataManager_NumpyArray_Timeseries(self._data)
|
|
356
|
+
else:
|
|
357
|
+
dim = len(self._data.shape) - 1
|
|
358
|
+
if dim == 1:
|
|
359
|
+
return data_management.DataManager_NumpyArray_IID(self._data)
|
|
360
|
+
elif dim == 2:
|
|
361
|
+
return data_management.DataManager_NumpyArray_IID(self._data, pattern=data_management.CIT_DataPatterned_PesistentInSpace)
|
|
362
|
+
|
|
363
|
+
def get_backend_config(self) -> ConfigureBackend:
|
|
364
|
+
return ConfigureBackend(
|
|
365
|
+
data_manager=self.get_data_manager(),
|
|
366
|
+
alpha=self.alpha,
|
|
367
|
+
alpha_homogeneity=self.alpha,
|
|
368
|
+
regimes_are_large=self.regimes_are_large,
|
|
369
|
+
min_regime_fraction=self.min_regime_fraction,
|
|
370
|
+
enable_weak_test=True,
|
|
371
|
+
enable_implication_test=True
|
|
372
|
+
).finalize()
|
|
373
|
+
def get_backend(self) -> IProvideIndependenceAtoms:
|
|
374
|
+
config_backend = self.get_backend_config()
|
|
375
|
+
return config_backend.get_backend()
|
|
376
|
+
|
|
377
|
+
# time-series only
|
|
378
|
+
def get_mci_backend(self) -> IProvideIndependenceAtoms:
|
|
379
|
+
return self.get_backend()
|
|
380
|
+
|
|
381
|
+
def alpha_homogeneity_pc1(self) -> float:
|
|
382
|
+
return self.alpha # "keep" configuration
|
|
383
|
+
def get_pc1_backend(self) -> IProvideIndependenceAtoms:
|
|
384
|
+
config_backend = ConfigureBackend(
|
|
385
|
+
data_manager=self.get_data_manager(),
|
|
386
|
+
alpha=self.alpha_pc1,
|
|
387
|
+
alpha_homogeneity=self.alpha_homogeneity_pc1(),
|
|
388
|
+
regimes_are_large=self.regimes_are_large,
|
|
389
|
+
min_regime_fraction=self.min_regime_fraction,
|
|
390
|
+
enable_weak_test=False,
|
|
391
|
+
enable_implication_test=False
|
|
392
|
+
).finalize()
|
|
393
|
+
return config_backend.get_backend()
|
|
394
|
+
|
|
395
|
+
def get_transitionable_backend(self) -> IndependenceAtoms_TimeSeries:
|
|
396
|
+
return IndependenceAtoms_TimeSeries(independence_atoms_pc1=self.get_pc1_backend(), independence_atoms_mci=self.get_mci_backend())
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def get_universal_cd(self) -> abstract_cd_t:
|
|
400
|
+
if self.is_timeseries:
|
|
401
|
+
from .bridges import tigramite
|
|
402
|
+
return tigramite.alg_pcmciplus(data_format=self.get_data_manager(), mci_transition_callback=self.get_transitionable_backend(), pcmci_obj_run_args=dict(tau_max=self.tau_max))
|
|
403
|
+
else:
|
|
404
|
+
from .bridges import causal_learn
|
|
405
|
+
return causal_learn.alg_fci(data_format=self.get_data_manager())
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def get_state_space_construction(self) -> IConstructStateSpace:
|
|
409
|
+
return state_space_construction.NoUnionCycles()
|
|
410
|
+
|
|
411
|
+
def get_controller(self) -> Controller:
|
|
412
|
+
if self.is_timeseries:
|
|
413
|
+
return ControllerTimeseriesMCI(universal_cd=self.get_universal_cd(), testing_backend=self.get_transitionable_backend(), state_space_construction=self.get_state_space_construction())
|
|
414
|
+
else:
|
|
415
|
+
return Controller(universal_cd=self.get_universal_cd(), testing_backend=self.get_backend(), state_space_construction=self.get_state_space_construction())
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def get_cit(self) -> ITestCI:
|
|
419
|
+
return self.get_backend_config().get_cit()
|
|
420
|
+
|
|
421
|
+
def get_indicator_resultion(self) -> IResolveRegimeStructure:
|
|
422
|
+
def resolve_by_dependence_score(data_blocks: data_management.BlockView) -> np.ndarray:
|
|
423
|
+
return self.get_cit().run_many(data_blocks).block_scores
|
|
424
|
+
return state_space_construction.ResolveByRepresentor(indicator_resolution_score=resolve_by_dependence_score, data_mgr=self.get_data_manager(), block_size=self.indicator_resolution_granularity)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def _run(self, data: np.ndarray) -> Result:
|
|
428
|
+
self._data = data
|
|
429
|
+
internal_result = self.get_controller().run_hccd()
|
|
430
|
+
return Result(hccd_result=internal_result, indicator_resolution=self.get_indicator_resultion())
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def run(self, data: np.ndarray) -> Result:
|
|
434
|
+
return self.finalize()._run(data)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
class ConfigureHCCD_LPCMCI(ConfigureHCCD):
|
|
438
|
+
def __init__(self, regimes_are_large: bool=True, alpha: float=0.01, alpha_pc1: float=0.1, tau_max: int=1):
|
|
439
|
+
ts_config_no_latents = configure_hccd_temporal_regimes(regimes_are_large=regimes_are_large, alpha=alpha, alpha_pc1=alpha_pc1, allow_latent_confounding=False, tau_max=tau_max)
|
|
440
|
+
from dataclasses import asdict
|
|
441
|
+
super().__init__(**asdict(ts_config_no_latents))
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def get_universal_cd(self) -> abstract_cd_t:
|
|
445
|
+
from .bridges import tigramite
|
|
446
|
+
return tigramite.alg_lpcmci(data_format=self.get_data_manager(),lpcmci_obj_run_args=dict(tau_max=self.tau_max))
|
|
447
|
+
|
|
448
|
+
def get_controller(self) -> Controller:
|
|
449
|
+
return ControllerTimeseriesLPCMCI(universal_cd=self.get_universal_cd(), testing_backend=self.get_transitionable_backend(), state_space_construction=self.get_state_space_construction())
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def configure_hccd_temporal_regimes(regimes_are_large: bool=True, alpha: float=0.01, alpha_pc1: float=0.1, allow_latent_confounding: bool=False, tau_max: int=1) -> ConfigureHCCD:
|
|
454
|
+
"""Get standard-configuration of HCCD for temporal regimes. Uses PCMCI+ [R20]_ as default CD-algorithm.
|
|
455
|
+
|
|
456
|
+
:param regimes_are_large: use hyper-parameter sets optimized for large regimes (True is recommended, especially for time-series data), defaults to True
|
|
457
|
+
:type regimes_are_large: bool, optional
|
|
458
|
+
:param alpha: confidence-parameter :math:`\\alpha` for hypothesis-tests, defaults to 0.01
|
|
459
|
+
:type alpha: float, optional
|
|
460
|
+
:param alpha_pc1: confidence-parameter :math:`\\alpha` for independence-tests in the PC1-phase, defaults to 0.1
|
|
461
|
+
:type alpha_pc1: float, optional
|
|
462
|
+
:param allow_latent_confounding: consider the possibility of latent confounding by using LPCMCI [GR20]_ (this implementation is currently experimental
|
|
463
|
+
and regime-discovery may have low recall, see [RR25]_\\ ), defaults to False
|
|
464
|
+
:type allow_latent_confounding: bool, optional
|
|
465
|
+
:param tau_max: maximum lag in considered time-window, defaults to 1
|
|
466
|
+
:type tau_max: int, optional
|
|
467
|
+
:return: HCCD configuration used by :py:func:`run_hccd_temporal_regimes`.
|
|
468
|
+
:rtype: ConfigureHCCD
|
|
469
|
+
"""
|
|
470
|
+
if allow_latent_confounding:
|
|
471
|
+
return ConfigureHCCD_LPCMCI(
|
|
472
|
+
alpha=alpha,
|
|
473
|
+
alpha_pc1=alpha_pc1,
|
|
474
|
+
regimes_are_large=regimes_are_large,
|
|
475
|
+
tau_max=tau_max
|
|
476
|
+
)
|
|
477
|
+
else:
|
|
478
|
+
return ConfigureHCCD(
|
|
479
|
+
is_timeseries=True,
|
|
480
|
+
alpha=alpha,
|
|
481
|
+
alpha_pc1=alpha_pc1,
|
|
482
|
+
regimes_are_large=regimes_are_large,
|
|
483
|
+
tau_max=tau_max
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
def run_hccd_temporal_regimes(data: np.ndarray, regimes_are_large: bool=True, alpha: float=0.01, alpha_pc1: float=0.1, allow_latent_confounding: bool=False, tau_max: int=1) -> Result:
|
|
487
|
+
"""Run preconfigured HCCD for temporal regimes. Uses PCMCI+ [R20]_ as default CD-algorithm.
|
|
488
|
+
|
|
489
|
+
:param data: observed data
|
|
490
|
+
:type data: np.ndarray
|
|
491
|
+
:param regimes_are_large: use hyper-parameter sets optimized for large regimes (True is recommended, especially for time-series data), defaults to True
|
|
492
|
+
:type regimes_are_large: bool, optional
|
|
493
|
+
:param alpha: confidence-parameter :math:`\\alpha` for hypothesis-tests, defaults to 0.01
|
|
494
|
+
:type alpha: float, optional
|
|
495
|
+
:param alpha_pc1: confidence-parameter :math:`\\alpha` for independence-tests in the PC1-phase, defaults to 0.1
|
|
496
|
+
:type alpha_pc1: float, optional
|
|
497
|
+
:param allow_latent_confounding: consider the possibility of latent confounding by using LPCMCI [GR20]_ (this implementation is currently experimental
|
|
498
|
+
and regime-discovery may have low recall, see [RR25]_\\ ), defaults to False
|
|
499
|
+
:type allow_latent_confounding: bool, optional
|
|
500
|
+
:param tau_max: maximum lag in considered time-window, defaults to 1
|
|
501
|
+
:type tau_max: int, optional
|
|
502
|
+
:return: Structured HCCD result.
|
|
503
|
+
:rtype: Result
|
|
504
|
+
"""
|
|
505
|
+
config = configure_hccd_temporal_regimes(regimes_are_large=regimes_are_large, alpha=alpha, alpha_pc1=alpha_pc1, allow_latent_confounding=allow_latent_confounding, tau_max=tau_max)
|
|
506
|
+
return config.run(data)
|
|
507
|
+
|
|
508
|
+
def configure_hccd_spatial_regimes(regimes_are_large: bool=True, alpha: float=0.01) -> ConfigureHCCD:
|
|
509
|
+
"""Get standard-configuration of HCCD for spatial (2 dimensional, non-time-series) data. Uses FCI [SGS01]_ as default CD-algorithm.
|
|
510
|
+
|
|
511
|
+
:param regimes_are_large: use hyper-parameter sets optimized for large regimes (True is recommended, especially for time-series data), defaults to True
|
|
512
|
+
:type regimes_are_large: bool, optional
|
|
513
|
+
:param alpha: confidence-parameter :math:`\\alpha` for hypothesis-tests, defaults to 0.01
|
|
514
|
+
:type alpha: float, optional
|
|
515
|
+
:return: HCCD configuration used by :py:func:`run_hccd_spatial_regimes`.
|
|
516
|
+
:rtype: ConfigureHCCD
|
|
517
|
+
"""
|
|
518
|
+
return ConfigureHCCD(
|
|
519
|
+
is_timeseries=False,
|
|
520
|
+
alpha=alpha,
|
|
521
|
+
regimes_are_large=regimes_are_large
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
def run_hccd_spatial_regimes(data: np.ndarray, regimes_are_large: bool=True, alpha: float=0.01) -> Result:
|
|
525
|
+
"""Run preconfigured HCCD for spatial (2 dimensional, non-time-series) data. Uses FCI [SGS01]_ as default CD-algorithm.
|
|
526
|
+
|
|
527
|
+
:param data: observed data
|
|
528
|
+
:type data: np.ndarray
|
|
529
|
+
:param regimes_are_large: use hyper-parameter sets optimized for large regimes (True is recommended, especially for time-series data), defaults to True
|
|
530
|
+
:type regimes_are_large: bool, optional
|
|
531
|
+
:param alpha: confidence-parameter :math:`\\alpha` for hypothesis-tests, defaults to 0.01
|
|
532
|
+
:type alpha: float, optional
|
|
533
|
+
:return: Structured HCCD result.
|
|
534
|
+
:rtype: Result
|
|
535
|
+
"""
|
|
536
|
+
config = configure_hccd_spatial_regimes(regimes_are_large=regimes_are_large, alpha=alpha)
|
|
537
|
+
return config.run(data)
|