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
pyagrum/clg/CLG.py
ADDED
|
@@ -0,0 +1,658 @@
|
|
|
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
|
+
The purpose of this module is to store a Conditional Linear Gaussian (CLG) in a graph structure.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
import pyagrum as gum
|
|
46
|
+
from typing import Dict, Tuple
|
|
47
|
+
import random
|
|
48
|
+
|
|
49
|
+
from .constants import NodeId
|
|
50
|
+
from .GaussianVariable import GaussianVariable
|
|
51
|
+
|
|
52
|
+
import pyagrum.lib.bn_vs_bn as gcm
|
|
53
|
+
|
|
54
|
+
import numpy as np
|
|
55
|
+
from scipy.stats import norm
|
|
56
|
+
import pandas as pd
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class CLG:
|
|
60
|
+
_graph: gum.DAG
|
|
61
|
+
_id2var: Dict[NodeId, GaussianVariable]
|
|
62
|
+
_name2id: Dict[str, NodeId]
|
|
63
|
+
_arc2coef: Dict[Tuple[NodeId, NodeId], float or int]
|
|
64
|
+
|
|
65
|
+
def __init__(self, clg=None):
|
|
66
|
+
self._graph = gum.DAG()
|
|
67
|
+
self._id2var = {}
|
|
68
|
+
self._name2id = {}
|
|
69
|
+
self._arc2coef = {}
|
|
70
|
+
|
|
71
|
+
if clg:
|
|
72
|
+
self.copy(clg)
|
|
73
|
+
|
|
74
|
+
def copy(self, clg):
|
|
75
|
+
for _, variable in clg._id2var.items():
|
|
76
|
+
new_variable = GaussianVariable(name=variable.name(), mu=variable.mu(), sigma=variable.sigma())
|
|
77
|
+
self.add(new_variable)
|
|
78
|
+
|
|
79
|
+
for (parent, child), coef in clg._arc2coef.items():
|
|
80
|
+
self.addArc(parent, child, coef)
|
|
81
|
+
|
|
82
|
+
def __str__(self):
|
|
83
|
+
return (
|
|
84
|
+
f"CLG{{nodes: {len(self.nodes())}, "
|
|
85
|
+
f"arcs: {len(self.arcs())}, "
|
|
86
|
+
f"parameters: {len(self.arcs()) + 2 * len(self.nodes())}}}"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def __repr__(self):
|
|
90
|
+
return str(self)
|
|
91
|
+
|
|
92
|
+
def add(self, var):
|
|
93
|
+
"""
|
|
94
|
+
Add a new variable to the CLG.
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
var : GaussianVariable
|
|
99
|
+
The variable to be added to the CLG.
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
NodeId
|
|
104
|
+
The id of the added variable.
|
|
105
|
+
|
|
106
|
+
Raises
|
|
107
|
+
------
|
|
108
|
+
ValueError
|
|
109
|
+
if the argument is None.
|
|
110
|
+
|
|
111
|
+
NameError
|
|
112
|
+
if the name of the variable is empty.
|
|
113
|
+
|
|
114
|
+
NameError
|
|
115
|
+
if a variable with the same name already exists in the CLG.
|
|
116
|
+
"""
|
|
117
|
+
if var is None:
|
|
118
|
+
raise ValueError("The argument cannot be None.")
|
|
119
|
+
if var.name() == "":
|
|
120
|
+
raise NameError(f"The name cannot be '{var.name()}'.")
|
|
121
|
+
if var.name() in self._name2id:
|
|
122
|
+
raise NameError(f"A variable with the same name ({var.name()}) already exists in this CLG.")
|
|
123
|
+
|
|
124
|
+
n = NodeId(self._graph.addNode())
|
|
125
|
+
self._id2var[n] = var
|
|
126
|
+
self._name2id[var.name()] = n
|
|
127
|
+
|
|
128
|
+
return n
|
|
129
|
+
|
|
130
|
+
def setMu(self, node, mu):
|
|
131
|
+
"""
|
|
132
|
+
Set the mean of a variable.
|
|
133
|
+
|
|
134
|
+
Parameters
|
|
135
|
+
----------
|
|
136
|
+
node : NodeId
|
|
137
|
+
The id of the variable.
|
|
138
|
+
mu : float
|
|
139
|
+
The new mean of the variable.
|
|
140
|
+
|
|
141
|
+
Raises
|
|
142
|
+
------
|
|
143
|
+
gum.NotFound
|
|
144
|
+
if the node is not found in the CLG.
|
|
145
|
+
"""
|
|
146
|
+
self._id2var[node].setMu(mu)
|
|
147
|
+
|
|
148
|
+
def setSigma(self, node, sigma):
|
|
149
|
+
"""
|
|
150
|
+
Set the standard deviation of a variable.
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
node : NodeId
|
|
155
|
+
The id of the variable.
|
|
156
|
+
sigma : float
|
|
157
|
+
The new standard deviation of the variable.
|
|
158
|
+
|
|
159
|
+
Raises
|
|
160
|
+
------
|
|
161
|
+
gum.NotFound
|
|
162
|
+
if the node is not found in the CLG.
|
|
163
|
+
"""
|
|
164
|
+
self._id2var[node].setSigma(sigma)
|
|
165
|
+
|
|
166
|
+
def nameOrId(self, val):
|
|
167
|
+
"""
|
|
168
|
+
Return the NodeId from the name or the NodeId.
|
|
169
|
+
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
val : NameOrId
|
|
173
|
+
The name or the NodeId of the variable.
|
|
174
|
+
|
|
175
|
+
Returns
|
|
176
|
+
-------
|
|
177
|
+
NodeId
|
|
178
|
+
The NodeId of the variable.
|
|
179
|
+
"""
|
|
180
|
+
return val if isinstance(val, int) else self._name2id[val]
|
|
181
|
+
|
|
182
|
+
def addArc(self, val1, val2, coef=1):
|
|
183
|
+
"""
|
|
184
|
+
Add an arc val->val2 with a coefficient coef to the CLG.
|
|
185
|
+
|
|
186
|
+
Parameters
|
|
187
|
+
----------
|
|
188
|
+
val1 : NameOrId
|
|
189
|
+
The name or the NodeId of the parent variable.
|
|
190
|
+
val2 : NameOrId
|
|
191
|
+
The name or the NodeId of the child variable.
|
|
192
|
+
coef : float or int
|
|
193
|
+
The coefficient of the arc.
|
|
194
|
+
|
|
195
|
+
Returns
|
|
196
|
+
-------
|
|
197
|
+
Tuple[NodeId, NodeId]
|
|
198
|
+
The tuple of the NodeIds of the parent and the child variables.
|
|
199
|
+
|
|
200
|
+
Raises
|
|
201
|
+
------
|
|
202
|
+
gum.NotFound
|
|
203
|
+
if one of the names is not found in the CLG.
|
|
204
|
+
ValueError
|
|
205
|
+
if the coefficient is 0.
|
|
206
|
+
"""
|
|
207
|
+
if coef == 0:
|
|
208
|
+
raise ValueError("The coefficient of an arc cannot be 0.")
|
|
209
|
+
|
|
210
|
+
n1 = self.nameOrId(val1)
|
|
211
|
+
n2 = self.nameOrId(val2)
|
|
212
|
+
|
|
213
|
+
self._graph.addArc(n1, n2)
|
|
214
|
+
self._arc2coef[(n1, n2)] = coef
|
|
215
|
+
|
|
216
|
+
return (n1, n2)
|
|
217
|
+
|
|
218
|
+
def existsArc(self, val1, val2):
|
|
219
|
+
"""
|
|
220
|
+
Check if an arc val->val2 exists.
|
|
221
|
+
|
|
222
|
+
Parameters
|
|
223
|
+
----------
|
|
224
|
+
val1 : NameOrId
|
|
225
|
+
The name or the NodeId of the parent variable.
|
|
226
|
+
val2 : NameOrId
|
|
227
|
+
The name or the NodeId of the child variable.
|
|
228
|
+
|
|
229
|
+
Returns
|
|
230
|
+
-------
|
|
231
|
+
bool
|
|
232
|
+
True if the arc exists.
|
|
233
|
+
|
|
234
|
+
Raises
|
|
235
|
+
------
|
|
236
|
+
gum.NotFound
|
|
237
|
+
if one of the names is not found in the CLG.
|
|
238
|
+
"""
|
|
239
|
+
n1 = self.nameOrId(val1)
|
|
240
|
+
n2 = self.nameOrId(val2)
|
|
241
|
+
|
|
242
|
+
return self._graph.existsArc(n1, n2)
|
|
243
|
+
|
|
244
|
+
def setCoef(self, val1, val2, coef):
|
|
245
|
+
"""
|
|
246
|
+
Set the coefficient of an arc val1->val2.
|
|
247
|
+
|
|
248
|
+
Parameters
|
|
249
|
+
----------
|
|
250
|
+
val1 : NameOrId
|
|
251
|
+
The name or the NodeId of the parent variable.
|
|
252
|
+
val2 : NameOrId
|
|
253
|
+
The name or the NodeId of the child variable.
|
|
254
|
+
coef : float or int
|
|
255
|
+
The new coefficient of the arc.
|
|
256
|
+
|
|
257
|
+
Raises
|
|
258
|
+
------
|
|
259
|
+
gum.NotFound
|
|
260
|
+
if one of the names is not found in the CLG.
|
|
261
|
+
ValueError
|
|
262
|
+
if the coefficient is 0.
|
|
263
|
+
ValueError
|
|
264
|
+
if the arc does not exist.
|
|
265
|
+
"""
|
|
266
|
+
if coef == 0:
|
|
267
|
+
raise ValueError("The coefficient of an arc cannot be 0.")
|
|
268
|
+
|
|
269
|
+
if not self.existsArc(val1, val2):
|
|
270
|
+
raise ValueError("The arc does not exist.")
|
|
271
|
+
|
|
272
|
+
n1 = self.nameOrId(val1)
|
|
273
|
+
n2 = self.nameOrId(val2)
|
|
274
|
+
|
|
275
|
+
self._arc2coef[(n1, n2)] = coef
|
|
276
|
+
|
|
277
|
+
def dag(self):
|
|
278
|
+
"""
|
|
279
|
+
Return the graph of the CLG (which is a DAG).
|
|
280
|
+
|
|
281
|
+
Returns
|
|
282
|
+
-------
|
|
283
|
+
gum.DAG
|
|
284
|
+
The graph of the CLG.
|
|
285
|
+
"""
|
|
286
|
+
return self._graph
|
|
287
|
+
|
|
288
|
+
def eraseArc(self, val1, val2):
|
|
289
|
+
"""
|
|
290
|
+
Erase the arc val->val2.
|
|
291
|
+
"""
|
|
292
|
+
self._graph.eraseArc(val1, val2)
|
|
293
|
+
|
|
294
|
+
def name(self, node):
|
|
295
|
+
"""
|
|
296
|
+
Return the associated name of the variable.
|
|
297
|
+
|
|
298
|
+
Parameters
|
|
299
|
+
----------
|
|
300
|
+
node : NodeId
|
|
301
|
+
The id of the variable.
|
|
302
|
+
|
|
303
|
+
Returns
|
|
304
|
+
-------
|
|
305
|
+
str
|
|
306
|
+
The associated name of the variable.
|
|
307
|
+
|
|
308
|
+
Raises
|
|
309
|
+
------
|
|
310
|
+
gum.NotFound
|
|
311
|
+
if the node is not found in the CLG.
|
|
312
|
+
"""
|
|
313
|
+
return self._id2var[node].name()
|
|
314
|
+
|
|
315
|
+
def idFromName(self, name):
|
|
316
|
+
"""
|
|
317
|
+
Return the NodeId from the name.
|
|
318
|
+
|
|
319
|
+
Parameters
|
|
320
|
+
----------
|
|
321
|
+
name : str
|
|
322
|
+
The name of the variable.
|
|
323
|
+
|
|
324
|
+
Returns
|
|
325
|
+
-------
|
|
326
|
+
NodeId
|
|
327
|
+
The NodeId of the variable.
|
|
328
|
+
|
|
329
|
+
Raises
|
|
330
|
+
------
|
|
331
|
+
gum.NotFound
|
|
332
|
+
if the name is not found in the CLG.
|
|
333
|
+
"""
|
|
334
|
+
return self._name2id[name]
|
|
335
|
+
|
|
336
|
+
def variable(self, val):
|
|
337
|
+
"""
|
|
338
|
+
Return the variable from the NodeId or from the name.
|
|
339
|
+
|
|
340
|
+
Parameters
|
|
341
|
+
----------
|
|
342
|
+
val : NameOrId
|
|
343
|
+
The name or the NodeId of the variable.
|
|
344
|
+
|
|
345
|
+
Returns
|
|
346
|
+
-------
|
|
347
|
+
GaussianVariable
|
|
348
|
+
The variable.
|
|
349
|
+
|
|
350
|
+
Raises
|
|
351
|
+
------
|
|
352
|
+
gum.NotFound
|
|
353
|
+
if val is not Found in the CLG.
|
|
354
|
+
"""
|
|
355
|
+
return self._id2var[self.nameOrId(val)]
|
|
356
|
+
|
|
357
|
+
def variables(self):
|
|
358
|
+
"""
|
|
359
|
+
Return the list of the variables in the CLG.
|
|
360
|
+
|
|
361
|
+
Returns
|
|
362
|
+
-------
|
|
363
|
+
List[GaussianVariable]
|
|
364
|
+
The list of the variables in the CLG.
|
|
365
|
+
"""
|
|
366
|
+
return [self.variable(i) for i in self.nodes()]
|
|
367
|
+
|
|
368
|
+
def nodes(self):
|
|
369
|
+
"""
|
|
370
|
+
Return the list of NodeIds in the CLG.
|
|
371
|
+
|
|
372
|
+
Returns
|
|
373
|
+
-------
|
|
374
|
+
List[NodeId]
|
|
375
|
+
The list of NodeIds in the CLG.
|
|
376
|
+
"""
|
|
377
|
+
return list(self._id2var.keys())
|
|
378
|
+
|
|
379
|
+
def names(self):
|
|
380
|
+
"""
|
|
381
|
+
Return the list of names in the CLG.
|
|
382
|
+
|
|
383
|
+
Returns
|
|
384
|
+
-------
|
|
385
|
+
List[str]
|
|
386
|
+
The list of names in the CLG.
|
|
387
|
+
"""
|
|
388
|
+
return list(self._name2id.keys())
|
|
389
|
+
|
|
390
|
+
def arcs(self):
|
|
391
|
+
"""
|
|
392
|
+
Return the list of arcs in the CLG.
|
|
393
|
+
|
|
394
|
+
Returns
|
|
395
|
+
-------
|
|
396
|
+
List[Tuple[NodeId, NodeId]]
|
|
397
|
+
The list of arcs in the CLG.
|
|
398
|
+
"""
|
|
399
|
+
return self._graph.arcs()
|
|
400
|
+
|
|
401
|
+
def coefArc(self, val1, val2):
|
|
402
|
+
"""
|
|
403
|
+
Return the coefficient of the arc val1->val2.
|
|
404
|
+
|
|
405
|
+
Parameters
|
|
406
|
+
----------
|
|
407
|
+
val1 : NameOrId
|
|
408
|
+
The name or the NodeId of the parent variable.
|
|
409
|
+
val2 : NameOrId
|
|
410
|
+
The name or the NodeId of the child variable.
|
|
411
|
+
|
|
412
|
+
Returns
|
|
413
|
+
-------
|
|
414
|
+
float
|
|
415
|
+
The coefficient of the arc.
|
|
416
|
+
|
|
417
|
+
Raises
|
|
418
|
+
------
|
|
419
|
+
pyagrum.NotFound
|
|
420
|
+
if one of the names is not found in the CLG.
|
|
421
|
+
pyagrum.NotFound
|
|
422
|
+
if the arc does not exist.
|
|
423
|
+
"""
|
|
424
|
+
if not self.existsArc(val1, val2):
|
|
425
|
+
raise gum.NotFound("The arc does not exist.")
|
|
426
|
+
|
|
427
|
+
n1 = self.nameOrId(val1)
|
|
428
|
+
n2 = self.nameOrId(val2)
|
|
429
|
+
|
|
430
|
+
return self._arc2coef[(n1, n2)]
|
|
431
|
+
|
|
432
|
+
def parents(self, val):
|
|
433
|
+
"""
|
|
434
|
+
Return the list of parent ids from the name or the id of a node.
|
|
435
|
+
|
|
436
|
+
Parameters
|
|
437
|
+
----------
|
|
438
|
+
val : NameOrId
|
|
439
|
+
The name or the NodeId of the variable.
|
|
440
|
+
|
|
441
|
+
Returns
|
|
442
|
+
-------
|
|
443
|
+
Set[NodeId]
|
|
444
|
+
The set of parent nodes' ids.
|
|
445
|
+
"""
|
|
446
|
+
return self._graph.parents(self.nameOrId(val))
|
|
447
|
+
|
|
448
|
+
def parent_names(self, val):
|
|
449
|
+
"""
|
|
450
|
+
Return the list of parents names from the name or the id of a node.
|
|
451
|
+
|
|
452
|
+
Parameters
|
|
453
|
+
----------
|
|
454
|
+
val : NameOrId
|
|
455
|
+
The name or the NodeId of the variable.
|
|
456
|
+
|
|
457
|
+
Returns
|
|
458
|
+
-------
|
|
459
|
+
List[str]
|
|
460
|
+
The list of val's parents' names.
|
|
461
|
+
"""
|
|
462
|
+
return [self.name(n) for n in self.parents(val)]
|
|
463
|
+
|
|
464
|
+
def children(self, val):
|
|
465
|
+
"""
|
|
466
|
+
Return the list of children ids from the name or the id of a node.
|
|
467
|
+
|
|
468
|
+
Parameters
|
|
469
|
+
----------
|
|
470
|
+
val : NameOrId
|
|
471
|
+
The name or the NodeId of the variable.
|
|
472
|
+
|
|
473
|
+
Returns
|
|
474
|
+
-------
|
|
475
|
+
Set[NodeId]
|
|
476
|
+
The set of children nodes' ids.
|
|
477
|
+
"""
|
|
478
|
+
return self._graph.children(self.nameOrId(val))
|
|
479
|
+
|
|
480
|
+
def children_names(self, val):
|
|
481
|
+
"""
|
|
482
|
+
Return the list of children names from the name or the id of a node.
|
|
483
|
+
|
|
484
|
+
Parameters
|
|
485
|
+
----------
|
|
486
|
+
val : NameOrId
|
|
487
|
+
The name or the NodeId of the variable.
|
|
488
|
+
|
|
489
|
+
Returns
|
|
490
|
+
-------
|
|
491
|
+
List[str]
|
|
492
|
+
The list of val's children's names.
|
|
493
|
+
"""
|
|
494
|
+
return [self.name(n) for n in self.children(val)]
|
|
495
|
+
|
|
496
|
+
def topologicalOrder(self):
|
|
497
|
+
"""
|
|
498
|
+
Return the topological order of the CLG.
|
|
499
|
+
|
|
500
|
+
Returns
|
|
501
|
+
-------
|
|
502
|
+
List[NodeId]
|
|
503
|
+
The list of NodeIds in the topological order.
|
|
504
|
+
"""
|
|
505
|
+
return self._graph.topologicalOrder()
|
|
506
|
+
|
|
507
|
+
def dag2dict(self):
|
|
508
|
+
"""
|
|
509
|
+
Return a dictionary representing the DAG of the CLG.
|
|
510
|
+
|
|
511
|
+
Returns
|
|
512
|
+
-------
|
|
513
|
+
C : Dict[NodeId, Set[NodeId]]
|
|
514
|
+
A directed graph DAG representing the causal structure.
|
|
515
|
+
"""
|
|
516
|
+
V = self.nodes()
|
|
517
|
+
C = {v: set() for v in V} # C is shown by a Adjacency List
|
|
518
|
+
|
|
519
|
+
for node in V:
|
|
520
|
+
for child in self.children(node):
|
|
521
|
+
C[node].add(child)
|
|
522
|
+
|
|
523
|
+
return C
|
|
524
|
+
|
|
525
|
+
def logLikelihood(self, data):
|
|
526
|
+
"""
|
|
527
|
+
Return the log-likelihood of the data.
|
|
528
|
+
|
|
529
|
+
Parameters
|
|
530
|
+
----------
|
|
531
|
+
data : csv file
|
|
532
|
+
The data.
|
|
533
|
+
|
|
534
|
+
Returns
|
|
535
|
+
-------
|
|
536
|
+
float
|
|
537
|
+
The log-likelihood of the data for the CLG.
|
|
538
|
+
"""
|
|
539
|
+
id2samples = {}
|
|
540
|
+
log_likelihood = 0
|
|
541
|
+
|
|
542
|
+
# collect the samples to a dict whose keys are NodeID
|
|
543
|
+
df = pd.read_csv(data)
|
|
544
|
+
for node in self.nodes():
|
|
545
|
+
id2samples[node] = df[self.name(node)].tolist()
|
|
546
|
+
|
|
547
|
+
# for each sample, compute the pdf of each node
|
|
548
|
+
for i in range(len(id2samples[0])):
|
|
549
|
+
joint_pdf = 1
|
|
550
|
+
for node in self.nodes():
|
|
551
|
+
# compute the real mu of node considering its parents
|
|
552
|
+
loc_mu = self._id2var[node].mu()
|
|
553
|
+
for parent in self.parents(node):
|
|
554
|
+
loc_mu += id2samples[parent][i] * self._arc2coef[(parent, node)]
|
|
555
|
+
|
|
556
|
+
# use norm.pdf to compute the pdf of each node
|
|
557
|
+
joint_pdf *= norm(loc_mu, self._id2var[node].sigma()).pdf(id2samples[node][i])
|
|
558
|
+
|
|
559
|
+
log_likelihood += np.log(joint_pdf)
|
|
560
|
+
|
|
561
|
+
return log_likelihood
|
|
562
|
+
|
|
563
|
+
def CompareStructure(self, clg_to_compare):
|
|
564
|
+
"""
|
|
565
|
+
We use the f-score to compare the causal structure of the two CLGs.
|
|
566
|
+
We create two BNs with the same structure as the two CLGs and then compare the two BNs.
|
|
567
|
+
|
|
568
|
+
Parameters
|
|
569
|
+
----------
|
|
570
|
+
clg_to_compare : CLG
|
|
571
|
+
The CLG to compare with.
|
|
572
|
+
|
|
573
|
+
Returns
|
|
574
|
+
-------
|
|
575
|
+
float
|
|
576
|
+
The f-score of the comparison.
|
|
577
|
+
"""
|
|
578
|
+
# Create a BN with the same structure as the CLG
|
|
579
|
+
bn = gum.BayesNet()
|
|
580
|
+
# add variables
|
|
581
|
+
for name in self.names():
|
|
582
|
+
new_variable = gum.LabelizedVariable(name, "a labelized variable", 2)
|
|
583
|
+
bn.add(new_variable)
|
|
584
|
+
# add arcs
|
|
585
|
+
for arc in self.arcs():
|
|
586
|
+
bn.addArc(arc[0], arc[1])
|
|
587
|
+
|
|
588
|
+
# Create a BN with the same structure as the clg_to_compare
|
|
589
|
+
bn_to_compare = gum.BayesNet()
|
|
590
|
+
# add variables
|
|
591
|
+
for name in clg_to_compare.names():
|
|
592
|
+
new_variable = gum.LabelizedVariable(name, "a labelized variable", 2)
|
|
593
|
+
bn_to_compare.add(new_variable)
|
|
594
|
+
# add arcs and edges
|
|
595
|
+
for arc in clg_to_compare.arcs():
|
|
596
|
+
bn_to_compare.addArc(arc[0], arc[1])
|
|
597
|
+
|
|
598
|
+
# We use the f-score to compare graphs of the two created BNs
|
|
599
|
+
cmp = gcm.GraphicalBNComparator(bn, bn_to_compare)
|
|
600
|
+
|
|
601
|
+
return cmp.scores()["fscore"]
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def randomCLG(nb_variables, names, MuMax=5, MuMin=-5, SigmaMax=10, SigmaMin=1, ArcCoefMax=10, ArcCoefMin=5):
|
|
605
|
+
"""
|
|
606
|
+
This function generates a random CLG with nb_variables variables.
|
|
607
|
+
|
|
608
|
+
Parameters
|
|
609
|
+
----------
|
|
610
|
+
nb_variables : int
|
|
611
|
+
The number of variables in the CLG.
|
|
612
|
+
names : str
|
|
613
|
+
The list of names of the variables.
|
|
614
|
+
MuMax : float
|
|
615
|
+
The maximum value of mu.
|
|
616
|
+
MuMin : float
|
|
617
|
+
The minimum value of mu.
|
|
618
|
+
SigmaMax : float
|
|
619
|
+
The maximum value of sigma.
|
|
620
|
+
SigmaMin : float
|
|
621
|
+
The minimum value of sigma.
|
|
622
|
+
ArcCoefMax : float
|
|
623
|
+
The maximum value of the coefficient of the arc.
|
|
624
|
+
ArcCoefMin : float
|
|
625
|
+
The minimum value of the coefficient of the arc.
|
|
626
|
+
|
|
627
|
+
Returns
|
|
628
|
+
-------
|
|
629
|
+
CLG
|
|
630
|
+
The random CLG.
|
|
631
|
+
"""
|
|
632
|
+
# Create a random BN with nb_variables variables
|
|
633
|
+
bn = gum.randomBN(names=names, n=nb_variables)
|
|
634
|
+
|
|
635
|
+
# Order names by their NodeIds
|
|
636
|
+
ordered_names = [""] * nb_variables
|
|
637
|
+
for name in bn.names():
|
|
638
|
+
ordered_names[bn.idFromName(name)] = name
|
|
639
|
+
|
|
640
|
+
# Create a random CLG with nb_variables variables(The CLG is created with the same structure as the BN)
|
|
641
|
+
clg = CLG()
|
|
642
|
+
# Add variables
|
|
643
|
+
for name in ordered_names:
|
|
644
|
+
new_variable = GaussianVariable(
|
|
645
|
+
name=name, mu=random.uniform(MuMin, MuMax), sigma=random.uniform(SigmaMin, SigmaMax)
|
|
646
|
+
)
|
|
647
|
+
clg.add(new_variable)
|
|
648
|
+
# Add arcs
|
|
649
|
+
for arc in bn.arcs():
|
|
650
|
+
clg.addArc(
|
|
651
|
+
val1=arc[0],
|
|
652
|
+
val2=arc[1],
|
|
653
|
+
coef=random.uniform(-1 * ArcCoefMax, -1 * ArcCoefMin)
|
|
654
|
+
if random.random() < 0.5
|
|
655
|
+
else random.uniform(ArcCoefMin, ArcCoefMax),
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
return clg
|