Beispiel #1
0
def uarray(nominal_values, std_devs=None):
    """
    Returns a NumPy array of numbers with uncertainties
    initialized with the given nominal values and standard
    deviations.

    nominal_values, std_devs -- valid arguments for numpy.array, with
    identical shapes (list of numbers, list of lists, numpy.ndarray,
    etc.).

    std_devs=None is only used for supporting legacy code, where
    nominal_values can be the tuple of nominal values and standard
    deviations.
    """

    if std_devs is None:  # Obsolete, single tuple argument call
        deprecation('uarray() should now be called with two arguments.')
        (nominal_values, std_devs) = nominal_values

    return (numpy.vectorize(
        # ! Looking up uncertainties.Variable beforehand through
        # '_Variable = uncertainties.Variable' does not result in a
        # significant speed up:
        lambda v, s: uncertainties.Variable(v, s),
        otypes=[object])(nominal_values, std_devs))
Beispiel #2
0
def uquantity_to_variable(uquantities):
    out = ()
    # Likely needs optimization
    for uquan in uquantities:
        var = uncert.Variable(uquan.value, uquan.std_dev)
        var.derivatives = uquan.derivatives
        out = out + (var, )
    return out
Beispiel #3
0
def _compare_derivatives(func, numerical_derivatives, num_args_list=None):
    """
    Checks the derivatives of a function 'func' (as returned by the
    wrap() wrapper), by comparing them to the
    'numerical_derivatives' functions.

    Raises a DerivativesDiffer exception in case of problem.

    These functions all take the number of arguments listed in
    num_args_list.  If num_args is None, it is automatically obtained.

    Tests are done on random arguments.
    """

    # print "Testing", func.__name__

    if not num_args_list:

        # Detecting automatically the correct number of arguments is not
        # always easy (because not all values are allowed, etc.):

        num_args_table = {
            'atanh': [1],
            'log': [1, 2]  # Both numbers of arguments are tested
        }
        if func.__name__ in num_args_table:
            num_args_list = num_args_table[func.__name__]
        else:

            num_args_list = []

            # We loop until we find reasonable function arguments:
            # We get the number of arguments by trial and error:
            for num_args in range(10):
                try:
                    #! Giving integer arguments is good for preventing
                    # certain functions from failing even though num_args
                    # is their correct number of arguments
                    # (e.g. math.ldexp(x, i), where i must be an integer)
                    func(*(1, ) * num_args)
                except TypeError:
                    pass  # Not the right number of arguments
                else:  # No error
                    # num_args is a good number of arguments for func:
                    num_args_list.append(num_args)

            if not num_args_list:
                raise Exception("Can't find a reasonable number of arguments"
                                " for function '%s'." % func.__name__)

    for num_args in num_args_list:

        # Argument numbers that will have a random integer value:
        integer_arg_nums = set()

        if func.__name__ == 'ldexp':
            # The second argument must be an integer:
            integer_arg_nums.add(1)

        while True:
            try:

                # We include negative numbers, for more thorough tests:
                args = [
                    random.choice(list(range(-10, 10)))
                    if arg_num in integer_arg_nums else uncertainties.Variable(
                        random.random() * 4 - 2, 0)
                    for arg_num in range(num_args)
                ]

                # 'args', but as scalar values:
                args_scalar = [uncertainties.nominal_value(v) for v in args]

                func_approx = func(*args)

                # Some functions yield simple Python constants, after
                # wrapping in wrap(): no test has to be performed.
                # Some functions also yield tuples...
                if isinstance(func_approx, AffineScalarFunc):

                    # We compare all derivatives:
                    for (arg_num, (arg, numerical_deriv)) in (enumerate(
                            zip(args, numerical_derivatives))):

                        # Some arguments might not be differentiable:
                        if isinstance(arg, int):
                            continue

                        fixed_deriv_value = func_approx.derivatives[arg]

                        num_deriv_value = numerical_deriv(*args_scalar)

                        # This message is useful: the user can see that
                        # tests are really performed (instead of not being
                        # performed, silently):
                        print("Testing %s at %s, arg #%d" %
                              (func.__name__, args, arg_num))

                        if not _numbers_close(fixed_deriv_value,
                                              num_deriv_value, 1e-4):

                            # It is possible that the result is NaN:

                            # ! Python 2.6+: this would be
                            # not math.isnan(func_approx):
                            if func_approx == func_approx:
                                raise DerivativesDiffer(
                                    "Derivative #%d of function '%s' may be"
                                    " wrong: at args = %s,"
                                    " value obtained = %.16f,"
                                    " while numerical approximation = %.16f." %
                                    (arg_num, func.__name__, args,
                                     fixed_deriv_value, num_deriv_value))

            except ValueError(err):  # Arguments out of range, or of wrong type
                # Factorial(real) lands here:
                if str(err).startswith('factorial'):
                    integer_arg_nums = set([0])
                continue  # We try with different arguments
            # Some arguments might have to be integers, for instance:
            except TypeError:
                if len(integer_arg_nums) == num_args:
                    raise Exception("Incorrect testing procedure: unable to "
                                    "find correct argument values for %s." %
                                    func.__name__)

                # Another argument might be forced to be an integer:
                integer_arg_nums.add(random.choice(list(range(num_args))))
            else:
                # We have found reasonable arguments, and the test passed:
                break
    # wrapped version of 'func', when it bears the same name as
    # 'func' (the name is used by repr(wrapped_func)).
    wrapped_func.__name__ = func.__name__

    return wrapped_func


###############################################################################
# Arrays

# Vectorized creation of an array of variables:

# ! Looking up uncertainties.Variable beforehand through '_Variable =
# uncertainties.Variable' does not result in a significant speed up:

_uarray = numpy.vectorize(lambda v, s: uncertainties.Variable(v, s),
                          otypes=[object])


def uarray((values, std_devs)):
    """
    Returns a NumPy array of numbers with uncertainties
    initialized with the given nominal values and standard
    deviations.

    values, std_devs -- valid arguments for numpy.array, with
    identical shapes (list of numbers, list of lists, numpy.ndarray,
    etc.).
    """

    return _uarray(values, std_devs)