## \file ucomponents.py
# \brief This file contains the module to model uncertain values.
# \author <a href="http://thomas.reidemeister.org/" target="_blank">
# Thomas Reidemeister</a>
## \namespace scuq::ucomponents
# \brief This namespace contains the classes to evaluate the
# uncertainty of scalar functions.
## \defgroup ucomponents The Uncertainty Module
#
# This module contains classes to model uncertain values.
# \author <a href="http://thomas.reidemeister.org/" target="_blank">
# Thomas Reidemeister</a>
# \addtogroup ucomponents
# @{
# Example for Uncertain Quantities
## This example shows how uncertain values can be used as quantities.
# Instead of encapsulating instances of <tt>quantities.Quantity</tt>
# inside an instance of <tt>ucomponents.UncertainInput</tt>, you should
# always encapsulate uncertain values inside quantities. Otherwise
# this will lead to unpredicable behavior.
# \see quantities The quantities module.
# \see ucomponents The module to evaluate the uncertainty of
# scalar models.
# \see cucomponents The module to evalute the uncertainty of
# complex-valued models
# \author Thomas Reidemeister
# \example UncertainQuantity.py
# standard modules
import numpy
# import operator
import contextlib
import numbers
import types
import warnings
# local modules
import scuq.arithmetic as arithmetic
import scuq.quantities as quantities
import scuq.units as units
import scuq.cucomponents as cucomponents
def _require_numeric(value, name="value", allow_rational=True):
types = (numbers.Number, arithmetic.RationalNumber) if allow_rational else numbers.Number
if not isinstance(value, types):
raise TypeError("%s must be numeric" % name)
def _reject_uncertain_or_quantity(value, name="value"):
if isinstance(value, UncertainComponent):
raise TypeError("%s must not be an UncertainComponent" % name)
if isinstance(value, quantities.Quantity):
raise TypeError("%s must not be a Quantity" % name)
def _require_component_operand(value, name="other"):
if not isinstance(value, (UncertainComponent, numbers.Number, arithmetic.RationalNumber)):
raise TypeError("%s must be an UncertainComponent or numeric value" % name)
def _require_context(value, name="context"):
if not isinstance(value, Context):
raise TypeError("%s must be a Context" % name)
def _require_uncertain_input(value, name="value"):
if not isinstance(value, UncertainInput):
raise TypeError("%s must be an UncertainInput" % name)
def _require_uncertain_component(value, name="component"):
if not isinstance(value, UncertainComponent):
raise TypeError("%s must be an UncertainComponent" % name)
def _deprecated_python2_method(name, replacement):
warnings.warn(
"%s is deprecated and kept only for Python 2 compatibility; use %s instead"
% (name, replacement),
DeprecationWarning,
stacklevel=2,
)
[docs]
def coerce(x, y):
t = type(x + y)
return (t(x), t(y))
[docs]
def clearDuplicates(alist):
"""Remove identical elements from a list
:param alist: A list that may contain identical elements.
:returns: A list that only contains unique elements.
"""
result = []
for newItem in alist:
contained = False
for oldItem in result:
if oldItem is newItem:
contained = True
break
if not contained:
result += [newItem]
return result
def _collect_component_tree(component):
"""Collect all uncertainty tree nodes reachable from *component*."""
nodes = []
seen = set()
stack = [component]
while stack:
node = stack.pop()
node_id = id(node)
if node_id in seen or not isinstance(node, UncertainComponent):
continue
seen.add(node_id)
nodes.append(node)
left = getattr(node, "_left", None)
right = getattr(node, "_right", None)
if left is not None:
stack.append(left)
if right is not None:
stack.append(right)
return nodes
@contextlib.contextmanager
def _cached_component_evaluation(component):
"""Temporarily cache tree-node values and partial uncertainties.
The cache is scoped to one uncertainty evaluation and is removed before the
caller regains control.
"""
value_cache = {}
uncertainty_cache = {}
originals = []
def make_value_wrapper(original):
def get_value(self):
key = id(self)
if key in value_cache:
return value_cache[key]
value = original()
value_cache[key] = value
return value
return get_value
def make_uncertainty_wrapper(original):
def get_uncertainty(self, partial):
key = (id(self), id(partial))
if key in uncertainty_cache:
return uncertainty_cache[key]
value = original(partial)
uncertainty_cache[key] = value
return value
return get_uncertainty
try:
for node in _collect_component_tree(component):
had_get_value = "get_value" in node.__dict__
had_get_uncertainty = "get_uncertainty" in node.__dict__
originals.append(
(
node,
had_get_value,
getattr(node, "get_value", None),
had_get_uncertainty,
getattr(node, "get_uncertainty", None),
)
)
node.get_value = types.MethodType(
make_value_wrapper(node.get_value), node
)
node.get_uncertainty = types.MethodType(
make_uncertainty_wrapper(node.get_uncertainty), node
)
yield
finally:
for node, had_get_value, get_value, had_get_uncertainty, get_uncertainty in originals:
if had_get_value:
node.get_value = get_value
else:
try:
del node.get_value
except AttributeError:
pass
if had_get_uncertainty:
node.get_uncertainty = get_uncertainty
else:
try:
del node.get_uncertainty
except AttributeError:
pass
[docs]
class UncertainComponent:
"""This is the abstract base class to model components of
uncertainty as described in by "The GUM Tree".
measurement uncertainty"; B. D. Hall; Industrial Research
Report 1291; Measurements Standards Laboratory New Zealand (2003).
"""
[docs]
def __init__(self):
"""Default constructor."""
raise NotImplementedError
[docs]
def arithmetic_check(self):
"""This method checks this instance for mathematical correctness.
You should overload this method, if your class is not defined
for specific argument values. If any (mathematical) invalid
values have been assigned, your implementation should raise an
ArithmeticError explaining the problem.
This method is usually called within the constructor of a class,
after the members have been initialized.
"""
# if not overriden, assume all arguements are possible
return True
[docs]
def get_value(self):
"""Abstract method: The implementation should return
a numeric value (e.g. float,int,long,or arithmetic.RationalNumber)
representing the value assigned to the component of uncertainty.
:returns: A numeric value, representing the value.
"""
raise NotImplementedError
[docs]
def get_uncertainty(self, component):
"""Abstract method: The implementation should return
a numeric value (e.g. float,int,long,or arithmetic.RationalNumber)
representing the standard uncertainty of this component.
:param component: Another instance of uncertainty.
If the argument is this instance the uncertainty is
returned, \frac{0}{1} should be returned otherwise.
This is analogous to taking the derivate
of an independent variable. For further explanation
see "The GUM tree".
:returns: A numeric value, representing the standard uncertainty. measurement uncertainty"; B. D. Hall; Industrial Research Report 1291; Measurements Standards Laboratory New Zealand (2003).
"""
raise NotImplementedError
[docs]
def depends_on(self):
"""Abstract method: The implementation should return a list of
the components of uncertainty, that this component depends on.
:returns: A list of the components of uncertainty.
"""
raise NotImplementedError
[docs]
def __getstate__(self):
"""Serialization using pickle.
:returns: A string that represents the serialized form of this instance.
"""
raise NotImplementedError
[docs]
def __setstate__(self, state):
"""Deserialization using pickle.
:param state: The state of the object.
"""
raise NotImplementedError
[docs]
def value_of(value):
"""A factory method, that can be used to create instances of
uncertain components. This method returns instances of
UncertainNumber depending on the argument.
:param value: An instance of UncertainNumber or a numeric
value.
:returns: The argument, if it is already an instance of UncertainNumber, or a new instance of UncertainInput (having an uncertainty of 0.0) if the argument is a numeric value (i.e. int,float...). encapsulate quantites in UncertainValues. Plase use Quantity(UncertainValue) instead.
"""
# do not encapsulate uncertain components
if isinstance(value, UncertainComponent):
return value
# do not encapsulate quantities
if isinstance(value, quantities.Quantity):
raise TypeError(
"You cannot encapsulate quantities in"
+ " Uncertain components;"
+ " Use Quantity(UncertainValue) instead"
)
# if a numeric input is given,
# assume it is an uncertain input
_require_numeric(value)
return UncertainInput(value, 0.0)
value_of = staticmethod(value_of)
[docs]
def gaussian(value, sigma, dof=arithmetic.INFINITY):
"""A factory method, that can be used to create instances of
uncertain components. This method returns uncertain inputs
that are quantified as a gaussian distribution, centered
at value, and having the uncertainty sigma.
:param value: A numeric value, representing x.
:param sigma: A numeric value, representing a
:param dof: The assigned number of degrees of freedom.
:returns: An uncertain component.
"""
_reject_uncertain_or_quantity(value)
_reject_uncertain_or_quantity(sigma, "sigma")
_require_numeric(value, allow_rational=False)
_require_numeric(sigma, "sigma", allow_rational=False)
return UncertainInput(value, sigma, dof)
gaussian = staticmethod(gaussian)
uniform = staticmethod(uniform)
[docs]
def triangular(value, halfwidth, dof=arithmetic.INFINITY):
"""A factory method, that can be used to create instances of
uncertain components. This method returns uncertain inputs
that are quantified as a triangular distribution, centered
at x, and having the half-width a.
:param value: A numeric value, representing x.
:param halfwidth: A numeric value, representing a
:param dof: The assigned number of degrees of freedom.
:returns: An uncertain component, having the uncertainty
"""
_reject_uncertain_or_quantity(value)
_reject_uncertain_or_quantity(halfwidth, "halfwidth")
_require_numeric(value, allow_rational=False)
_require_numeric(halfwidth, "halfwidth", allow_rational=False)
uncertainty = halfwidth / numpy.sqrt(6.0)
return UncertainInput(value, uncertainty, dof)
triangular = staticmethod(triangular)
[docs]
def beta(value, p, q, dof=arithmetic.INFINITY):
"""A factory method, that can be used to create instances of
uncertain components. This method returns uncertain inputs
that are quantified as a beta distribution, having the
parameters p and q .
:param value: The assigned value.
:param p: A numeric value, representing p.
:param q: A numeric value, representing q
:param dof: The assigned number of degrees of freedom.
:returns: An uncertain component.
"""
_reject_uncertain_or_quantity(p, "p")
_reject_uncertain_or_quantity(value)
_reject_uncertain_or_quantity(q, "q")
_require_numeric(p, "p", allow_rational=False)
_require_numeric(value, allow_rational=False)
_require_numeric(q, "q", allow_rational=False)
uncertainty = numpy.sqrt(p * q / ((p + q) ** 2.0 * (p + q + 1.0)))
return UncertainInput(value, uncertainty, dof)
beta = staticmethod(beta)
[docs]
def arcsine(value, dof=arithmetic.INFINITY):
"""A factory method, that can be used to create instances of
uncertain components. This method returns uncertain inputs
that are quantified as an arcsin distribution.
:returns: An uncertain component.
"""
_reject_uncertain_or_quantity(value)
_require_numeric(value, allow_rational=False)
return UncertainComponent.beta(value, 0.5, 0.5, dof)
arcsine = staticmethod(arcsine)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has equal attributes as the argument
"""
raise NotImplementedError
### Emulation of numeric behaviour
[docs]
def __eq__(self, other):
r"""This method is an alias for (self is other). It checks
if the argument is identical with the current instance.
Imagine you want to compare
with sin(a \pm u_a) \times (b \pm u_b) with
Since in the first case the values are identical, they
are dependent. In the second case the values are the same,
but we do not know about their independence. Therefore the
second case needs a different handling. In order not to
confuse these two cases, this method has to check
for identity.
:param other: Another instance of UncertainComponent
:returns: True, if the argument is identical to the current instance.
"""
return self is other
[docs]
def __ne__(self, other):
"""This method is an alias for not(self is other). It checks
if the argument is not identical with the current instance.
:param other: Another instance of UncertainComponent
:returns: True, if the argument is not identical to the current instance.
"""
return not (self is other)
def __cmp__(self, other):
_deprecated_python2_method("__cmp__", "the rich comparison operators")
def cmp(a, b):
return (a > b) - (a < b)
sval = self.get_value()
try:
oval = other.get_value()
except AttributeError:
oval = other
return cmp(sval, oval)
def __lt__(self, other):
sval = self.get_value()
try:
oval = other.get_value()
except AttributeError:
oval = other
return sval < oval
def __le__(self, other):
sval = self.get_value()
try:
oval = other.get_value()
except AttributeError:
oval = other
return sval <= oval
def __gt__(self, other):
sval = self.get_value()
try:
oval = other.get_value()
except AttributeError:
oval = other
return sval > oval
def __ge__(self, other):
sval = self.get_value()
try:
oval = other.get_value()
except AttributeError:
oval = other
return sval >= oval
[docs]
def __add__(self, other):
"""This method adds the argument to this instance.
it will be converted using UncertainComponent.value_of.
:param other: A numeric value.
"""
_require_component_operand(other)
return Add(self, other)
[docs]
def arctan2(self, other):
"""This method provides an interface for
numpy.arctan2.
:param other: A numeric value.
"""
if not isinstance(other, UncertainComponent):
tmp, other = coerce(self, other)
return numpy.arctan2(tmp, other)
return ArcTan2(self, other)
[docs]
def hypot(self, other):
"""This method provides an interface for
numpy.hypot.
:param other: A numeric value.
"""
if not isinstance(other, UncertainComponent):
tmp, other = coerce(self, other)
return numpy.hypot(tmp, other)
return numpy.sqrt(self * self + other * other)
[docs]
def __sub__(self, other):
"""This method substracts the argument from this instance.
it will be converted using UncertainComponent.value_of.
:param other: A numeric value.
"""
_require_component_operand(other)
return Sub(self, other)
[docs]
def __mul__(self, other):
"""This method multiplies the argument by this instance.
it will be converted using UncertainComponent.value_of.
:param other: A numeric value.
"""
_require_component_operand(other)
return Mul(self, other)
[docs]
def __truediv__(self, other):
"""This method divides this instance by the argument.
it will be converted using UncertainComponent.value_of.
:param other: A numeric value.
"""
_require_component_operand(other)
return Div(self, other)
[docs]
def __pow__(self, other):
"""This method raises this to the power of the argument.
it will be converted using UncertainComponent.value_of.
:param other: A numeric value.
"""
_require_component_operand(other)
return Pow(self, other)
[docs]
def __radd__(self, other):
"""This method adds this instance to the argument.
it will be converted using UncertainComponent.value_of.
:param other: A numeric value.
"""
_require_component_operand(other)
return Add(other, self)
[docs]
def __rsub__(self, other):
"""This method substracts this instance from the argument.
it will be converted using UncertainComponent.value_of.
:param other: A numeric value.
"""
_require_component_operand(other)
return Sub(other, self)
[docs]
def __rmul__(self, other):
"""This method multiplies the argument by this instance.
it will be converted using UncertainComponent.value_of.
:param other: A numeric value.
"""
_require_component_operand(other)
return Mul(other, self)
[docs]
def __rtruediv__(self, other):
"""This method divides the argument by this instance.
it will be converted using UncertainComponent.value_of.
:param other: A numeric value.
"""
_require_component_operand(other)
return Div(other, self)
[docs]
def __rpow__(self, other):
"""This method raises the argument to the power of this instance.
it will be converted using UncertainComponent.value_of.
:param other: A numeric value.
"""
_require_component_operand(other)
return Pow(other, self)
[docs]
def __neg__(self):
"""This method negates this instance."""
return Neg(self)
[docs]
def __invert__(self):
"""Inverts this instance."""
return 1.0 / self
[docs]
def __abs__(self):
"""This method returs the absolute value of this instance."""
return Abs(self)
[docs]
def set_context(self, context):
"""Assign a context to this component.
This method is only used in combination with __str__.
If a context is assigned to the instance, the correlation
coefficients will be considered for __str__. Otherwise __str__
assumes that there is no correlation among the inputs.
:param context: An instance of Context.
"""
self._context = context
[docs]
def eval(self):
"""Factory method to get get an object with evaluated uncertainties.
:returns: object with <value> and <uncertainty>
"""
context = None
try:
context = self._context
except AttributeError:
context = Context()
_require_context(context)
uncert = context.uncertainty(self)
value = self.get_value()
return UncertainInput(value, uncert)
[docs]
def __str__(self):
"""This method returs the absolute value of this instance.
:returns: A string of the form "<value> +- <uncertainty>" or "<value> +- <uncertainty> [NC]", if no context was provided.
"""
bnc = False
context = None
try:
context = self._context
except AttributeError:
context = Context()
bnc = len(self.depends_on()) > 1
_require_context(context)
uncert = context.uncertainty(self)
value = self.get_value()
if bnc:
return str(value) + " +/- " + str(uncert) + " [NC]"
else:
return str(value) + " +/- " + str(uncert)
### Numpy compliance
[docs]
def arccos(self):
"""This method provides the broadcast interface for
numpy.arccos.
:returns: The inverse Cosine of this component.
"""
return ArcCos(self)
[docs]
def arccosh(self):
"""This method provides the broadcast interface for
numpy.arccosh.
:returns: The inverse hyperbolic Cosine of this component.
"""
return ArcCosh(self)
[docs]
def arcsin(self):
"""This method provides the broadcast interface for
numpy.arcsin.
:returns: The inverse Sine of this component.
"""
return ArcSin(self)
[docs]
def arcsinh(self):
"""This method provides the broadcast interface for
numpy.arcsinh.
:returns: The inverse hyperbolic Sine of this component.
"""
return ArcSinh(self)
[docs]
def arctan(self):
"""This method provides the broadcast interface for
numpy.arctan.
:returns: The inverse Tangent of this component.
"""
return ArcTan(self)
[docs]
def arctanh(self):
"""This method provides the broadcast interface for
numpy.arctanh.
:returns: The inverse hyperbolic Tangent of this component.
"""
return ArcTanh(self)
[docs]
def cos(self):
"""This method provides the broadcast interface for
numpy.cos.
:returns: The Cosine of this component.
"""
return Cos(self)
[docs]
def cosh(self):
"""This method provides the broadcast interface for
numpy.cosh.
:returns: The hyperbolic Cosine of this component.
"""
return Cosh(self)
[docs]
def tan(self):
"""This method provides the broadcast interface for
numpy.tan.
:returns: The Tangent of this component.
"""
return Tan(self)
[docs]
def tanh(self):
"""This method provides the broadcast interface for
numpy.tanh.
:returns: The hyperbolic Tangent of this component.
"""
return Tanh(self)
[docs]
def log10(self):
"""This method provides the broadcast interface for
numpy.log10.
:returns: The decadic Logarithm of this component.
"""
return Log(self) / Log(10.0)
[docs]
def log2(self):
"""This method provides the broadcast interface for
numpy.log2.
:returns: The decadic Logarithm of this component.
"""
return Log(self) / Log(2.0)
[docs]
def sin(self):
"""This method provides the broadcast interface for
numpy.sin.
:returns: The Sine of this component.
"""
return Sin(self)
[docs]
def sinh(self):
"""This method provides the broadcast interface for
numpy.sinh.
:returns: The hyperbolic Sine of this component.
"""
return Sinh(self)
[docs]
def sqrt(self):
"""This method provides the broadcast interface for
numpy.sqrt.
:returns: The Square Root of this component.
"""
return Sqrt(self)
[docs]
def square(self):
"""This method provides the broadcast interface for
numpy.sqrt.
:returns: The Square Root of this component.
"""
return self * self
[docs]
def fabs(self):
"""This method provides the broadcast interface for
numpy.fabs.
:returns: The absolute value of this component.
"""
return Abs(self)
[docs]
def absolute(self):
"""This method provides the broadcast interface for
numpy.absolute.
:returns: The absolute value of this component.
"""
return Abs(self)
[docs]
def exp(self):
"""This method provides the broadcast interface for
numpy.exp.
:returns: The Exponential of this component.
"""
return Exp(self)
[docs]
def log(self):
"""This method provides the broadcast interface for
numpy.log.
:returns: The Natural Logarithm of this component.
"""
return Log(self)
[docs]
def __coerce__(self, other):
"""Implementation of coercion rules.
See also: Coercion - The page describing the coercion rules.
"""
_deprecated_python2_method("__coerce__", "explicit conversion")
if isinstance(other, UncertainComponent):
return (self, other)
elif isinstance(other, quantities.Quantity):
new_self = quantities.Quantity.value_of(self)
return (new_self, other)
elif isinstance(other, cucomponents.CUncertainComponent):
raise NotImplementedError(
"You must not mix scalar and" + " complex-valued uncertain values"
)
elif (
isinstance(other, arithmetic.RationalNumber)
or isinstance(other, int)
or isinstance(other, int)
or isinstance(other, float)
):
other = UncertainComponent.value_of(other)
return (self, other)
elif isinstance(other, complex):
raise NotImplementedError(
"Please use the module cucomponents"
+ " to evaluate the uncertainty of complex"
+ "-valued quantities"
)
elif isinstance(other, units.Unit):
raise NotImplementedError(
"You cannot declare a unit as uncertain."
+ " Please use the quantities module"
+ " for that!"
)
else:
raise NotImplementedError()
[docs]
class BinaryOperation(UncertainComponent):
"""The abstract base class for modelling binary operations.
This class provides the abstract interface for GUM-tree-nodes
that have two silblings.
"""
## The right silbling of the operation.
_right = None
## The left silbling of the operation.
_left = None
[docs]
def __init__(self, left, right):
"""Default constructor.
constructor explicitly in order to
initialize the silblings!
:param left: Left silbling of this instance.
:param right: Right silbling of this instance.
"""
if left is None:
raise ValueError("left must not be None")
if right is None:
raise ValueError("right must not be None")
self._right = UncertainComponent.value_of(right)
self._left = UncertainComponent.value_of(left)
[docs]
def depends_on(self):
"""Get the components of uncertainty, that this class depends on.
:returns: A list of the components of uncertainty.
"""
list = self._left.depends_on()
list += self._right.depends_on()
return clearDuplicates(list)
[docs]
def get_right(self):
"""Return the right silbling.
:returns: The right silbling.
"""
return self._right
[docs]
def get_left(self):
"""Return the left silbling.
:returns: The left silbling.
"""
return self._left
[docs]
def __getstate__(self):
"""Serialization using pickle.
:returns: A string that represents the serialized form of this instance.
"""
return (self._left, self._right)
[docs]
def __setstate__(self, state):
"""Deserialization using pickle.
:param state: The state of the object.
"""
self._left, self._right = state
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, BinaryOperation):
return False
return self._right.equal_debug(other._right) and self._left.equal_debug(
other._left
)
[docs]
class UnaryOperation(UncertainComponent):
"""The abstract base class for modelling unary operations.
This class provides the abstract interface for GUM-tree-nodes
that have one silbling.
"""
## The silbling of the operation.
_right = None
[docs]
def __init__(self, right):
"""Default constructor.
:param right: The silbling of this instance.
"""
if right is None:
raise ValueError("right must not be None")
self._right = UncertainComponent.value_of(right)
[docs]
def depends_on(self):
"""Abstract method: The implementation should return a list of
the components of uncertainty, that this component depends on.
:returns: A list of the components of uncertainty.
"""
return clearDuplicates(self._right.depends_on())
[docs]
def get_silbling(self):
"""Return the silbling.
:returns: The silbling.
"""
return self._right
[docs]
def __getstate__(self):
"""Serialization using pickle.
:returns: A string that represents the serialized form of this instance.
"""
return self._right
[docs]
def __setstate__(self, state):
"""Deserialization using pickle.
:param state: The state of the object.
"""
self._right = state
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, UnaryOperation):
return False
return self._right.equal_debug(other._right)
[docs]
class Add(BinaryOperation):
"""This class models GUM-tree nodes that add two silblings."""
[docs]
def __init__(self, left, right):
"""Default constructor.
:param left: Left silbling of this instance.
:param right: Right silbling of this instance.
"""
BinaryOperation.__init__(self, left, right)
[docs]
def get_value(self):
"""Returns the sum of the silblings assigned.
:returns: A numeric value, representing the sum of the silblings.
"""
return self.get_left().get_value() + self.get_right().get_value()
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = x_1 + x_2 then
the resulting uncertainty is u(y) = u(x_1) + u(x_2) .
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
return self.get_left().get_uncertainty(
component
) + self.get_right().get_uncertainty(component)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Add):
return False
return BinaryOperation.equal_debug(self, other)
[docs]
class ArcTan2(BinaryOperation):
"""This class models the inverse two-argument tangent."""
[docs]
def __init__(self, left, right):
"""Default constructor.
:param left: Left silbling of this instance.
:param right: Right silbling of this instance.
"""
BinaryOperation.__init__(self, left, right)
[docs]
def get_value(self):
"""Returns the sum of the silblings assigned.
:returns: A numeric value, representing the inverse two-argument tangent of the inputs.
"""
return numpy.arctan2(self.get_left().get_value(), self.get_right().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = x_1 + x_2 then
the resulting uncertainty is u(y) = u(x_1) + u(x_2) .
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
lhs = self.get_left().get_uncertainty(component)
rhs = self.get_right().get_uncertainty(component)
lhs_val = self.get_left().get_value()
rhs_val = self.get_right().get_value()
return (
-rhs_val / (lhs_val**2 + rhs_val**2) * lhs
+ lhs_val / (lhs_val**2 + rhs_val**2) * rhs
)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, ArcTan2):
return False
return BinaryOperation.equal_debug(self, other)
[docs]
class Mul(BinaryOperation):
"""This class models GUM-tree nodes that multiply two silblings."""
[docs]
def __init__(self, left, right):
"""Default constructor.
:param left: Left silbling of this instance.
:param right: Right silbling of this instance.
"""
BinaryOperation.__init__(self, left, right)
[docs]
def get_value(self):
"""Returns the product of the silblings assigned.
:returns: A numeric value, representing the product of the silblings.
"""
return self.get_left().get_value() * self.get_right().get_value()
[docs]
def get_uncertainty(self, component):
r"""Returns the uncertainty of this node.
Let the node represent the operation y = x_1 \times x_2 then
the resulting uncertainty is
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
return self.get_right().get_value() * self.get_left().get_uncertainty(
component
) + self.get_left().get_value() * self.get_right().get_uncertainty(component)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Mul):
return False
return BinaryOperation.equal_debug(self, other)
[docs]
class Div(BinaryOperation):
"""This class models GUM-tree nodes that divide two silblings."""
[docs]
def __init__(self, left, right):
"""Default constructor.
:param left: Left silbling of this instance.
:param right: Right silbling of this instance.
"""
BinaryOperation.__init__(self, left, right)
self.arithmetic_check()
[docs]
def arithmetic_check(self):
"""Checks for divide by zero.
as value.
"""
if self.get_right().get_value() == 0.0:
raise ArithmeticError("Divide by Zero")
[docs]
def get_value(self):
"""Returns the fraction of the silblings assigned.
:returns: A numeric value, representing the fraction of the silblings.
"""
return self.get_left().get_value() / self.get_right().get_value()
[docs]
def get_uncertainty(self, component):
r"""Returns the uncertainty of this node.
Let the node represent the operation y = \frac{x_1}{x_2} then
the resulting uncertainty is
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x_1 = self.get_left().get_uncertainty(component)
u_x_2 = self.get_right().get_uncertainty(component)
x_1 = self.get_left().get_value()
x_2 = self.get_right().get_value()
return u_x_1 / x_2 - u_x_2 * x_1 / (x_2 * x_2)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Div):
return False
return BinaryOperation.equal_debug(self, other)
[docs]
class Sub(BinaryOperation):
"""This class models GUM-tree nodes that take the difference of
the two silblings.
"""
[docs]
def __init__(self, left, right):
"""Default constructor.
:param left: Left silbling of this instance.
:param right: Right silbling of this instance.
"""
BinaryOperation.__init__(self, left, right)
[docs]
def get_value(self):
"""Returns the difference of the silblings assigned.
:returns: A numeric value, representing the difference of the silblings.
"""
return self.get_left().get_value() - self.get_right().get_value()
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = x_1 - x_2 then
the resulting uncertainty is u(y) = u(x_1) - u(x_2) .
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x_1 = self.get_left().get_uncertainty(component)
u_x_2 = self.get_right().get_uncertainty(component)
return u_x_1 - u_x_2
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Sub):
return False
return BinaryOperation.equal_debug(self, other)
[docs]
class Pow(BinaryOperation):
"""This class models GUM-tree nodes that raise the left silbling
to the power of the right one.
"""
[docs]
def __init__(self, left, right):
"""Default constructor.
:param left: Left silbling of this instance.
:param right: Right silbling of this instance.
"""
BinaryOperation.__init__(self, left, right)
[docs]
def get_value(self):
"""Returns the power pow(left,right)
of the silblings assigned.
:returns: A numeric value, representing the power of the silblings.
"""
return self.get_left().get_value() ** self.get_right().get_value()
[docs]
def get_uncertainty(self, component):
r"""Returns the uncertainty of this node.
Let the node represent the operation
::
y = x_1^{x_2}
then the resulting uncertainty is
::
u(y) = x_2 \times x_1^{x_2-1} \times u(x_1) + x_1^{x_2} \times ln(x_1) \times u(x_2) .
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x_1 = self.get_left().get_uncertainty(component)
u_x_2 = self.get_right().get_uncertainty(component)
x_1 = self.get_left().get_value()
x_2 = self.get_right().get_value()
if x_1 == 0.0 and x_2 < 1.0:
raise ArithmeticError(
"The uncertainty of a power is not defined for zero base "
"and exponent smaller than one"
)
if abs(u_x_2) <= 1e-12:
return x_2 * numpy.power(x_1, (x_2 - 1.0)) * u_x_1
if x_1 <= 0.0:
raise ArithmeticError(
"The uncertainty of a power with uncertain exponent requires "
"a positive base"
)
return (x_2 * numpy.power(x_1, (x_2 - 1.0)) * u_x_1 +
numpy.power(x_1, x_2) * u_x_2 * numpy.log(x_1))
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Pow):
return False
return BinaryOperation.equal_debug(self, other)
[docs]
class Cos(UnaryOperation):
"""This class models the GUM-tree-nodes that take the Cosine of a
silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
[docs]
def get_value(self):
"""Returns the Cosine of the silbling.
:returns: A numeric value, representing the Cosine of the silblings.
"""
return numpy.cos(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = cos(x) then
the resulting uncertainty is u(y) = -sin(x) \times u(x) .
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return -numpy.sin(x) * u_x
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Cos):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class Sin(UnaryOperation):
"""This class models the GUM-tree-nodes that take the Sine of a
silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
[docs]
def get_value(self):
"""Returns the Sine of the silbling.
:returns: A numeric value, representing the Sine of the silblings.
"""
return numpy.sin(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = sin(x) then
the resulting uncertainty is u(y) = cos(x) \times u(x) .
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return numpy.cos(x) * u_x
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Sin):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class Tan(UnaryOperation):
r"""This class models the GUM-tree-nodes that take the Tangent of a
silbling.
this class may return invalid values instead of raising an
OverflowError for values close to n\times\frac{\pi}{2}.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
self.arithmetic_check()
[docs]
def get_value(self):
"""Returns the Tangent of the silbling.
:returns: A numeric value, representing the Tangent of the silblings.
"""
return numpy.tan(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = tan(x) then
the resulting uncertainty is u(y) = \frac{u(x)}{cos^2(x)}.
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return u_x / (numpy.cos(x) * numpy.cos(x))
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Tan):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class Sqrt(UnaryOperation):
"""This class models the GUM-tree-nodes that take the square root of a
silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
# self.arithmetic_check()
[docs]
def arithmetic_check(self):
"""Checks for undefined arguments."""
if self.get_silbling().get_value() < 0.0:
raise ArithmeticError("The argument must be positive")
[docs]
def get_value(self):
"""Returns the square root of the silbling.
:returns: A numeric value, representing the square root of the silblings.
"""
return numpy.sqrt(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
r"""Returns the uncertainty of this node.
Let the node represent the operation y = \sqrt{x} then
the resulting uncertainty is u(y) = \frac{1}{2\sqrt{x}}u(x).
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return 0.5 / numpy.sqrt(x) * u_x
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Sqrt):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class Log(UnaryOperation):
"""This class models the GUM-tree-nodes that take the Natural Logarithm of a
silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
self.arithmetic_check()
[docs]
def arithmetic_check(self):
"""Checks for undefined arguments."""
if self.get_silbling().get_value() < 0.0:
raise ArithmeticError("The argument must be positive")
[docs]
def get_value(self):
"""Returns the Natural Logarithm of the silbling.
:returns: A numeric value, representing the Natural Logarithm of the silblings.
"""
return numpy.log(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = ln(x) then
the resulting uncertainty is u(y) = \frac{1}{x}u(x).
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
if not isinstance(x, float):
return arithmetic.RationalNumber(1) / x * u_x
return u_x / x
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Log):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class ArcSin(UnaryOperation):
"""This class models the GUM-tree-nodes that take the Arc Sine of a
silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
self.arithmetic_check()
[docs]
def arithmetic_check(self):
"""Checks for undefined arguments."""
value = self.get_silbling().get_value()
if value < -1.0 or value > 1.0:
raise ArithmeticError("The argument must be within [-1,1]")
[docs]
def get_value(self):
"""Returns the Arc Sine of the silbling.
:returns: A numeric value, representing the Arc Sine of the silblings.
"""
return numpy.arcsin(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
r"""Returns the uncertainty of this node.
Let the node represent the operation y = arcsin(x) then
the resulting uncertainty is u(y) = \frac{1}{\sqrt{1-x^2}}u(x).
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return u_x / numpy.sqrt(1.0 - x * x)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, ArcSin):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class ArcSinh(UnaryOperation):
"""This class models the GUM-tree-nodes that take the inverse
Hyperbolic Sine of a silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
[docs]
def get_value(self):
"""Returns the inverse Hyperbolic Sine of a silbling.
:returns: A numeric value, representing the inverse Hyperbolic Sine of a silbling.
"""
return numpy.arcsinh(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = arcsinh(x) then
the resulting uncertainty is
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return u_x / numpy.sqrt(1.0 + x * x)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, ArcSinh):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class ArcCos(UnaryOperation):
"""This class models the GUM-tree-nodes that take the Arcus Cosine of a
silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
self.arithmetic_check()
[docs]
def arithmetic_check(self):
"""Checks for undefined arguments."""
value = self.get_silbling().get_value()
if value < -1.0 or value > 1.0:
raise ArithmeticError("The argument must be within [-1,1]")
[docs]
def get_value(self):
"""Returns the Arc Cosine of the silbling.
:returns: A numeric value, representing the Arc Cosine of the silblings.
"""
return numpy.arccos(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
r"""Returns the uncertainty of this node.
Let the node represent the operation y = arccos(x) then
the resulting uncertainty is u(y) = -\frac{1}{\sqrt{1-x^2}}u(x).
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return -u_x / numpy.sqrt(1.0 - x * x)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, ArcCos):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class ArcCosh(UnaryOperation):
"""This class models the GUM-tree-nodes that take the inverse
Hyperbolic Cosine.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
self.arithmetic_check()
[docs]
def arithmetic_check(self):
r"""Checks for undefined arguments.
within (1,\infty].
"""
value = self.get_silbling().get_value()
if value <= 1.0:
raise ArithmeticError("The argument must be within (1,inf]")
[docs]
def get_value(self):
"""Returns the Arc Cosine of the silbling.
:returns: A numeric value, representing the Arc Cosine of the silblings.
"""
return numpy.arccosh(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = arccosh(x) then
the resulting uncertainty is
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return u_x / (numpy.sqrt(x - 1.0) * numpy.sqrt(x + 1.0))
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, ArcCosh):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class ArcTan(UnaryOperation):
"""This class models the GUM-tree-nodes that take the Arcus Tangent of a
silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
[docs]
def get_value(self):
"""Returns the Arc Tangent of the silbling.
:returns: A numeric value, representing the Arc Tangent of the silblings.
"""
return numpy.arctan(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = arcsin(x) then
the resulting uncertainty is u(y) = -\frac{1}{1+x^2}u(x).
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return u_x / (1.0 + x * x)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, ArcTan):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class ArcTanh(UnaryOperation):
"""This class models the GUM-tree-nodes that take the inverse
Hyperbolic Tangent of a silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
self.arithmetic_check()
[docs]
def arithmetic_check(self):
"""Checks for undefined arguments.
within (-1,1).
"""
value = self.get_silbling().get_value()
if value <= -1.0 or value >= 1.0:
raise ArithmeticError("The argument must be within (-1,1)")
[docs]
def get_value(self):
"""Returns the Arc Tangent of the silbling.
:returns: A numeric value, representing the inverse hyperbolic Tangent of the silblings.
"""
return numpy.arctanh(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = arctanh(x) then
the resulting uncertainty is u(y) = \frac{1}{1-x^2}u(x).
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return u_x / (1.0 - x * x)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, ArcTanh):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class Cosh(UnaryOperation):
"""This class models the GUM-tree-nodes that take the Hyperbolic Cosine of a
silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
[docs]
def get_value(self):
"""Returns the Hyperbolic Cosine of the silbling.
:returns: A numeric value, representing the Hyperbolic Cosine of the silblings.
"""
return numpy.cosh(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = cosh(x) then
the resulting uncertainty is u(y) = sinh(x) u(x).
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return u_x * numpy.sinh(x)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Cosh):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class Sinh(UnaryOperation):
"""This class models the GUM-tree-nodes that take the Hyperbolic Sine of a
silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
[docs]
def get_value(self):
"""Returns the Hyperbolic Sine of the silbling.
:returns: A numeric value, representing the Hyperbolic Sine of the silbling.
"""
return numpy.sinh(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = sinh(x) then
the resulting uncertainty is u(y) = cosh(x)u(x).
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return u_x * numpy.cosh(x)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Sinh):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class Tanh(UnaryOperation):
"""This class models the GUM-tree-nodes that take the Hyperbolic Tangent of a
silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
[docs]
def get_value(self):
"""Returns the Hyperbolic Tangent of the silbling.
:returns: A numeric value, representing the Hyperbolic Sine of the silbling.
"""
return numpy.tanh(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = tanh(x) then
the resulting uncertainty is u(y) = (1 - tanh^{2}(x)) u(x).
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return u_x * (1.0 - numpy.tanh(x) * numpy.tanh(x))
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Tanh):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class Exp(UnaryOperation):
"""This class models the GUM-tree-nodes that take the exponential of a
silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
[docs]
def get_value(self):
"""Returns the exponential of the silbling.
:returns: A numeric value, representing the exponential of the silbling.
"""
return numpy.exp(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = e^x then
the resulting uncertainty is u(y) = x \times e^x \times u(x).
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
x = self.get_silbling().get_value()
return u_x * numpy.exp(x)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Exp):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class Abs(UnaryOperation):
"""This class models the GUM-tree-nodes that take the absolute value of a
silbling.
"""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
[docs]
def get_value(self):
"""Returns the exponential of the silbling.
:returns: A numeric value, representing the absolute value of the silbling.
"""
return numpy.absolute(self.get_silbling().get_value())
[docs]
def get_uncertainty(self, component):
"""Return the uncertainty of this node.
For ``y = |x|``, this returns ``u(y) = |u(x)|``.
"""
u_x = self.get_silbling().get_uncertainty(component)
return numpy.absolute(u_x)
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Abs):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class Neg(UnaryOperation):
"""This class models the unary negation as GUM-tree-element."""
[docs]
def __init__(self, right):
"""Default constructor.
:param right: Right silbling of this instance.
"""
UnaryOperation.__init__(self, right)
[docs]
def get_value(self):
"""Returns the exponential of the silbling.
:returns: A numeric value, representing the negative value of the silbling.
"""
return -self.get_silbling().get_value()
[docs]
def get_uncertainty(self, component):
"""Returns the uncertainty of this node.
Let the node represent the operation y = -x then
the resulting uncertainty is u(y) = - u(x) .
:param component: Another instance of uncertainty.
:returns: A numeric value, representing the standard uncertainty.
"""
u_x = self.get_silbling().get_uncertainty(component)
return -u_x
[docs]
def equal_debug(self, other):
"""A method that is only used for serialization checking.
:param other: Another instance of UncertainComponent
:returns: True, if the instance has the same attribute values as the argument
"""
other = UncertainComponent.value_of(other)
if not isinstance(other, Neg):
return False
return UnaryOperation.equal_debug(self, other)
[docs]
class Context:
r"""This class provides the context for an uncertainty evaluation.
It maintains the correlation between the inputs and can be used
to evaluate the combined standard uncertainty, as shown below.
Let your model be y = f(x_1,x_2,\ldots,x_N), then
\left(\frac{\delta f}{\delta x_i} \right)^2 u^2(x_i)
+ 2 \sum_{i=1}^{N}\sum_{j=i+1}^{N}
\frac{\delta f}{\delta x_i}\frac{\delta f}{\delta x_j}
u(x_i,x_j).
"""
[docs]
def __init__(self):
"""This method initializes the correlation matrix of this context.
contexts. Thus, you could maintain various correlation
models.
"""
self._correlationMatrix = {}
[docs]
def set_correlation(self, firstItem, secondItem, corr):
"""This method sets the correlation coefficient r(x_1,x_2)
of two inputs. Where
:param firstItem: Is x_1 as denoted above.
:param secondItem: Is x_2 as denoted above.
:param corr: The correlation as described by r(x_1,x_2) .
"""
# masquerade quantities
if isinstance(firstItem, quantities.Quantity) or isinstance(
secondItem, quantities.Quantity
):
firstItem = quantities.Quantity.value_of(firstItem)
secondItem = quantities.Quantity.value_of(secondItem)
firstUnit = firstItem.get_default_unit()
secondUnit = secondItem.get_default_unit()
firstComp = firstItem.get_value(firstUnit)
secondComp = secondItem.get_value(secondUnit)
self.set_correlation(firstComp, secondComp, corr)
return
_require_uncertain_input(firstItem, "firstItem")
_require_uncertain_input(secondItem, "secondItem")
# autocorrelation automatically implied
if firstItem == secondItem:
return
# Update the covariance (lookup-table)
self._correlationMatrix[(firstItem, secondItem)] = corr
# ensure symmetry
self._correlationMatrix[(secondItem, firstItem)] = corr
[docs]
def get_correlation(self, firstItem, secondItem):
"""This method returns the correlation coefficient r(x_1,x_2)
of two inputs. Where
If no correlation has been defined before, this method returns
``0.0``, except for x_1 = x_2. In the last case this
method returns ``1.0``.
:param firstItem: Is x_1 as denoted above.
:param secondItem: Is x_2 as denoted above.
"""
if isinstance(firstItem, quantities.Quantity) or isinstance(
secondItem, quantities.Quantity
):
firstItem = quantities.Quantity.value_of(firstItem)
secondItem = quantities.Quantity.value_of(secondItem)
firstUnit = firstItem.get_default_unit()
secondUnit = secondItem.get_default_unit()
firstComp = firstItem.get_value(firstUnit)
secondComp = secondItem.get_value(secondUnit)
return self.get_correlation(firstComp, secondComp)
if firstItem == secondItem:
return 1.0
return self._correlationMatrix.get((firstItem, secondItem), 0.0)
[docs]
def uncertainty(self, component):
"""This method returns the combined standard uncertainty of an
uncertain value.
:param component: The component of uncertainty to evaluate.
:returns: The standard uncertainty.
"""
if isinstance(component, quantities.Quantity):
unit = component.get_default_unit()
ucomp = component.get_value(unit)
return quantities.Quantity(unit, self.uncertainty(ucomp))
_require_uncertain_component(component)
components = component.depends_on()
result = 0.0
with _cached_component_evaluation(component):
partials = [
(comp, component.get_uncertainty(comp))
for comp in components
]
if not self._correlationMatrix:
result = sum(partial * partial for _comp, partial in partials)
return numpy.sqrt(result)
for index, (comp1, partial1) in enumerate(partials):
result += partial1 * partial1
for comp2, partial2 in partials[index + 1:]:
result += (
2.0
* partial1
* self.get_correlation(comp1, comp2)
* partial2
)
return numpy.sqrt(result)
[docs]
def value_uncertainty_unit(self, component):
"""This method returns the value, combined standard uncertainty, and unit of an
uncertain value.
:param component: The component of uncertainty to evaluate.
:returns: (value, The standard uncertainty,unit).
"""
# if( isinstance( component, quantities.Quantity ) ):
try:
unit = component.get_default_unit()
except AttributeError:
# does not have unit
unit = None
if unit:
ucomp = component.get_value(unit)
else:
ucomp = component.get_value()
try:
value = ucomp.get_value()
if unit:
uncertainty = self.uncertainty(component).get_value(unit)
else:
uncertainty = self.uncertainty(component).get_value()
except AttributeError:
value = ucomp
uncertainty = 0.0
return value, uncertainty, unit
# assert( isinstance( component, UncertainComponent ) )
[docs]
def dof(self, component):
r"""This method calculates the effective degrees of freedom using
the Welch-Satterthwaite formulae:
::
{\sum_{i=1}^N \frac{\left(
\frac{\delta f}{\delta x_i}\right)^4 u^4(x_i)}{\nu_i}}
Where u_c(y) is the combined standard uncertainty,
no standard procedure in python to declare infinity, we use
our own constant for it.
:param component: The component of uncertainty.
:returns: The effective degrees of freedom \nu_{eff} .
"""
if isinstance(component, quantities.Quantity):
unit = component.get_default_unit()
ucomp = component.get_value(unit)
return self.dof(ucomp)
_require_uncertain_component(component)
# Used to calculate the nominator of the formula described above.
u_c = self.uncertainty(component)
components = component.depends_on()
# check for inifinity (i.e. if one component is infinite,
# the entire result will be infinite) and calculate the
# denominator of the formula described above.
sum = 0.0
for comp in components:
_require_uncertain_input(comp, "comp")
dof = comp.get_dof()
if dof == 0.0:
return arithmetic.INFINITY
elif dof == arithmetic.INFINITY:
continue
else:
sum += (component.get_uncertainty(comp)) ** 4 / dof
dof_eff = u_c**4 / sum
return dof_eff
## Assign the current context to the given component.
# \attention This method is only useful in combination with
# UncertainComponent.__str__. The context assigned is
# not passed to operations performed on <tt>component</tt>.
# \see UncertainComponent.__str__
# \param self
# \param component The component to which the context should be
# attached.
# \return component having the context assigned.
[docs]
def value_of(self, component):
# assert(isinstance(component, UncertainComponent))
if isinstance(component, UncertainComponent):
component.set_context(self)
elif isinstance(component, quantities.Quantity):
component._value.set_context(self)
return component
## @}