What is an uncertain number

Important

Uncertain Number refers to a unified class of mathematical constructs useful for uncertainty representation that generalize real numbers, intervals, probability distributions, interval bounds on probability distributions (i.e. probability boxes (p-boxes)), and finite Dempster-Shafer structures (DSS).

The Python library pyuncertainnumber provides a closed computation environmnent where one can easily compute with uncertain numbers. Similar to automatic inference, one only needs to characterise the uncertain quantities of interest in his own analysis as uncertain numbers given the empirical knowledge, and then pyuncertainnumber will provide automatic uncertainty quantification.

These constructs are closely related to each other. pyuncertainnumber facilitates the computations of mixed-type uncertainties, which is common in real-world engineering analyses, since it is provided a consistent interface to work with uncertain numbers.

Constructs of uncertain numbers
import pyuncertainnumber as pun  # yeah, pun intended
import numpy as np

Interval type of uncertain number

an interval indicates a value imprecisely known within a range where no assumptions of likelihood about the enclosed values are made;

pun.I(3, 4)
UncertainNumber(essence='interval', intervals=[3.0,4.0], _construct=[3.0,4.0], nominal_value=3.5)

Distribution type of uncertain number

pun.D("gaussian", (4, 1))
UncertainNumber(essence='distribution', _construct=dist ~ gaussian(4, 1), nominal_value=4.0)

P-box type of uncertain number

P-boxes can be considered as interval bounds on cumulative distributions and Parametric p-boxes \(F_{X}(x|\theta^{I})\) are probability distributions whose parameters \(\theta^{I}\) and samples are intervals, and an interval (\(I = [a, b]\)) can be identified as a p-box \([H_a(x), H_b(x)]\) whose bounds are unit step functions denoted by \(H(x)\);

pun.normal([4, 6], 1)
UncertainNumber(essence='pbox', _construct=Pbox ~ (range=[0.910, 9.090], mean=[4.000, 6.000], var=[1.000, 1.000], shape=norm), nominal_value=5.0)

Dempster Shafer structure type of uncertain number

a DSS can be deemed as a discrete distribution with interval quantiles. A p-box can be discretised into a DSS with pairs of intervals (focal elements) and probability masses \(\{([a_i, b_i], p_i)_{1}^{N}\}\), and conversely a DSS can be stacked with a list of intervals.

# dempster-shafer-type uncertain number
pun.DSS(intervals=[[1,5], [3,6]], masses=[0.5, 0.5])
UncertainNumber(essence='pbox', _construct=Pbox ~ (range=[1.000, 6.000], mean=[2.002, 5.497], var=[666.000, 666.000]), nominal_value=3.749)

Note

Besides the above constructors, one can also use the canonical constructor style to construct uncertain numbers where one can specify many fields related to the quantity of interest, for example the units.

un = pun.UncertainNumber(
    name='elas_modulus', 
    symbol='E', 
    units='Pa', 
    essence='pbox', 
    pbox_parameters=['gaussian', ([0,12],[1,4])])

Information about the uncertain number can be hinted from the describe() method.

un.describe(style="verbose")
'This is a pbox-type Uncertain Number that follows a gaussian distribution with parameters ([0, 12], [1, 4])'

what you can do with uncertain numbers

The framework of uncertain numbers, embedded in the library pyuncertainnumber, facilitates trustworthy management of uncertainty. It provides capabilities across the typical uncertainty analysis pipeline, encompassing characterisation, aggregation, propagation, and applications including reliability analysis and optimisation under uncertainty, especailly with a focus on imprecise probabilities.

With the idea of duck-typing, one can directly compute with uncertain numbers through a Python function by drop-in replacements of real numbers (represented by basic Python numeric types or Numpy arrays). Numpy ufuncs are also supported in the definition of the function. Moreover, in cases when you cannot access the function (i.e. non-intrusive), there are metholodogies in house to deal with.

def foo(x, y, z):
    return x**3 + np.exp(y) - 5*z
# a deterministic call of the function with plain numbers will yield a result
foo(1.5, 2., 3)
np.float64(-4.23594390106935)
# uncertainty arithmetic 

i = pun.I(1, 2)
p = pun.normal([2, 3], 0.5)
dss = pun.DSS(intervals=[[1,5], [3,6]], masses=[0.5, 0.5])

foo(i, p, dss)
UncertainNumber(essence='pbox', _construct=Pbox ~ (range=[-27.424, 97.171], mean=[-20.473, 25.895], var=[20.938, 154.317]), nominal_value=2.711)

additional examples

see Validation: discrepancy assessment for propagation, see Aggregation of various sources of information, expert elicitation, or evidence for aggregation.

More examples demonstrating these capabilities are underway. Stay tuned!

Tip

As shown in Getting started, one can always fall back on computing with those constructs directly for low-level controls during computation.

from pyuncertainnumber import pba
i = pba.I(1, 2)
p = pba.normal([2, 3], 0.5)
dss = pba.DSS(intervals=[[1,5], [3,6]], masses=[0.5, 0.5])

foo(i, p, dss)
Pbox ~ (range=[-27.424, 97.171], mean=[-20.473, 25.895], var=[20.938, 154.317])