pyAgrum-nightly 2.3.1.9.dev202512261765915415__cp310-abi3-macosx_10_15_x86_64.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.
Files changed (107) hide show
  1. pyagrum/__init__.py +165 -0
  2. pyagrum/_pyagrum.so +0 -0
  3. pyagrum/bnmixture/BNMInference.py +268 -0
  4. pyagrum/bnmixture/BNMLearning.py +376 -0
  5. pyagrum/bnmixture/BNMixture.py +464 -0
  6. pyagrum/bnmixture/__init__.py +60 -0
  7. pyagrum/bnmixture/notebook.py +1058 -0
  8. pyagrum/causal/_CausalFormula.py +280 -0
  9. pyagrum/causal/_CausalModel.py +436 -0
  10. pyagrum/causal/__init__.py +81 -0
  11. pyagrum/causal/_causalImpact.py +356 -0
  12. pyagrum/causal/_dSeparation.py +598 -0
  13. pyagrum/causal/_doAST.py +761 -0
  14. pyagrum/causal/_doCalculus.py +361 -0
  15. pyagrum/causal/_doorCriteria.py +374 -0
  16. pyagrum/causal/_exceptions.py +95 -0
  17. pyagrum/causal/_types.py +61 -0
  18. pyagrum/causal/causalEffectEstimation/_CausalEffectEstimation.py +1175 -0
  19. pyagrum/causal/causalEffectEstimation/_IVEstimators.py +718 -0
  20. pyagrum/causal/causalEffectEstimation/_RCTEstimators.py +132 -0
  21. pyagrum/causal/causalEffectEstimation/__init__.py +46 -0
  22. pyagrum/causal/causalEffectEstimation/_backdoorEstimators.py +774 -0
  23. pyagrum/causal/causalEffectEstimation/_causalBNEstimator.py +324 -0
  24. pyagrum/causal/causalEffectEstimation/_frontdoorEstimators.py +396 -0
  25. pyagrum/causal/causalEffectEstimation/_learners.py +118 -0
  26. pyagrum/causal/causalEffectEstimation/_utils.py +466 -0
  27. pyagrum/causal/notebook.py +172 -0
  28. pyagrum/clg/CLG.py +658 -0
  29. pyagrum/clg/GaussianVariable.py +111 -0
  30. pyagrum/clg/SEM.py +312 -0
  31. pyagrum/clg/__init__.py +63 -0
  32. pyagrum/clg/canonicalForm.py +408 -0
  33. pyagrum/clg/constants.py +54 -0
  34. pyagrum/clg/forwardSampling.py +202 -0
  35. pyagrum/clg/learning.py +776 -0
  36. pyagrum/clg/notebook.py +480 -0
  37. pyagrum/clg/variableElimination.py +271 -0
  38. pyagrum/common.py +60 -0
  39. pyagrum/config.py +319 -0
  40. pyagrum/ctbn/CIM.py +513 -0
  41. pyagrum/ctbn/CTBN.py +573 -0
  42. pyagrum/ctbn/CTBNGenerator.py +216 -0
  43. pyagrum/ctbn/CTBNInference.py +459 -0
  44. pyagrum/ctbn/CTBNLearner.py +161 -0
  45. pyagrum/ctbn/SamplesStats.py +671 -0
  46. pyagrum/ctbn/StatsIndepTest.py +355 -0
  47. pyagrum/ctbn/__init__.py +79 -0
  48. pyagrum/ctbn/constants.py +54 -0
  49. pyagrum/ctbn/notebook.py +264 -0
  50. pyagrum/defaults.ini +199 -0
  51. pyagrum/deprecated.py +95 -0
  52. pyagrum/explain/_ComputationCausal.py +75 -0
  53. pyagrum/explain/_ComputationConditional.py +48 -0
  54. pyagrum/explain/_ComputationMarginal.py +48 -0
  55. pyagrum/explain/_CustomShapleyCache.py +110 -0
  56. pyagrum/explain/_Explainer.py +176 -0
  57. pyagrum/explain/_Explanation.py +70 -0
  58. pyagrum/explain/_FIFOCache.py +54 -0
  59. pyagrum/explain/_ShallCausalValues.py +204 -0
  60. pyagrum/explain/_ShallConditionalValues.py +155 -0
  61. pyagrum/explain/_ShallMarginalValues.py +155 -0
  62. pyagrum/explain/_ShallValues.py +296 -0
  63. pyagrum/explain/_ShapCausalValues.py +208 -0
  64. pyagrum/explain/_ShapConditionalValues.py +126 -0
  65. pyagrum/explain/_ShapMarginalValues.py +191 -0
  66. pyagrum/explain/_ShapleyValues.py +298 -0
  67. pyagrum/explain/__init__.py +81 -0
  68. pyagrum/explain/_explGeneralizedMarkovBlanket.py +152 -0
  69. pyagrum/explain/_explIndependenceListForPairs.py +146 -0
  70. pyagrum/explain/_explInformationGraph.py +264 -0
  71. pyagrum/explain/notebook/__init__.py +54 -0
  72. pyagrum/explain/notebook/_bar.py +142 -0
  73. pyagrum/explain/notebook/_beeswarm.py +174 -0
  74. pyagrum/explain/notebook/_showShapValues.py +97 -0
  75. pyagrum/explain/notebook/_waterfall.py +220 -0
  76. pyagrum/explain/shapley.py +225 -0
  77. pyagrum/lib/__init__.py +46 -0
  78. pyagrum/lib/_colors.py +390 -0
  79. pyagrum/lib/bn2graph.py +299 -0
  80. pyagrum/lib/bn2roc.py +1026 -0
  81. pyagrum/lib/bn2scores.py +217 -0
  82. pyagrum/lib/bn_vs_bn.py +605 -0
  83. pyagrum/lib/cn2graph.py +305 -0
  84. pyagrum/lib/discreteTypeProcessor.py +1102 -0
  85. pyagrum/lib/discretizer.py +58 -0
  86. pyagrum/lib/dynamicBN.py +390 -0
  87. pyagrum/lib/explain.py +57 -0
  88. pyagrum/lib/export.py +84 -0
  89. pyagrum/lib/id2graph.py +258 -0
  90. pyagrum/lib/image.py +387 -0
  91. pyagrum/lib/ipython.py +307 -0
  92. pyagrum/lib/mrf2graph.py +471 -0
  93. pyagrum/lib/notebook.py +1821 -0
  94. pyagrum/lib/proba_histogram.py +552 -0
  95. pyagrum/lib/utils.py +138 -0
  96. pyagrum/pyagrum.py +31495 -0
  97. pyagrum/skbn/_MBCalcul.py +242 -0
  98. pyagrum/skbn/__init__.py +49 -0
  99. pyagrum/skbn/_learningMethods.py +282 -0
  100. pyagrum/skbn/_utils.py +297 -0
  101. pyagrum/skbn/bnclassifier.py +1014 -0
  102. pyagrum_nightly-2.3.1.9.dev202512261765915415.dist-info/LICENSE.md +12 -0
  103. pyagrum_nightly-2.3.1.9.dev202512261765915415.dist-info/LICENSES/LGPL-3.0-or-later.txt +304 -0
  104. pyagrum_nightly-2.3.1.9.dev202512261765915415.dist-info/LICENSES/MIT.txt +18 -0
  105. pyagrum_nightly-2.3.1.9.dev202512261765915415.dist-info/METADATA +145 -0
  106. pyagrum_nightly-2.3.1.9.dev202512261765915415.dist-info/RECORD +107 -0
  107. pyagrum_nightly-2.3.1.9.dev202512261765915415.dist-info/WHEEL +4 -0
