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/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)