1
2
3
4
5
6 '''
7 Calculations with full error propagation for quantities with uncertainties.
8 Derivatives can also be calculated.
9
10 Web user guide: http://packages.python.org/uncertainties/.
11
12 Example of possible calculation: (0.2 +/- 0.01)**2 = 0.04 +/- 0.004.
13
14 Correlations between expressions are correctly taken into account (for
15 instance, with x = 0.2+/-0.01, 2*x-x-x is exactly zero, as is y-x-x
16 with y = 2*x).
17
18 Examples:
19
20 import uncertainties
21 from uncertainties import ufloat
22 from uncertainties.umath import * # sin(), etc.
23
24 # Mathematical operations:
25 x = ufloat((0.20, 0.01)) # x = 0.20+/-0.01
26 x = ufloat("0.20+/-0.01") # Other representation
27 x = ufloat("0.20(1)") # Other representation
28 x = ufloat("0.20") # Implicit uncertainty of +/-1 on the last digit
29 print x**2 # Square: prints "0.04+/-0.004"
30 print sin(x**2) # Prints "0.0399...+/-0.00399..."
31
32 print x.position_in_sigmas(0.17) # Prints "-3.0": deviation of -3 sigmas
33
34 # Access to the nominal value, and to the uncertainty:
35 square = x**2 # Square
36 print square # Prints "0.04+/-0.004"
37 print square.nominal_value # Prints "0.04"
38 print square.std_dev() # Prints "0.004..."
39
40 print square.derivatives[x] # Partial derivative: 0.4 (= 2*0.20)
41
42 # Correlations:
43 u = ufloat((1, 0.05), "u variable") # Tag
44 v = ufloat((10, 0.1), "v variable")
45 sum_value = u+v
46
47 u.set_std_dev(0.1) # Standard deviations can be updated on the fly
48 print sum_value - u - v # Prints "0.0" (exact result)
49
50 # List of all sources of error:
51 print sum_value # Prints "11+/-0.1414..."
52 for (var, error) in sum_value.error_components().iteritems():
53 print "%s: %f" % (var.tag, error) # Individual error components
54
55 # Covariance matrices:
56 cov_matrix = uncertainties.covariance_matrix([u, v, sum_value])
57 print cov_matrix # 3x3 matrix
58
59 # Correlated variables can be constructed from a covariance matrix, if
60 # NumPy is available:
61 (u2, v2, sum2) = uncertainties.correlated_values([1, 10, 11],
62 cov_matrix)
63 print u2 # Value and uncertainty of u: correctly recovered (1+/-0.1)
64 print uncertainties.covariance_matrix([u2, v2, sum2]) # == cov_matrix
65
66 - The main function provided by this module is ufloat, which creates
67 numbers with uncertainties (Variable objects). Variable objects can
68 be used as if they were regular Python numbers. The main attributes
69 and methods of Variable objects are defined in the documentation of
70 the Variable class.
71
72 - Valid operations on numbers with uncertainties include basic
73 mathematical functions (addition, etc.).
74
75 Most operations from the standard math module (sin, etc.) can be applied
76 on numbers with uncertainties by using their generalization from the
77 uncertainties.umath module:
78
79 from uncertainties.umath import sin
80 print sin(ufloat("1+/-0.01")) # 0.841...+/-0.005...
81 print sin(1) # umath.sin() also works on floats, exactly like math.sin()
82
83 Logical operations (>, ==, etc.) are also supported.
84
85 Basic operations on NumPy arrays or matrices of numbers with
86 uncertainties can be performed:
87
88 2*numpy.array([ufloat((1, 0.01)), ufloat((2, 0.1))])
89
90 More complex operations on NumPy arrays can be performed through the
91 dedicated uncertainties.unumpy sub-module (see its documentation).
92
93 Calculations that are performed through non-Python code (Fortran, C,
94 etc.) can handle numbers with uncertainties instead of floats through
95 the provided wrap() wrapper:
96
97 import uncertainties
98
99 # wrapped_f is a version of f that can take arguments with
100 # uncertainties, even if f only takes floats:
101 wrapped_f = uncertainties.wrap(f)
102
103 If some derivatives of the wrapped function f are known (analytically,
104 or numerically), they can be given to wrap()--see the documentation
105 for wrap().
106
107 - Utility functions are also provided: the covariance matrix between
108 random variables can be calculated with covariance_matrix(), or used
109 as input for the definition of correlated quantities (correlated_values()
110 function--defined only if the NumPy module is available).
111
112 - Mathematical expressions involving numbers with uncertainties
113 generally return AffineScalarFunc objects, which also print as a value
114 with uncertainty. Their most useful attributes and methods are
115 described in the documentation for AffineScalarFunc. Note that
116 Variable objects are also AffineScalarFunc objects. UFloat is an
117 alias for AffineScalarFunc, provided as a convenience: testing whether
118 a value carries an uncertainty handled by this module should be done
119 with insinstance(my_value, UFloat).
120
121 - Mathematically, numbers with uncertainties are, in this package,
122 probability distributions. These probabilities are reduced to two
123 numbers: a nominal value and an uncertainty. Thus, both variables
124 (Variable objects) and the result of mathematical operations
125 (AffineScalarFunc objects) contain these two values (respectively in
126 their nominal_value attribute and through their std_dev() method).
127
128 The uncertainty of a number with uncertainty is simply defined in
129 this package as the standard deviation of the underlying probability
130 distribution.
131
132 The numbers with uncertainties manipulated by this package are assumed
133 to have a probability distribution mostly contained around their
134 nominal value, in an interval of about the size of their standard
135 deviation. This should cover most practical cases. A good choice of
136 nominal value for a number with uncertainty is thus the median of its
137 probability distribution, the location of highest probability, or the
138 average value.
139
140 - When manipulating ensembles of numbers, some of which contain
141 uncertainties, it can be useful to access the nominal value and
142 uncertainty of all numbers in a uniform manner:
143
144 x = ufloat("3+/-0.1")
145 print nominal_value(x) # Prints 3
146 print std_dev(x) # Prints 0.1
147 print nominal_value(3) # Prints 3: nominal_value works on floats
148 print std_dev(3) # Prints 0: std_dev works on floats
149
150 - Probability distributions (random variables and calculation results)
151 are printed as:
152
153 nominal value +/- standard deviation
154
155 but this does not imply any property on the nominal value (beyond the
156 fact that the nominal value is normally inside the region of high
157 probability density), or that the probability distribution of the
158 result is symmetrical (this is rarely strictly the case).
159
160 - Linear approximations of functions (around the nominal values) are
161 used for the calculation of the standard deviation of mathematical
162 expressions with this package.
163
164 The calculated standard deviations and nominal values are thus
165 meaningful approximations as long as the functions involved have
166 precise linear expansions in the region where the probability
167 distribution of their variables is the largest. It is therefore
168 important that uncertainties be small. Mathematically, this means
169 that the linear term of functions around the nominal values of their
170 variables should be much larger than the remaining higher-order terms
171 over the region of significant probability.
172
173 For instance, sin(0+/-0.01) yields a meaningful standard deviation
174 since it is quite linear over 0+/-0.01. However, cos(0+/-0.01) yields
175 an approximate standard deviation of 0 (because the cosine is not well
176 approximated by a line around 0), which might not be precise enough
177 for all applications.
178
179 - Comparison operations (>, ==, etc.) on numbers with uncertainties
180 have a pragmatic semantics, in this package: numbers with
181 uncertainties can be used wherever Python numbers are used, most of
182 the time with a result identical to the one that would be obtained
183 with their nominal value only. However, since the objects defined in
184 this module represent probability distributions and not pure numbers,
185 comparison operator are interpreted in a specific way.
186
187 The result of a comparison operation ("==", ">", etc.) is defined so as
188 to be essentially consistent with the requirement that uncertainties
189 be small: the value of a comparison operation is True only if the
190 operation yields True for all infinitesimal variations of its random
191 variables, except, possibly, for an infinitely small number of cases.
192
193 Example:
194
195 "x = 3.14; y = 3.14" is such that x == y
196
197 but
198
199 x = ufloat((3.14, 0.01))
200 y = ufloat((3.14, 0.01))
201
202 is not such that x == y, since x and y are independent random
203 variables that almost never give the same value. However, x == x
204 still holds.
205
206 The boolean value (bool(x), "if x...") of a number with uncertainty x
207 is the result of x != 0.
208
209 - The uncertainties package is for Python 2.5 and above.
210
211 - This package contains tests. They can be run either manually or
212 automatically with the nose unit testing framework (nosetests).
213
214 (c) 2009-2010 by Eric O. LEBIGOT (EOL) <eric.lebigot@normalesup.org>.
215 Please send feature requests, bug reports, or feedback to this address.
216
217 Please support future development by donating $5 or more through PayPal!
218
219 This software is released under a dual license. (1) The BSD license.
220 (2) Any other license, as long as it is obtained from the original
221 author.'''
222
223
224
225
226
227
228
229
230
231 from __future__ import division
232
233 import re
234 import math
235 from math import sqrt, log
236 import copy
237
238
239 __version_info__ = (1, 7, 1)
240 __version__ = '.'.join(str(num) for num in __version_info__)
241
242 __author__ = 'Eric O. LEBIGOT (EOL)'
243
244
245
246 __all__ = [
247
248
249
250
251 'ufloat',
252
253
254 'nominal_value',
255 'std_dev',
256
257
258 'covariance_matrix',
259
260
261
262
263
264 'UFloat',
265
266
267
268 'wrap',
269
270
271
272
273
274 'partial_derivative'
275
276 ]
277
278
279
280 -def set_doc(doc_string):
281 """
282 Decorator function that sets the docstring to the given text.
283
284 It is useful for functions whose docstring is calculated
285 (including string substitutions).
286 """
287 def set_doc_string(func):
288 func.__doc__ = doc_string
289 return func
290 return set_doc_string
291
292
293
294
295
296
297 -class NotUpcast(Exception):
299
301 """
302 Transforms x into a constant affine scalar function
303 (AffineScalarFunc), unless it is already an AffineScalarFunc (in
304 which case x is returned unchanged).
305
306 Raises an exception unless 'x' belongs to some specific classes of
307 objects that are known not to depend on AffineScalarFunc objects
308 (which then cannot be considered as constants).
309 """
310
311 if isinstance(x, AffineScalarFunc):
312 return x
313
314
315 if isinstance(x, (float, int, complex, long)):
316
317 return AffineScalarFunc(x, {})
318
319
320 raise NotUpcast("%s cannot be converted to a number with"
321 " uncertainty" % type(x))
322
324 """
325 Returns a function that numerically calculates the partial
326 derivative of function f with respect to its argument number
327 param_num.
328
329 The step parameter represents the shift of the parameter used in
330 the numerical approximation.
331 """
332
333 def partial_derivative_of_f(*args):
334 """
335 Partial derivative, calculated with the (-epsilon, +epsilon)
336 method, which is more precise than the (0, +epsilon) method.
337 """
338
339
340 shifted_args = list(args)
341
342
343
344 step = 1e-8*abs(shifted_args[param_num])
345 if not step:
346 step = 1e-8
347
348 shifted_args[param_num] += step
349 shifted_f_plus = f(*shifted_args)
350
351 shifted_args[param_num] -= 2*step
352 shifted_f_minus = f(*shifted_args)
353
354 return (shifted_f_plus - shifted_f_minus)/2/step
355
356 return partial_derivative_of_f
357
359 """
360 Sequence with the successive numerical derivatives of a function.
361 """
362
363
364
366 """
367 'function' is the function whose derivatives can be computed.
368 """
369 self._function = function
370
372 """
373 Returns the n-th numerical derivative of the function.
374 """
375 return partial_derivative(self._function, n)
376
377 -def wrap(f, derivatives_funcs=None):
378 """
379 Wraps function f so that, when applied to numbers with
380 uncertainties (AffineScalarFunc objects) or float-like arguments,
381 f returns a local approximation of its values (in the form of an
382 object of class AffineScalarFunc). In this case, if none of the
383 arguments of f involves variables [i.e. Variable objects], f
384 simply returns its usual result.
385
386 When f is not called on AffineScalarFunc or float-like
387 arguments, the original result of f is returned.
388
389 If supplied, 'derivatives_funcs' is a sequence or iterator that
390 generally contains functions; each successive function is the
391 partial derivatives of f with respect to the corresponding
392 variable (one function for each argument of f, which takes as many
393 arguments as f). If derivatives_funcs is None, or if
394 derivatives_funcs contains a finite number of elements, then
395 missing derivatives are calculated numerically through
396 partial_derivative().
397
398 Example: wrap(math.sin) is a sine function that can be applied to
399 numbers with uncertainties. Its derivative will be calculated
400 numerically. wrap(math.sin, [None]) would have produced the same
401 result. wrap(math.sin, [math.cos]) is the same function, but with
402 an analytically defined derivative.
403 """
404
405 if derivatives_funcs is None:
406 derivatives_funcs = NumericalDerivatives(f)
407 else:
408
409
410
411
412 try:
413 len(derivatives_funcs)
414 except TypeError:
415 pass
416 else:
417 derivatives_funcs = [
418 partial_derivative(f, k) if derivative is None
419 else derivative
420 for (k, derivative) in enumerate(derivatives_funcs)]
421
422
423
424 @set_doc("""\
425 Version of %s(...) that returns an affine approximation
426 (AffineScalarFunc object), if its result depends on variables
427 (Variable objects). Otherwise, returns a simple constant (when
428 applied to constant arguments).
429
430 Warning: arguments of the function that are not AffineScalarFunc
431 objects must not depend on uncertainties.Variable objects in any
432 way. Otherwise, the dependence of the result in
433 uncertainties.Variable objects will be incorrect.
434
435 Original documentation:
436 %s""" % (f.__name__, f.__doc__))
437 def f_with_affine_output(*args):
438
439
440 try:
441 aff_funcs = map(to_affine_scalar, args)
442
443 except NotUpcast:
444
445
446
447
448
449
450
451
452 if any(isinstance(arg, AffineScalarFunc) for arg in args):
453
454
455
456
457 return NotImplemented
458 else:
459
460
461
462
463
464 return f(*args)
465
466
467
468 args_values = [e.nominal_value for e in aff_funcs]
469 f_nominal_value = f(*args_values)
470
471
472
473
474 variables = set()
475 for expr in aff_funcs:
476 variables |= set(expr.derivatives)
477
478
479
480
481
482
483
484
485
486
487
488
489
490 if not variables or isinstance(f_nominal_value, bool):
491 return f_nominal_value
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523 derivatives_wrt_args = []
524 for (arg, derivative) in zip(aff_funcs, derivatives_funcs):
525 derivatives_wrt_args.append(derivative(*args_values)
526 if arg.derivatives
527 else 0)
528
529
530
531
532
533
534 derivatives_wrt_vars = dict((var, 0.) for var in variables)
535
536
537
538
539 for (func, f_derivative) in zip(aff_funcs, derivatives_wrt_args):
540 for (var, func_derivative) in func.derivatives.iteritems():
541 derivatives_wrt_vars[var] += f_derivative * func_derivative
542
543
544 return AffineScalarFunc(f_nominal_value, derivatives_wrt_vars)
545
546
547
548 f_with_affine_output.__name__ = f.__name__
549
550 return f_with_affine_output
551
553 """
554 Takes an operator op(x, y) and wraps it.
555
556 The constructed operator returns func(x, to_affine_scalar(y)) if y
557 can be upcast with to_affine_scalar(); otherwise, it returns
558 NotImplemented.
559
560 Thus, func() is only called on two AffineScalarFunc objects, if
561 its first argument is an AffineScalarFunc.
562 """
563
564 def op_on_upcast_args(x, y):
565 """
566 Returns %s(self, to_affine_scalar(y)) if y can be upcast
567 through to_affine_scalar. Otherwise returns NotImplemented.
568 """ % func.__name__
569
570 try:
571 y_with_uncert = to_affine_scalar(y)
572 except NotUpcast:
573
574
575
576
577 return NotImplemented
578 else:
579 return func(x, y_with_uncert)
580
581 return op_on_upcast_args
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599 -def _eq_on_aff_funcs(self, y_with_uncert):
600 """
601 __eq__ operator, assuming that both self and y_with_uncert are
602 AffineScalarFunc objects.
603 """
604 difference = self - y_with_uncert
605
606
607 return not(difference._nominal_value or difference.std_dev())
608
610 """
611 __ne__ operator, assuming that both self and y_with_uncert are
612 AffineScalarFunc objects.
613 """
614
615 return not _eq_on_aff_funcs(self, y_with_uncert)
616
618 """
619 __gt__ operator, assuming that both self and y_with_uncert are
620 AffineScalarFunc objects.
621 """
622 return self._nominal_value > y_with_uncert._nominal_value
623
625 """
626 __ge__ operator, assuming that both self and y_with_uncert are
627 AffineScalarFunc objects.
628 """
629
630 return (_gt_on_aff_funcs(self, y_with_uncert)
631 or _eq_on_aff_funcs(self, y_with_uncert))
632
634 """
635 __lt__ operator, assuming that both self and y_with_uncert are
636 AffineScalarFunc objects.
637 """
638 return self._nominal_value < y_with_uncert._nominal_value
639
641 """
642 __le__ operator, assuming that both self and y_with_uncert are
643 AffineScalarFunc objects.
644 """
645
646 return (_lt_on_aff_funcs(self, y_with_uncert)
647 or _eq_on_aff_funcs(self, y_with_uncert))
648
652 """
653 Affine functions that support basic mathematical operations
654 (addition, etc.). Such functions can for instance be used for
655 representing the local (linear) behavior of any function.
656
657 This class is mostly meant to be used internally.
658
659 This class can also be used to represent constants.
660
661 The variables of affine scalar functions are Variable objects.
662
663 AffineScalarFunc objects include facilities for calculating the
664 'error' on the function, from the uncertainties on its variables.
665
666 Main attributes and methods:
667
668 - nominal_value, std_dev(): value at the origin / nominal value,
669 and standard deviation.
670
671 - error_components(): error_components()[x] is the error due to
672 Variable x.
673
674 - derivatives: derivatives[x] is the (value of the) derivative
675 with respect to Variable x. This attribute is a dictionary
676 whose keys are the Variable objects on which the function
677 depends.
678
679 All the Variable objects on which the function depends are in
680 'derivatives'.
681
682 - position_in_sigmas(x): position of number x with respect to the
683 nominal value, in units of the standard deviation.
684 """
685
686
687 __slots__ = ('_nominal_value', 'derivatives')
688
689
690
691
692
693
694
695
696 - def __init__(self, nominal_value, derivatives):
697 """
698 nominal_value -- value of the function at the origin.
699 nominal_value must not depend in any way of the Variable
700 objects in 'derivatives' (the value at the origin of the
701 function being defined is a constant).
702
703 derivatives -- maps each Variable object on which the function
704 being defined depends to the value of the derivative with
705 respect to that variable, taken at the nominal value of all
706 variables.
707
708 Warning: the above constraint is not checked, and the user is
709 responsible for complying with it.
710 """
711
712
713
714
715
716
717
718
719
720 self._nominal_value = float(nominal_value)
721 self.derivatives = derivatives
722
723
724
725 @property
727 "Nominal value of the random number."
728 return self._nominal_value
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
750 """
751 Equivalent to self != 0.
752 """
753
754
755
756
757
758
759 return self != 0.
760
761
762 __bool__ = __nonzero__
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793 __eq__ = _force_aff_func_args(_eq_on_aff_funcs)
794
795 __ne__ = _force_aff_func_args(_ne_on_aff_funcs)
796 __gt__ = _force_aff_func_args(_gt_on_aff_funcs)
797
798
799
800
801 __ge__ = _force_aff_func_args(_ge_on_aff_funcs)
802
803 __lt__ = _force_aff_func_args(_lt_on_aff_funcs)
804 __le__ = _force_aff_func_args(_le_on_aff_funcs)
805
806
807
808
809
811 """
812 Individual components of the standard deviation of the affine
813 function (in absolute value), returned as a dictionary with
814 Variable objects as keys.
815
816 This method assumes that the derivatives contained in the
817 object take scalar values (and are not a tuple, like what
818 math.frexp() returns, for instance).
819 """
820
821
822 error_components = {}
823 for (variable, derivative) in self.derivatives.iteritems():
824
825 error_components[variable] = abs(derivative*variable._std_dev)
826
827 return error_components
828
830 """
831 Standard deviation of the affine function.
832
833 This method assumes that the function returns scalar results.
834
835 This returned standard deviation depends on the current
836 standard deviations [std_dev()] of the variables (Variable
837 objects) involved.
838 """
839
840
841
842
843
844
845 return sqrt(sum(
846 delta**2 for delta in self.error_components().itervalues()))
847
849 """
850 Uses the to_string() conversion function on both the nominal
851 value and the standard deviation, and returns a string that
852 describes them.
853
854 to_string() is typically repr() or str().
855 """
856
857 (nominal_value, std_dev) = (self._nominal_value, self.std_dev())
858
859
860
861
862
863
864
865
866 return ("%s+/-%s" % (to_string(nominal_value), to_string(std_dev))
867 if std_dev
868 else to_string(nominal_value))
869
872
875
877 """
878 Returns 'value' - nominal value, in units of the standard
879 deviation.
880
881 Raises a ValueError exception if the standard deviation is zero.
882 """
883 try:
884
885
886 return (value - self._nominal_value) / self.std_dev()
887 except ZeroDivisionError:
888 raise ValueError("The standard deviation is zero:"
889 " undefined result.")
890
892 """
893 Hook for the standard copy module.
894
895 The returned AffineScalarFunc is a completely fresh copy,
896 which is fully independent of any variable defined so far.
897 New variables are specially created for the returned
898 AffineScalarFunc object.
899 """
900 return AffineScalarFunc(
901 self._nominal_value,
902 dict((copy.deepcopy(var), deriv)
903 for (var, deriv) in self.derivatives.iteritems()))
904
906 """
907 Hook for the pickle module.
908 """
909 obj_slot_values = dict((k, getattr(self, k)) for k in
910
911
912 AffineScalarFunc.__slots__)
913 return obj_slot_values
914
921
922
923 UFloat = AffineScalarFunc
926
927 """
928 Returns operators with a reflection, along with their derivatives
929 (for float operands).
930 """
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949 derivatives_list = {
950 'add': ("1.", "1."),
951
952
953
954
955 'div': ("1/y", "-x/y**2"),
956 'floordiv': ("0.", "0."),
957
958
959 'mod': ("1.", "partial_derivative(float.__mod__, 1)(x, y)"),
960 'mul': ("y", "x"),
961 'pow': ("y*x**(y-1)", "log(x)*x**y"),
962 'sub': ("1.", "-1."),
963 'truediv': ("1/y", "-x/y**2")
964 }
965
966
967 ops_with_reflection = {}
968 for (op, derivatives) in derivatives_list.iteritems():
969 ops_with_reflection[op] = [
970 eval("lambda x, y: %s" % expr) for expr in derivatives ]
971
972 ops_with_reflection["r"+op] = [
973 eval("lambda y, x: %s" % expr) for expr in reversed(derivatives)]
974
975 return ops_with_reflection
976
977
978 _ops_with_reflection = get_ops_with_reflection()
979
980
981 _modified_operators = []
984 """
985 Adds many operators (__add__, etc.) to the AffineScalarFunc class.
986 """
987
988
989
990
991
992
993
994
995
996
997
998
999
1000 simple_numerical_operators_derivatives = {
1001 'abs': lambda x: 1. if x>=0 else -1.,
1002 'neg': lambda x: -1.,
1003 'pos': lambda x: 1.,
1004 'trunc': lambda x: 0.
1005 }
1006
1007 for (op, derivative) in \
1008 simple_numerical_operators_derivatives.iteritems():
1009
1010 attribute_name = "__%s__" % op
1011
1012
1013
1014 if attribute_name in dir(float):
1015 setattr(AffineScalarFunc, attribute_name,
1016 wrap(getattr(float, attribute_name),
1017 [derivative]))
1018 _modified_operators.append(op)
1019
1020
1021
1022
1023 for (op, derivatives) in _ops_with_reflection.iteritems():
1024 attribute_name = '__%s__' % op
1025 setattr(AffineScalarFunc, attribute_name,
1026 wrap(getattr(float, attribute_name), derivatives))
1027
1028
1029
1030
1031 for coercion_type in ('complex', 'int', 'long', 'float'):
1032 def raise_error(self):
1033 raise TypeError("can't convert an affine function (%s)"
1034 ' to %s; use x.nominal_value'
1035
1036 % (self.__class__, coercion_type))
1037
1038 setattr(AffineScalarFunc, '__%s__' % coercion_type, raise_error)
1039
1040 add_operators_to_AffineScalarFunc()
1043 """
1044 Representation of a float-like scalar random variable, along with
1045 its uncertainty.
1046 """
1047
1048
1049 __slots__ = ('_std_dev', 'tag')
1050
1051 - def __init__(self, value, std_dev, tag=None):
1052 """
1053 The nominal value and the standard deviation of the variable
1054 are set. These values must be scalars.
1055
1056 'tag' is a tag that the user can associate to the variable. This
1057 is useful for tracing variables.
1058
1059 The meaning of the nominal value is described in the main
1060 module documentation.
1061 """
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072 value = float(value)
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085 super(Variable, self).__init__(value, {self: 1.})
1086
1087
1088
1089
1090
1091
1092
1093 assert std_dev >= 0, "the error must be a positive number"
1094
1095
1096 self._std_dev = std_dev
1097
1098 self.tag = tag
1099
1100
1101
1102
1103
1105 """
1106 Updates the standard deviation of the variable to a new value.
1107 """
1108
1109
1110
1111
1112
1113 self._std_dev = value
1114
1115
1117 """
1118 Uses the to_string() conversion function on both the nominal
1119 value and standard deviation and returns a string that
1120 describes the number.
1121
1122 to_string() is typically repr() or str().
1123 """
1124 num_repr = super(Variable, self)._general_representation(to_string)
1125
1126
1127
1128
1129
1130
1131 return (num_repr if ((self.tag is None) or (to_string != repr))
1132 else "< %s = %s >" % (self.tag, num_repr))
1133
1135 """
1136 Hook for the standard copy module.
1137 """
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148 return Variable(self.nominal_value, self.std_dev(), self.tag)
1149
1151 """
1152 Hook for the standard copy module.
1153
1154 A new variable is created.
1155 """
1156
1157
1158
1159
1160
1161
1162
1163 return self.__copy__()
1164
1166 """
1167 Hook for the standard pickle module.
1168 """
1169 obj_slot_values = dict((k, getattr(self, k)) for k in self.__slots__)
1170 obj_slot_values.update(AffineScalarFunc.__getstate__(self))
1171
1172 return obj_slot_values
1173
1175 """
1176 Hook for the standard pickle module.
1177 """
1178 for (name, value) in data_dict.iteritems():
1179 setattr(self, name, value)
1180
1186 """
1187 Returns the nominal value of x if it is a quantity with
1188 uncertainty (i.e., an AffineScalarFunc object); otherwise, returns
1189 x unchanged.
1190
1191 This utility function is useful for transforming a series of
1192 numbers, when only some of them generally carry an uncertainty.
1193 """
1194
1195 return x.nominal_value if isinstance(x, AffineScalarFunc) else x
1196
1198 """
1199 Returns the standard deviation of x if it is a quantity with
1200 uncertainty (i.e., an AffineScalarFunc object); otherwise, returns
1201 the float 0.
1202
1203 This utility function is useful for transforming a series of
1204 numbers, when only some of them generally carry an uncertainty.
1205 """
1206
1207 return x.std_dev() if isinstance(x, AffineScalarFunc) else 0.
1208
1210 """
1211 Returns a matrix that contains the covariances between the given
1212 sequence of numbers with uncertainties (AffineScalarFunc objects).
1213 The resulting matrix implicitly depends on their ordering in
1214 'functions'.
1215
1216 The covariances are floats (never int objects).
1217
1218 The returned covariance matrix is the exact linear approximation
1219 result, if the nominal values of the functions and of their
1220 variables are their mean. Otherwise, the returned covariance
1221 matrix should be close to it linear approximation value.
1222 """
1223
1224
1225 covariance_matrix = []
1226 for (i1, expr1) in enumerate(functions):
1227 derivatives1 = expr1.derivatives
1228 vars1 = set(derivatives1)
1229 coefs_expr1 = []
1230 for (i2, expr2) in enumerate(functions[:i1+1]):
1231 derivatives2 = expr2.derivatives
1232 coef = 0.
1233 for var in vars1.intersection(derivatives2):
1234
1235 coef += (derivatives1[var]*derivatives2[var]*var._std_dev**2)
1236 coefs_expr1.append(coef)
1237 covariance_matrix.append(coefs_expr1)
1238
1239
1240 for (i, covariance_coefs) in enumerate(covariance_matrix):
1241 covariance_coefs.extend(covariance_matrix[j][i]
1242 for j in range(i+1, len(covariance_matrix)))
1243
1244 return covariance_matrix
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254 try:
1255 import numpy
1256 except ImportError:
1257 pass
1258 else:
1306
1307 __all__.append('correlated_values')
1308
1309
1310
1311
1312 POSITIVE_DECIMAL_UNSIGNED = r'(\d+)(\.\d*)?'
1313
1314
1315
1316 NUMBER_WITH_UNCERT_RE_STR = '''
1317 ([+-])? # Sign
1318 %s # Main number
1319 (?:\(%s\))? # Optional uncertainty
1320 ([eE][+-]?\d+)? # Optional exponent
1321 ''' % (POSITIVE_DECIMAL_UNSIGNED, POSITIVE_DECIMAL_UNSIGNED)
1322
1323 NUMBER_WITH_UNCERT_RE = re.compile(
1324 "^%s$" % NUMBER_WITH_UNCERT_RE_STR, re.VERBOSE)
1327 """
1328 Returns (value, error) from a string representing a number with
1329 uncertainty like 12.34(5), 12.34(142), 12.5(3.4) or 12.3(4.2)e3.
1330 If no parenthesis is given, an uncertainty of one on the last
1331 digit is assumed.
1332
1333 Raises ValueError if the string cannot be parsed.
1334 """
1335
1336 match = NUMBER_WITH_UNCERT_RE.search(representation)
1337
1338 if match:
1339
1340
1341
1342 (sign, main_int, main_dec, uncert_int, uncert_dec,
1343 exponent) = match.groups()
1344 else:
1345 raise ValueError("Unparsable number representation: '%s'."
1346 " Was expecting a string of the form 1.23(4)"
1347 " or 1.234" % representation)
1348
1349
1350 value = float(''.join((sign or '',
1351 main_int,
1352 main_dec or '.0',
1353 exponent or '')))
1354
1355 if uncert_int is None:
1356
1357
1358 uncert_int = '1'
1359
1360
1361 uncert_int = '0'
1362
1363
1364 if uncert_dec is not None:
1365 uncert = float("%s%s" % (uncert_int, uncert_dec or ''))
1366 else:
1367
1368
1369
1370
1371 num_digits_after_period = (0 if main_dec is None
1372 else len(main_dec)-1)
1373 uncert = int(uncert_int)/10**num_digits_after_period
1374
1375
1376 uncert *= float("1%s" % (exponent or ''))
1377
1378 return (value, uncert)
1379
1385 """
1386 Given a string that represents a number with uncertainty, returns the
1387 nominal value and the uncertainty.
1388
1389 The string can be of the form:
1390 - 124.5+/-0.15
1391 - 124.50(15)
1392 - 124.50(123)
1393 - 124.5
1394
1395 When no numerical error is given, an uncertainty of 1 on the last
1396 digit is implied.
1397
1398 Raises ValueError if the string cannot be parsed.
1399 """
1400
1401 try:
1402
1403 (value, uncert) = representation.split('+/-')
1404 except ValueError:
1405
1406 parsed_value = parse_error_in_parentheses(representation)
1407 else:
1408 try:
1409 parsed_value = (float(value), float(uncert))
1410 except ValueError:
1411 raise ValueError('Cannot parse %s: was expecting a number'
1412 ' like 1.23+/-0.1' % representation)
1413
1414 return parsed_value
1415
1416 -def ufloat(representation, tag=None):
1417 """
1418 Returns a random variable (Variable object).
1419
1420 Converts the representation of a number into a number with
1421 uncertainty (a random variable, defined by a nominal value and
1422 a standard deviation).
1423
1424 The representation can be a (value, standard deviation) sequence,
1425 or a string.
1426
1427 Strings of the form '12.345+/-0.015', '12.345(15)', or '12.3' are
1428 recognized (see full list below). In the last case, an
1429 uncertainty of +/-1 is assigned to the last digit.
1430
1431 'tag' is an optional string tag for the variable. Variables
1432 don't have to have distinct tags. Tags are useful for tracing
1433 what values (and errors) enter in a given result (through the
1434 error_components() method).
1435
1436 Examples of valid string representations:
1437
1438 -1.23(3.4)
1439 -1.34(5)
1440 1(6)
1441 3(4.2)
1442 -9(2)
1443 1234567(1.2)
1444 12.345(15)
1445 -12.3456(78)e-6
1446 0.29
1447 31.
1448 -31.
1449 31
1450 -3.1e10
1451 169.0(7)
1452 169.1(15)
1453 """
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469 if isinstance(representation, basestring):
1470 representation = str_to_number_with_uncert(representation)
1471
1472
1473
1474
1475
1476
1477
1478 if tag is not None:
1479 assert ((type(tag) is str) or (type(tag) is unicode)), \
1480 "The tag can only be a string."
1481
1482
1483 return Variable(*representation, **{'tag': tag})
1484
1489 """
1490 Wrapper for legacy code. Obsolete: do not use. Use ufloat
1491 instead.
1492 """
1493 import warnings
1494 warnings.warn("NumberWithUncert is obsolete."
1495 " Use ufloat instead.", DeprecationWarning,
1496 stacklevel=2)
1497 return ufloat(*args)
1498
1500 """
1501 Wrapper for legacy code. Obsolete: do not use. Use ufloat
1502 instead.
1503 """
1504 import warnings
1505 warnings.warn("num_with_uncert is obsolete."
1506 " Use ufloat instead.", DeprecationWarning,
1507 stacklevel=2)
1508 return ufloat(*args)
1509
1511 """
1512 Wrapper for legacy code. Obsolete: do not use. Use
1513 unumpy.uarray instead.
1514 """
1515 import warnings
1516 warnings.warn("uncertainties.array_u is obsolete."
1517 " Use uncertainties.unumpy.uarray instead.",
1518 DeprecationWarning,
1519 stacklevel=2)
1520 import uncertainties.unumpy
1521 return uncertainties.unumpy.uarray(*args)
1522
1524 """
1525 Wrapper for legacy code. Obsolete: do not use. Use
1526 unumpy.nominal_values instead.
1527 """
1528 import warnings
1529 warnings.warn("uncertainties.nominal_values is obsolete."
1530 " Use uncertainties.unumpy.nominal_values instead.",
1531 DeprecationWarning,
1532 stacklevel=2)
1533 import uncertainties.unumpy
1534 return uncertainties.unumpy.nominal_values(*args)
1535
1537 """
1538 Wrapper for legacy code. Obsolete: do not use. Use ufloat
1539 instead.
1540 """
1541 import warnings
1542 warnings.warn("uncertainties.std_devs is obsolete."
1543 " Use uncertainties.unumpy.std_devs instead.",
1544 DeprecationWarning,
1545 stacklevel=2)
1546 import uncertainties.unumpy
1547 return uncertainties.unumpy.std_devs(*args)
1548