Skip to main content

Dubious is a Python library for propagating uncertainty through numerical computations

Project description

Dubious

Dubious is a Python library for propagating uncertainty through numerical computations. Instead of collapsing uncertain values into single numbers early, Dubious lets you represent values as probability distributions, combine them with normal arithmetic operations, and only evaluate the resulting uncertainty when you ask for it.

from dubious import Normal, Beta, Uncertain, Context

normal = Normal(5, 4)
normal2 = Normal(10,2)

x = Uncertain(normal) + Uncertain(normal2)

print(f"variance: {x.var()} mean: {x.mean()} q(0.05): {x.quantile(0.05)}")

Rounded output: variance: 19.9, mean: 15, q(0.05): 7.7


The core idea behind Dubious is lazy uncertainty propagation. We don't calculate approximations at each step, instead we build a graph of operations applied to uncertain values, and traverse it upon sampling. You can construct complex expressions from uncertain inputs in a simple and readable manner, and evaluate the result using Monte Carlo simulations.

By default, distributions are assumed to be independent. We can correlate two uncertain objects using a.corr(b,rho), implemented via Gaussian copula (see notes for details).

After applying any numerical operations to Uncertain objects, sampling and evaluation only occur when calling a function like mean(), quantile() or sample() is called.

Full documentation can be found at: https://dubious.readthedocs.io/en/latest/api/modules.html

Installation

With python v3.9+ pip install dubious

Notes for the user

If several instances of the same Uncertain object are involved in an operation these are assumed to represent the same variable so the samples used to calculate these values for each will be identical.

Correlation between Uncertain objects is currently implemented using Gaussian Copula. This rank based correlation and the rho value used to correlate different objects is NOT the same as the pearson coefficient.

Numpy RNG objects do not need to be provided by default, but they can be optionally provided for any function that generates its result using MC sampling methods, you can alternatively just provide a seed.

Classes

Distribution(): Currently supporting Normal, LogNormal, Beta and Uniform distributions. Distribution objects also support using other distribution objects for their parameters, although this may lead to unexpected behaviour in cases where parameters can become negative. For each you distribution can get mean(), var(), quantile and sample().

Uncertain(): Uncertain objects are the wrapper for distributions that allow them to be used like numeric values. Alongside being able to perform numeric operations on these uncertain objects, they support the same properties as standard distributions (mean, variance, sampling and quantile). You can apply the exact same operations on these objects you might apply to real data, and easily calculate the propagated uncertainty that comes from using several unreliable input values.

To ensure the same output after repeated calls, Uncertain objects support freeze() and unfreeze(), although this only freezes a signel Uncertain object. It is recommended to instead freeze the entire context for deterministic results.

Context(): Context objects own the graph through which we manage the uncertainty propagation. You can add uncertain objects from different contexts, although this is slightly less performant than first creating a context object, and then creating all new uncertain objects with ctx = Your context object.

Context objects also support freeze() and unfreeze(), it is recommended to freeze results through context objects. If you are allowing Uncertain objects to create their own contexts try my_uncertain_object.ctx.freeze() to freeze the entire graph.


Some examples:

from dubious.distributions import Normal
from dubious.core import Uncertain, Context

#Create a shared context.
ctx = Context()

# Define our Length distribution  (about 10 ± 1)
length_dist = Normal(10, 1)
length = Uncertain(length_dist, ctx=ctx)

# Define Width as 5 ± 0.5
width_dist = Normal(5, 0.5)
width = Uncertain(width_dist, ctx=ctx)

#Compute area using normal arithmetic
area = length * width

#Inspect the uncertainty
print("Mean area:", area.mean())
print("Variance:", area.var())
print("Some samples:", area.sample(5))

We can also use distribution and uncertain objects as parameters.

from dubious.distributions import Normal, Beta
from dubious.core import Uncertain, Context

ctx = Context()

#We can define distribution parameters with other distributions.
normal = Normal(10, 1)
beta = Beta(3,normal)

x = Uncertain(normal, ctx=ctx)
y = Uncertain(beta, ctx=ctx)

#Apply some arithmetic.
x = x*y

print(x.sample(5))

#We can also use uncertain distributions to define parameters.
normal3 = Normal(y+2, 3)
print(normal3.mean())

An example of correlation:

#two non-Gaussian marginals correlated using copula
from dubious.distributions import Beta, LogNormal
from dubious.core import Uncertain, Context

ctx = Context()

conv = Uncertain(Beta(20,80), ctx=ctx)
traffic = Uncertain(LogNormal(8.0,0.4), ctx=ctx)

conv.corr(traffic, 0.7)

sales = conv * traffic

print(f"Mean: {sales.mean()}")
print(f"p10, p90: {sales.quantile(0.1)}, {sales.quantile(0.9)}")

Mean: 685.3054124550143 p10, p90: 282.88651636845674, 1185.6903692334781

Correlated uncertainty propagation currently matches a Gaussian-copula reference to within ~0.25% relative error on tail quantiles.

0.3

Added

  • Added correlation via Gaussian Copula
  • Added freeze() and unfreeze() functions to uncertain and context objects. They will ensure the same set of samples is used for all function calls while frozen. Context freezing is recommended in most cases as it freezes every random node in the graph as well as constants. Freezing uncertain objects individually will lead to semi-random behaviour that isn't as useful in most cases.
  • Benchmarks and additional testing
  • Documentation now on https://dubious.readthedocs.io/

Changed

  • Changed import structure. Instead of everything living in the main name space, we have core, distributions and umath.
  • Seeds defaulted to 0 which meant that everything was deterministic by default. We now default as random and only when a seed or rng object is provided are outputs deterministic.

Fixed

0.2

Added

  • Context objects now handle graph ownership and can be merged, e.g. Uncertain objects from different contexts can be used together.
  • Uncertain objects and Distributions now inherit from Sampleable and can both be used as input parameters
  • Added log, sin, cos, tan, asin, acos and atan operations for Uncertain objects in umath. Umath functions also support normal numbers.

Fixed

  • Some functions had inconsistent requirements regarding numpy generators. Now all do not require one but give the option of either providing one or a seed.

0.1.1

Fixed

  • 0.1 release had a major bug making most uncertain methods unusable on other machines... oops

0.1

  • First release

Added

Distribution objects

  • Normal,
  • LogNormal
  • Uniform
  • Beta
  • Support for using distribution objects as params
  • Uncertain objects
  • Standard arithmetic through dunder methods

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dubious-0.3.0.tar.gz (19.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dubious-0.3.0-py3-none-any.whl (20.6 kB view details)

Uploaded Python 3

File details

Details for the file dubious-0.3.0.tar.gz.

File metadata

  • Download URL: dubious-0.3.0.tar.gz
  • Upload date:
  • Size: 19.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.3

File hashes

Hashes for dubious-0.3.0.tar.gz
Algorithm Hash digest
SHA256 e1469ebea6ec8e7f9d7dd8c56ebcfb62da5b9257034151af642636f45bf09199
MD5 5502e1b90212307bad3cebb4a8119ad4
BLAKE2b-256 9f560d040f9e9f306842d3e38b58e2e592c5091404c35ca87e244913f6d7b4d2

See more details on using hashes here.

File details

Details for the file dubious-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: dubious-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 20.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.3

File hashes

Hashes for dubious-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 864c70fcff9ba759dd96bec49e3b8f9e139b3c2000700900d8a41d131d92f665
MD5 cfe11633d26dd20b71ca52b481f5111c
BLAKE2b-256 d25b991cd2f6f53615c6b174509d1d2dd3b590a58b7fc5383c82c7bdb026d875

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page