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,464 @@
|
|
|
1
|
+
from typing import Dict, List, Set
|
|
2
|
+
from tempfile import TemporaryDirectory
|
|
3
|
+
from zipfile import ZipFile
|
|
4
|
+
|
|
5
|
+
############################################################################
|
|
6
|
+
# This file is part of the aGrUM/pyAgrum library. #
|
|
7
|
+
# #
|
|
8
|
+
# Copyright (c) 2005-2025 by #
|
|
9
|
+
# - Pierre-Henri WUILLEMIN(_at_LIP6) #
|
|
10
|
+
# - Christophe GONZALES(_at_AMU) #
|
|
11
|
+
# #
|
|
12
|
+
# The aGrUM/pyAgrum library is free software; you can redistribute it #
|
|
13
|
+
# and/or modify it under the terms of either : #
|
|
14
|
+
# #
|
|
15
|
+
# - the GNU Lesser General Public License as published by #
|
|
16
|
+
# the Free Software Foundation, either version 3 of the License, #
|
|
17
|
+
# or (at your option) any later version, #
|
|
18
|
+
# - the MIT license (MIT), #
|
|
19
|
+
# - or both in dual license, as here. #
|
|
20
|
+
# #
|
|
21
|
+
# (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) #
|
|
22
|
+
# #
|
|
23
|
+
# This aGrUM/pyAgrum library is distributed in the hope that it will be #
|
|
24
|
+
# useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, #
|
|
25
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS #
|
|
26
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
|
|
27
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
|
|
28
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, #
|
|
29
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #
|
|
30
|
+
# OTHER DEALINGS IN THE SOFTWARE. #
|
|
31
|
+
# #
|
|
32
|
+
# See LICENCES for more details. #
|
|
33
|
+
# #
|
|
34
|
+
# SPDX-FileCopyrightText: Copyright 2005-2025 #
|
|
35
|
+
# - Pierre-Henri WUILLEMIN(_at_LIP6) #
|
|
36
|
+
# - Christophe GONZALES(_at_AMU) #
|
|
37
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later OR MIT #
|
|
38
|
+
# #
|
|
39
|
+
# Contact : info_at_agrum_dot_org #
|
|
40
|
+
# homepage : http://agrum.gitlab.io #
|
|
41
|
+
# gitlab : https://gitlab.com/agrumery/agrum #
|
|
42
|
+
# #
|
|
43
|
+
############################################################################
|
|
44
|
+
|
|
45
|
+
import os
|
|
46
|
+
import pyagrum
|
|
47
|
+
import shutil
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class IMixture:
|
|
51
|
+
_bns: Dict[str, pyagrum.BayesNet]
|
|
52
|
+
_weights: Dict[str, float]
|
|
53
|
+
_refBN: pyagrum.BayesNet
|
|
54
|
+
_refName: str
|
|
55
|
+
|
|
56
|
+
def __init__(self):
|
|
57
|
+
raise NotImplementedError("class IMixture is not supposed to be initialized !")
|
|
58
|
+
|
|
59
|
+
def __repr__(self):
|
|
60
|
+
nameref = self._refBN.property("name")
|
|
61
|
+
ret = f"(reference BN : {nameref}), "
|
|
62
|
+
for name in self.names():
|
|
63
|
+
ret += "(" + name + ", w=" + str(self._weights[name]) + "), "
|
|
64
|
+
if len(ret) > 1:
|
|
65
|
+
ret = ret[:-2]
|
|
66
|
+
return ret
|
|
67
|
+
|
|
68
|
+
def names(self) -> List[str]:
|
|
69
|
+
"""
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
List[str]
|
|
73
|
+
The list of names of the BNs in the model (reference BN not included).
|
|
74
|
+
"""
|
|
75
|
+
return list(self._bns.keys()).copy()
|
|
76
|
+
|
|
77
|
+
def size(self) -> int:
|
|
78
|
+
"""
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
int
|
|
82
|
+
The number of BNs in the model (reference BN not included).
|
|
83
|
+
"""
|
|
84
|
+
return len(self._bns)
|
|
85
|
+
|
|
86
|
+
def setWeight(self, name: str, w: float):
|
|
87
|
+
"""
|
|
88
|
+
Changes the weight of a BN in the model.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
name : str
|
|
93
|
+
Name of the BN to modify.
|
|
94
|
+
w : float
|
|
95
|
+
Value of the new weight.
|
|
96
|
+
|
|
97
|
+
Raises
|
|
98
|
+
------
|
|
99
|
+
pyagrum.InvalidArgument
|
|
100
|
+
If the weight is negative.
|
|
101
|
+
pyagrum.NotFound
|
|
102
|
+
If the given name doesn't correspond to the name of a BN in the model.
|
|
103
|
+
"""
|
|
104
|
+
if w < 0:
|
|
105
|
+
raise pyagrum.InvalidArgument("weight is negative !")
|
|
106
|
+
if name not in self._bns:
|
|
107
|
+
raise pyagrum.NotFound(f"'{name}' isn't in the model !")
|
|
108
|
+
self._weights[name] = w
|
|
109
|
+
|
|
110
|
+
def weight(self, name: str) -> float:
|
|
111
|
+
"""
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
name : str
|
|
115
|
+
Name of the BN.
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
float
|
|
120
|
+
The weight of the BN with name ``name``.
|
|
121
|
+
|
|
122
|
+
Raises
|
|
123
|
+
------
|
|
124
|
+
pyagrum.NotFound
|
|
125
|
+
If the given name doesn't correspond to the name of a BN in the model.
|
|
126
|
+
"""
|
|
127
|
+
if name not in self._bns:
|
|
128
|
+
raise pyagrum.NotFound(f"{name} isn't in the model")
|
|
129
|
+
return self._weights[name]
|
|
130
|
+
|
|
131
|
+
def weights(self) -> Dict[str, float]:
|
|
132
|
+
"""
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
Dict[str, float]
|
|
136
|
+
The weights of all the BNs in the model.
|
|
137
|
+
"""
|
|
138
|
+
return self._weights.copy()
|
|
139
|
+
|
|
140
|
+
def add(self, name: str, bn: pyagrum.BayesNet, w: float = 1):
|
|
141
|
+
"""
|
|
142
|
+
Adds a BN to the model. If the model doesn't have a reference BN when trying to add an element,
|
|
143
|
+
the BN (before adding new element) with highest weight becomes the new reference.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
name : str
|
|
148
|
+
Name of the BN to add.
|
|
149
|
+
bn : pyagrum.BayesNet
|
|
150
|
+
BN to add.
|
|
151
|
+
w : float
|
|
152
|
+
Weight of the BN.
|
|
153
|
+
|
|
154
|
+
Raises
|
|
155
|
+
------
|
|
156
|
+
pyagrum.InvalidArgument
|
|
157
|
+
If the weight is negative.
|
|
158
|
+
pyagrum.InvalidArgument
|
|
159
|
+
If the names of the variables in the BN to add are differents from the one in the reference BN.
|
|
160
|
+
pyagrum.InvalidArgument
|
|
161
|
+
If the variables in the BN to add are differents from the one in the reference BN.
|
|
162
|
+
pyagrum.InvalidArgument
|
|
163
|
+
If the name in argument is the same as the reference BN's name.
|
|
164
|
+
pyagrum.ArgumentError
|
|
165
|
+
If the name in argument already exists for a different BN in the model.
|
|
166
|
+
"""
|
|
167
|
+
if w < 0:
|
|
168
|
+
raise pyagrum.InvalidArgument("weight is negative !")
|
|
169
|
+
if name == self._refName:
|
|
170
|
+
raise pyagrum.InvalidArgument("Can't give name used by reference BN")
|
|
171
|
+
|
|
172
|
+
if self._refBN is None:
|
|
173
|
+
self._refBN = pyagrum.BayesNet(bn)
|
|
174
|
+
self._refBN.setProperty("name", "refBN")
|
|
175
|
+
# self._refTrueName = name
|
|
176
|
+
self._bns[name] = pyagrum.BayesNet(bn)
|
|
177
|
+
self._bns[name].setProperty("name", name)
|
|
178
|
+
self._weights[name] = w
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
if bn.names() != self._refBN.names():
|
|
182
|
+
raise pyagrum.InvalidArgument("variables names are different from the reference BN variables")
|
|
183
|
+
if set([bn.variable(name) for name in bn.names()]) != set(
|
|
184
|
+
[self._refBN.variable(name) for name in self._refBN.names()]
|
|
185
|
+
):
|
|
186
|
+
raise pyagrum.InvalidArgument("variables are different from those in the reference BN")
|
|
187
|
+
|
|
188
|
+
if name in self._bns and bn == self._bns[name]:
|
|
189
|
+
self._weights[name] += w
|
|
190
|
+
elif name not in self._bns and bn in self._bns.values():
|
|
191
|
+
# logging.warning("bn already exists with a different name")
|
|
192
|
+
for tmp in self._bns:
|
|
193
|
+
if bn == self._bns[tmp]:
|
|
194
|
+
self._weights[tmp] += w
|
|
195
|
+
break
|
|
196
|
+
elif name not in self._bns and bn not in self._bns.values():
|
|
197
|
+
self._bns[name] = pyagrum.BayesNet(bn)
|
|
198
|
+
self._bns[name].setProperty("name", name)
|
|
199
|
+
self._weights[name] = w
|
|
200
|
+
else:
|
|
201
|
+
raise pyagrum.ArgumentError("name already exists for different bn")
|
|
202
|
+
|
|
203
|
+
def remove(self, name: str):
|
|
204
|
+
"""
|
|
205
|
+
Removes a BN from the model.
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
name : str
|
|
210
|
+
Name of the BN to remove.
|
|
211
|
+
|
|
212
|
+
Raises
|
|
213
|
+
------
|
|
214
|
+
pyagrum.NotFound
|
|
215
|
+
If the given name doesn't correspond to the name of a BN in the model.
|
|
216
|
+
"""
|
|
217
|
+
if name not in self._bns:
|
|
218
|
+
raise pyagrum.NotFound(f"'{name}' isn't in the model !")
|
|
219
|
+
self._bns.pop(name)
|
|
220
|
+
self._weights.pop(name)
|
|
221
|
+
|
|
222
|
+
def isNormalized(self) -> bool:
|
|
223
|
+
"""
|
|
224
|
+
Checks if the model is normalized (the sum of the weights equals 1).
|
|
225
|
+
"""
|
|
226
|
+
return round(sum(self._weights.values()), 14) == 1
|
|
227
|
+
|
|
228
|
+
def normalize(self):
|
|
229
|
+
"""
|
|
230
|
+
Normalizes the weights.
|
|
231
|
+
"""
|
|
232
|
+
total = sum(self._weights.values())
|
|
233
|
+
for name in self._bns.keys():
|
|
234
|
+
self.setWeight(name, self.weight(name) / total)
|
|
235
|
+
|
|
236
|
+
def isValid(self) -> bool:
|
|
237
|
+
"""
|
|
238
|
+
Checks if all the weights are equal to 0. Valid if sum of the weights is not 0.
|
|
239
|
+
|
|
240
|
+
Returns
|
|
241
|
+
-------
|
|
242
|
+
bool
|
|
243
|
+
True if weights are valid. False otherwise.
|
|
244
|
+
"""
|
|
245
|
+
return sum(self._weights.values()) != 0
|
|
246
|
+
|
|
247
|
+
def BN(self, name: str) -> "pyagrum.BayesNet":
|
|
248
|
+
"""
|
|
249
|
+
Parameters
|
|
250
|
+
----------
|
|
251
|
+
name : str
|
|
252
|
+
Name of the variable.
|
|
253
|
+
|
|
254
|
+
Returns
|
|
255
|
+
-------
|
|
256
|
+
pyagrum.BayesNet
|
|
257
|
+
A copy of the BN with name ``name`` in the model.
|
|
258
|
+
|
|
259
|
+
Raises
|
|
260
|
+
------
|
|
261
|
+
pyagrum.NotFound
|
|
262
|
+
If the given name doesn't correspond to the name of a BN in the model.
|
|
263
|
+
"""
|
|
264
|
+
if name not in self._bns:
|
|
265
|
+
raise pyagrum.NotFound(f"{name} isn't in the mixture")
|
|
266
|
+
res = pyagrum.BayesNet(self._bns[name])
|
|
267
|
+
res.setProperty("name", name)
|
|
268
|
+
return res
|
|
269
|
+
|
|
270
|
+
def BNs(self) -> List[pyagrum.BayesNet]:
|
|
271
|
+
"""
|
|
272
|
+
Returns
|
|
273
|
+
-------
|
|
274
|
+
List[pyagrum.BayesNet]
|
|
275
|
+
A list containing a copy of all BNs in the model.
|
|
276
|
+
"""
|
|
277
|
+
return [self.BN(name) for name in self.names()]
|
|
278
|
+
|
|
279
|
+
def zeroBNs(self) -> Set[str]:
|
|
280
|
+
"""
|
|
281
|
+
Returns
|
|
282
|
+
-------
|
|
283
|
+
Set[str]
|
|
284
|
+
The names of the BNs in the model that have weight with value 0.
|
|
285
|
+
"""
|
|
286
|
+
return {name for name in self.names() if self.weight(name) == 0}
|
|
287
|
+
|
|
288
|
+
def existsArc(self, a, b):
|
|
289
|
+
"""
|
|
290
|
+
Counts the number of time arc ``a`` -> ``b`` appears among all BNs in the model.
|
|
291
|
+
|
|
292
|
+
Parameters
|
|
293
|
+
----------
|
|
294
|
+
a : str | int
|
|
295
|
+
Tail of the arc.
|
|
296
|
+
b : str | int
|
|
297
|
+
Head of the arc.
|
|
298
|
+
|
|
299
|
+
Returns
|
|
300
|
+
-------
|
|
301
|
+
int
|
|
302
|
+
The number of time arc ``a`` -> ``b`` appears.
|
|
303
|
+
"""
|
|
304
|
+
count = 0
|
|
305
|
+
for bn in self.BNs():
|
|
306
|
+
if bn.existsArc(a, b):
|
|
307
|
+
count += 1
|
|
308
|
+
return count
|
|
309
|
+
|
|
310
|
+
def variable(self, name: str):
|
|
311
|
+
"""
|
|
312
|
+
Parameters
|
|
313
|
+
----------
|
|
314
|
+
name : str
|
|
315
|
+
Name of the variable.
|
|
316
|
+
|
|
317
|
+
Returns
|
|
318
|
+
-------
|
|
319
|
+
pyagrum.LabelizedVariable
|
|
320
|
+
The corresponding variable.
|
|
321
|
+
"""
|
|
322
|
+
if self._refBN is None:
|
|
323
|
+
raise pyagrum.NotFound("Reference BN is None")
|
|
324
|
+
return self._refBN.variable(name)
|
|
325
|
+
|
|
326
|
+
def saveBIF(self, fname: str):
|
|
327
|
+
"""
|
|
328
|
+
Saves a Mixture in BIF format and zip it.
|
|
329
|
+
|
|
330
|
+
Parameters
|
|
331
|
+
----------
|
|
332
|
+
fname : str
|
|
333
|
+
Name of the file (without extenstion).
|
|
334
|
+
"""
|
|
335
|
+
with TemporaryDirectory("", fname, None) as temp_dir:
|
|
336
|
+
txt_name = f"{temp_dir}/weights.txt"
|
|
337
|
+
with open(txt_name, "w+") as f_txt:
|
|
338
|
+
for e in self.weights():
|
|
339
|
+
f_txt.write(f"{e}:{self.weight(e)}\n")
|
|
340
|
+
ref_name = "ref_" + self._refBN.property("name")
|
|
341
|
+
self._refBN.saveBIF(f"{temp_dir}/{ref_name}.bif")
|
|
342
|
+
for name in self.names():
|
|
343
|
+
file_name = f"{temp_dir}/{name}.bif"
|
|
344
|
+
self.BN(name).saveBIF(file_name)
|
|
345
|
+
shutil.make_archive(fname, "zip", temp_dir)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
class BNMixture(IMixture):
|
|
349
|
+
"""
|
|
350
|
+
Experimental model. The class contains a list of BNs. Each BN has its own weight.
|
|
351
|
+
"""
|
|
352
|
+
|
|
353
|
+
def __init__(self):
|
|
354
|
+
self._bns = {}
|
|
355
|
+
self._weights = {}
|
|
356
|
+
self._refBN = None
|
|
357
|
+
self._refName = "refBN"
|
|
358
|
+
|
|
359
|
+
def updateRef(self):
|
|
360
|
+
"""
|
|
361
|
+
Updates the reference BN. The new reference BN is the one with maximum weight.
|
|
362
|
+
"""
|
|
363
|
+
highest = max(list(self._weights.keys()), key=lambda x: self._weights[x])
|
|
364
|
+
newRef = pyagrum.BayesNet(self._bns[highest])
|
|
365
|
+
newRef.setProperty("name", self._refName)
|
|
366
|
+
# self._refTrueName = highest
|
|
367
|
+
self._refBN = newRef
|
|
368
|
+
|
|
369
|
+
@staticmethod
|
|
370
|
+
def loadBIF(filename):
|
|
371
|
+
"""
|
|
372
|
+
Retrieve a BNMixture from a file.
|
|
373
|
+
|
|
374
|
+
Parameters
|
|
375
|
+
----------
|
|
376
|
+
filename : str
|
|
377
|
+
Zip file containing the mixture.
|
|
378
|
+
|
|
379
|
+
Returns
|
|
380
|
+
-------
|
|
381
|
+
BNMixture
|
|
382
|
+
The stored BNMixture
|
|
383
|
+
"""
|
|
384
|
+
with TemporaryDirectory("", "tmp", None) as temp_dir:
|
|
385
|
+
# temp_dir = mkdtemp("", "tmp", None)
|
|
386
|
+
with ZipFile(filename, "r") as zf:
|
|
387
|
+
zf.extractall(temp_dir)
|
|
388
|
+
weights = {}
|
|
389
|
+
with open(f"{temp_dir}/weights.txt", "r") as wf:
|
|
390
|
+
for line in wf:
|
|
391
|
+
name, w = line.split(":")
|
|
392
|
+
weights[name] = float(w)
|
|
393
|
+
bnm = BNMixture()
|
|
394
|
+
for path in os.listdir(temp_dir):
|
|
395
|
+
name, _ = path.split(".")
|
|
396
|
+
if name in weights:
|
|
397
|
+
bni = pyagrum.BayesNet()
|
|
398
|
+
bni.loadBIF(f"{temp_dir}/{path}")
|
|
399
|
+
bnm.add(name, bni, w=weights[name])
|
|
400
|
+
bnm.updateRef()
|
|
401
|
+
return bnm
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
class BootstrapMixture(IMixture):
|
|
405
|
+
"""
|
|
406
|
+
Experimental model base on bootstraping. The class contains reference BN and a list of BNs. Each BN has its own weight except for the reference BN.
|
|
407
|
+
The reference BN is used so that every other BN added later contains the same variables as the reference BN.
|
|
408
|
+
|
|
409
|
+
Parameters
|
|
410
|
+
----------
|
|
411
|
+
name : str
|
|
412
|
+
Name of the first BN to add to the model. It is used for reference.
|
|
413
|
+
bn : pyagrum.BayesNet
|
|
414
|
+
BN to add. Adding new BNs to the model is allowed only if the variables are the same in the first and new BN.
|
|
415
|
+
"""
|
|
416
|
+
|
|
417
|
+
def __init__(self, name: str, bn: pyagrum.BayesNet):
|
|
418
|
+
self._refBN = pyagrum.BayesNet(bn)
|
|
419
|
+
self._refBN.setProperty("name", name)
|
|
420
|
+
self._bns = {}
|
|
421
|
+
self._weights = {}
|
|
422
|
+
self._refName = name
|
|
423
|
+
|
|
424
|
+
@staticmethod
|
|
425
|
+
def loadBIF(filename):
|
|
426
|
+
"""
|
|
427
|
+
Retrieve a BootstrapMixture from a file.
|
|
428
|
+
|
|
429
|
+
Parameters
|
|
430
|
+
----------
|
|
431
|
+
filename : str
|
|
432
|
+
Zip file containing the mixture.
|
|
433
|
+
|
|
434
|
+
Returns
|
|
435
|
+
-------
|
|
436
|
+
BootstrapMixture
|
|
437
|
+
The stored BootstrapMixture.
|
|
438
|
+
"""
|
|
439
|
+
with TemporaryDirectory("", "tmp", None) as temp_dir:
|
|
440
|
+
with ZipFile(filename, "r") as zf:
|
|
441
|
+
zf.extractall(temp_dir)
|
|
442
|
+
weights = {}
|
|
443
|
+
with open(f"{temp_dir}/weights.txt", "r") as wf:
|
|
444
|
+
for line in wf:
|
|
445
|
+
name, w = line.split(":")
|
|
446
|
+
weights[name] = float(w)
|
|
447
|
+
bn_dict = {}
|
|
448
|
+
refBN = None
|
|
449
|
+
for path in os.listdir(temp_dir):
|
|
450
|
+
name, form = path.split(".")
|
|
451
|
+
if form != "bif":
|
|
452
|
+
continue
|
|
453
|
+
bni = pyagrum.BayesNet()
|
|
454
|
+
bni.loadBIF(f"{temp_dir}/{path}")
|
|
455
|
+
if name in weights:
|
|
456
|
+
bn_dict[name] = bni
|
|
457
|
+
bn_dict[name].setProperty("name", name)
|
|
458
|
+
else:
|
|
459
|
+
refBN = bni
|
|
460
|
+
refBN.setProperty("name", name)
|
|
461
|
+
bnm = BootstrapMixture(refBN.property("name"), refBN)
|
|
462
|
+
for name in bn_dict:
|
|
463
|
+
bnm.add(name, bn_dict[name], weights[name])
|
|
464
|
+
return bnm
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
|
|
43
|
+
__author__ = "Pierre-Henri Wuillemin, Rodolphe Tavernier"
|
|
44
|
+
__copyright__ = "(c) 2022-2023 PARIS"
|
|
45
|
+
|
|
46
|
+
from .BNMixture import BNMixture, BootstrapMixture, IMixture
|
|
47
|
+
from .BNMInference import BNMixtureInference, BootstrapMixtureInference, IMixtureInference
|
|
48
|
+
from .BNMLearning import BNMLearner, BNMBootstrapLearner, IMixtureLearner
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"BNMixture",
|
|
52
|
+
"BootstrapMixture",
|
|
53
|
+
"IMixture",
|
|
54
|
+
"BNMixtureInference",
|
|
55
|
+
"BootstrapMixtureInference",
|
|
56
|
+
"IMixtureInference",
|
|
57
|
+
"BNMLearner",
|
|
58
|
+
"BNMBootstrapLearner",
|
|
59
|
+
"IMixtureLearner",
|
|
60
|
+
]
|