pyAgrum-nightly 2.3.0.9.dev202512061764412981__cp310-abi3-macosx_11_0_arm64.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.
- pyagrum/__init__.py +165 -0
- pyagrum/_pyagrum.so +0 -0
- pyagrum/bnmixture/BNMInference.py +268 -0
- pyagrum/bnmixture/BNMLearning.py +376 -0
- pyagrum/bnmixture/BNMixture.py +464 -0
- pyagrum/bnmixture/__init__.py +60 -0
- pyagrum/bnmixture/notebook.py +1058 -0
- pyagrum/causal/_CausalFormula.py +280 -0
- pyagrum/causal/_CausalModel.py +436 -0
- pyagrum/causal/__init__.py +81 -0
- pyagrum/causal/_causalImpact.py +356 -0
- pyagrum/causal/_dSeparation.py +598 -0
- pyagrum/causal/_doAST.py +761 -0
- pyagrum/causal/_doCalculus.py +361 -0
- pyagrum/causal/_doorCriteria.py +374 -0
- pyagrum/causal/_exceptions.py +95 -0
- pyagrum/causal/_types.py +61 -0
- pyagrum/causal/causalEffectEstimation/_CausalEffectEstimation.py +1175 -0
- pyagrum/causal/causalEffectEstimation/_IVEstimators.py +718 -0
- pyagrum/causal/causalEffectEstimation/_RCTEstimators.py +132 -0
- pyagrum/causal/causalEffectEstimation/__init__.py +46 -0
- pyagrum/causal/causalEffectEstimation/_backdoorEstimators.py +774 -0
- pyagrum/causal/causalEffectEstimation/_causalBNEstimator.py +324 -0
- pyagrum/causal/causalEffectEstimation/_frontdoorEstimators.py +396 -0
- pyagrum/causal/causalEffectEstimation/_learners.py +118 -0
- pyagrum/causal/causalEffectEstimation/_utils.py +466 -0
- pyagrum/causal/notebook.py +171 -0
- pyagrum/clg/CLG.py +658 -0
- pyagrum/clg/GaussianVariable.py +111 -0
- pyagrum/clg/SEM.py +312 -0
- pyagrum/clg/__init__.py +63 -0
- pyagrum/clg/canonicalForm.py +408 -0
- pyagrum/clg/constants.py +54 -0
- pyagrum/clg/forwardSampling.py +202 -0
- pyagrum/clg/learning.py +776 -0
- pyagrum/clg/notebook.py +480 -0
- pyagrum/clg/variableElimination.py +271 -0
- pyagrum/common.py +60 -0
- pyagrum/config.py +319 -0
- pyagrum/ctbn/CIM.py +513 -0
- pyagrum/ctbn/CTBN.py +573 -0
- pyagrum/ctbn/CTBNGenerator.py +216 -0
- pyagrum/ctbn/CTBNInference.py +459 -0
- pyagrum/ctbn/CTBNLearner.py +161 -0
- pyagrum/ctbn/SamplesStats.py +671 -0
- pyagrum/ctbn/StatsIndepTest.py +355 -0
- pyagrum/ctbn/__init__.py +79 -0
- pyagrum/ctbn/constants.py +54 -0
- pyagrum/ctbn/notebook.py +264 -0
- pyagrum/defaults.ini +199 -0
- pyagrum/deprecated.py +95 -0
- pyagrum/explain/_ComputationCausal.py +75 -0
- pyagrum/explain/_ComputationConditional.py +48 -0
- pyagrum/explain/_ComputationMarginal.py +48 -0
- pyagrum/explain/_CustomShapleyCache.py +110 -0
- pyagrum/explain/_Explainer.py +176 -0
- pyagrum/explain/_Explanation.py +70 -0
- pyagrum/explain/_FIFOCache.py +54 -0
- pyagrum/explain/_ShallCausalValues.py +204 -0
- pyagrum/explain/_ShallConditionalValues.py +155 -0
- pyagrum/explain/_ShallMarginalValues.py +155 -0
- pyagrum/explain/_ShallValues.py +296 -0
- pyagrum/explain/_ShapCausalValues.py +208 -0
- pyagrum/explain/_ShapConditionalValues.py +126 -0
- pyagrum/explain/_ShapMarginalValues.py +191 -0
- pyagrum/explain/_ShapleyValues.py +298 -0
- pyagrum/explain/__init__.py +81 -0
- pyagrum/explain/_explGeneralizedMarkovBlanket.py +152 -0
- pyagrum/explain/_explIndependenceListForPairs.py +146 -0
- pyagrum/explain/_explInformationGraph.py +264 -0
- pyagrum/explain/notebook/__init__.py +54 -0
- pyagrum/explain/notebook/_bar.py +142 -0
- pyagrum/explain/notebook/_beeswarm.py +174 -0
- pyagrum/explain/notebook/_showShapValues.py +97 -0
- pyagrum/explain/notebook/_waterfall.py +220 -0
- pyagrum/explain/shapley.py +225 -0
- pyagrum/lib/__init__.py +46 -0
- pyagrum/lib/_colors.py +390 -0
- pyagrum/lib/bn2graph.py +299 -0
- pyagrum/lib/bn2roc.py +1026 -0
- pyagrum/lib/bn2scores.py +217 -0
- pyagrum/lib/bn_vs_bn.py +605 -0
- pyagrum/lib/cn2graph.py +305 -0
- pyagrum/lib/discreteTypeProcessor.py +1102 -0
- pyagrum/lib/discretizer.py +58 -0
- pyagrum/lib/dynamicBN.py +390 -0
- pyagrum/lib/explain.py +57 -0
- pyagrum/lib/export.py +84 -0
- pyagrum/lib/id2graph.py +258 -0
- pyagrum/lib/image.py +387 -0
- pyagrum/lib/ipython.py +307 -0
- pyagrum/lib/mrf2graph.py +471 -0
- pyagrum/lib/notebook.py +1821 -0
- pyagrum/lib/proba_histogram.py +552 -0
- pyagrum/lib/utils.py +138 -0
- pyagrum/pyagrum.py +31495 -0
- pyagrum/skbn/_MBCalcul.py +242 -0
- pyagrum/skbn/__init__.py +49 -0
- pyagrum/skbn/_learningMethods.py +282 -0
- pyagrum/skbn/_utils.py +297 -0
- pyagrum/skbn/bnclassifier.py +1014 -0
- pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/LICENSE.md +12 -0
- pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/LICENSES/LGPL-3.0-or-later.txt +304 -0
- pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/LICENSES/MIT.txt +18 -0
- pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/METADATA +145 -0
- pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/RECORD +107 -0
- pyagrum_nightly-2.3.0.9.dev202512061764412981.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
|
+
)
|