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,258 @@
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 provide tools for mapping Influcence Diagram (and inference) in dot language in order to
43
+ be displayed/saved as image.
44
+ """
45
+
46
+ import time
47
+ import math
48
+ import hashlib
49
+ from tempfile import mkdtemp
50
+
51
+ import pydot as dot
52
+
53
+ import pyagrum as gum
54
+ import pyagrum.lib._colors as gumcols
55
+ from pyagrum.lib.proba_histogram import saveFigProba
56
+
57
+
58
+ def ID2dot(diag, size=None):
59
+ """
60
+ create a pydot representation of the influence diagram
61
+
62
+ Parameters
63
+ ----------
64
+ diag: pyagrum.InfluenceDiagram
65
+ the model
66
+ size: int|str
67
+ the size of the visualization
68
+
69
+ Returns
70
+ -------
71
+ pydot.Dot
72
+ the dot representation of the influence diagram
73
+ """
74
+ res = "digraph {\n"
75
+
76
+ # chance node
77
+ res += f'''
78
+ node [fillcolor="{gum.config["influenceDiagram", "default_chance_bgcolor"]}",
79
+ fontcolor="{gum.config["influenceDiagram", "default_chance_fgcolor"]}",
80
+ style=filled,shape={gum.config["influenceDiagram", "chance_shape"]},
81
+ height=0,margin=0.1];
82
+ '''
83
+ for node in diag.nodes():
84
+ if diag.isChanceNode(node):
85
+ res += ' "' + diag.variable(node).name() + '";' + "\n"
86
+
87
+ # decision node
88
+ res += f'''
89
+ node [fillcolor="{gum.config["influenceDiagram", "default_decision_bgcolor"]}",
90
+ fontcolor="{gum.config["influenceDiagram", "default_decision_fgcolor"]}",
91
+ style=filled,shape={gum.config["influenceDiagram", "decision_shape"]},
92
+ height=0,margin=0.1];
93
+ '''
94
+ for node in diag.nodes():
95
+ if diag.isDecisionNode(node):
96
+ res += ' "' + diag.variable(node).name() + '";' + "\n"
97
+
98
+ # utility node
99
+ res += f'''
100
+ node [fillcolor="{gum.config["influenceDiagram", "default_utility_bgcolor"]}",
101
+ fontcolor="{gum.config["influenceDiagram", "default_utility_fgcolor"]}",
102
+ style=filled,shape={gum.config["influenceDiagram", "utility_shape"]}, height=0,margin=0.1];
103
+ '''
104
+ for node in diag.nodes():
105
+ if diag.isUtilityNode(node):
106
+ res += ' "' + diag.variable(node).name() + '";' + "\n"
107
+
108
+ # arcs
109
+ res += "\n"
110
+ for node in diag.nodes():
111
+ for chi in diag.children(node):
112
+ res += ' "' + diag.variable(node).name() + '"->"' + diag.variable(chi).name() + '"'
113
+ if diag.isDecisionNode(chi):
114
+ res += (
115
+ f' [style="{gum.config["influenceDiagram", "decision_arc_style"]}", color = "{gumcols.getBlackInTheme()}"]'
116
+ )
117
+ elif diag.isUtilityNode(chi):
118
+ res += (
119
+ f' [style="{gum.config["influenceDiagram", "utility_arc_style"]}", color = "{gumcols.getBlackInTheme()}"]'
120
+ )
121
+ else:
122
+ res += f' [color = "{gumcols.getBlackInTheme()}"]'
123
+ res += ";\n"
124
+ res += "}"
125
+
126
+ g = dot.graph_from_dot_data(res)[0]
127
+
128
+ # workaround for some badly parsed graph (pyparsing>=3.03)
129
+ g.del_node('"\\n"')
130
+
131
+ if size is None:
132
+ size = gum.config["influenceDiagram", "default_id_size"]
133
+ # dynamic member makes pylink unhappy
134
+ # pylint: disable=no-member
135
+ g.set_size(size)
136
+ return g
137
+
138
+
139
+ def LIMIDinference2dot(diag, size, engine, evs, targets):
140
+ """
141
+ create a pydot representation of an inference in a influence diagram
142
+
143
+ Parameters
144
+ ----------
145
+ diag: pyagrum.InfluenceDiagram
146
+ the model
147
+ size: float|str
148
+ the size of the rendered graph
149
+ engine: pyagrum.InfluenceDiagramInference
150
+ the inference algorithm used. If None, ShaferShenoyLIMIDInference will be used
151
+ evs: Dict[str,str|int|List[float]]
152
+ the evidence
153
+ targets: Set[str]
154
+ set of targetted variable. If targets={} then each node is a target
155
+
156
+ Returns
157
+ -------
158
+ pydot.Dot
159
+ the representation of the inference
160
+ """
161
+ startTime = time.time()
162
+ if engine is None:
163
+ ie = gum.ShaferShenoyLIMIDInference(diag)
164
+ else:
165
+ ie = engine
166
+ ie.setEvidence(evs)
167
+ ie.makeInference()
168
+ stopTime = time.time()
169
+ meu = ie.MEU()
170
+
171
+ temp_dir = mkdtemp("", "tmp", None) # with TemporaryDirectory() as temp_dir:
172
+
173
+ dotstr = 'digraph structs {\n fontcolor="' + gumcols.getBlackInTheme() + '";bgcolor="transparent";'
174
+
175
+ fontname, fontsize = gumcols.fontFromMatplotlib()
176
+ dotstr += f'node[fontname="{fontname}",fontsize="{fontsize}"];'
177
+
178
+ fmt = "." + gum.config["influenceDiagram", "utility_visible_digits"] + "f"
179
+ if gum.config.asBool["influenceDiagram", "utility_show_loss"]:
180
+ titut = f"mEL {-meu['mean']:{fmt}}"
181
+ else:
182
+ titut = f"MEU {meu['mean']:{fmt}}"
183
+ if gum.config.asBool["influenceDiagram", "utility_show_stdev"]:
184
+ titut += f" (stdev={math.sqrt(meu['variance']):{fmt}})"
185
+
186
+ slabel = f'label="{titut}'
187
+
188
+ if gum.config.asBool["notebook", "show_inference_time"]:
189
+ slabel += f"\nInference in {1000 * (stopTime - startTime):6.2f}ms"
190
+ dotstr += slabel + '";\n'
191
+
192
+ for nid in diag.nodes():
193
+ name = diag.variable(nid).name()
194
+
195
+ # defaults
196
+ if diag.isChanceNode(nid):
197
+ bgcolor = gum.config["influenceDiagram", "default_chance_bgcolor"]
198
+ fgcolor = gum.config["influenceDiagram", "default_chance_fgcolor"]
199
+ shape = gum.config["influenceDiagram", "chance_shape"]
200
+ elif diag.isDecisionNode(nid):
201
+ bgcolor = gum.config["influenceDiagram", "default_decision_bgcolor"]
202
+ fgcolor = gum.config["influenceDiagram", "default_decision_fgcolor"]
203
+ shape = gum.config["influenceDiagram", "decision_shape"]
204
+ else: # diag.isUtilityNode(nid):
205
+ bgcolor = gum.config["influenceDiagram", "default_utility_bgcolor"]
206
+ fgcolor = gum.config["influenceDiagram", "default_utility_fgcolor"]
207
+ shape = gum.config["influenceDiagram", "utility_shape"]
208
+
209
+ # 'hard' colour for evidence (?)
210
+ if nid in ie.hardEvidenceNodes() | ie.softEvidenceNodes():
211
+ bgcolor = gum.config["notebook", "evidence_bgcolor"]
212
+ fgcolor = gum.config["notebook", "evidence_fgcolor"]
213
+
214
+ styleattribute = "style=filled, height=0,margin=0.1"
215
+ colorattribute = f'fillcolor="{bgcolor}", fontcolor="{fgcolor}", color="#000000"'
216
+
217
+ if not diag.isUtilityNode(nid):
218
+ if len(targets) == 0 or name in targets or nid in targets:
219
+ filename = temp_dir + hashlib.md5(name.encode()).hexdigest() + "." + gum.config["notebook", "graph_format"]
220
+ saveFigProba(ie.posterior(name), filename, bgcolor=bgcolor, util=ie.posteriorUtility(nid), txtcolor=fgcolor)
221
+ dotstr += f' "{name}" [shape=rectangle,image="{filename}",label="", {colorattribute}];\n'
222
+ else:
223
+ dotstr += f' "{name}" [{colorattribute},shape={shape},{styleattribute}]'
224
+ else: # utility node
225
+ mv = ie.meanVar(name)
226
+
227
+ if gum.config.asBool["influenceDiagram", "utility_show_loss"]:
228
+ coef = -1
229
+ else:
230
+ coef = 1
231
+
232
+ fmt = f".{gum.config.asInt['influenceDiagram', 'utility_visible_digits']}f"
233
+ labut = f"{name} : {coef * mv['mean']:{fmt}}"
234
+ if gum.config.asBool["influenceDiagram", "utility_show_stdev"]:
235
+ labut += f" ({math.sqrt(mv['variance']):{fmt}})"
236
+
237
+ dotstr += f' "{name}" [label="{labut}",{colorattribute},{styleattribute},shape={shape}]'
238
+
239
+ # arcs
240
+ dotstr += "\n"
241
+ for node in diag.nodes():
242
+ for chi in diag.children(node):
243
+ dotstr += ' "' + diag.variable(node).name() + '"->"' + diag.variable(chi).name() + '"'
244
+ if diag.isDecisionNode(chi):
245
+ dotstr += f' [style="{gum.config["influenceDiagram", "decision_arc_style"]}"]'
246
+ elif diag.isUtilityNode(chi):
247
+ dotstr += f' [style="{gum.config["influenceDiagram", "utility_arc_style"]}"]'
248
+ dotstr += ";\n"
249
+ dotstr += "}"
250
+
251
+ g = dot.graph_from_dot_data(dotstr)[0]
252
+
253
+ if size is None:
254
+ size = gum.config["influenceDiagram", "default_id_inference_size"]
255
+ g.set_size(size)
256
+ g.temp_dir = temp_dir
257
+
258
+ return g
pyagrum/lib/image.py ADDED
@@ -0,0 +1,387 @@
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
+ tools for exporting model and inference as image
43
+ """
44
+
45
+ import os
46
+ import tempfile
47
+ import re
48
+ import matplotlib.image as mpimg
49
+ import pydot as dot
50
+
51
+ import pyagrum as gum
52
+ from pyagrum.lib.bn2graph import BN2dot, BNinference2dot
53
+ from pyagrum.lib.cn2graph import CN2dot, CNinference2dot
54
+ from pyagrum.lib.id2graph import ID2dot, LIMIDinference2dot
55
+ from pyagrum.lib.mrf2graph import MRF2UGdot, MRFinference2UGdot
56
+ from pyagrum.lib.mrf2graph import MRF2FactorGraphdot, MRFinference2FactorGraphdot
57
+ import pyagrum.lib._colors as gumcols
58
+
59
+
60
+ def export(model, filename=None, **kwargs):
61
+ """
62
+ export the graphical representation of the model in filename (png, pdf,etc.)
63
+
64
+ Parameters
65
+ ----------
66
+ model: pyagrum.GraphicalModel
67
+ the model to show (pyagrum.BayesNet, pyagrum.MarkovRandomField, pyagrum.InfluenceDiagram or pyagrum.Tensor)
68
+ filename: str
69
+ the name of the resulting file (suffix in ['pdf', 'png', 'fig', 'jpg', 'svg', 'ps']). If filename is None, the result is a np.array ready to be used with imshow().
70
+
71
+ Note
72
+ ----
73
+ Model can also just possess a method `toDot()` or even be a simple string in dot syntax.
74
+ """
75
+ if filename is None:
76
+ tmp = tempfile.NamedTemporaryFile(suffix=".png", delete=False)
77
+ export(model, tmp.name, **kwargs)
78
+ img = mpimg.imread(tmp.name)
79
+ try:
80
+ os.remove(tmp.name)
81
+ except PermissionError: # probably windows error : file still 'used' ... grrr...
82
+ pass
83
+ return img
84
+
85
+ fmt_image = filename.split(".")[-1]
86
+ if fmt_image not in ["pdf", "png", "fig", "jpg", "svg", "ps"]:
87
+ raise NameError(
88
+ f"{filename} in not a correct filename for export : extension '{fmt_image}' not in [pdf,png,fig,jpg,svg]."
89
+ )
90
+
91
+ if isinstance(model, gum.BayesNet):
92
+ fig = BN2dot(model, **kwargs)
93
+ elif isinstance(model, gum.MarkovRandomField):
94
+ if gum.config["notebook", "default_markovrandomfield_view"] == "graph":
95
+ fig = MRF2UGdot(model, **kwargs)
96
+ else:
97
+ fig = MRF2FactorGraphdot(model, **kwargs)
98
+ elif isinstance(model, gum.InfluenceDiagram):
99
+ fig = ID2dot(model, **kwargs)
100
+ elif isinstance(model, gum.CredalNet):
101
+ fig = CN2dot(model, **kwargs)
102
+ elif isinstance(model, dot.Dot):
103
+ fig = model
104
+ elif hasattr(model, "toDot"):
105
+ fig = dot.graph_from_dot_data(model.toDot())[0]
106
+ elif isinstance(model, str):
107
+ fig = dot.graph_from_dot_data(model)[0]
108
+ else:
109
+ raise gum.InvalidArgument(
110
+ "Argument model should be a PGM (BayesNet, MarkovRandomField or Influence Diagram) or has a method `toDot()` or is a string"
111
+ )
112
+
113
+ gumcols.prepareDot(fig, **kwargs).write(filename, format=fmt_image)
114
+
115
+
116
+ def prepareShowInference(
117
+ model,
118
+ engine=None,
119
+ evs=None,
120
+ targets=None,
121
+ size=None,
122
+ nodeColor=None,
123
+ factorColor=None,
124
+ arcWidth=None,
125
+ arcColor=None,
126
+ cmapNode=None,
127
+ cmapArc=None,
128
+ graph=None,
129
+ view=None,
130
+ ):
131
+ """
132
+ Transform an inference for a model in a dot representation
133
+
134
+ Parameters
135
+ ----------
136
+ model: pyAgrum:GraphicalModel
137
+ the model in which to infer (pyagrum.BayesNet, pyagrum.MarkovRandomField or pyagrum.InfluenceDiagram)
138
+ filename: str
139
+ the name of the resulting file (suffix in ['pdf', 'png', 'ps']). If filename is None, the result is a np.array ready to be used with imshow().
140
+ engine: pyagrum.Inference
141
+ inference algorithm used. If None, gum.LazyPropagation will be used for BayesNet,gum.ShaferShenoy for gum.MarkovRandomField and gum.ShaferShenoyLIMIDInference for gum.InfluenceDiagram.
142
+ evs: Dict[str,str|int]
143
+ map of evidence
144
+ targets: Set[str|int]
145
+ set of targets
146
+ size: str
147
+ size of the rendered graph
148
+ nodeColor: Dict[int,float]
149
+ a nodeMap of values (between 0 and 1) to be shown as color of nodes (with special colors for 0 and 1)
150
+ factorColor: Dict[int,float]
151
+ a nodeMap of values (between 0 and 1) to be shown as color of factors (in MarkovRandomField representation)
152
+ arcWidth: Dict[(int,int),float]
153
+ a arcMap of values to be shown as width of arcs
154
+ arcColor: Dict[(int,int),float]
155
+ a arcMap of values (between 0 and 1) to be shown as color of arcs
156
+ cmapNode: matplotlib.colors.ColorMap
157
+ color map to show the color of nodes and arcs
158
+ cmapArc: matplotlib.colors.ColorMap
159
+ color map to show the vals of Arcs.
160
+ graph: pyagrum.Graph
161
+ only shows nodes that have their id in the graph (and not in the whole BN)
162
+ view: str
163
+ graph | factorgraph | None (default) for Markov random field
164
+
165
+ Raises
166
+ ------
167
+ pyagrum.InvalidArgument:
168
+ if the arg is invalid
169
+
170
+ Returns
171
+ -------
172
+ str
173
+ the obtained graph as a string
174
+ """
175
+ if size is None:
176
+ size = gum.config["notebook", "default_graph_inference_size"]
177
+
178
+ if evs is None:
179
+ evs = {}
180
+
181
+ if targets is None:
182
+ targets = {}
183
+
184
+ if isinstance(model, gum.BayesNet):
185
+ if engine is None:
186
+ engine = gum.LazyPropagation(model)
187
+ return BNinference2dot(
188
+ model,
189
+ size=size,
190
+ engine=engine,
191
+ evs=evs,
192
+ targets=targets,
193
+ nodeColor=nodeColor,
194
+ arcWidth=arcWidth,
195
+ arcColor=arcColor,
196
+ cmapNode=cmapNode,
197
+ cmapArc=cmapArc,
198
+ )
199
+ if isinstance(model, gum.MarkovRandomField):
200
+ if view is None:
201
+ view = gum.config["notebook", "default_markovrandomfield_view"]
202
+ if engine is None:
203
+ engine = gum.ShaferShenoyMRFInference(model)
204
+
205
+ if view == "graph":
206
+ return MRFinference2UGdot(
207
+ model,
208
+ size=size,
209
+ engine=engine,
210
+ evs=evs,
211
+ targets=targets,
212
+ nodeColor=nodeColor,
213
+ factorColor=factorColor,
214
+ arcWidth=arcWidth,
215
+ arcColor=arcColor,
216
+ cmapNode=cmapNode,
217
+ cmapArc=cmapArc,
218
+ )
219
+ # view=factor graph
220
+ return MRFinference2FactorGraphdot(
221
+ model,
222
+ size=size,
223
+ engine=engine,
224
+ evs=evs,
225
+ targets=targets,
226
+ nodeColor=nodeColor,
227
+ factorColor=factorColor,
228
+ cmapNode=cmapNode,
229
+ )
230
+ if isinstance(model, gum.InfluenceDiagram):
231
+ if engine is None:
232
+ engine = gum.ShaferShenoyLIMIDInference(model)
233
+ return LIMIDinference2dot(model, size=size, engine=engine, evs=evs, targets=targets)
234
+ if isinstance(model, gum.CredalNet):
235
+ if engine is None:
236
+ engine = gum.CNMonteCarloSampling(model)
237
+ return CNinference2dot(
238
+ model,
239
+ size=size,
240
+ engine=engine,
241
+ evs=evs,
242
+ targets=targets,
243
+ nodeColor=nodeColor,
244
+ arcWidth=arcWidth,
245
+ arcColor=arcColor,
246
+ cmapNode=cmapNode,
247
+ )
248
+
249
+ raise gum.InvalidArgument("Argument model should be a PGM (BayesNet, MarkovRandomField or Influence Diagram)")
250
+
251
+
252
+ def prepareLinksForSVG(mainSvg):
253
+ """
254
+ Inlining links in svg
255
+
256
+ Parameters
257
+ ----------
258
+ mainSvg: str
259
+ the main svg to be changed
260
+
261
+ Returns
262
+ ------
263
+ str
264
+ the new version with inlined links
265
+ """
266
+ re_images = re.compile(r"(<image [^>]*>)")
267
+ re_xlink = re.compile(r"xlink:href=\"([^\"]*)")
268
+ re_viewbox = re.compile(r"(viewBox=\"[^\"]*\")")
269
+
270
+ # analyze mainSvg (find the secondary svgs)
271
+ __fragments = {}
272
+ for img in re.finditer(re_images, mainSvg):
273
+ # print(img)
274
+ secondarySvg = re.findall(re_xlink, img.group(1))[0]
275
+ content = ""
276
+ with open(secondarySvg, encoding="utf8") as f:
277
+ inSvg = False
278
+ for line in f:
279
+ if line[0:4] == "<svg":
280
+ inSvg = True
281
+ viewBox = re.findall(re_viewbox, line)[0]
282
+ # print("VIEWBOX {}".format(viewBox))
283
+ elif inSvg:
284
+ content += line
285
+ __fragments[secondarySvg] = (viewBox, content)
286
+
287
+ if len(__fragments) > 0:
288
+ # replace image tags by svg tags
289
+ img2svg = re.sub(r"<image ([^>]*)/>", r"<svg \g<1>>", mainSvg)
290
+
291
+ # insert secondaries into main
292
+ def ___insertSecondarySvgs(matchObj):
293
+ vb, code = __fragments[matchObj.group(1)]
294
+ return vb + matchObj.group(2) + code
295
+
296
+ mainSvg = re.sub(r'xlink:href="([^"]*)"(.*>)', ___insertSecondarySvgs, img2svg)
297
+
298
+ # remove buggy white-space (for notebooks)
299
+ mainSvg = mainSvg.replace("white-space:pre;", "")
300
+ return mainSvg
301
+
302
+
303
+ def dot_as_svg_string(gr, size):
304
+ """
305
+ repr a pydot graph in a notebook
306
+
307
+ Parameters
308
+ ----------
309
+ size : str
310
+ size of the rendered graph
311
+ """
312
+ if size is not None:
313
+ gr.set_size(size)
314
+
315
+ gsvg = prepareLinksForSVG(gr.create_svg(encoding="utf-8").decode("utf-8"))
316
+ return gsvg
317
+
318
+
319
+ def exportInference(model, filename=None, **kwargs):
320
+ """
321
+ the graphical representation of an inference in a notebook
322
+
323
+ Parameters
324
+ ----------
325
+ model: pyAgrum:GraphicalModel
326
+ the model in which to infer (pyagrum.BayesNet, pyagrum.MarkovRandomField or pyagrum.InfluenceDiagram)
327
+ filename: str
328
+ the name of the resulting file (suffix in ['pdf', 'png', 'ps']). If filename is None, the result is a np.array ready to be used with imshow().
329
+ engine: pyagrum.Inference
330
+ inference algorithm used. If None, gum.LazyPropagation will be used for BayesNet,gum.ShaferShenoy for gum.MarkovRandomField and gum.ShaferShenoyLIMIDInference for gum.InfluenceDiagram.
331
+ evs: Dict[str,str|int]
332
+ map of evidence
333
+ targets: Set[str|int]
334
+ set of targets
335
+ size: str
336
+ size of the rendered graph
337
+ nodeColor: Dict[int,float]
338
+ a nodeMap of values (between 0 and 1) to be shown as color of nodes (with special colors for 0 and 1)
339
+ factorColor: Dict[int,float]
340
+ a nodeMap of values (between 0 and 1) to be shown as color of factors (in MarkovRandomField representation)
341
+ arcWidth: Dict[(int,int),float]
342
+ a arcMap of values to be shown as width of arcs
343
+ arcColor: Dict[(int,int),float]
344
+ a arcMap of values (between 0 and 1) to be shown as color of arcs
345
+ cmap: matplotlib.colors.ColorMap
346
+ color map to show the color of nodes and arcs
347
+ cmapArc: matplotlib.colors.ColorMap
348
+ color map to show the vals of Arcs.
349
+ graph: pyagrum.Graph
350
+ only shows nodes that have their id in the graph (and not in the whole BN)
351
+ view: str
352
+ graph | factorgraph | None (default) for Markov random field
353
+
354
+ Returns
355
+ -------
356
+ str|dot.Dot
357
+ the desired representation of the inference
358
+ """
359
+ if filename is None:
360
+ tmp = tempfile.NamedTemporaryFile(suffix=".png", delete=False)
361
+ exportInference(model, tmp.name, **kwargs)
362
+ img = mpimg.imread(tmp.name)
363
+ try:
364
+ os.remove(tmp.name)
365
+ except PermissionError: # probably windows error : file still 'used' ... grrr...
366
+ pass
367
+ return img
368
+
369
+ fmt_image = filename.split(".")[-1]
370
+ if fmt_image not in ["pdf", "png", "ps"]:
371
+ raise NameError(f"{filename} in not a correct filename for export : extension '{fmt_image}' not in [pdf,png,ps].")
372
+
373
+ import cairosvg
374
+
375
+ if "size" in kwargs:
376
+ size = kwargs["size"]
377
+ else:
378
+ size = gum.config["notebook", "default_graph_inference_size"]
379
+
380
+ svgtxt = dot_as_svg_string(gumcols.prepareDot(prepareShowInference(model, **kwargs), **kwargs), size=size)
381
+
382
+ if fmt_image == "pdf":
383
+ cairosvg.svg2pdf(bytestring=svgtxt, write_to=filename)
384
+ elif fmt_image == "png":
385
+ cairosvg.svg2png(bytestring=svgtxt, write_to=filename)
386
+ else: # format=="ps"
387
+ cairosvg.svg2ps(bytestring=svgtxt, write_to=filename)