Skip to content

Commit

Permalink
Merge branch 'release'
Browse files Browse the repository at this point in the history
  • Loading branch information
lebigot committed Jul 26, 2013
2 parents 9383721 + 4afcc67 commit 260eca4
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 28 deletions.
7 changes: 6 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@

distutils.core.setup(
name='uncertainties',
version='2.4',
version='2.4.1',
author='Eric O. LEBIGOT (EOL)',
author_email='[email protected]',
url='http://pythonhosted.org/uncertainties/',
Expand Down Expand Up @@ -168,6 +168,11 @@
Main changes:
- 2.4.1: In ``uncertainties.umath``, functions ``ceil()``, ``floor()``, \
``isinf()``, ``isnan()`` and ``trunc()`` now return values of \
the same type as the corresponding ``math`` module function \
(instead of generally returning a value with a zero uncertainty \
``...+/-0``).
- 2.4: Extensive support for the formatting_ of numbers with uncertainties. \
A zero uncertainty is now explicitly displayed as the integer 0. \
The new formats are generally understood by ``ufloat_fromstr()``. \
Expand Down
2 changes: 1 addition & 1 deletion uncertainties/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@
import inspect

# Numerical version:
__version_info__ = (2, 4)
__version_info__ = (2, 4, 1)
__version__ = '.'.join(map(str, __version_info__))

__author__ = 'Eric O. LEBIGOT (EOL) <[email protected]>'
Expand Down
9 changes: 7 additions & 2 deletions uncertainties/lib1to2/test_1to2.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# import if the Python version is not high enough, instead of having
# like here a whole indented block?

if sys.version_info < (2, 7) or sys.version_info[:2] == (3, 3):
if sys.version_info < (2, 7):
# This package uses lib2to3, which requires Python 2.6+.

# !! Nosetests for Python 2.6 also fails (it looks like it tries
Expand All @@ -29,7 +29,12 @@
else:

import os
import lib2to3.tests.support as support
try:
# lib2to3 test support seems to have moved to a new place in 2013:
import test.test_lib2to3.support as support
except ImportError:
# Pre-~2013 path for lib2to3 test support
import lib2to3.tests.support as support

# The lib1to2.fixes package given to lib2to3 is the *local* package
# (not to another installed module). This is important for the
Expand Down
21 changes: 14 additions & 7 deletions uncertainties/test_umath.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,17 +216,24 @@ def test_math_module():
# math.factorial()):
assert umath.factorial(4) == 24

# Boolean functions:
assert not umath.isinf(x)

# Comparison, possibly between an AffineScalarFunc object and a
# boolean, which makes things more difficult for this code:
assert umath.isinf(x) == False

# fsum is special because it does not take a fixed number of
# variables:
assert umath.fsum([x, x]).nominal_value == -3

# Functions that give locally constant results are tested: they
# should give the same result as their float equivalent:
for name in umath.locally_cst_funcs:

try:
func = getattr(umath, name)
except AttributeError:
continue # Not in the math module, so not in umath either

assert func(x) == func(x.nominal_value)
# The type should be left untouched. For example, isnan()
# should always give a boolean:
assert type(func(x)) == type(func(x.nominal_value))

# The same exceptions should be generated when numbers with uncertainties
# are used:

Expand Down
63 changes: 46 additions & 17 deletions uncertainties/umath.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,21 @@
# no_std_wrapping = ['modf', 'frexp', 'ldexp', 'fsum', 'factorial']

# Functions with numerical derivatives:
num_deriv_funcs = ['fmod', 'gamma', 'isinf', 'isnan',
'lgamma', 'trunc']
num_deriv_funcs = ['fmod', 'gamma', 'lgamma']

# Functions are by definition locally constant (on real
# numbers): their value does not depend on the uncertainty (because
# this uncertainty is supposed to lead to a good linear approximation
# of the function in the uncertainty region). The type of their output
# for floats is preserved, as users should not care about deviations
# in their value: their value is locally constant due to the nature of
# the function (0 derivative). This situation is similar to that of
# comparisons (==, >, etc.).
locally_cst_funcs = ['ceil', 'floor', 'isinf', 'isnan', 'trunc']

# Functions that do not belong in many_scalars_to_scalar_funcs, but
# that have a version that handles uncertainties:
# that have a version that handles uncertainties. These functions are
# also not in numpy (see unumpy/core.py).
non_std_wrapped_funcs = []

# Function that copies the relevant attributes from generalized
Expand Down Expand Up @@ -153,7 +163,6 @@ def log_der0(*args):
'atan2': [lambda y, x: x/(x**2+y**2), # Correct for x == 0
lambda y, x: -y/(x**2+y**2)], # Correct for x == 0
'atanh': [lambda x: 1/(1-x**2)],
'ceil': [lambda x: 0],
'copysign': [lambda x, y: (1 if x >= 0 else -1) * math.copysign(1, y),
lambda x, y: 0],
'cos': [lambda x: -math.sin(x)],
Expand All @@ -164,7 +173,6 @@ def log_der0(*args):
'exp': [math.exp],
'expm1': [math.exp],
'fabs': [lambda x: 1 if x >= 0 else -1],
'floor': [lambda x: 0],
'hypot': [lambda x, y: x/math.hypot(x, y),
lambda x, y: y/math.hypot(x, y)],
'log': [log_der0,
Expand All @@ -191,6 +199,25 @@ def log_der0(*args):

this_module = sys.modules[__name__]

def wrap_locally_cst_func(func):
'''
Returns a function that returns the same arguments as func, but
after converting any AffineScalarFunc object to its nominal value.
This function is useful for wrapping functions that are locally
constant: the uncertainties should have no role in the result
(since they are supposed to keep the function linear and hence,
here, constant).
'''
def wrapped_func(*args, **kwargs):
args_float = map(uncertainties.nominal_value, args)
# !! In Python 2.7+, dictionary comprehension: {argname:...}
kwargs_float = dict(
(arg_name, uncertainties.nominal_value(value))
for (arg_name, value) in kwargs.iteritems())
return func(*args_float, **kwargs_float)
return wrapped_func

# for (name, attr) in vars(math).items():
for name in dir(math):

Expand All @@ -200,20 +227,23 @@ def log_der0(*args):
# Functions whose derivatives are calculated numerically by
# this module fall here (isinf, fmod,...):
derivatives = [] # Means: numerical calculation required
else:
elif name not in locally_cst_funcs:
continue # 'name' not wrapped by this module (__doc__, e, etc.)

# Errors during the calculation of the derivatives are converted
# to a NaN result: it is assumed that a mathematical calculation
# that cannot be calculated indicates a non-defined derivative
# (the derivatives in fixed_derivatives must be written this way):
derivatives = map(uncertainties.nan_if_exception, derivatives)

func = getattr(math, name)

setattr(this_module, name,
wraps(uncertainties.wrap(func, derivatives), func))


if name in locally_cst_funcs:
wrapped_func = wrap_locally_cst_func(func)
else: # Function with analytical or numerical derivatives:
# Errors during the calculation of the derivatives are converted
# to a NaN result: it is assumed that a mathematical calculation
# that cannot be calculated indicates a non-defined derivative
# (the derivatives in fixed_derivatives must be written this way):
wrapped_func = uncertainties.wrap(
func, map(uncertainties.nan_if_exception, derivatives))

setattr(this_module, name, wraps(wrapped_func, func))

many_scalars_to_scalar_funcs.append(name)

###############################################################################
Expand Down Expand Up @@ -290,7 +320,6 @@ def modf(x):
# This function was not called with an AffineScalarFunc
# argument: there is no need to return numbers with uncertainties:
return (frac_part, int_part)

many_scalars_to_scalar_funcs.append('modf')

@uncertainties.set_doc(math.ldexp.__doc__)
Expand Down
3 changes: 3 additions & 0 deletions uncertainties/unumpy/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,9 @@ def define_vectorized_funcs():
for f_name in ['acos', 'acosh', 'asin', 'atan', 'atan2', 'atanh'])

new_func_names = [func_name_translations.get(function_name, function_name)
# The functions from umath.non_std_wrapped_funcs
# (available from umath) are normally not in
# numpy, so they are not included here:
for function_name in umath.many_scalars_to_scalar_funcs]

for (function_name, unumpy_name) in zip(
Expand Down

0 comments on commit 260eca4

Please sign in to comment.