Modifying the value of a float-like object
Arnaud Delobelle
arnodel at googlemail.com
Wed Apr 15 07:00:19 EDT 2009
Eric.Le.Bigot at spectro.jussieu.fr writes:
> As for your idea of "straight-forward interval arithmetic", it's a
> good one, but I'd have to redefine lots of functions from the math
> module, to use them explicitly in my code, etc.: this is heavy; I was
> looking for a light-weight alternative, where all calculations are
> expressed in Python as if all numbers were regular floats, and where
> you don't have to redefine any mathematical operation. In other
> words, I'm looking for a robust way of implementing the derive()
> function of Peter without knowing anything about which function uses
> which "numbers with an uncertainty": the equivalent of Peter's derive
> () would simply successively change all "numbers with uncertainty"
> defined so far and see how the result of a given calculation varies--
> the variables that are not used in the calculated expression don't
> change the result, for instance. I understand that this is
> computationally inefficient (too many variables might be changed), but
> I don't need speed, only robustness in the propagation of errors, and
> a simple and very legible calculation code, and ideally with only a
> few lines of Python.
I still don't understand why you need mutable floats.
Here is a suggestion, based on Petter Otten's Value class and his derive
and calc functions. I've modified Value slightly so that it implements
unary - and binary +, *, -, /, **. The 'for name in dir(math)' loop at
the end wraps each function in the math module in valueified (defined
below) so that they accept floats or Values.
---------------- uncert.py ---------------
def valueified(f):
"""
Change a function that accepts floats to a function that accepts
any of float or Value.
"""
def decorated(*args):
ret = calc(f, map(Value, args))
return ret if ret.err else ret.value
return decorated
class Value(object):
def __init__(self, value, err=0):
if isinstance(value, Value):
value, err = value.value, value.err
self.value = float(value)
self.err = float(err)
def __repr__(self):
return "%r +- %r" % (self.value, self.err)
__neg__ = valueified(float.__neg__)
for op in 'add', 'sub', 'mul', 'div', 'pow':
for r in '', 'r':
exec """__%s__ = valueified(float.__%s__)""" % (r+op, r+op)
del op, r
def derive(f, values, i, eps=1e-5):
x1 = f(*values)
values = list(values)
values[i] += eps
x2 = f(*values)
return (x2-x1)/eps
def calc(f, args):
values = [v.value for v in args]
errs = [v.err for v in args]
sigma = 0
for i, (v, e) in enumerate(zip(values, errs)):
x = derive(f, values, i)*e
sigma += x*x
return Value(f(*values), sigma**0.5)
builtinf = type(sum)
import math
for name in dir(math):
obj = getattr(math, name)
if isinstance(obj, builtinf):
setattr(math, name, valueified(obj))
---------- end of uncert.py ---------------
Example:
marigold:junk arno$ python -i uncert.py
>>> a = Value(2, 0.1)
>>> b = Value(7, 2)
>>> a+b
9.0 +- 2.0024984393742682
>>> a*b
14.0 +- 4.0607881007017825
>>> from math import *
>>> sin(a)
0.90929742682568171 +- 0.041615138303141563
>>> sin(2)
0.90929742682568171
>>> 2*a
4.0 +- 0.20000000000131024
>>> 2**b
128.0 +- 177.44629319622615
>>> a/b
0.2857142857142857 +- 0.082873111674175479
>>> sqrt(a**2+b**2)
7.2801098892805181 +- 1.9232454006952127
>>> hypot(a, b)
7.2801098892805181 +- 1.9232442191491188
>>> #etc
Isn't this what you need?
--
Arnaud
More information about the Python-list
mailing list