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.
Files changed (107) hide show
  1. pyagrum/__init__.py +165 -0
  2. pyagrum/_pyagrum.so +0 -0
  3. pyagrum/bnmixture/BNMInference.py +268 -0
  4. pyagrum/bnmixture/BNMLearning.py +376 -0
  5. pyagrum/bnmixture/BNMixture.py +464 -0
  6. pyagrum/bnmixture/__init__.py +60 -0
  7. pyagrum/bnmixture/notebook.py +1058 -0
  8. pyagrum/causal/_CausalFormula.py +280 -0
  9. pyagrum/causal/_CausalModel.py +436 -0
  10. pyagrum/causal/__init__.py +81 -0
  11. pyagrum/causal/_causalImpact.py +356 -0
  12. pyagrum/causal/_dSeparation.py +598 -0
  13. pyagrum/causal/_doAST.py +761 -0
  14. pyagrum/causal/_doCalculus.py +361 -0
  15. pyagrum/causal/_doorCriteria.py +374 -0
  16. pyagrum/causal/_exceptions.py +95 -0
  17. pyagrum/causal/_types.py +61 -0
  18. pyagrum/causal/causalEffectEstimation/_CausalEffectEstimation.py +1175 -0
  19. pyagrum/causal/causalEffectEstimation/_IVEstimators.py +718 -0
  20. pyagrum/causal/causalEffectEstimation/_RCTEstimators.py +132 -0
  21. pyagrum/causal/causalEffectEstimation/__init__.py +46 -0
  22. pyagrum/causal/causalEffectEstimation/_backdoorEstimators.py +774 -0
  23. pyagrum/causal/causalEffectEstimation/_causalBNEstimator.py +324 -0
  24. pyagrum/causal/causalEffectEstimation/_frontdoorEstimators.py +396 -0
  25. pyagrum/causal/causalEffectEstimation/_learners.py +118 -0
  26. pyagrum/causal/causalEffectEstimation/_utils.py +466 -0
  27. pyagrum/causal/notebook.py +171 -0
  28. pyagrum/clg/CLG.py +658 -0
  29. pyagrum/clg/GaussianVariable.py +111 -0
  30. pyagrum/clg/SEM.py +312 -0
  31. pyagrum/clg/__init__.py +63 -0
  32. pyagrum/clg/canonicalForm.py +408 -0
  33. pyagrum/clg/constants.py +54 -0
  34. pyagrum/clg/forwardSampling.py +202 -0
  35. pyagrum/clg/learning.py +776 -0
  36. pyagrum/clg/notebook.py +480 -0
  37. pyagrum/clg/variableElimination.py +271 -0
  38. pyagrum/common.py +60 -0
  39. pyagrum/config.py +319 -0
  40. pyagrum/ctbn/CIM.py +513 -0
  41. pyagrum/ctbn/CTBN.py +573 -0
  42. pyagrum/ctbn/CTBNGenerator.py +216 -0
  43. pyagrum/ctbn/CTBNInference.py +459 -0
  44. pyagrum/ctbn/CTBNLearner.py +161 -0
  45. pyagrum/ctbn/SamplesStats.py +671 -0
  46. pyagrum/ctbn/StatsIndepTest.py +355 -0
  47. pyagrum/ctbn/__init__.py +79 -0
  48. pyagrum/ctbn/constants.py +54 -0
  49. pyagrum/ctbn/notebook.py +264 -0
  50. pyagrum/defaults.ini +199 -0
  51. pyagrum/deprecated.py +95 -0
  52. pyagrum/explain/_ComputationCausal.py +75 -0
  53. pyagrum/explain/_ComputationConditional.py +48 -0
  54. pyagrum/explain/_ComputationMarginal.py +48 -0
  55. pyagrum/explain/_CustomShapleyCache.py +110 -0
  56. pyagrum/explain/_Explainer.py +176 -0
  57. pyagrum/explain/_Explanation.py +70 -0
  58. pyagrum/explain/_FIFOCache.py +54 -0
  59. pyagrum/explain/_ShallCausalValues.py +204 -0
  60. pyagrum/explain/_ShallConditionalValues.py +155 -0
  61. pyagrum/explain/_ShallMarginalValues.py +155 -0
  62. pyagrum/explain/_ShallValues.py +296 -0
  63. pyagrum/explain/_ShapCausalValues.py +208 -0
  64. pyagrum/explain/_ShapConditionalValues.py +126 -0
  65. pyagrum/explain/_ShapMarginalValues.py +191 -0
  66. pyagrum/explain/_ShapleyValues.py +298 -0
  67. pyagrum/explain/__init__.py +81 -0
  68. pyagrum/explain/_explGeneralizedMarkovBlanket.py +152 -0
  69. pyagrum/explain/_explIndependenceListForPairs.py +146 -0
  70. pyagrum/explain/_explInformationGraph.py +264 -0
  71. pyagrum/explain/notebook/__init__.py +54 -0
  72. pyagrum/explain/notebook/_bar.py +142 -0
  73. pyagrum/explain/notebook/_beeswarm.py +174 -0
  74. pyagrum/explain/notebook/_showShapValues.py +97 -0
  75. pyagrum/explain/notebook/_waterfall.py +220 -0
  76. pyagrum/explain/shapley.py +225 -0
  77. pyagrum/lib/__init__.py +46 -0
  78. pyagrum/lib/_colors.py +390 -0
  79. pyagrum/lib/bn2graph.py +299 -0
  80. pyagrum/lib/bn2roc.py +1026 -0
  81. pyagrum/lib/bn2scores.py +217 -0
  82. pyagrum/lib/bn_vs_bn.py +605 -0
  83. pyagrum/lib/cn2graph.py +305 -0
  84. pyagrum/lib/discreteTypeProcessor.py +1102 -0
  85. pyagrum/lib/discretizer.py +58 -0
  86. pyagrum/lib/dynamicBN.py +390 -0
  87. pyagrum/lib/explain.py +57 -0
  88. pyagrum/lib/export.py +84 -0
  89. pyagrum/lib/id2graph.py +258 -0
  90. pyagrum/lib/image.py +387 -0
  91. pyagrum/lib/ipython.py +307 -0
  92. pyagrum/lib/mrf2graph.py +471 -0
  93. pyagrum/lib/notebook.py +1821 -0
  94. pyagrum/lib/proba_histogram.py +552 -0
  95. pyagrum/lib/utils.py +138 -0
  96. pyagrum/pyagrum.py +31495 -0
  97. pyagrum/skbn/_MBCalcul.py +242 -0
  98. pyagrum/skbn/__init__.py +49 -0
  99. pyagrum/skbn/_learningMethods.py +282 -0
  100. pyagrum/skbn/_utils.py +297 -0
  101. pyagrum/skbn/bnclassifier.py +1014 -0
  102. pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/LICENSE.md +12 -0
  103. pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/LICENSES/LGPL-3.0-or-later.txt +304 -0
  104. pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/LICENSES/MIT.txt +18 -0
  105. pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/METADATA +145 -0
  106. pyagrum_nightly-2.3.0.9.dev202512061764412981.dist-info/RECORD +107 -0
  107. pyagrum_nightly-2.3.0.9.dev202512061764412981.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