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,552 @@
|
|
|
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
|
+
import math
|
|
42
|
+
import numpy as np
|
|
43
|
+
|
|
44
|
+
import matplotlib.pyplot as plt
|
|
45
|
+
from matplotlib.ticker import AutoMinorLocator
|
|
46
|
+
|
|
47
|
+
import pyagrum as gum
|
|
48
|
+
|
|
49
|
+
LEFT_ARROW = "\u2190"
|
|
50
|
+
RIGHT_ARROW = "\u2192"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _stats(pot):
|
|
54
|
+
mu = 0.0
|
|
55
|
+
mu2 = 0.0
|
|
56
|
+
v = pot.variable(0)
|
|
57
|
+
for i, p in enumerate(pot.tolist()):
|
|
58
|
+
x = v.numerical(i)
|
|
59
|
+
mu += p * x
|
|
60
|
+
mu2 += p * x * x
|
|
61
|
+
return mu, math.sqrt(mu2 - mu * mu)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _getTitleHisto(p, show_mu_sigma=True):
|
|
65
|
+
var = p.variable(0)
|
|
66
|
+
|
|
67
|
+
if var.varType() == 1 or not show_mu_sigma: # type=1 is for gum.LabelizedVariable
|
|
68
|
+
return var.name()
|
|
69
|
+
|
|
70
|
+
(mu, std) = _stats(p)
|
|
71
|
+
if std == 0.0:
|
|
72
|
+
return var.name()
|
|
73
|
+
else:
|
|
74
|
+
return f"{var.name()}\n$\\mu={mu:.2f}$; $\\sigma={std:.2f}$"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def __limits(p):
|
|
78
|
+
"""return vals and labs to show in the histograme
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
p : gum.Tensor
|
|
83
|
+
the marginal to analyze
|
|
84
|
+
"""
|
|
85
|
+
var = p.variable(0)
|
|
86
|
+
la = [var.label(int(i)) for i in np.arange(var.domainSize())]
|
|
87
|
+
v = p.tolist()
|
|
88
|
+
nzmin = None
|
|
89
|
+
nzmax = None
|
|
90
|
+
l = len(v) - 1
|
|
91
|
+
for i in range(l + 1):
|
|
92
|
+
if v[i] != 0 and nzmin is None:
|
|
93
|
+
if i > 0:
|
|
94
|
+
nzmin = i - 1
|
|
95
|
+
else:
|
|
96
|
+
nzmin = -1
|
|
97
|
+
if v[l - i] != 0:
|
|
98
|
+
if nzmax is None:
|
|
99
|
+
if i > 0:
|
|
100
|
+
nzmax = l - i + 1
|
|
101
|
+
else:
|
|
102
|
+
nzmax = -1
|
|
103
|
+
|
|
104
|
+
mi = 0 if nzmin in [-1, None] else nzmin
|
|
105
|
+
ma = l if nzmax in [-1, None] else nzmax
|
|
106
|
+
if mi > 0:
|
|
107
|
+
mi -= 1
|
|
108
|
+
if ma < l:
|
|
109
|
+
ma += 1
|
|
110
|
+
|
|
111
|
+
res = range(mi, ma + 1)
|
|
112
|
+
lres = la[mi : ma + 1]
|
|
113
|
+
# if nzmin not in [-1, 0, None]:
|
|
114
|
+
if mi > 1:
|
|
115
|
+
lres[0] = LEFT_ARROW + var.label(mi)
|
|
116
|
+
# if nzmax not in [-1, var.domainSize()-1, None]:
|
|
117
|
+
if ma < var.domainSize() - 2:
|
|
118
|
+
lres[-1] = var.label(ma + 1) + RIGHT_ARROW
|
|
119
|
+
|
|
120
|
+
return res, [v[i] for i in res], lres
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _getProbaLine(p, scale=1.0, txtcolor="black"):
|
|
124
|
+
"""
|
|
125
|
+
compute the representation of a matplotlib.fill_between for a mono-dim Tensor
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
p : pyagrum.Tensor
|
|
130
|
+
the mono-dimensional Tensor
|
|
131
|
+
scale : float
|
|
132
|
+
the scale
|
|
133
|
+
txtcolor : str
|
|
134
|
+
color for text
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
matplotlib.Figure
|
|
139
|
+
a matplotlib figure for a Tensor p.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
var = p.variable(0)
|
|
143
|
+
if gum.config["notebook", "histogram_mode"] == "compact":
|
|
144
|
+
ra, v, lv = __limits(p)
|
|
145
|
+
else:
|
|
146
|
+
lv = [var.label(int(i)) for i in np.arange(var.domainSize())]
|
|
147
|
+
v = p.tolist()
|
|
148
|
+
ra = range(var.domainSize())
|
|
149
|
+
|
|
150
|
+
if var.domainSize() > 15:
|
|
151
|
+
nbr_step = 15
|
|
152
|
+
max_step = var.domainSize() - 1
|
|
153
|
+
while True:
|
|
154
|
+
if nbr_step == 2 or max_step % nbr_step == 0:
|
|
155
|
+
step = max_step // nbr_step
|
|
156
|
+
break
|
|
157
|
+
nbr_step -= 1
|
|
158
|
+
else:
|
|
159
|
+
step = 1
|
|
160
|
+
|
|
161
|
+
fig = plt.figure()
|
|
162
|
+
fig.set_figwidth(min(scale * 6, scale * len(v) / 4.0))
|
|
163
|
+
fig.set_figheight(scale * 2)
|
|
164
|
+
|
|
165
|
+
ax = fig.add_subplot(111)
|
|
166
|
+
ax.set_xticks(ra[::step])
|
|
167
|
+
ax.set_xticklabels(lv[::step], rotation="vertical")
|
|
168
|
+
ax.fill_between(ra, v, color=gum.config["notebook", "histogram_color"])
|
|
169
|
+
|
|
170
|
+
ax.set_ylim(bottom=0, top=1.05 * p.max())
|
|
171
|
+
ax.set_title(_getTitleHisto(p, True), color=txtcolor)
|
|
172
|
+
|
|
173
|
+
ax.get_xaxis().grid(True)
|
|
174
|
+
ax.get_yaxis().grid(True)
|
|
175
|
+
ax.margins(0)
|
|
176
|
+
|
|
177
|
+
ax.set_facecolor("w")
|
|
178
|
+
return fig
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _getProbaV(p, scale=1.0, util=None, txtcolor="black"):
|
|
182
|
+
"""
|
|
183
|
+
compute the representation of a vertical histogram for a mono-dim Tensor
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
p : pyagrum.Tensor
|
|
188
|
+
the mono-dimensional Tensor
|
|
189
|
+
util : pyagrum.Tensor
|
|
190
|
+
an (optional) secondary Tensor (values in labels)
|
|
191
|
+
txtcolor : str
|
|
192
|
+
color for text
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
matplotlib.Figure
|
|
197
|
+
a matplotlib histogram for a Tensor p.
|
|
198
|
+
|
|
199
|
+
"""
|
|
200
|
+
if gum.config["notebook", "histogram_mode"] == "compact":
|
|
201
|
+
ra, v, lv = __limits(p)
|
|
202
|
+
else:
|
|
203
|
+
var = p.variable(0)
|
|
204
|
+
if util is not None:
|
|
205
|
+
lu = util.toarray()
|
|
206
|
+
coef = -1 if gum.config.asBool["influenceDiagram", "utility_show_loss"] else 1
|
|
207
|
+
fmt = f".{gum.config.asInt['influenceDiagram', 'utility_visible_digits']}f"
|
|
208
|
+
lv = [f"{var.label(int(i))} [{coef * lu[i]:{fmt}}]" for i in np.arange(var.domainSize())]
|
|
209
|
+
else:
|
|
210
|
+
lv = [var.label(int(i)) for i in np.arange(var.domainSize())]
|
|
211
|
+
v = p.tolist()
|
|
212
|
+
ra = range(len(v))
|
|
213
|
+
|
|
214
|
+
fig = plt.figure()
|
|
215
|
+
fig.set_figwidth(scale * len(v) / 4.0)
|
|
216
|
+
fig.set_figheight(scale * 2)
|
|
217
|
+
|
|
218
|
+
ax = fig.add_subplot(111)
|
|
219
|
+
|
|
220
|
+
bars = ax.bar(ra, v, align="center", color=gum.config["notebook", "histogram_color"])
|
|
221
|
+
ma = p.max()
|
|
222
|
+
|
|
223
|
+
if gum.config.asBool["notebook", "histogram_use_percent"]:
|
|
224
|
+
perc = 100
|
|
225
|
+
suffix = "%"
|
|
226
|
+
else:
|
|
227
|
+
perc = 1
|
|
228
|
+
suffix = ""
|
|
229
|
+
for b in bars:
|
|
230
|
+
if b.get_height() != 0:
|
|
231
|
+
txt = f"{b.get_height() * perc:.{gum.config.asInt['notebook', 'histogram_vertical_visible_digits']}f}{suffix}"
|
|
232
|
+
ax.text(b.get_x() + 0.5, ma, txt, ha="center", va="top", rotation="vertical", alpha=0.7)
|
|
233
|
+
|
|
234
|
+
ax.set_ylim(bottom=0, top=p.max())
|
|
235
|
+
ax.set_yticklabels([])
|
|
236
|
+
ax.set_xticks(ra)
|
|
237
|
+
ax.set_xticklabels(lv, rotation="vertical", color=txtcolor)
|
|
238
|
+
# even if utility, now we do show the mean/sigma of the distribution.
|
|
239
|
+
ax.set_title(_getTitleHisto(p, True), color=txtcolor)
|
|
240
|
+
ax.get_yaxis().grid(True)
|
|
241
|
+
ax.margins(0)
|
|
242
|
+
ax.set_facecolor("w")
|
|
243
|
+
|
|
244
|
+
return fig
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _getProbaH(p, scale=1.0, util=None, txtcolor="black"):
|
|
248
|
+
"""
|
|
249
|
+
compute the representation of a horizontal histogram for a mono-dim Tensor
|
|
250
|
+
|
|
251
|
+
Parameters
|
|
252
|
+
----------
|
|
253
|
+
p : pyagrum.Tensor
|
|
254
|
+
the mono-dimensional Tensor
|
|
255
|
+
scale : scale for the size of the graph
|
|
256
|
+
util : pyagrum.Tensor
|
|
257
|
+
an (optional) secondary Tensor (values in labels)
|
|
258
|
+
txtcolor : str
|
|
259
|
+
color for text
|
|
260
|
+
|
|
261
|
+
Returns
|
|
262
|
+
-------
|
|
263
|
+
matplotlib.Figure
|
|
264
|
+
a matplotlib histogram for a Tensor p.
|
|
265
|
+
"""
|
|
266
|
+
var = p.variable(0)
|
|
267
|
+
ra = np.arange(var.domainSize())
|
|
268
|
+
|
|
269
|
+
ra_reverse = np.arange(var.domainSize() - 1, -1, -1) # reverse order
|
|
270
|
+
|
|
271
|
+
if util is not None:
|
|
272
|
+
lu = util.toarray()
|
|
273
|
+
fmt = f".{gum.config.asInt['influenceDiagram', 'utility_visible_digits']}f"
|
|
274
|
+
|
|
275
|
+
if gum.config.asBool["influenceDiagram", "utility_show_loss"]:
|
|
276
|
+
vx = [f"{var.label(int(i))} [{-lu[i] if lu[i] != 0 else 0:{fmt}}]" for i in ra_reverse]
|
|
277
|
+
else:
|
|
278
|
+
vx = [f"{var.label(int(i))} [{lu[i]:{fmt}}]" for i in ra_reverse]
|
|
279
|
+
else:
|
|
280
|
+
vx = [var.label(int(i)) for i in ra_reverse]
|
|
281
|
+
|
|
282
|
+
fig = plt.figure()
|
|
283
|
+
fig.set_figheight(scale * var.domainSize() / 4.0)
|
|
284
|
+
fig.set_figwidth(scale * 2)
|
|
285
|
+
|
|
286
|
+
ax = fig.add_subplot(111)
|
|
287
|
+
ax.set_facecolor("white")
|
|
288
|
+
|
|
289
|
+
vals = p.tolist()
|
|
290
|
+
vals.reverse()
|
|
291
|
+
bars = ax.barh(ra, vals, align="center", color=gum.config["notebook", "histogram_color"])
|
|
292
|
+
|
|
293
|
+
if gum.config.asBool["notebook", "histogram_use_percent"]:
|
|
294
|
+
perc = 100
|
|
295
|
+
suffix = "%"
|
|
296
|
+
else:
|
|
297
|
+
perc = 1
|
|
298
|
+
suffix = ""
|
|
299
|
+
for b in bars:
|
|
300
|
+
if b.get_width() != 0:
|
|
301
|
+
txt = f"{b.get_width() * perc:.{gum.config.asInt['notebook', 'histogram_horizontal_visible_digits']}f}{suffix}"
|
|
302
|
+
ax.text(1, b.get_y(), txt, ha="right", va="bottom", alpha=0.7)
|
|
303
|
+
|
|
304
|
+
ax.set_xlim(0, 1)
|
|
305
|
+
ax.set_yticks(np.arange(var.domainSize()))
|
|
306
|
+
ax.set_yticklabels(vx, color=txtcolor)
|
|
307
|
+
ax.set_xticklabels([])
|
|
308
|
+
# ax.set_xlabel('Probability')
|
|
309
|
+
# Even if utility, now we do show the mean/sigma of the distribution.
|
|
310
|
+
ax.set_title(_getTitleHisto(p, True), color=txtcolor)
|
|
311
|
+
ax.get_xaxis().grid(True)
|
|
312
|
+
ax.margins(0)
|
|
313
|
+
|
|
314
|
+
return fig
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _getHistoForDiscretized(p, scale=1, txtcolor="Black"):
|
|
318
|
+
var = p.variable(0)
|
|
319
|
+
vx = var.ticks()
|
|
320
|
+
widths = [vx[i + 1] - vx[i] for i in range(len(vx) - 1)]
|
|
321
|
+
vals = [v / w for v, w in zip(p.tolist(), widths)]
|
|
322
|
+
|
|
323
|
+
lim1 = 0
|
|
324
|
+
lim2 = len(vals) - 1
|
|
325
|
+
if gum.config["notebook", "histogram_mode"] == "compact":
|
|
326
|
+
while vals[lim1] <= gum.config.asFloat["notebook", "histogram_epsilon"]:
|
|
327
|
+
lim1 += 1
|
|
328
|
+
if lim1 > 0:
|
|
329
|
+
lim1 -= 1
|
|
330
|
+
|
|
331
|
+
while vals[lim2] <= gum.config.asFloat["notebook", "histogram_epsilon"]:
|
|
332
|
+
lim2 -= 1
|
|
333
|
+
if lim2 < len(vals) - 1:
|
|
334
|
+
lim2 += 1
|
|
335
|
+
if lim2 >= len(vx):
|
|
336
|
+
lim2 -= 1
|
|
337
|
+
|
|
338
|
+
fig = plt.figure()
|
|
339
|
+
fw = scale * max(10, (lim2 - lim1)) / 8
|
|
340
|
+
fig.set_figwidth(fw)
|
|
341
|
+
fig.set_figheight(scale)
|
|
342
|
+
|
|
343
|
+
ax = fig.add_subplot(111)
|
|
344
|
+
ax.set_facecolor("white")
|
|
345
|
+
delta = 0.025 * (vx[lim2 + 1] - vx[lim1])
|
|
346
|
+
ax.set_xlim(vx[lim1] - delta, vx[lim2 + 1] + delta)
|
|
347
|
+
ax.set_xticks([vx[lim1], (vx[lim2 + 1] + vx[lim1]) / 2, vx[lim2 + 1]])
|
|
348
|
+
ax.set_xticklabels(
|
|
349
|
+
[f"{vx[lim1]:.2f}", f"{(vx[lim2 + 1] + vx[lim1]) / 2:.2f}", f"{vx[lim2 + 1]:.2f}"],
|
|
350
|
+
color=txtcolor,
|
|
351
|
+
rotation="vertical" if fw < 1.3 else "horizontal",
|
|
352
|
+
)
|
|
353
|
+
ax.xaxis.set_minor_locator(AutoMinorLocator())
|
|
354
|
+
ax.tick_params(which="minor", length=4)
|
|
355
|
+
ax.set_ylim(bottom=0, top=1.05 * max(vals))
|
|
356
|
+
ax.get_xaxis().grid(True, which="both", zorder=0)
|
|
357
|
+
|
|
358
|
+
_ = ax.bar(
|
|
359
|
+
vx[lim1 : 1 + lim2],
|
|
360
|
+
height=vals[lim1 : 1 + lim2],
|
|
361
|
+
width=widths[lim1 : 1 + lim2],
|
|
362
|
+
align="edge",
|
|
363
|
+
color=gum.config["notebook", "histogram_color"],
|
|
364
|
+
edgecolor=gum.config["notebook", "histogram_edge_color"],
|
|
365
|
+
zorder=33,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
# Even if utility, now we do show the mean/sigma of the distribution.
|
|
369
|
+
ax.set_title(_getTitleHisto(p, True), color=txtcolor)
|
|
370
|
+
ax.margins(0)
|
|
371
|
+
|
|
372
|
+
return fig
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def proba2histo(p, scale=None, util=None, txtcolor="Black"):
|
|
376
|
+
"""
|
|
377
|
+
compute the representation of a histogram for a mono-dim Tensor
|
|
378
|
+
|
|
379
|
+
Parameters
|
|
380
|
+
----------
|
|
381
|
+
p : pyagrum.Tensor
|
|
382
|
+
the mono-dimensional Tensor
|
|
383
|
+
scale : float
|
|
384
|
+
scale for the size of the graph
|
|
385
|
+
util : pyagrum.Tensor
|
|
386
|
+
an (optional) secondary Tensor (values in labels)
|
|
387
|
+
txtcolor : str
|
|
388
|
+
color for text
|
|
389
|
+
|
|
390
|
+
Returns
|
|
391
|
+
-------
|
|
392
|
+
matplotlib.Figure
|
|
393
|
+
a matplotlib histogram for a Tensor p.
|
|
394
|
+
"""
|
|
395
|
+
# if util is not None:
|
|
396
|
+
# if scale is None:
|
|
397
|
+
# scale = 1.0
|
|
398
|
+
# return _getProbaH(p, scale, util=util, txtcolor=txtcolor)
|
|
399
|
+
|
|
400
|
+
isev = p.min() == 0.0 and p.max() == 1.0 and p.sum() == 1.0
|
|
401
|
+
|
|
402
|
+
if p.variable(0).varType() == gum.VarType_DISCRETIZED:
|
|
403
|
+
if gum.config["notebook", "histogram_discretized_visualisation"] == "histogram":
|
|
404
|
+
if scale is None:
|
|
405
|
+
scale = gum.config.asFloat["notebook", "histogram_discretized_scale"]
|
|
406
|
+
return _getHistoForDiscretized(p, scale, txtcolor)
|
|
407
|
+
|
|
408
|
+
if scale is None:
|
|
409
|
+
scale = 1.0
|
|
410
|
+
|
|
411
|
+
if p.variable(0).domainSize() > int(gum.config["notebook", "histogram_line_threshold"]) and not (isev):
|
|
412
|
+
return _getProbaLine(p, scale, txtcolor=txtcolor)
|
|
413
|
+
|
|
414
|
+
if p.variable(0).domainSize() > int(gum.config["notebook", "histogram_horizontal_threshold"]):
|
|
415
|
+
return _getProbaV(p, scale, txtcolor=txtcolor)
|
|
416
|
+
|
|
417
|
+
return _getProbaH(p, scale, util=util, txtcolor=txtcolor)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def saveFigProba(p, filename, util=None, bgcolor=None, txtcolor="Black"):
|
|
421
|
+
"""
|
|
422
|
+
save a figure which is the representation of a histogram for a mono-dim Tensor
|
|
423
|
+
|
|
424
|
+
Parameters
|
|
425
|
+
----------
|
|
426
|
+
p : pyagrum.Tensor
|
|
427
|
+
the mono-dimensional Tensor
|
|
428
|
+
filename: str
|
|
429
|
+
the name of the saved file
|
|
430
|
+
util : pyagrum.Tensor
|
|
431
|
+
an (optional) secondary Tensor (values in labels)
|
|
432
|
+
bgcolor: str
|
|
433
|
+
color for background (transparent if None)
|
|
434
|
+
txtcolor : str
|
|
435
|
+
color for text
|
|
436
|
+
"""
|
|
437
|
+
fig = proba2histo(p, util=util, txtcolor=txtcolor)
|
|
438
|
+
|
|
439
|
+
if bgcolor is None:
|
|
440
|
+
fc = gum.config["notebook", "figure_facecolor"]
|
|
441
|
+
else:
|
|
442
|
+
fc = bgcolor
|
|
443
|
+
|
|
444
|
+
fig.savefig(
|
|
445
|
+
filename,
|
|
446
|
+
bbox_inches="tight",
|
|
447
|
+
transparent=False,
|
|
448
|
+
facecolor=fc,
|
|
449
|
+
pad_inches=0.05,
|
|
450
|
+
dpi=fig.dpi,
|
|
451
|
+
format=gum.config["notebook", "graph_format"],
|
|
452
|
+
)
|
|
453
|
+
plt.close(fig)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def probaMinMaxH(pmin, pmax, scale=1.0, txtcolor="black"):
|
|
457
|
+
"""
|
|
458
|
+
compute the representation of a horizontal histogram for a mono-dim Tensor
|
|
459
|
+
|
|
460
|
+
Parameters
|
|
461
|
+
----------
|
|
462
|
+
pmin,pmax : pyagrum.Tensor
|
|
463
|
+
two mono-dimensional Tensor
|
|
464
|
+
scale : float
|
|
465
|
+
scale for the size of the graph
|
|
466
|
+
txtcolor : str
|
|
467
|
+
color for text
|
|
468
|
+
|
|
469
|
+
Returns
|
|
470
|
+
-------
|
|
471
|
+
matplotlib.Figure
|
|
472
|
+
a matplotlib histogram for a bi-Tensor pmin,pmax.
|
|
473
|
+
"""
|
|
474
|
+
var = pmin.variable(0)
|
|
475
|
+
ra = np.arange(var.domainSize())
|
|
476
|
+
|
|
477
|
+
ra_reverse = np.arange(var.domainSize() - 1, -1, -1) # reverse order
|
|
478
|
+
vx = [var.label(int(i)) for i in ra_reverse]
|
|
479
|
+
|
|
480
|
+
fig = plt.figure()
|
|
481
|
+
fig.set_figheight(scale * var.domainSize() / 4.0)
|
|
482
|
+
fig.set_figwidth(scale * 2)
|
|
483
|
+
|
|
484
|
+
ax = fig.add_subplot(111)
|
|
485
|
+
ax.set_facecolor("white")
|
|
486
|
+
|
|
487
|
+
vmin = pmin.tolist()
|
|
488
|
+
vmin.reverse()
|
|
489
|
+
vmax = pmax.tolist()
|
|
490
|
+
vmax.reverse()
|
|
491
|
+
barsmax = ax.barh(ra, vmax, align="center", color="#BBFFAA")
|
|
492
|
+
barsmin = ax.barh(ra, vmin, align="center", color=gum.config["notebook", "histogram_color"])
|
|
493
|
+
|
|
494
|
+
if gum.config.asBool["notebook", "histogram_use_percent"]:
|
|
495
|
+
perc = 100
|
|
496
|
+
suffix = "%"
|
|
497
|
+
else:
|
|
498
|
+
perc = 1
|
|
499
|
+
suffix = ""
|
|
500
|
+
|
|
501
|
+
for b in barsmax:
|
|
502
|
+
txt = f"{b.get_width() * perc:.{gum.config.asInt['notebook', 'histogram_horizontal_visible_digits']}f}{suffix}"
|
|
503
|
+
ax.text(1, b.get_y(), txt, ha="right", va="bottom")
|
|
504
|
+
for b in barsmin:
|
|
505
|
+
txt = f"{b.get_width() * perc:.{gum.config.asInt['notebook', 'histogram_horizontal_visible_digits']}f}{suffix}"
|
|
506
|
+
ax.text(0, b.get_y(), txt, ha="left", va="bottom")
|
|
507
|
+
|
|
508
|
+
ax.set_xlim(0, 1)
|
|
509
|
+
ax.set_yticks(np.arange(var.domainSize()))
|
|
510
|
+
ax.set_yticklabels(vx, color=txtcolor)
|
|
511
|
+
ax.set_xticklabels([])
|
|
512
|
+
ax.set_title(pmin.variable(0).name(), color=txtcolor)
|
|
513
|
+
ax.get_xaxis().grid(True)
|
|
514
|
+
ax.margins(0)
|
|
515
|
+
|
|
516
|
+
return fig
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def saveFigProbaMinMax(pmin, pmax, filename, bgcolor=None, txtcolor="Black"):
|
|
520
|
+
"""
|
|
521
|
+
save a figure which is the representation of a histogram for a bi-Tensor (min,max)
|
|
522
|
+
|
|
523
|
+
Parameters
|
|
524
|
+
----------
|
|
525
|
+
pmin : pyagrum.Tensor
|
|
526
|
+
the mono-dimensional Tensor for min values
|
|
527
|
+
pmax : pyagrum.Tensor
|
|
528
|
+
the mono-dimensional Tensor for max value
|
|
529
|
+
filename : str
|
|
530
|
+
the name of the saved file
|
|
531
|
+
bgcolor: str
|
|
532
|
+
color for background (transparent if None)
|
|
533
|
+
txtcolor : str
|
|
534
|
+
color for text
|
|
535
|
+
"""
|
|
536
|
+
fig = probaMinMaxH(pmin, pmax, txtcolor=txtcolor)
|
|
537
|
+
|
|
538
|
+
if bgcolor is None:
|
|
539
|
+
fc = gum.config["notebook", "figure_facecolor"]
|
|
540
|
+
else:
|
|
541
|
+
fc = bgcolor
|
|
542
|
+
|
|
543
|
+
fig.savefig(
|
|
544
|
+
filename,
|
|
545
|
+
bbox_inches="tight",
|
|
546
|
+
transparent=False,
|
|
547
|
+
facecolor=fc,
|
|
548
|
+
pad_inches=0.05,
|
|
549
|
+
dpi=fig.dpi,
|
|
550
|
+
format=gum.config["notebook", "graph_format"],
|
|
551
|
+
)
|
|
552
|
+
plt.close(fig)
|
pyagrum/lib/utils.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
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
|
+
collection of utilities for pyagrum.lib
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from typing import Dict
|
|
46
|
+
import csv
|
|
47
|
+
import pydot as dot
|
|
48
|
+
|
|
49
|
+
import pyagrum as gum
|
|
50
|
+
from collections import namedtuple
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def setDarkTheme():
|
|
54
|
+
"""change the color for arcs and text in graphs to be more visible in dark theme"""
|
|
55
|
+
gum.config["notebook", "default_arc_color"] = "#AAAAAA"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def setLightTheme():
|
|
59
|
+
"""change the color for arcs and text in graphs to be more visible in light theme"""
|
|
60
|
+
gum.config["notebook", "default_arc_color"] = "#4A4A4A"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def getBlackInTheme():
|
|
64
|
+
"""return the color used for arc and text in graphs"""
|
|
65
|
+
return gum.config["notebook", "default_arc_color"]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
DotPoint = namedtuple("DotPoint", ["x", "y"])
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def dot_layout(g: dot.Dot) -> Dict[str, DotPoint]:
|
|
72
|
+
"""
|
|
73
|
+
extract the layout from a dot graph
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
g : pydot.Dot
|
|
78
|
+
the graph
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
Dict[str,Tuple[float, float]]
|
|
83
|
+
the layout i.e. the position of each node, identified by their name.
|
|
84
|
+
"""
|
|
85
|
+
return {
|
|
86
|
+
l[1]: DotPoint(float(l[2]), float(l[3]))
|
|
87
|
+
for l in csv.reader(g.create(format="plain").decode("utf8").split("\n"), delimiter=" ", quotechar='"')
|
|
88
|
+
if len(l) > 3 and l[0] == "node"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def apply_dot_layout(g: dot.Dot, layout: Dict[str, DotPoint]):
|
|
93
|
+
"""
|
|
94
|
+
apply a layout to a dot graph
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
g : pydot.Dot
|
|
99
|
+
the graph
|
|
100
|
+
|
|
101
|
+
layout : Dict[str,Tuple[float, float]]
|
|
102
|
+
the layout i.e. the position of each node, identified by their name.
|
|
103
|
+
"""
|
|
104
|
+
g.set_layout("fdp") # force directed placement
|
|
105
|
+
for n in g.get_nodes():
|
|
106
|
+
name = n.get_name()
|
|
107
|
+
if name in {"edge", "node"}: # default attributes for all following nodes or edges
|
|
108
|
+
continue
|
|
109
|
+
if name[0] == '"':
|
|
110
|
+
pos = layout[name[1:-1]]
|
|
111
|
+
else:
|
|
112
|
+
pos = layout[name]
|
|
113
|
+
n.set_pos(f'"{pos.x:.2f},{pos.y:.2f}!"')
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
async def async_html2image(htmlcontent: str, filename: str):
|
|
117
|
+
"""
|
|
118
|
+
convert an html content to an image. The format is determined by the extension of the filename.
|
|
119
|
+
This function is asynchronous and uses playwright. To install playwright, use `pip install playwright` and then `playwright install`.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
htmlcontent : str
|
|
124
|
+
the html content to convert
|
|
125
|
+
filename: str
|
|
126
|
+
the filename of the image to create. The format is determined by the extension of the filename (at least pdf and png).
|
|
127
|
+
"""
|
|
128
|
+
from playwright.async_api import async_playwright
|
|
129
|
+
|
|
130
|
+
playwright = await async_playwright().start()
|
|
131
|
+
browser = await playwright.chromium.launch(headless=True)
|
|
132
|
+
page = await browser.new_page()
|
|
133
|
+
await page.set_content(htmlcontent)
|
|
134
|
+
if filename.endswith(".pdf"):
|
|
135
|
+
await page.pdf(path=filename, scale=1, print_background=True, width="0")
|
|
136
|
+
else:
|
|
137
|
+
await page.screenshot(path=filename, type=filename.split(".")[-1])
|
|
138
|
+
return
|