eric.le.bi...@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 -- http://mail.python.org/mailman/listinfo/python-list