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.
- 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 +172 -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.1.9.dev202512261765915415.dist-info/LICENSE.md +12 -0
- pyagrum_nightly-2.3.1.9.dev202512261765915415.dist-info/LICENSES/LGPL-3.0-or-later.txt +304 -0
- pyagrum_nightly-2.3.1.9.dev202512261765915415.dist-info/LICENSES/MIT.txt +18 -0
- pyagrum_nightly-2.3.1.9.dev202512261765915415.dist-info/METADATA +145 -0
- pyagrum_nightly-2.3.1.9.dev202512261765915415.dist-info/RECORD +107 -0
- pyagrum_nightly-2.3.1.9.dev202512261765915415.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,374 @@
|
|
|
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 files define some routines for front and back door
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from typing import Optional
|
|
46
|
+
import itertools as it
|
|
47
|
+
|
|
48
|
+
import pyagrum
|
|
49
|
+
|
|
50
|
+
from pyagrum.causal._types import NameSet, NodeId, NodeSet
|
|
51
|
+
from pyagrum.causal._dSeparation import isParent, dSep_reduce, descendants, isDSep_parents
|
|
52
|
+
|
|
53
|
+
# pylint: disable=unused-import
|
|
54
|
+
import pyagrum.causal # for annotations
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def backdoor_path(bn: "pyagrum.BayesNet", x: str, y: str, zset: NameSet = None) -> bool:
|
|
58
|
+
"""
|
|
59
|
+
Predicate on the existence of an open back door path from ``x`` to ``y``,
|
|
60
|
+
conditioning on the set of variables ``zset``
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
bn: pyagrum.BayesNez
|
|
65
|
+
the DAG model
|
|
66
|
+
x: str
|
|
67
|
+
name of source node
|
|
68
|
+
y: str
|
|
69
|
+
name of destination node
|
|
70
|
+
zset: Set[str]
|
|
71
|
+
names of conditioning nodes
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
bool
|
|
76
|
+
True if such backdoor exists
|
|
77
|
+
"""
|
|
78
|
+
if zset is None:
|
|
79
|
+
zset = set()
|
|
80
|
+
return not isDSep_parents(bn, x, y, zset)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def exists_unblocked_directed_path(bn: "pyagrum.BayesNet", x: str, y: str, zset: NameSet = None) -> bool:
|
|
84
|
+
"""
|
|
85
|
+
Predicate on the existence of a directed path from ``x`` to ``y`` in the Bayesian network ``bn``
|
|
86
|
+
not blocked by nodes of ``zset``
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
bn: pyagrum.BayesNez
|
|
91
|
+
the DAG model
|
|
92
|
+
x: str
|
|
93
|
+
name of source node
|
|
94
|
+
y: str
|
|
95
|
+
name of destination node
|
|
96
|
+
zset: Set[str]
|
|
97
|
+
names of conditioning nodes
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
bool
|
|
102
|
+
"""
|
|
103
|
+
if zset is None:
|
|
104
|
+
zset = set()
|
|
105
|
+
if y in bn.children(x):
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
for c in bn.children(x):
|
|
109
|
+
if (c not in zset) and exists_unblocked_directed_path(bn, c, y, zset):
|
|
110
|
+
return True
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def is_frontdoor(bn: "pyagrum.BayesNet", x: str, y: str, zset: NameSet) -> bool:
|
|
115
|
+
"""
|
|
116
|
+
Tests whether or not ``zset`` satisifies the front door criterion for ``x`` and ``y``, in the Bayesian network ``bn``
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
bn: pyagrum.BayesNez
|
|
121
|
+
the DAG model
|
|
122
|
+
x: str
|
|
123
|
+
name of source node
|
|
124
|
+
y: str
|
|
125
|
+
name of destination node
|
|
126
|
+
zset: Set[str]
|
|
127
|
+
names of conditioning nodes
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
bool
|
|
132
|
+
"""
|
|
133
|
+
if exists_unblocked_directed_path(bn, x, y, zset):
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
if len(zset & backdoor_reach(bn, x)) != 0:
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
bn_reduit = dSep_reduce(bn, zset | {x, y})
|
|
140
|
+
for i in zset:
|
|
141
|
+
if backdoor_path(bn_reduit, i, y, {x}):
|
|
142
|
+
return False
|
|
143
|
+
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def is_backdoor(bn: "pyagrum.BayesNet", x: str, y: str, zset: NameSet) -> bool:
|
|
148
|
+
"""
|
|
149
|
+
Tests whether or not ``zset`` satisifies the back door criterion for ``x`` and ``y``, in the Bayesian network ``bn``
|
|
150
|
+
|
|
151
|
+
Parameters
|
|
152
|
+
----------
|
|
153
|
+
bn: pyagrum.BayesNez
|
|
154
|
+
the DAG model
|
|
155
|
+
x: str
|
|
156
|
+
name of source node
|
|
157
|
+
y: str
|
|
158
|
+
name of destination node
|
|
159
|
+
zset: Set[str]
|
|
160
|
+
names of conditioning nodes
|
|
161
|
+
|
|
162
|
+
Returns
|
|
163
|
+
-------
|
|
164
|
+
bool
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
desc_x = descendants(bn, x)
|
|
168
|
+
if len(desc_x & zset) != 0:
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
return isDSep_parents(bn, x, y, zset)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def backdoor_reach(bn: "pyagrum.BayesNet", a: NodeId) -> NodeSet:
|
|
175
|
+
"""
|
|
176
|
+
Returns the set of nodes that can be reached through a backdoor path from ``a`` in the graph ``bn``
|
|
177
|
+
|
|
178
|
+
Parameters
|
|
179
|
+
----------
|
|
180
|
+
bn: pyagrum.BayesNet
|
|
181
|
+
the model
|
|
182
|
+
a: int
|
|
183
|
+
the backdoor-ed node
|
|
184
|
+
|
|
185
|
+
Returns
|
|
186
|
+
-------
|
|
187
|
+
Set[int]
|
|
188
|
+
set of nodes
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
def inner_br(bn: "pyagrum.BayesNet", x: NodeId, pht: bool, reach0: NodeSet, reach1: NodeSet):
|
|
192
|
+
for c in bn.children(x):
|
|
193
|
+
if c not in reach0 and c not in reach1:
|
|
194
|
+
reach1.add(c)
|
|
195
|
+
inner_br(bn, c, True, reach0, reach1)
|
|
196
|
+
|
|
197
|
+
if not pht:
|
|
198
|
+
for p in bn.parents(x):
|
|
199
|
+
if p not in reach0:
|
|
200
|
+
reach0.add(p)
|
|
201
|
+
inner_br(bn, p, False, reach0, reach1)
|
|
202
|
+
|
|
203
|
+
r = {a}
|
|
204
|
+
r.union(bn.parents(a))
|
|
205
|
+
l = {a}
|
|
206
|
+
for pa in bn.parents(a):
|
|
207
|
+
inner_br(bn, pa, False, r, l)
|
|
208
|
+
s = r | l
|
|
209
|
+
if a in s:
|
|
210
|
+
s.remove(a)
|
|
211
|
+
return s
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def nodes_on_dipath(bn: "pyagrum.BayesNet", x: NodeId, y: NodeId) -> Optional[NodeSet]:
|
|
215
|
+
"""
|
|
216
|
+
Returns the set of nodes through which there is a directed path from `x` to `y` in the graph `bn`
|
|
217
|
+
|
|
218
|
+
Parameters
|
|
219
|
+
----------
|
|
220
|
+
bn: pyagrum.BayesNet
|
|
221
|
+
x int
|
|
222
|
+
y: int
|
|
223
|
+
|
|
224
|
+
Returns
|
|
225
|
+
-------
|
|
226
|
+
Set[int] (maybe None)
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
def inner_nod(g: "pyagrum.BayesNet", a: NodeId, b: NodeId) -> Optional[NodeSet]:
|
|
230
|
+
if b == a:
|
|
231
|
+
return set()
|
|
232
|
+
|
|
233
|
+
inners = {a}
|
|
234
|
+
children = g.children(a)
|
|
235
|
+
if len(children) == 0:
|
|
236
|
+
return None
|
|
237
|
+
|
|
238
|
+
found = False
|
|
239
|
+
for c in children:
|
|
240
|
+
s = inner_nod(g, c, b)
|
|
241
|
+
if s is not None:
|
|
242
|
+
found = True
|
|
243
|
+
inners |= s
|
|
244
|
+
if found:
|
|
245
|
+
return inners
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
r = inner_nod(bn, x, y)
|
|
249
|
+
if r:
|
|
250
|
+
r.remove(x)
|
|
251
|
+
|
|
252
|
+
return r
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def backdoor_generator(bn: "pyagrum.BayesNet", cause: NodeId, effect: NodeId, not_bd: NodeSet = None):
|
|
256
|
+
"""
|
|
257
|
+
Generates backdoor sets for the pair of nodes `(cause, effect)` in the graph `bn` excluding the nodes in the set
|
|
258
|
+
`not_bd` (optional)
|
|
259
|
+
|
|
260
|
+
Parameters
|
|
261
|
+
----------
|
|
262
|
+
bn: pyagrum.BayesNet
|
|
263
|
+
cause: int
|
|
264
|
+
effect: int
|
|
265
|
+
not_bd: Set[int] default=None
|
|
266
|
+
|
|
267
|
+
Yields
|
|
268
|
+
-------
|
|
269
|
+
List[int]
|
|
270
|
+
the different backdoors
|
|
271
|
+
"""
|
|
272
|
+
if len(bn.parents(cause)) == 0: # no parent of cause, no backdoor
|
|
273
|
+
return
|
|
274
|
+
if isParent(effect, cause, bn): # causalDagFromBN(bn)):
|
|
275
|
+
return
|
|
276
|
+
|
|
277
|
+
if not_bd is None:
|
|
278
|
+
not_bd = set()
|
|
279
|
+
|
|
280
|
+
# simplifying the graph
|
|
281
|
+
interest = {cause, effect}
|
|
282
|
+
G = dSep_reduce(bn, interest)
|
|
283
|
+
|
|
284
|
+
# removing the non connected in G without descendants
|
|
285
|
+
# GG is a trash graph just to find the disjointed nodes in G
|
|
286
|
+
GG = pyagrum.DiGraph(G)
|
|
287
|
+
for i in descendants(bn, cause, set()):
|
|
288
|
+
GG.eraseNode(i)
|
|
289
|
+
|
|
290
|
+
# we only keep interesting connex components
|
|
291
|
+
for nodes in GG.connectedComponents().values():
|
|
292
|
+
if nodes.isdisjoint(interest):
|
|
293
|
+
for n in nodes:
|
|
294
|
+
G.eraseNode(n)
|
|
295
|
+
|
|
296
|
+
possible = set(G.nodes()) - (descendants(bn, cause, set()) | interest | not_bd)
|
|
297
|
+
if len(possible) == 0:
|
|
298
|
+
return
|
|
299
|
+
|
|
300
|
+
backdoors = set()
|
|
301
|
+
|
|
302
|
+
for i in range(len(possible)):
|
|
303
|
+
for subset in it.combinations(possible, i + 1):
|
|
304
|
+
sub = frozenset(subset)
|
|
305
|
+
worth_testing = True
|
|
306
|
+
for s in backdoors:
|
|
307
|
+
if s <= sub:
|
|
308
|
+
worth_testing = False
|
|
309
|
+
if worth_testing and isDSep_parents(G, {cause}, {effect}, sub):
|
|
310
|
+
backdoors.add(sub)
|
|
311
|
+
yield list(subset)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def frontdoor_generator(bn: "pyagrum.BayesNet", x: NodeId, y: NodeId, not_fd: NodeSet = None):
|
|
315
|
+
"""
|
|
316
|
+
Generates frontdoor sets for the pair of nodes `(x, y)` in the graph `bn` excluding the nodes in the set
|
|
317
|
+
`not_fd` (optional)
|
|
318
|
+
|
|
319
|
+
Parameters
|
|
320
|
+
----------
|
|
321
|
+
bn: pyagrum.BayesNet
|
|
322
|
+
x: int
|
|
323
|
+
y: int
|
|
324
|
+
not_fd: Set[int] default=None
|
|
325
|
+
|
|
326
|
+
Yields
|
|
327
|
+
-------
|
|
328
|
+
List[int]
|
|
329
|
+
the different frontdoors
|
|
330
|
+
"""
|
|
331
|
+
if isParent(x, y, bn):
|
|
332
|
+
return
|
|
333
|
+
|
|
334
|
+
if not_fd is None:
|
|
335
|
+
not_fd = set()
|
|
336
|
+
|
|
337
|
+
possible = nodes_on_dipath(bn, x, y)
|
|
338
|
+
|
|
339
|
+
nodiPath = False
|
|
340
|
+
if possible is None:
|
|
341
|
+
nodiPath = True
|
|
342
|
+
possible = None
|
|
343
|
+
for _, cc in bn.connectedComponents().items():
|
|
344
|
+
if x in cc and y in cc:
|
|
345
|
+
possible = cc
|
|
346
|
+
if possible is None:
|
|
347
|
+
return
|
|
348
|
+
possible -= {x, y}
|
|
349
|
+
|
|
350
|
+
possible -= backdoor_reach(bn, x)
|
|
351
|
+
possible -= not_fd
|
|
352
|
+
impossible = set()
|
|
353
|
+
g = dSep_reduce(bn, {x, y} | possible)
|
|
354
|
+
for z in possible:
|
|
355
|
+
if backdoor_path(g, {z}, {y}, {x}):
|
|
356
|
+
impossible.add(z)
|
|
357
|
+
possible -= impossible
|
|
358
|
+
frontdoors = set()
|
|
359
|
+
|
|
360
|
+
if nodiPath:
|
|
361
|
+
for s in possible:
|
|
362
|
+
yield [s]
|
|
363
|
+
return
|
|
364
|
+
|
|
365
|
+
for i in range(len(possible)):
|
|
366
|
+
for subset in it.combinations(possible, i + 1):
|
|
367
|
+
sub = frozenset(subset)
|
|
368
|
+
worth_testing = True
|
|
369
|
+
for s in frontdoors:
|
|
370
|
+
if s <= sub:
|
|
371
|
+
worth_testing = False
|
|
372
|
+
if worth_testing and not exists_unblocked_directed_path(bn, x, y, sub):
|
|
373
|
+
frontdoors.add(sub)
|
|
374
|
+
yield list(subset)
|
|
@@ -0,0 +1,95 @@
|
|
|
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 defines the specific exception for causal package
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from ._types import NameSet
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class HedgeException(Exception):
|
|
49
|
+
"""
|
|
50
|
+
Represents an hedge exception for a causal query
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
msg: str
|
|
55
|
+
observables: NameSet
|
|
56
|
+
gs: ???
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, msg: str, observables: NameSet, gs):
|
|
60
|
+
"""
|
|
61
|
+
Represents an hedge exception for a causal query
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
msg: str
|
|
66
|
+
observables: NameSet
|
|
67
|
+
gs: ???
|
|
68
|
+
"""
|
|
69
|
+
self.message = msg
|
|
70
|
+
self.type = "HedgeException"
|
|
71
|
+
self.observables = observables
|
|
72
|
+
self.gs = gs
|
|
73
|
+
super().__init__(self.message)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class UnidentifiableException(Exception):
|
|
77
|
+
"""
|
|
78
|
+
Represents an unidentifiability for a causal query
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
msg: str
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def __init__(self, msg):
|
|
86
|
+
"""
|
|
87
|
+
Represents an unidentifiability for a causal query
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
msg: str
|
|
92
|
+
"""
|
|
93
|
+
self.message = msg
|
|
94
|
+
self.type = "Unidentifiable"
|
|
95
|
+
super().__init__(self.message)
|
pyagrum/causal/_types.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
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 defines the types for the causal package
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from typing import NewType
|
|
46
|
+
from typing import Set, List, Tuple, Union
|
|
47
|
+
|
|
48
|
+
# pylint: disable=unused-import
|
|
49
|
+
import pyagrum.causal # for annotations
|
|
50
|
+
|
|
51
|
+
DirectedModel = Union["pyagrum.BayesNet", "pyagrum.DAG", "pyagrum.causal.CausalModel"]
|
|
52
|
+
|
|
53
|
+
NodeId = NewType("NodeId", int)
|
|
54
|
+
NodeList = List[NodeId]
|
|
55
|
+
NodeSet = Set[NodeId]
|
|
56
|
+
|
|
57
|
+
ArcSet = Set[Tuple[NodeId, NodeId]]
|
|
58
|
+
NameSet = Set[str]
|
|
59
|
+
|
|
60
|
+
LatentDescriptor = Tuple[str, Tuple[str, str]]
|
|
61
|
+
LatentDescriptorList = List[LatentDescriptor]
|