@@ -0,0 +1,361 @@
1
+ ############################################################################
2
+ # This file is part of the aGrUM/pyAgrum library. #
3
+ # #
4
+ # Copyright (c) 2005-2025 by #
5
+ # - Pierre-Henri WUILLEMIN(_at_LIP6) #
6
+ # - Christophe GONZALES(_at_AMU) #
7
+ # #
8
+ # The aGrUM/pyAgrum library is free software; you can redistribute it #
9
+ # and/or modify it under the terms of either : #
10
+ # #
11
+ # - the GNU Lesser General Public License as published by #
12
+ # the Free Software Foundation, either version 3 of the License, #
13
+ # or (at your option) any later version, #
14
+ # - the MIT license (MIT), #
15
+ # - or both in dual license, as here. #
16
+ # #
17
+ # (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) #
18
+ # #
19
+ # This aGrUM/pyAgrum library is distributed in the hope that it will be #
20
+ # useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, #
21
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS #
22
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
23
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
24
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, #
25
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #
26
+ # OTHER DEALINGS IN THE SOFTWARE. #
27
+ # #
28
+ # See LICENCES for more details. #
29
+ # #
30
+ # SPDX-FileCopyrightText: Copyright 2005-2025 #
31
+ # - Pierre-Henri WUILLEMIN(_at_LIP6) #
32
+ # - Christophe GONZALES(_at_AMU) #
33
+ # SPDX-License-Identifier: LGPL-3.0-or-later OR MIT #
34
+ # #
35
+ # Contact : info_at_agrum_dot_org #
36
+ # homepage : http://agrum.gitlab.io #
37
+ # gitlab : https://gitlab.com/agrumery/agrum #
38
+ # #
39
+ ############################################################################
40
+
41
+ """
42
+ This file computes the causal impact of intervention in a causal model
43
+ """
44
+
45
+ import itertools as it
46
+ from typing import List, Set, Optional, Union
47
+
48
+ import pyagrum
49
+
50
+ from pyagrum.causal._types import NameSet, NodeSet
51
+ from pyagrum.causal._dSeparation import dSep_reduce, isDSep, ancester
52
+ from pyagrum.causal._doAST import ASTtree, ASTdiv, ASTjointProba, ASTsum, ASTmult, ASTposteriorProba, productOfTrees
53
+ from pyagrum.causal._exceptions import HedgeException
54
+
55
+ from pyagrum.causal._CausalModel import inducedCausalSubModel, CausalModel
56
+ from pyagrum.causal._CausalFormula import CausalFormula
57
+
58
+ # pylint: disable=unused-import
59
+ import pyagrum.causal # for annotations
60
+
61
+
62
+ def doCalculusWithObservation(
63
+ cm: CausalModel, on: Set[str], doing: NameSet, knowing: Optional[NameSet] = None
64
+ ) -> CausalFormula:
65
+ """
66
+ Compute the CausalFormula for an impact analysis given the causal model, the observed variables and the
67
+ variable on which there will be intervention.
68
+
69
+ Parameters
70
+ ----------
71
+ cm: CausalModel
72
+ the causal model
73
+ on: Set[str]
74
+ the variables of interest
75
+ doing: Set[str]
76
+ the interventions
77
+ knowing: Set[str] default=None
78
+ the observations
79
+
80
+ Returns
81
+ -------
82
+ CausalFormula
83
+ if possible, returns the formula to compute this intervention
84
+
85
+ Raises
86
+ ------
87
+ HedgeException, UnidentifiableException
88
+ if this calculous is not possible
89
+ """
90
+ if knowing is None or len(knowing) == 0:
91
+ return doCalculus(cm, on, doing)
92
+
93
+ lOn = list(on)
94
+ iDoing = {cm.idFromName(i) for i in doing}
95
+ iOn = {cm.idFromName(i) for i in lOn}
96
+ iKnowing = {cm.idFromName(i) for i in knowing}
97
+
98
+ removedArcs = set()
99
+
100
+ for e in iDoing:
101
+ for p in cm.parents(e):
102
+ cm.eraseCausalArc(p, e)
103
+ removedArcs.add((p, e))
104
+ for e in iKnowing:
105
+ for c in cm.children(e):
106
+ cm.eraseCausalArc(e, c)
107
+ removedArcs.add((e, c))
108
+ rg = dSep_reduce(cm, iDoing | iOn | iKnowing)
109
+ for a, b in removedArcs:
110
+ cm.addCausalArc(a, b)
111
+
112
+ for i in iKnowing:
113
+ if isDSep(rg, {i}, iOn, iDoing | (iKnowing - {i})):
114
+ try:
115
+ return doCalculusWithObservation(cm, on, doing | {cm.names()[i]}, knowing - {cm.names()[i]})
116
+ except HedgeException:
117
+ pass
118
+
119
+ p = doCalculus(cm, on | knowing, doing)
120
+ q = doCalculus(cm, knowing, doing)
121
+
122
+ return CausalFormula(cm, ASTdiv(p.root, q.root), on, doing, knowing)
123
+
124
+
125
+ def _cDecomposition(cm: CausalModel) -> List[Set[int]]:
126
+ undi = pyagrum.UndiGraph()
127
+ s = set(cm.nodes()) - cm.latentVariablesIds()
128
+ for n in s:
129
+ undi.addNodeWithId(n)
130
+
131
+ for latent in cm.latentVariablesIds():
132
+ for a, b in it.combinations(cm.children(latent), 2):
133
+ undi.addEdge(a, b)
134
+
135
+ def undiCComponent(g, n, se):
136
+ for i in g.neighbours(n):
137
+ if i not in se:
138
+ se.add(i)
139
+ undiCComponent(g, i, se)
140
+
141
+ components = []
142
+ while len(s) != 0:
143
+ c = s.pop()
144
+ sc = set([c])
145
+ undiCComponent(undi, c, sc)
146
+ s -= sc
147
+ components.append(sc)
148
+ return components
149
+
150
+
151
+ def _topological_sort(cm: CausalModel) -> List[int]:
152
+ lt = cm.latentVariablesIds()
153
+
154
+ dc = {i: len(set(cm.parents(i)) - lt) for i in (set(cm.nodes()) - lt)}
155
+
156
+ order = []
157
+ while len(dc) != 0:
158
+ rem = set()
159
+ for i in dc:
160
+ if dc[i] == 0:
161
+ order.append(i)
162
+ rem.add(i)
163
+
164
+ for r in rem:
165
+ for c in cm.children(r):
166
+ dc[c] -= 1
167
+ del dc[r]
168
+
169
+ return order
170
+
171
+
172
+ def doCalculus(cm: CausalModel, on: Union[str, NameSet], doing: Union[str, NameSet]) -> CausalFormula:
173
+ """
174
+ Compute the CausalFormula for computing an impact analysis given the causal model, the observed variables and the
175
+ variable on
176
+ which there will be intervention. Note that there is no value neither for ``on`` nor for ``doing`` variables
177
+
178
+ :param on: the variables of interest
179
+ :param cm: the causal model
180
+ :param doing: the interventions
181
+ :return: the CausalFormula for computing this causal impact
182
+ """
183
+ X = doing if isinstance(doing, set) else {doing} # set of keys in doing
184
+ Y = on if isinstance(on, set) else {on}
185
+
186
+ return CausalFormula(cm, identifyingIntervention(cm, Y, X), on, doing)
187
+
188
+
189
+ def identifyingIntervention(cm: CausalModel, Y: NameSet, X: NameSet, P: ASTtree = None) -> ASTtree:
190
+ """
191
+ Following Shpitser, Ilya and Judea Pearl. 'Identification of Conditional Interventional Distributions.' UAI2006 and
192
+ 'Complete Identification Methods for the Causal Hierarchy' JMLR 2008
193
+
194
+ :param cm: the causal model
195
+ :param Y: The variables of interest (named following the paper)
196
+ :param X: The variable of intervention (named following the paper)
197
+ :param P: The ASTtree representing the calculus in construction
198
+ :return: the ASTtree representing the calculus
199
+ """
200
+ iX = {cm.idFromName(i) for i in X}
201
+ iY = {cm.idFromName(i) for i in Y}
202
+ iV = set(cm.nodes()) - cm.latentVariablesIds()
203
+ V = {cm.names()[i] for i in iV}
204
+
205
+ # 1 ------------------------------------------------
206
+ if len(X) == 0:
207
+ if P is None:
208
+ lY = list(Y)
209
+ return ASTjointProba(lY)
210
+
211
+ vy = V - Y
212
+ if len(vy) != 0:
213
+ lvy = list(vy)
214
+ return ASTsum(lvy, P)
215
+
216
+ return P
217
+
218
+ # 2 -------------------------------------------
219
+ iAnY = set()
220
+ for i in iY:
221
+ ancester(i, cm, iAnY)
222
+ iAnY |= iY
223
+ AnY = {cm.names()[i] for i in iAnY}
224
+
225
+ if len(cm.nodes()) != len(AnY):
226
+ ivAny = list(iV - iAnY)
227
+
228
+ if P is not None:
229
+ vAny = [cm.names()[j] for j in ivAny]
230
+ P = ASTsum(vAny, P)
231
+ return identifyingIntervention(inducedCausalSubModel(cm, iAnY), Y, X & AnY, P)
232
+
233
+ # 3 -----------------------------------------------
234
+ rmArcs = set()
235
+
236
+ for e in iX:
237
+ for p in cm.parents(e):
238
+ cm.eraseCausalArc(p, e)
239
+ rmArcs.add((p, e))
240
+
241
+ ianY = set()
242
+ for i in iY:
243
+ ancester(i, cm, ianY)
244
+ ianY |= iY
245
+ iW = (iV - iX) - ianY
246
+
247
+ for a, b in rmArcs:
248
+ cm.addCausalArc(a, b)
249
+
250
+ if len(iW) != 0:
251
+ W = {cm.names()[i] for i in iW}
252
+ return identifyingIntervention(cm, Y, X | W, P)
253
+
254
+ gvx = inducedCausalSubModel(cm, iV - iX)
255
+ icd = _cDecomposition(gvx)
256
+ cd = []
257
+ for s in icd:
258
+ cd.append({cm.names()[i] for i in s})
259
+
260
+ # 4----------------
261
+ if len(cd) > 1:
262
+ t = identifyingIntervention(cm, cd[0], V - cd[0], P)
263
+ for si in cd[1:]:
264
+ Pp = P.copy() if P is not None else None
265
+ t = ASTmult(identifyingIntervention(cm, si, V - si, Pp), t)
266
+
267
+ vyx = V - (X | Y)
268
+ lvyx = list(vyx)
269
+
270
+ if len(lvyx) == 0:
271
+ return t
272
+ return ASTsum(lvyx, t)
273
+
274
+ S = cd[0]
275
+ iS = icd[0]
276
+ cdg = _cDecomposition(cm)
277
+
278
+ # 5-------------------------
279
+ if len(cdg) == 1 and len(cdg[0]) == len(V):
280
+ raise HedgeException(f"Hedge Error: G={V}, G[S]={S}", V, S)
281
+
282
+ # 6--------------------------
283
+ gs = inducedCausalSubModel(cm, iS)
284
+ if set(gs.nodes()) - gs.latentVariablesIds() in cdg:
285
+ vpi = []
286
+ prb = []
287
+ to = _topological_sort(cm)
288
+ for v in S:
289
+ vpi = to[: to.index(cm.idFromName(v))]
290
+ nvpi = {cm.names()[i] for i in vpi}
291
+
292
+ if len(nvpi) == 0:
293
+ prb.append(ASTjointProba([v]))
294
+ else:
295
+ if P is None:
296
+ prb.append(ASTposteriorProba(cm.causalBN(), {v}, nvpi))
297
+ else:
298
+ prb.append(ASTdiv(P.copy(), ASTsum(v, P.copy())))
299
+
300
+ prod = productOfTrees(prb)
301
+
302
+ remaining = S - Y
303
+ if len(remaining) == 0:
304
+ return prod
305
+ else:
306
+ return ASTsum(list(remaining), prod)
307
+
308
+ # 7------------------------------------------
309
+ for ispr in cdg:
310
+ if iS <= ispr:
311
+ spr = {cm.names()[i] for i in ispr}
312
+ prb = []
313
+ top = _topological_sort(cm)
314
+
315
+ for v in spr:
316
+ vpi = top[: top.index(cm.idFromName(v))]
317
+ nvpi = {cm.names()[i] for i in vpi}
318
+
319
+ if len(nvpi) == 0:
320
+ prb.append(ASTjointProba([v]))
321
+ else:
322
+ prb.append(ASTposteriorProba(cm.causalBN(), {v}, nvpi))
323
+
324
+ P = productOfTrees(prb)
325
+ return identifyingIntervention(inducedCausalSubModel(cm, ispr), Y, X & spr, P)
326
+
327
+ return None
328
+
329
+
330
+ def getBackDoorTree(cm: CausalModel, x: str, y: str, zset: NodeSet) -> ASTtree:
331
+ """
332
+ Create an CausalFormula representing a backdoor zset from x to y in the causal mode lcm
333
+
334
+ :param cm: causal model
335
+ :param x: impacting node
336
+ :param y: impacted node
337
+ :param zset: backdoor set
338
+ :return: the ASTtree for the backoor criteria
339
+ """
340
+ zp = [cm.names()[i] for i in zset]
341
+ return ASTsum(zp, ASTmult(ASTposteriorProba(cm.causalBN(), {y}, set([x] + zp)), ASTjointProba(zp)))
342
+
343
+
344
+ def getFrontDoorTree(cm: CausalModel, x: str, y: str, zset: NodeSet) -> ASTtree:
345
+ """
346
+ Create an AdsT representing a frontdoor zset from x to y in the causal model
347
+
348
+ :param cm: causal model
349
+ :param x: impacting node
350
+ :param y: impacted node
351
+ :param zset: frontdoor set
352
+ :return: the ASTtree for the frontdoot critreroia
353
+ """
354
+ zp = [cm.names()[i] for i in zset]
355
+ return ASTsum(
356
+ zp,
357
+ ASTmult(
358
+ ASTposteriorProba(cm.causalBN(), set(zp), {x}),
359
+ ASTsum([x], ASTmult(ASTposteriorProba(cm.causalBN(), {y}, set([x] + zp)), ASTjointProba([x]))),
360
+ ),
361
+ )