| | """Tools for arithmetic error propagation.""" |
| |
|
| | from itertools import repeat, combinations |
| |
|
| | from sympy.core.add import Add |
| | from sympy.core.mul import Mul |
| | from sympy.core.power import Pow |
| | from sympy.core.singleton import S |
| | from sympy.core.symbol import Symbol |
| | from sympy.functions.elementary.exponential import exp |
| | from sympy.simplify.simplify import simplify |
| | from sympy.stats.symbolic_probability import RandomSymbol, Variance, Covariance |
| | from sympy.stats.rv import is_random |
| |
|
| | _arg0_or_var = lambda var: var.args[0] if len(var.args) > 0 else var |
| |
|
| |
|
| | def variance_prop(expr, consts=(), include_covar=False): |
| | r"""Symbolically propagates variance (`\sigma^2`) for expressions. |
| | This is computed as as seen in [1]_. |
| | |
| | Parameters |
| | ========== |
| | |
| | expr : Expr |
| | A SymPy expression to compute the variance for. |
| | consts : sequence of Symbols, optional |
| | Represents symbols that are known constants in the expr, |
| | and thus have zero variance. All symbols not in consts are |
| | assumed to be variant. |
| | include_covar : bool, optional |
| | Flag for whether or not to include covariances, default=False. |
| | |
| | Returns |
| | ======= |
| | |
| | var_expr : Expr |
| | An expression for the total variance of the expr. |
| | The variance for the original symbols (e.g. x) are represented |
| | via instance of the Variance symbol (e.g. Variance(x)). |
| | |
| | Examples |
| | ======== |
| | |
| | >>> from sympy import symbols, exp |
| | >>> from sympy.stats.error_prop import variance_prop |
| | >>> x, y = symbols('x y') |
| | |
| | >>> variance_prop(x + y) |
| | Variance(x) + Variance(y) |
| | |
| | >>> variance_prop(x * y) |
| | x**2*Variance(y) + y**2*Variance(x) |
| | |
| | >>> variance_prop(exp(2*x)) |
| | 4*exp(4*x)*Variance(x) |
| | |
| | References |
| | ========== |
| | |
| | .. [1] https://en.wikipedia.org/wiki/Propagation_of_uncertainty |
| | |
| | """ |
| | args = expr.args |
| | if len(args) == 0: |
| | if expr in consts: |
| | return S.Zero |
| | elif is_random(expr): |
| | return Variance(expr).doit() |
| | elif isinstance(expr, Symbol): |
| | return Variance(RandomSymbol(expr)).doit() |
| | else: |
| | return S.Zero |
| | nargs = len(args) |
| | var_args = list(map(variance_prop, args, repeat(consts, nargs), |
| | repeat(include_covar, nargs))) |
| | if isinstance(expr, Add): |
| | var_expr = Add(*var_args) |
| | if include_covar: |
| | terms = [2 * Covariance(_arg0_or_var(x), _arg0_or_var(y)).expand() \ |
| | for x, y in combinations(var_args, 2)] |
| | var_expr += Add(*terms) |
| | elif isinstance(expr, Mul): |
| | terms = [v/a**2 for a, v in zip(args, var_args)] |
| | var_expr = simplify(expr**2 * Add(*terms)) |
| | if include_covar: |
| | terms = [2*Covariance(_arg0_or_var(x), _arg0_or_var(y)).expand()/(a*b) \ |
| | for (a, b), (x, y) in zip(combinations(args, 2), |
| | combinations(var_args, 2))] |
| | var_expr += Add(*terms) |
| | elif isinstance(expr, Pow): |
| | b = args[1] |
| | v = var_args[0] * (expr * b / args[0])**2 |
| | var_expr = simplify(v) |
| | elif isinstance(expr, exp): |
| | var_expr = simplify(var_args[0] * expr**2) |
| | else: |
| | |
| | var_expr = Variance(expr) |
| | return var_expr |
| |
|