Modifying the value of a float-like object
Arnaud Delobelle
arnodel at googlemail.com
Wed Apr 15 11:33:22 EDT 2009
Eric.Le.Bigot at spectro.jussieu.fr writes:
> Arnaud, your code is very interesting!
>
> On Apr 15, 1:00 pm, Arnaud Delobelle <arno... at googlemail.com> wrote:
>> I still don't understand why you need mutable floats.
>
> Here is why: the code that your proposed (or any code that does
> straightforward error propagation, for that matter) does not generally
> calculate uncertainties correctly:
>
>>>> a=Value(1, 0.1)
>>>> a-a
> 0.0 +- 0.14142135623823598
>
> The correct result is obviously 0.0 +- 0.0. This is the effect of
> what I referred to in previous posts as "correlated errors".
>
> Anyway, I learned some interesting stuff about what you can do in
> Python, thanks to your code! :)
I still don't think mutable floats are necessary. Here is an approach
below - I'll let the code speak because I have to do some shopping!
It still relies on Peter Otten's method for error calculation - which I
trust is good as my numerical analysis is to say the least very rusty!
---------- uncert2.py ----------
def uexpr(x):
return x if isinstance(x, UBase) else UVal(x)
def uified(f):
def decorated(*args):
args = map(uexpr, args)
basis = set()
for arg in args:
basis |= arg.basis
uf = lambda values: f(*(x.f(values) for x in args))
ret = UExpr(basis, uf)
return ret if ret.err else ret.value
return decorated
class UBase(object):
def __repr__(self):
return "%r +- %r" % (self.value, self.err)
def __hash__(self):
return id(self)
__neg__ = uified(float.__neg__)
for op in 'add', 'sub', 'mul', 'div', 'pow':
for r in '', 'r':
exec """__%s__ = uified(float.__%s__)""" % (r+op, r+op)
del op, r
class UVal(UBase):
def __init__(self, value, err=0):
if isinstance(value, UVal):
value, err = value.value, value.err
self.value = value
self.err = err
self.basis = set([self])
def f(self, values):
return values[self]
class UExpr(UBase):
def __init__(self, basis, f):
self.basis = basis
self.f = f
self.calc()
def derive(self, i, eps=1e-5):
values = dict((x, x.value) for x in self.basis)
values[i] += eps
x2 = self.f(values)
return (x2-self.value)/eps
def calc(self):
sigma = 0
values = dict((x, x.value) for x in self.basis)
self.value = self.f(values)
for i in self.basis:
x = self.derive(i)*i.err
sigma += x*x
self.err = sigma**0.5
builtinf = type(sum)
import math
for name in dir(math):
obj = getattr(math, name)
if isinstance(obj, builtinf):
setattr(math, name, uified(obj))
----------------------------------------
Example:
marigold:junk arno$ python -i uncert2.py
>>> a = UVal(2.0, 0.1)
>>> b = UVal(10.0, 1)
>>> a + a
4.0 +- 0.20000000000131024
>>> a*b
20.0 +- 2.2360679774310253
>>> a - a
0.0
>>> a / a
1.0
>>> from math import *
>>> pow(b, a)
100.0 +- 30.499219977998791
>>> sin(a) - tan(b)
0.26093659936659497 +- 1.4209904731243463
>>> sin(a)*sin(a) + cos(a)*cos(a)
1.0
>>> sin(a)/cos(a) - tan(a)
0.0
>>> a*b - b*a
0.0
>>> # Etc...
--
Arnaud
More information about the Python-list
mailing list