1# (c) Copyright by Pierre-Henri Wuillemin, UPMC, 2017
2# (pierre-henri.wuillemin@lip6.fr)
3
4# Permission to use, copy, modify, and distribute this
5# software and its documentation for any purpose and
6# without fee or royalty is hereby granted, provided
7# that the above copyright notice appear in all copies
8# and that both that copyright notice and this permission
9# notice appear in supporting documentation or portions
10# thereof, including modifications, that you make.
11
12# THE AUTHOR P.H. WUILLEMIN  DISCLAIMS ALL WARRANTIES
13# WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
14# WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
15# SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT
16# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
17# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
18# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
20# OR PERFORMANCE OF THIS SOFTWARE!
21import math
22
23import matplotlib.pyplot as plt
24import numpy as np
25import pyAgrum as gum
26
27
28def _stats(pot):
29  mu = 0.0
30  mu2 = 0.0
31  v = pot.variable(0)
32  for i, p in enumerate(pot.tolist()):
33    x = v.numerical(i)
34    mu += p * x
35    mu2 += p * x * x
36  return (mu, math.sqrt(mu2 - mu * mu))
37
38
39def _getTitleHisto(p, showMuSigma=True):
40  var = p.variable(0)
41  if var.varType() == 1 or not showMuSigma:  # Labelized
42    return var.name()
43
44  (mu, std) = _stats(p)
45  return f"${var.name()}$\n$\\mu={mu:.2f}$; $\\sigma={std:.2f}$"
46
47
48def __limits(p):
49  """return vals and labs to show in the histograme
50
51  Parameters
52  ----------
53    p : gum.Potential
54      the marginal to analyze
55  """
56  var = p.variable(0)
57  la = [var.label(int(i)) for i in np.arange(var.domainSize())]
58  v = p.tolist()
59  nzmin = None
60  nzmax = None
61  l = len(v) - 1
62  for i in range(l + 1):
63    if v[i] != 0:
64      if nzmin is None:
65        if i > 0:
66          nzmin = i - 1
67        else:
68          nzmin = -1
69    if v[l - i] != 0:
70      if nzmax is None:
71        if i > 0:
72          nzmax = l - i + 1
73        else:
74          nzmax = -1
75
76  mi = 0 if nzmin in [-1, None] else nzmin
77  ma = l if nzmax in [-1, None] else nzmax
78
79  res = range(mi, ma + 1)
80  lres = la[mi:ma + 1]
81  if nzmin not in [-1, None]:
82    lres[0] = "..."
83  if nzmax not in [-1, None]:
84    lres[-1] = "..."
85
86  return res, [v[i] for i in res], lres
87
88
89def _getProbaLine(p, scale=1.0, txtcolor="black"):
90  """
91  compute the representation of a matplotlib.fill_between for a mono-dim Potential
92
93  Parameters
94  ----------
95    p : pyAgrum.Potential
96      the mono-dimensional Potential
97    scale : float
98      the scale
99    txtcolor : str
100      color for text
101
102  Returns
103  -------
104  matplotlib.Figure
105    a matplotlib figure for a Potential p.
106  """
107
108  var = p.variable(0)
109  if gum.config['notebook', 'histogram_mode'] == "compact":
110    ra, v, lv = __limits(p)
111  else:
112    lv = [var.label(int(i)) for i in np.arange(var.domainSize())]
113    v = p.tolist()
114    ra = range(len(v))
115
116  fig = plt.figure()
117  fig.set_figwidth(min(scale * 6, scale * len(v) / 4.0))
118  fig.set_figheight(scale * 2)
119
120  ax = fig.add_subplot(111)
121  ax.fill_between(ra, v, color=gum.config['notebook', 'histogram_color'])
122
123  ax.set_ylim(bottom=0, top=1.05 * p.max())
124  # ax.set_xticks(ra)
125  # ax.set_xticklabels(lv, color=txtcolor)
126  ax.set_title(_getTitleHisto(p, True), color=txtcolor)
127
128  ax.get_xaxis().grid(True)
129  ax.get_yaxis().grid(True)
130  ax.margins(0)
131
132  ax.set_facecolor('w')
133
134  return fig
135
136
137def _getProbaV(p, scale=1.0, util=None, txtcolor="black"):
138  """
139  compute the representation of a vertical histogram for a mono-dim Potential
140
141  Parameters
142  ----------
143    p : pyAgrum.Potential
144      the mono-dimensional Potential
145    util : pyAgrum.Potential
146      an (optional) secondary Potential (values in labels)
147    txtcolor : str
148      color for text
149
150  Returns
151  -------
152  matplotlib.Figure
153    a matplotlib histogram for a Potential p.
154
155  """
156  if gum.config['notebook', 'histogram_mode'] == "compact":
157    ra, v, lv = __limits(p)
158  else:
159    var = p.variable(0)
160    if util is not None:
161      lu = util.toarray()
162      coef = -1 if gum.config["influenceDiagram", "utility_show_loss"] == "True" else 1
163      fmt = "." + gum.config["influenceDiagram", "utility_visible_digits"] + "f"
164      lv = [f"{var.label(int(i))} [{coef * lu[i]:{fmt}}]"
165            for i in np.arange(var.domainSize())]
166    else:
167      lv = [var.label(int(i)) for i in np.arange(var.domainSize())]
168    v = p.tolist()
169    ra = range(len(v))
170
171  fig = plt.figure()
172  fig.set_figwidth(scale * len(v) / 4.0)
173  fig.set_figheight(scale * 2)
174
175  ax = fig.add_subplot(111)
176
177  bars = ax.bar(ra, v,
178                align='center',
179                color=gum.config['notebook', 'histogram_color'])
180  ma = p.max()
181
182  for b in bars:
183    if b.get_height() != 0:
184      txt = txt = f"{b.get_height():.{gum.config['notebook', 'histogram_horizontal_visible_digits']}}"
185      ax.text(b.get_x(), ma, txt, ha='left', va='top', rotation='vertical')
186
187  ax.set_ylim(bottom=0, top=p.max())
188  ax.set_xticks(ra)
189  ax.set_xticklabels(lv, rotation='vertical', color=txtcolor)
190  # if utility, we do not show the mean/sigma of the proba.
191  ax.set_title(_getTitleHisto(p, util is None), color=txtcolor)
192  ax.get_yaxis().grid(True)
193  ax.margins(0)
194  ax.set_facecolor('w')
195
196  return fig
197
198
199def _getProbaH(p, scale=1.0, util=None, txtcolor="black"):
200  """
201  compute the representation of an horizontal histogram for a mono-dim Potential
202
203  Parameters
204  ----------
205    p : pyAgrum.Potential
206      the mono-dimensional Potential
207    util : pyAgrum.Potential
208      an (optional) secondary Potential (values in labels)
209    txtcolor : str
210      color for text
211
212  Returns
213  -------
214  matplotlib.Figure
215    a matplotlib histogram for a Potential p.
216  """
217  var = p.variable(0)
218  ra = np.arange(var.domainSize())
219
220  ra_reverse = np.arange(var.domainSize() - 1, -1, -1)  # reverse order
221
222  if util is not None:
223    lu = util.toarray()
224    fmt = "." + gum.config["influenceDiagram", "utility_visible_digits"] + "f"
225
226    if gum.config["influenceDiagram", "utility_show_loss"] == "True":
227      vx = [f"{var.label(int(i))} [{-lu[i] if lu[i] != 0 else 0:{fmt}}]" for i in ra_reverse]
228    else:
229      vx = [f"{var.label(int(i))} [{lu[i]:{fmt}}]" for i in ra_reverse]
230  else:
231    vx = [var.label(int(i)) for i in ra_reverse]
232
233  fig = plt.figure()
234  fig.set_figheight(scale * var.domainSize() / 4.0)
235  fig.set_figwidth(scale * 2)
236
237  ax = fig.add_subplot(111)
238  ax.set_facecolor('white')
239
240  vals = p.tolist()
241  vals.reverse()
242  bars = ax.barh(ra, vals,
243                 align='center',
244                 color=gum.config['notebook', 'histogram_color'])
245
246  for b in bars:
247    if b.get_width() != 0:
248      txt = f"{b.get_width():.{gum.config['notebook', 'histogram_horizontal_visible_digits']}}"
249      ax.text(1, b.get_y(), txt, ha='right', va='bottom')
250
251  ax.set_xlim(0, 1)
252  ax.set_yticks(np.arange(var.domainSize()))
253  ax.set_yticklabels(vx, color=txtcolor)
254  ax.set_xticklabels([])
255  # ax.set_xlabel('Probability')
256  # if utility, we do not show the mean/sigma of the proba.
257  ax.set_title(_getTitleHisto(p, util is None), color=txtcolor)
258  ax.get_xaxis().grid(True)
259  ax.margins(0)
260
261  return fig
262
263
264def proba2histo(p, scale=1.0, util=None, txtcolor="Black"):
265  """
266  compute the representation of an histogram for a mono-dim Potential
267
268  Parameters
269  ----------
270    p : pyAgrum.Potential
271      the mono-dimensional Potential
272    util : pyAgrum.Potential
273      an (optional) secondary Potential (values in labels)
274    txtcolor : str
275      color for text
276
277  Returns
278  -------
279  matplotlib.Figure
280    a matplotlib histogram for a Potential p.
281  """
282  if util is not None:
283    return _getProbaH(p, scale, util=util, txtcolor=txtcolor)
284
285  if p.variable(0).domainSize() > int(gum.config['notebook', 'histogram_line_threshold']):
286    return _getProbaLine(p, scale, txtcolor=txtcolor)
287
288  if p.variable(0).domainSize() > int(gum.config['notebook', 'histogram_horizontal_threshold']):
289    return _getProbaV(p, scale, txtcolor=txtcolor)
290
291  return _getProbaH(p, scale, util=util, txtcolor=txtcolor)
292
293
294def saveFigProba(p, filename, util=None, bgcol=None, txtcolor="Black"):
295  """
296  save a figure  which is the representation of an histogram for a mono-dim Potential
297
298  Parameters
299  ----------
300    p : pyAgrum.Potential
301      the mono-dimensional Potential
302    util : pyAgrum.Potential
303      an (optional) secondary Potential (values in labels)
304    bgcolor: str
305      color for background (transparent if None)
306    txtcolor : str
307      color for text
308  """
309  fig = proba2histo(p, util=util, txtcolor=txtcolor)
310
311  if bgcol is None:
312    fc = gum.config["notebook", "figure_facecolor"]
313  else:
314    fc = bgcol
315
316  fig.savefig(filename, bbox_inches='tight', transparent=False, facecolor=fc,
317              pad_inches=0.05, dpi=fig.dpi, format=gum.config["notebook", "graph_format"])
318  plt.close(fig)
319
320
321def probaMinMaxH(pmin, pmax, scale=1.0, txtcolor="black"):
322  """
323  compute the representation of an horizontal histogram for a mono-dim Potential
324
325  Parameters
326  ----------
327    pmin,pmax : pyAgrum.Potential
328      two mono-dimensional Potential
329    txtcolor : str
330      color for text
331
332  Returns
333  -------
334  matplotlib.Figure
335    a matplotlib histogram for a bi-Potential pmin,pmax.
336  """
337  var = pmin.variable(0)
338  ra = np.arange(var.domainSize())
339
340  ra_reverse = np.arange(var.domainSize() - 1, -1, -1)  # reverse order
341  vx = [var.label(int(i)) for i in ra_reverse]
342
343  fig = plt.figure()
344  fig.set_figheight(scale * var.domainSize() / 4.0)
345  fig.set_figwidth(scale * 2)
346
347  ax = fig.add_subplot(111)
348  ax.set_facecolor('white')
349
350  vmin = pmin.tolist()
351  vmin.reverse()
352  vmax = pmax.tolist()
353  vmax.reverse()
354  barsmax = ax.barh(ra, vmax,
355                    align='center',
356                    color="#BBFFAA")
357  barsmin = ax.barh(ra, vmin,
358                    align='center',
359                    color=gum.config['notebook', 'histogram_color'])
360
361  for b in barsmax:
362    txt = f"{b.get_width():.{gum.config['notebook', 'histogram_horizontal_visible_digits']}}"
363    ax.text(1, b.get_y(), txt, ha='right', va='bottom')
364  for b in barsmin:
365    txt = f"{b.get_width():.{gum.config['notebook', 'histogram_horizontal_visible_digits']}}"
366    ax.text(0, b.get_y(), txt, ha='left', va='bottom')
367
368  ax.set_xlim(0, 1)
369  ax.set_yticks(np.arange(var.domainSize()))
370  ax.set_yticklabels(vx, color=txtcolor)
371  ax.set_xticklabels([])
372  ax.set_title(pmin.variable(0).name(), color=txtcolor)
373  ax.get_xaxis().grid(True)
374  ax.margins(0)
375
376  return fig
377
378
379def saveFigProbaMinMax(pmin, pmax, filename, bgcol=None, txtcolor="Black"):
380  """
381  save a figure  which is the representation of an histogram for a bi-Potential (min,max)
382
383  Parameters
384  ----------
385    pmin : pyAgrum.Potential
386      the mono-dimensional Potential for min values
387    pmax : pyAgrum.Potential
388      the mono-dimensional Potential for max value
389    bgcolor: str
390      color for background (transparent if None)
391    txtcolor : str
392      color for text
393  """
394  fig = probaMinMaxH(pmin, pmax, txtcolor=txtcolor)
395
396  if bgcol is None:
397    fc = gum.config["notebook", "figure_facecolor"]
398  else:
399    fc = bgcol
400
401  fig.savefig(filename, bbox_inches='tight', transparent=False, facecolor=fc,
402              pad_inches=0.05, dpi=fig.dpi, format=gum.config["notebook", "graph_format"])
403  plt.close(fig)
404