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))
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
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)