metrolopy 0.6.5__py3-none-any.whl → 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- metrolopy/__init__.py +5 -4
- metrolopy/budget.py +61 -50
- metrolopy/builtin_constants.py +903 -0
- metrolopy/constant.py +108 -104
- metrolopy/constcom.py +84 -83
- metrolopy/distributions.py +120 -39
- metrolopy/exceptions.py +7 -9
- metrolopy/fit.py +3 -3
- metrolopy/functions.py +4 -4
- metrolopy/gummy.py +571 -530
- metrolopy/indexed.py +69 -20
- metrolopy/logunit.py +1 -1
- metrolopy/mean.py +8 -9
- metrolopy/miscunits.py +21 -6
- metrolopy/nummy.py +224 -180
- metrolopy/offsetunit.py +2 -3
- metrolopy/prefixedunit.py +24 -23
- metrolopy/relunits.py +1 -2
- metrolopy/siunits.py +7 -5
- metrolopy/tests/__init__.py +6 -6
- metrolopy/tests/test_complex.py +22 -0
- metrolopy/tests/test_create.py +10 -6
- metrolopy/tests/test_gummy.py +93 -43
- metrolopy/tests/test_misc.py +4 -1
- metrolopy/tests/test_operations.py +58 -2
- metrolopy/tests/test_ubreakdown.py +6 -2
- metrolopy/ummy.py +890 -898
- metrolopy/unit.py +287 -182
- metrolopy/unitparser.py +40 -42
- metrolopy/unitutils.py +183 -159
- metrolopy/usunits.py +14 -13
- metrolopy/version.py +1 -1
- {metrolopy-0.6.5.dist-info → metrolopy-1.0.1.dist-info}/METADATA +23 -5
- metrolopy-1.0.1.dist-info/RECORD +45 -0
- metrolopy-0.6.5.dist-info/RECORD +0 -44
- {metrolopy-0.6.5.dist-info → metrolopy-1.0.1.dist-info}/WHEEL +0 -0
- {metrolopy-0.6.5.dist-info → metrolopy-1.0.1.dist-info}/licenses/LICENSE +0 -0
- {metrolopy-0.6.5.dist-info → metrolopy-1.0.1.dist-info}/top_level.txt +0 -0
- {metrolopy-0.6.5.dist-info → metrolopy-1.0.1.dist-info}/zip-safe +0 -0
metrolopy/ummy.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# module ummy
|
|
4
4
|
|
|
5
|
-
# Copyright (C)
|
|
5
|
+
# Copyright (C) 2025 National Research Council Canada
|
|
6
6
|
# Author: Harold Parks
|
|
7
7
|
|
|
8
8
|
# This file is part of MetroloPy.
|
|
@@ -29,27 +29,29 @@ of gummy.
|
|
|
29
29
|
"""
|
|
30
30
|
|
|
31
31
|
import numpy as np
|
|
32
|
-
import weakref
|
|
33
32
|
from collections import namedtuple
|
|
34
33
|
from warnings import warn
|
|
35
|
-
from math import isnan,isinf,log,
|
|
34
|
+
from math import isnan,isinf,log,pi,sqrt,copysign,floor
|
|
36
35
|
from fractions import Fraction
|
|
37
|
-
from numbers import Rational,Integral,Real,Complex
|
|
36
|
+
from numbers import Rational,Integral,Real,Complex,Number
|
|
38
37
|
from .printing import PrettyPrinter,MetaPrettyPrinter
|
|
39
38
|
from .dfunc import Dfunc
|
|
40
39
|
from .exceptions import UncertiantyPrecisionWarning
|
|
41
|
-
from .unit import Unit,one,Quantity
|
|
40
|
+
from .unit import Unit,one,Quantity,MFraction
|
|
41
|
+
from decimal import Decimal,localcontext,InvalidOperation
|
|
42
|
+
from abc import ABCMeta
|
|
42
43
|
|
|
43
44
|
try:
|
|
44
|
-
from mpmath import mp,mpf
|
|
45
|
+
from mpmath import mp,mpf
|
|
45
46
|
except:
|
|
46
47
|
mp = mpf = None
|
|
47
48
|
|
|
49
|
+
|
|
48
50
|
_FInfo = namedtuple('_FIinfo','rel_u precision warn_u ')
|
|
49
51
|
_iinfo = _FInfo(rel_u=0,precision=0,warn_u=0)
|
|
50
52
|
_finfodict = {}
|
|
51
53
|
def _getfinfo(x):
|
|
52
|
-
if isinstance(x,Rational):
|
|
54
|
+
if isinstance(x,Rational) or isinstance(x,Decimal):
|
|
53
55
|
return (_iinfo,x)
|
|
54
56
|
|
|
55
57
|
if mpf is not None and isinstance(x,mpf):
|
|
@@ -85,6 +87,11 @@ def _combu(a,b,c):
|
|
|
85
87
|
# with 1 >= c >= -1,
|
|
86
88
|
# this method avoids overflows or underflows when taking the squares
|
|
87
89
|
|
|
90
|
+
if b == 0:
|
|
91
|
+
return a
|
|
92
|
+
if a == 0:
|
|
93
|
+
return b
|
|
94
|
+
|
|
88
95
|
if isnan(a) or isnan(b):
|
|
89
96
|
try:
|
|
90
97
|
return type(a)('nan')
|
|
@@ -93,39 +100,37 @@ def _combu(a,b,c):
|
|
|
93
100
|
|
|
94
101
|
if abs(b) > abs(a):
|
|
95
102
|
a,b = b,a
|
|
96
|
-
if b == 0:
|
|
97
|
-
return abs(a)
|
|
98
103
|
|
|
99
104
|
r = b/a
|
|
100
105
|
x = 1 + r**2 + 2*c*r
|
|
101
106
|
|
|
102
107
|
if x < 0:
|
|
103
108
|
if x < -1e6:
|
|
104
|
-
# something horrible happened,
|
|
109
|
+
# something horrible happened, a bug must have generated a value of
|
|
110
|
+
# for c that allows this
|
|
105
111
|
raise FloatingPointError('u is sqrt of ' + str(a**2*x))
|
|
106
|
-
# maybe possibly get here due to a floating point rounding error
|
|
112
|
+
# maybe possibly get here due to a floating point rounding error
|
|
107
113
|
return 0
|
|
108
114
|
|
|
109
|
-
|
|
115
|
+
if hasattr(x,'sqrt'):
|
|
116
|
+
return abs(a)*x.sqrt()
|
|
117
|
+
return abs(a)*x**0.5
|
|
110
118
|
|
|
111
119
|
def _isscalar(x):
|
|
120
|
+
if isinstance(x,str):
|
|
121
|
+
return True
|
|
112
122
|
try:
|
|
113
123
|
len(x)
|
|
114
124
|
return False
|
|
115
125
|
except:
|
|
116
126
|
return True
|
|
117
|
-
|
|
118
|
-
def _floor(x):
|
|
119
|
-
if x < 0:
|
|
120
|
-
return int(x) - 1
|
|
121
|
-
return int(x)
|
|
122
127
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
return -1
|
|
126
|
-
return 1
|
|
128
|
+
def sign(x):
|
|
129
|
+
return copysign(1,x)
|
|
127
130
|
|
|
128
131
|
def _format_exp(fmt,xp):
|
|
132
|
+
if xp == 0:
|
|
133
|
+
return ''
|
|
129
134
|
if fmt == 'html':
|
|
130
135
|
ex = ' × 10<sup>' + str(xp) + '</sup>'
|
|
131
136
|
elif fmt == 'latex':
|
|
@@ -137,8 +142,175 @@ def _format_exp(fmt,xp):
|
|
|
137
142
|
ex += str(xp)
|
|
138
143
|
return ex
|
|
139
144
|
|
|
145
|
+
def _to_decimal(x,max_digits=None):
|
|
146
|
+
# called from the ummy and gummy tostring methods to convert x and u values.
|
|
147
|
+
# The Decimal data type has some convinent methods for rounding numbers to
|
|
148
|
+
# show a given number of significant figures.
|
|
149
|
+
|
|
150
|
+
if mpf is not None and isinstance(x,mpf):
|
|
151
|
+
xd = Decimal(mp.nstr(x,n=mp.dps))
|
|
152
|
+
elif isinstance(x,Rational):
|
|
153
|
+
if max_digits is None:
|
|
154
|
+
max_digits = ummy.max_digits
|
|
155
|
+
with localcontext(prec=max_digits):
|
|
156
|
+
try:
|
|
157
|
+
xd = Decimal(x.numerator)/Decimal(x.denominator)
|
|
158
|
+
except:
|
|
159
|
+
try:
|
|
160
|
+
xd = Decimal(str(x.numerator))/Decimal(str(x.denominator))
|
|
161
|
+
except:
|
|
162
|
+
xd = Decimal(float(x.numerator))/Decimal(float(x.denominator))
|
|
163
|
+
else:
|
|
164
|
+
try:
|
|
165
|
+
xd = Decimal(x)
|
|
166
|
+
except:
|
|
167
|
+
try:
|
|
168
|
+
xd = Decimal(str(x))
|
|
169
|
+
except:
|
|
170
|
+
xd = Decimal(float(x))
|
|
171
|
+
return xd
|
|
172
|
+
|
|
173
|
+
def _decimal_str(x,fmt='unicode',rtrim=False,dplace=0,dalign=None,th_spaces=True):
|
|
174
|
+
# string formats a Decimal number, putting digits in groups of three
|
|
175
|
+
# separated by spaces if necessary
|
|
176
|
+
|
|
177
|
+
if x.is_nan():
|
|
178
|
+
return 'nan'
|
|
179
|
+
if x.is_infinite() and x > 0:
|
|
180
|
+
if fmt == 'html':
|
|
181
|
+
return '∞'
|
|
182
|
+
if fmt == 'latex':
|
|
183
|
+
return r'\infty'
|
|
184
|
+
if fmt == 'ascii':
|
|
185
|
+
return 'inf'
|
|
186
|
+
return '\u221E'
|
|
187
|
+
if x.is_infinite() and x < 0:
|
|
188
|
+
if fmt == 'html':
|
|
189
|
+
return '-∞'
|
|
190
|
+
if fmt == 'latex':
|
|
191
|
+
return r'-\infty'
|
|
192
|
+
if fmt == 'ascii':
|
|
193
|
+
return '-inf'
|
|
194
|
+
return '-\u221E'
|
|
195
|
+
|
|
196
|
+
s,d,e = x.as_tuple()
|
|
197
|
+
|
|
198
|
+
if dalign is not None:
|
|
199
|
+
dplace = len(d) + e - 1 - dalign
|
|
200
|
+
|
|
201
|
+
if rtrim:
|
|
202
|
+
if dplace is not None and dplace > 0:
|
|
203
|
+
dp = dplace
|
|
204
|
+
else:
|
|
205
|
+
dp = 0
|
|
206
|
+
|
|
207
|
+
i = len(d)
|
|
208
|
+
while i > dp + 1 and d[i-1] == 0:
|
|
209
|
+
i -= 1
|
|
210
|
+
if i != len(d):
|
|
211
|
+
d = d[:i]
|
|
212
|
+
|
|
213
|
+
if dplace is None:
|
|
214
|
+
th_spaces = False
|
|
215
|
+
else:
|
|
216
|
+
th_spaces &= (dplace > 3 or len(d) - dplace > 5)
|
|
217
|
+
if dplace < 0:
|
|
218
|
+
d = (0,)*(-dplace) + d
|
|
219
|
+
dplace = 0
|
|
220
|
+
elif x != 0 and dplace > len(d) - 1:
|
|
221
|
+
d = d + (0,)*(dplace - len(d) + 1)
|
|
222
|
+
|
|
223
|
+
if th_spaces:
|
|
224
|
+
if fmt == 'html':
|
|
225
|
+
sp = ' '
|
|
226
|
+
elif fmt == 'latex':
|
|
227
|
+
sp = r'\,'
|
|
228
|
+
else:
|
|
229
|
+
sp = ' '
|
|
230
|
+
|
|
231
|
+
if s and x != 0:
|
|
232
|
+
txt = '-'
|
|
233
|
+
else:
|
|
234
|
+
txt = ''
|
|
235
|
+
|
|
236
|
+
for i in range(len(d)):
|
|
237
|
+
if i > 0 and dplace is not None:
|
|
238
|
+
if i == dplace + 1:
|
|
239
|
+
txt += '.'
|
|
240
|
+
elif th_spaces and (i - dplace - 1) % 3 == 0:
|
|
241
|
+
txt += sp
|
|
242
|
+
txt += chr(48 + d[i])
|
|
243
|
+
|
|
244
|
+
return txt
|
|
245
|
+
|
|
246
|
+
def _xtype(x):
|
|
247
|
+
# this is used when type conversions are needed, e.g. to convert float
|
|
248
|
+
# correlations or uncertainties to Decimal when dealing with ummy's
|
|
249
|
+
# with Decimal x values.
|
|
250
|
+
|
|
251
|
+
if isinstance(x,np.ndarray):
|
|
252
|
+
tp = x.dtype.type
|
|
253
|
+
else:
|
|
254
|
+
tp = type(x)
|
|
255
|
+
if not issubclass(tp,Number):
|
|
256
|
+
return float
|
|
257
|
+
if issubclass(tp,Integral):
|
|
258
|
+
return MFraction.fromnum
|
|
259
|
+
return tp
|
|
260
|
+
|
|
261
|
+
def _combrd(xl,du):
|
|
262
|
+
ret = _udict(xl[0]._ref)
|
|
263
|
+
for r in ret:
|
|
264
|
+
#ret[r] *= sign(xl[0]._u)*du[0]
|
|
265
|
+
ret[r] *= du[0]
|
|
266
|
+
|
|
267
|
+
for i in range(len(xl) - 1):
|
|
268
|
+
c = sign(xl[i+1]._u)*du[i+1]
|
|
269
|
+
for k,v in xl[i+1]._ref.items():
|
|
270
|
+
f = v*c
|
|
271
|
+
if k in ret:
|
|
272
|
+
ret[k] += f
|
|
273
|
+
else:
|
|
274
|
+
ret[k] = f
|
|
275
|
+
|
|
276
|
+
n = sum([i**2 for i in ret.values()])
|
|
277
|
+
if abs(n-1) > ummy.correlation_tolerance:
|
|
278
|
+
n = sqrt(n)
|
|
279
|
+
for k,v in ret.items():
|
|
280
|
+
ret[k] /= n
|
|
281
|
+
|
|
282
|
+
for k,v in list(ret.items()):
|
|
283
|
+
x = ret[k]
|
|
284
|
+
if x < -1 + ummy.correlation_tolerance:
|
|
285
|
+
return _udict(((k,-1),))
|
|
286
|
+
elif x > 1 - ummy.correlation_tolerance:
|
|
287
|
+
return _udict(((k,1),))
|
|
288
|
+
elif abs(ret[k]) < ummy.correlation_tolerance:
|
|
289
|
+
del ret[k]
|
|
290
|
+
|
|
291
|
+
return ret
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
# Correlations between ummy's are stored in a _udict instance.
|
|
295
|
+
# The keys in the _udict are _UmmyRef instances that reprensent independent
|
|
296
|
+
# variables and store the dof and utype of the variable. The values are the
|
|
297
|
+
# correlation of the ummy with those independent variables (to within a sign)
|
|
298
|
+
# and are also equal to the deriviative of the ummy with respect to each of
|
|
299
|
+
# those variables divided by the combined standard uncertainty of the ummy.
|
|
300
|
+
class _udict(dict):
|
|
301
|
+
pass
|
|
140
302
|
|
|
141
|
-
class
|
|
303
|
+
class _UmmyRef:
|
|
304
|
+
__slots__ = 'dof','utype'
|
|
305
|
+
def __init__(self,dof=None,utype=None,dist=None):
|
|
306
|
+
self.dof = dof
|
|
307
|
+
self.utype = utype
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class MetaUmmy(MetaPrettyPrinter,ABCMeta):
|
|
311
|
+
pass
|
|
312
|
+
|
|
313
|
+
class ummy(Dfunc,PrettyPrinter,Number,metaclass=MetaUmmy):
|
|
142
314
|
max_dof = 10000 # any larger dof will be rounded to float('inf')
|
|
143
315
|
nsig = 2 # The number of digits to quote the uncertainty to
|
|
144
316
|
thousand_spaces = True # If true spaces will be placed between groups of three digits.
|
|
@@ -155,7 +327,12 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
155
327
|
# rounding errors
|
|
156
328
|
rounding_u = False
|
|
157
329
|
|
|
158
|
-
max_digits =
|
|
330
|
+
max_digits = 20
|
|
331
|
+
|
|
332
|
+
# correlations with absolute values smaller that this are rounded to
|
|
333
|
+
# zero and correlations samaller than -1 + correlation_tolerance or
|
|
334
|
+
# bigger than 1 - correlation_tolerance are rounded to -1 or 1 respectively.
|
|
335
|
+
correlation_tolerance = 1e-14
|
|
159
336
|
|
|
160
337
|
def __init__(self,x,u=0,dof=float('inf'),utype=None):
|
|
161
338
|
if isinstance(x,Quantity):
|
|
@@ -165,26 +342,25 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
165
342
|
self._copy(x,self,formatting=False)
|
|
166
343
|
return
|
|
167
344
|
|
|
168
|
-
|
|
169
|
-
float(x)
|
|
170
|
-
except TypeError:
|
|
171
|
-
raise TypeError('x must be a real number')
|
|
172
|
-
|
|
173
|
-
try:
|
|
174
|
-
float(u)
|
|
175
|
-
if not isnan(u) and u < 0:
|
|
176
|
-
raise TypeError()
|
|
177
|
-
except TypeError:
|
|
178
|
-
raise TypeError('u must be a real number >= 0')
|
|
179
|
-
|
|
180
|
-
if u == 0:
|
|
181
|
-
dof = float('inf')
|
|
182
|
-
utype = None
|
|
183
|
-
|
|
345
|
+
# MFractions convert implicitly to Decimal or mpf
|
|
184
346
|
if isinstance(x,Fraction):
|
|
185
|
-
x = MFraction(x)
|
|
347
|
+
x = MFraction(x)
|
|
186
348
|
if isinstance(u,Fraction):
|
|
187
349
|
u = MFraction(u)
|
|
350
|
+
|
|
351
|
+
# sometimes numpy.array of shape () appear and cause problems
|
|
352
|
+
if isinstance(x,np.ndarray):
|
|
353
|
+
x = x.item()
|
|
354
|
+
if isinstance(u,np.ndarray):
|
|
355
|
+
u = u.item()
|
|
356
|
+
|
|
357
|
+
try:
|
|
358
|
+
float(x)
|
|
359
|
+
hash(x)
|
|
360
|
+
except TypeError:
|
|
361
|
+
raise TypeError('x must be a real number') from None
|
|
362
|
+
except ValueError:
|
|
363
|
+
raise ValueError('x must be a real number') from None
|
|
188
364
|
|
|
189
365
|
if self.rounding_u and not isinstance(x,Rational):
|
|
190
366
|
finfo,x = _getfinfo(x)
|
|
@@ -193,10 +369,6 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
193
369
|
else:
|
|
194
370
|
u = _combu(u,float(abs(x)*finfo.rel_u),0)
|
|
195
371
|
|
|
196
|
-
#uinfo = _getfinfo(u)[0]
|
|
197
|
-
#if uc < uinfo.warn_u and u is not 0:
|
|
198
|
-
#warn('a gummy/ummy was defined with an uncertainty too small to be accurately represented by a float\nuncertainty values may contain significant numerical errors',UncertiantyPrecisionWarning)
|
|
199
|
-
|
|
200
372
|
self._finfo = finfo
|
|
201
373
|
else:
|
|
202
374
|
self._finfo = None
|
|
@@ -205,29 +377,80 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
205
377
|
self._x = x
|
|
206
378
|
self._u = u
|
|
207
379
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
380
|
+
if not isinf(u) and not isnan(u) and u != 0:
|
|
381
|
+
if isinstance(dof,_udict):
|
|
382
|
+
# If the ummy has been created from a mathematical operation
|
|
383
|
+
# between other ummy's then correlations with the independent
|
|
384
|
+
# variables are stored in the _udict. The keys in the _udict
|
|
385
|
+
# are _UmmyRef instances that represent independent
|
|
386
|
+
# variables and store the dof and utype of the variable. The
|
|
387
|
+
# values are the correlation of the ummy with those independent
|
|
388
|
+
# variables (to within a sign) and are also equal to the
|
|
389
|
+
# deriviative of the ummy with respect to each of those variables
|
|
390
|
+
# divided by self._u. The actual value of the
|
|
391
|
+
# dof property will be computed with the W-S apporoximation.
|
|
392
|
+
# u may be negative indicating that it is sharing a _ref
|
|
393
|
+
# _udict with another ummy and has correlation = -1 with that
|
|
394
|
+
# ummy: e.g. if the ummy is the result of the operation 1 - x
|
|
395
|
+
# we don't bother to create a new _udict for the ummy, we just
|
|
396
|
+
# use x._ref with self._u having the opposite sign from x._u.
|
|
211
397
|
self._ref = dof
|
|
212
|
-
|
|
398
|
+
|
|
399
|
+
# _dof and _utype are set to None for now and will be computed
|
|
400
|
+
# if and when the dof and utype properties are called for the
|
|
401
|
+
# first time.
|
|
402
|
+
self._dof = None
|
|
403
|
+
self._utype = None
|
|
213
404
|
else:
|
|
405
|
+
# The ummy represents an independent variable, that is u > 0
|
|
406
|
+
# and it is not (at this moment) correlated with any other
|
|
407
|
+
# ummy's.
|
|
408
|
+
|
|
214
409
|
try:
|
|
215
|
-
|
|
216
|
-
|
|
410
|
+
float(u)
|
|
411
|
+
hash(u)
|
|
412
|
+
if not isnan(u) and u < 0:
|
|
413
|
+
raise ValueError()
|
|
414
|
+
except TypeError:
|
|
415
|
+
raise TypeError('u must be a real number >= 0') from None
|
|
416
|
+
except ValueError:
|
|
417
|
+
raise ValueError('u must be a real number >= 0') from None
|
|
418
|
+
|
|
419
|
+
try:
|
|
420
|
+
if dof > self.max_dof:
|
|
421
|
+
dof = float('inf')
|
|
217
422
|
else:
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
423
|
+
if isinstance(dof,Integral):
|
|
424
|
+
dof = int(dof)
|
|
425
|
+
else:
|
|
426
|
+
dof = float(dof)
|
|
427
|
+
if dof <= 0 or isnan(dof):
|
|
428
|
+
raise ValueError()
|
|
429
|
+
except ValueError:
|
|
430
|
+
raise ValueError('dof must be a real number > 0') from None
|
|
221
431
|
except TypeError:
|
|
222
|
-
raise TypeError('dof must be a real number > 0')
|
|
223
|
-
if
|
|
224
|
-
|
|
432
|
+
raise TypeError('dof must be a real number > 0') from None
|
|
433
|
+
if utype is not None and not isinstance(utype,str):
|
|
434
|
+
raise TypeError('utype must be str or None')
|
|
435
|
+
|
|
436
|
+
self._dof = dof
|
|
437
|
+
if utype is None:
|
|
438
|
+
self._utype = [None]
|
|
439
|
+
else:
|
|
440
|
+
self._utype = [utype]
|
|
225
441
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
442
|
+
# create an _UmmyRef to represent the actual independent
|
|
443
|
+
# variable and create a _udict with one entry defining
|
|
444
|
+
# the correlation with that _UmmyRef to be 1
|
|
445
|
+
self._ref = _udict(((_UmmyRef(dof,utype),1),))
|
|
229
446
|
else:
|
|
447
|
+
# if u == 0, then the ummy will have no correlation with any
|
|
448
|
+
# independent variables, no utype and the dof property will return
|
|
449
|
+
# float('inf')
|
|
450
|
+
|
|
230
451
|
self._ref = None
|
|
452
|
+
self._dof = None
|
|
453
|
+
self._utype = [None]
|
|
231
454
|
|
|
232
455
|
@property
|
|
233
456
|
def x(self):
|
|
@@ -235,7 +458,7 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
235
458
|
|
|
236
459
|
@property
|
|
237
460
|
def u(self):
|
|
238
|
-
return self._u
|
|
461
|
+
return abs(self._u)
|
|
239
462
|
|
|
240
463
|
@property
|
|
241
464
|
def dof(self):
|
|
@@ -246,30 +469,71 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
246
469
|
ummy is based on. If the ummy was created as the result of an
|
|
247
470
|
operation between two or more other gummys, then the dof is the effective
|
|
248
471
|
number of degrees of freedom calculated using the Welch-Satterthwaite
|
|
249
|
-
approximation.
|
|
250
|
-
approximation is used that takes into account correlations, see
|
|
251
|
-
[R. Willink, Metrologia, 44, 340 (2007)]. However correlations are
|
|
252
|
-
not handled perfectly. So if accurate dof calculations are need, care
|
|
253
|
-
should be taken to ensure that correlations are not generated in
|
|
254
|
-
intermediate calculations.
|
|
472
|
+
approximation.
|
|
255
473
|
"""
|
|
474
|
+
if self._dof is not None:
|
|
475
|
+
return self._dof
|
|
476
|
+
|
|
256
477
|
if self._ref is None:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
#
|
|
260
|
-
#
|
|
261
|
-
|
|
262
|
-
|
|
478
|
+
self._dof = float('inf')
|
|
479
|
+
else:
|
|
480
|
+
# if _dof has not been set yet, calculate it using the
|
|
481
|
+
# Welch-Satterthwaite approximation and store the result in
|
|
482
|
+
# self._dof. self._ref is a dictionary and the keys are _UmmyRef
|
|
483
|
+
# instances representing the indedpendent variables and store the
|
|
484
|
+
# dof for those variables. The values are the correlations of
|
|
485
|
+
# self with those independent variables and these correlations
|
|
486
|
+
# are equal to the derivative of self with respect to each
|
|
487
|
+
# independent variable divided by self.u.
|
|
488
|
+
|
|
489
|
+
rdof = 0
|
|
490
|
+
for k,v in self._ref.items():
|
|
491
|
+
if k.dof is not None:
|
|
492
|
+
rdof += v**4/k.dof
|
|
493
|
+
if rdof > 0:
|
|
494
|
+
rdof = 1/rdof
|
|
495
|
+
if rdof > self.max_dof:
|
|
496
|
+
rdof = float('inf')
|
|
497
|
+
elif rdof < 1:
|
|
498
|
+
rdof = 1
|
|
499
|
+
self._dof = rdof
|
|
500
|
+
else:
|
|
501
|
+
self._dof = float('inf')
|
|
502
|
+
|
|
503
|
+
return self._dof
|
|
263
504
|
|
|
264
505
|
@property
|
|
265
|
-
def
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
506
|
+
def utype(self):
|
|
507
|
+
"""
|
|
508
|
+
`str`, `None` or a list containing strings and possibly `None`
|
|
509
|
+
|
|
510
|
+
An arbitrary string value labeling the uncertainty type or or a
|
|
511
|
+
list of types if the gummy was constructed from independent
|
|
512
|
+
variables with different utypes.
|
|
513
|
+
"""
|
|
514
|
+
if self._utype is None:
|
|
515
|
+
# if _utype has not been set yet, go through all the independent
|
|
516
|
+
# variables in self._ref and put all the utypes found in a set.
|
|
517
|
+
self._utype = []
|
|
518
|
+
for k in self._ref:
|
|
519
|
+
if k.utype not in self._utype:
|
|
520
|
+
self._utype.append(k.utype)
|
|
521
|
+
|
|
522
|
+
if len(self._utype) == 1:
|
|
523
|
+
return self._utype[0]
|
|
524
|
+
return self._utype
|
|
525
|
+
|
|
526
|
+
@property
|
|
527
|
+
def isindependent(self):
|
|
528
|
+
"""
|
|
529
|
+
`bool`, read-only
|
|
530
|
+
|
|
531
|
+
Returns `True` if the ummy is an independent variable. That is the
|
|
532
|
+
ummy has u > 0 and was not correlated with any other ummy's when it was
|
|
533
|
+
created or is perfectly correlated or anti-correlated (correlation
|
|
534
|
+
coefficeint 1 or -1) with such an ummy.'
|
|
535
|
+
"""
|
|
536
|
+
return (self._ref is not None) and len(self._ref) == 1
|
|
273
537
|
|
|
274
538
|
@property
|
|
275
539
|
def finfo(self):
|
|
@@ -278,16 +542,17 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
278
542
|
return self._finfo
|
|
279
543
|
|
|
280
544
|
@staticmethod
|
|
281
|
-
def _copy(s,r,formatting=True,
|
|
545
|
+
def _copy(s,r,formatting=True,totype=None):
|
|
282
546
|
# copies attributes of s to r
|
|
283
|
-
if
|
|
284
|
-
r._x =
|
|
285
|
-
r._u =
|
|
547
|
+
if totype is not None:
|
|
548
|
+
r._x = totype(s._x)
|
|
549
|
+
r._u = totype(s._u)
|
|
286
550
|
else:
|
|
287
551
|
r._x = s._x
|
|
288
552
|
r._u = s._u
|
|
289
553
|
r._ref = s._ref
|
|
290
|
-
r.
|
|
554
|
+
r._dof = s._dof
|
|
555
|
+
r._utype = s._utype
|
|
291
556
|
r._finfo = s._finfo
|
|
292
557
|
|
|
293
558
|
if formatting:
|
|
@@ -296,24 +561,24 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
296
561
|
if s.thousand_spaces != type(r).thousand_spaces:
|
|
297
562
|
r.thousand_spaces = s.thousand_spaces
|
|
298
563
|
|
|
299
|
-
def copy(self,formatting=True,
|
|
564
|
+
def copy(self,formatting=True,totype=None):
|
|
300
565
|
"""
|
|
301
566
|
Returns a copy of the ummy. If the `formatting` parameter is
|
|
302
567
|
`True` the display formatting information will be copied and if
|
|
303
568
|
`False` the display formatting will be set to the default for a
|
|
304
|
-
new gummy. The default for `formatting` is `True`. If
|
|
305
|
-
is
|
|
569
|
+
new gummy. The default for `formatting` is `True`. If totype
|
|
570
|
+
is defined the x and u properties will be converted to type `totype`
|
|
306
571
|
before copying.
|
|
307
572
|
"""
|
|
308
573
|
r = type(self).__new__(type(self))
|
|
309
|
-
self._copy(self,r,formatting=formatting,
|
|
574
|
+
self._copy(self,r,formatting=formatting,totype=totype)
|
|
310
575
|
return r
|
|
311
576
|
|
|
312
577
|
def tofloat(self):
|
|
313
578
|
"""
|
|
314
|
-
Returns a copy of the gummy with x an u converted to
|
|
579
|
+
Returns a copy of the gummy with x an u converted to `float`.
|
|
315
580
|
"""
|
|
316
|
-
return self.copy(formatting=False,
|
|
581
|
+
return self.copy(formatting=False,totype=float)
|
|
317
582
|
|
|
318
583
|
def splonk(self):
|
|
319
584
|
"""
|
|
@@ -327,21 +592,27 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
327
592
|
"""
|
|
328
593
|
Returns the correlation coefficient between `self` and `g`.
|
|
329
594
|
"""
|
|
330
|
-
if not isinstance(g,ummy):
|
|
595
|
+
if not isinstance(g,ummy) or self._ref is None or g._ref is None:
|
|
331
596
|
return 0
|
|
332
|
-
|
|
597
|
+
|
|
598
|
+
c = sign(self._u)*sign(g._u)*sum(g._ref[k]*v for k,v in self._ref.items() if k in g._ref)
|
|
599
|
+
|
|
600
|
+
if abs(c) < ummy.correlation_tolerance:
|
|
333
601
|
return 0
|
|
334
|
-
|
|
602
|
+
if c < -1 + ummy.correlation_tolerance:
|
|
603
|
+
return -1
|
|
604
|
+
if c > 1 - ummy.correlation_tolerance:
|
|
605
|
+
return 1
|
|
606
|
+
|
|
607
|
+
return float(c)
|
|
335
608
|
|
|
336
609
|
def covariance(self,g):
|
|
337
610
|
"""
|
|
338
611
|
Returns the covariance between `self` and `g`.
|
|
339
612
|
"""
|
|
340
|
-
if
|
|
341
|
-
return
|
|
342
|
-
|
|
343
|
-
return 0
|
|
344
|
-
return self._u*g._u*(self._refs*g._refs)*self._ref.cor(g._ref)
|
|
613
|
+
if isinstance(g,ummy):
|
|
614
|
+
return self.u*g.u*self.correlation(g)
|
|
615
|
+
return 0
|
|
345
616
|
|
|
346
617
|
@staticmethod
|
|
347
618
|
def correlation_matrix(gummys):
|
|
@@ -356,64 +627,11 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
356
627
|
"""
|
|
357
628
|
Returns the variance-covariance matrix of a list or array of ummys.
|
|
358
629
|
"""
|
|
359
|
-
return [[b.covariance(a) if isinstance(b,ummy) else 0 for b in gummys]
|
|
630
|
+
return [[float(b.covariance(a)) if isinstance(b,ummy) else 0 for b in gummys]
|
|
360
631
|
for a in gummys]
|
|
361
|
-
|
|
362
|
-
@staticmethod
|
|
363
|
-
def _set_correlation_matrix(gummys, matrix):
|
|
364
|
-
n = len(gummys)
|
|
365
|
-
m = np.asarray(matrix)
|
|
366
|
-
|
|
367
|
-
if not np.allclose(m.T,m,atol = 10*_GummyRef._cortol):
|
|
368
|
-
raise ValueError('the correlation matrix is not symmetric')
|
|
369
|
-
e,v = np.linalg.eigh(m)
|
|
370
|
-
if any(e <= -_GummyRef._cortol):
|
|
371
|
-
raise ValueError('the correlation matrix is not positive semi-definate')
|
|
372
|
-
|
|
373
|
-
if m.shape != (n,n):
|
|
374
|
-
raise ValueError('matrix must have shape len(gummys) x len(gummys)')
|
|
375
|
-
|
|
376
|
-
# Check that the diagonal elements are 1 (leaving room for rounding errors).
|
|
377
|
-
for i in range(n):
|
|
378
|
-
if abs(m[i][i] - 1.0) > 0.01:
|
|
379
|
-
raise ValueError('correlation matrix diagonal elements must be 1')
|
|
380
|
-
|
|
381
|
-
# Set the correlations using the off diagonal elements.
|
|
382
|
-
for i in range(0,n):
|
|
383
|
-
for j in range ((i+1),n):
|
|
384
|
-
if m[i][j] != 0:
|
|
385
|
-
if gummys[i]._ref is not None and gummys[j]._ref is not None:
|
|
386
|
-
gummys[i]._ref.set_cor(gummys[j],m[i][j])
|
|
387
|
-
else:
|
|
388
|
-
raise ValueError('constants cannot have a non-zero correlation')
|
|
389
|
-
|
|
390
|
-
@staticmethod
|
|
391
|
-
def _set_covariance_matrix(gummys, matrix):
|
|
392
|
-
n = len(gummys)
|
|
393
|
-
try:
|
|
394
|
-
assert len(matrix) == n
|
|
395
|
-
for r in matrix:
|
|
396
|
-
assert len(r) == n
|
|
397
|
-
except:
|
|
398
|
-
raise ValueError('the covariance matrix must be a square matrix of the same length as gummys')
|
|
399
|
-
|
|
400
|
-
# Set the u of each gummy using the diagonal of the matrix.
|
|
401
|
-
for i in range(n):
|
|
402
|
-
if matrix[i][i] == 0:
|
|
403
|
-
gummys[i]._u = 0
|
|
404
|
-
gummys[i]._ref = None
|
|
405
|
-
else:
|
|
406
|
-
if gummys[i]._ref is None:
|
|
407
|
-
gummys[i]._ref = _GummyRef()
|
|
408
|
-
gummys[i]._u = matrix[i][i]**0.5
|
|
409
|
-
|
|
410
|
-
# Set the correlations
|
|
411
|
-
m = [[e/(gummys[i]._u*gummys[j]._u) for j,e in enumerate(r)]
|
|
412
|
-
for i,r in enumerate(matrix)]
|
|
413
|
-
ummy._set_correlation_matrix(gummys,m)
|
|
414
632
|
|
|
415
633
|
@classmethod
|
|
416
|
-
def create(cls,x,u=None,dof=None,correlation_matrix=None,
|
|
634
|
+
def create(cls,x,u=None,dof=None,utype=None,correlation_matrix=None,
|
|
417
635
|
covariance_matrix=None):
|
|
418
636
|
"""
|
|
419
637
|
A class method that creates a list of (possibly) correlated ummys.
|
|
@@ -447,84 +665,162 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
447
665
|
a list of ummys
|
|
448
666
|
"""
|
|
449
667
|
|
|
450
|
-
|
|
668
|
+
n = len(x)
|
|
451
669
|
if covariance_matrix is not None:
|
|
452
670
|
if correlation_matrix is not None:
|
|
453
671
|
raise TypeError('correlation_matrix and covariance_matrix cannot both be specified')
|
|
672
|
+
try:
|
|
673
|
+
u = [sqrt(covariance_matrix[i][i]) for i in range(n)]
|
|
674
|
+
except ValueError:
|
|
675
|
+
raise ValueError('the diagonal elements of the covariance matrix must be positive real numbers')
|
|
676
|
+
except IndexError:
|
|
677
|
+
raise ValueError('covariance must have shape len(gummys) x len(gummys)')
|
|
678
|
+
correlation_matrix = [[covariance_matrix[i][j]/(u[i]*u[j]) for i in range(n)] for j in range(n)]
|
|
679
|
+
else:
|
|
680
|
+
if u is None:
|
|
681
|
+
u = [0]*n
|
|
682
|
+
elif _isscalar(u):
|
|
683
|
+
u = [u]*n
|
|
684
|
+
|
|
685
|
+
if correlation_matrix is not None:
|
|
686
|
+
if not _isscalar(dof):
|
|
687
|
+
if any(i != dof[0] for i in dof):
|
|
688
|
+
raise TypeError('dof cannot be set individually of a correlation of covariance matrix is specified')
|
|
689
|
+
dof = dof[0]
|
|
690
|
+
if not _isscalar(utype):
|
|
691
|
+
if any(i != utype[0] for i in utype):
|
|
692
|
+
raise TypeError('utype cannot be set individually of a correlation of covariance matrix is specified')
|
|
693
|
+
utype = utype[0]
|
|
454
694
|
|
|
455
|
-
|
|
456
|
-
if u is None:
|
|
457
|
-
u = [0]*n
|
|
458
|
-
elif _isscalar(u):
|
|
459
|
-
u = [u]*n
|
|
460
|
-
if dof is None:
|
|
461
|
-
dof = [float('inf')]*n
|
|
462
|
-
elif _isscalar(dof):
|
|
463
|
-
dof = [dof]*n
|
|
695
|
+
m = np.asarray(correlation_matrix)
|
|
464
696
|
|
|
465
|
-
|
|
697
|
+
if not np.allclose(m.T,m,atol = sqrt(ummy.correlation_tolerance)):
|
|
698
|
+
raise ValueError('the correlation matrix is not symmetric')
|
|
699
|
+
|
|
700
|
+
if m.shape != (n,n):
|
|
701
|
+
raise ValueError('the correlation matrix must have shape len(gummys) x len(gummys)')
|
|
466
702
|
|
|
467
|
-
|
|
468
|
-
|
|
703
|
+
for i in range(n):
|
|
704
|
+
if abs(m[i][i] - 1.0) > sqrt(ummy.correlation_tolerance):
|
|
705
|
+
raise ValueError('correlation matrix diagonal elements must be 1')
|
|
706
|
+
|
|
707
|
+
# The correlated values can be constructed from a linear combination
|
|
708
|
+
# of indepedent variables with coefficients equal to the matrix
|
|
709
|
+
# square root of the correlation matrix.
|
|
710
|
+
val,vec = np.linalg.eig(m)
|
|
711
|
+
|
|
712
|
+
# Round eigenvalues very close to zero to zero, allowing for small
|
|
713
|
+
# negative values that may appear due to the finite numerical
|
|
714
|
+
# precision.
|
|
715
|
+
for i in range(n):
|
|
716
|
+
if val[i] < -ummy.correlation_tolerance:
|
|
717
|
+
raise ValueError('the correlation matrix is not positive semi-definate')
|
|
718
|
+
if val[i] < ummy.correlation_tolerance:
|
|
719
|
+
val[i] = 0
|
|
720
|
+
|
|
721
|
+
# matrix sqaure root of m
|
|
722
|
+
sqrtm = vec*np.sqrt(val)@np.linalg.inv(vec)
|
|
723
|
+
|
|
724
|
+
ind = [_UmmyRef(dof,utype) for _ in range(n)]
|
|
725
|
+
idof = [_udict(((ind[j],float(sqrtm[i][j])) for j in range(n)))
|
|
726
|
+
for i in range(n)]
|
|
727
|
+
|
|
728
|
+
ret = [cls(x[i],u=u[i],dof=idof[i]) for i in range(n)]
|
|
729
|
+
|
|
730
|
+
else:
|
|
731
|
+
if dof is None:
|
|
732
|
+
dof = [float('inf')]*n
|
|
733
|
+
elif _isscalar(dof):
|
|
734
|
+
dof = [dof]*n
|
|
735
|
+
|
|
736
|
+
if utype is None or isinstance(utype,str):
|
|
737
|
+
utype = [utype]*n
|
|
469
738
|
|
|
470
|
-
|
|
471
|
-
cls._set_covariance_matrix(ret, covariance_matrix)
|
|
739
|
+
ret = [cls(x[i],u=u[i],dof=dof[i],utype=utype[i]) for i in range(n)]
|
|
472
740
|
|
|
473
741
|
return ret
|
|
474
742
|
|
|
475
743
|
def tostring(self,fmt='unicode',nsig=None,**kwds):
|
|
476
744
|
if nsig is None:
|
|
477
745
|
nsig = self.nsig
|
|
746
|
+
if nsig > self.max_digits - 2:
|
|
747
|
+
nsig = self.max_digits - 2
|
|
748
|
+
if nsig < 1:
|
|
749
|
+
nsig = 1
|
|
478
750
|
|
|
479
|
-
x = float(self._x)
|
|
480
751
|
try:
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
752
|
+
uexp = 0
|
|
753
|
+
xexp = 0
|
|
754
|
+
dp = 0
|
|
755
|
+
xd = _to_decimal(self._x,max_digits=self.max_digits)
|
|
756
|
+
ud = _to_decimal(self.u)
|
|
757
|
+
with localcontext(prec=self.max_digits):
|
|
758
|
+
if ud == 0 or not ud.is_finite():
|
|
759
|
+
if xd.is_finite():
|
|
760
|
+
nm = False
|
|
761
|
+
nd = self.max_digits
|
|
762
|
+
if self.finfo is not None and self.finfo.precision > 0 and self.finfo.precision <= nd:
|
|
763
|
+
nd = self.finfo.precision
|
|
764
|
+
nm = True
|
|
765
|
+
xd = round(xd,nd-xd.adjusted()-1)
|
|
766
|
+
if xd == 0:
|
|
767
|
+
xexp = 0
|
|
768
|
+
else:
|
|
769
|
+
xexp = xd.adjusted()
|
|
770
|
+
|
|
771
|
+
if self._x == xd or nm:
|
|
772
|
+
xd = xd.normalize()
|
|
493
773
|
else:
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
774
|
+
ud = round(ud,nsig-ud.adjusted()-1)
|
|
775
|
+
ud = round(ud,nsig-ud.adjusted()-1) # round again in case 9 round up to 10 in the last line
|
|
776
|
+
uexp = ud.adjusted()
|
|
777
|
+
|
|
778
|
+
if xd.is_finite():
|
|
779
|
+
try:
|
|
780
|
+
xd = xd.quantize(ud)
|
|
781
|
+
except InvalidOperation:
|
|
782
|
+
xd = round(xd,self.max_digits-xd.adjusted()-1)
|
|
783
|
+
xexp = xd.adjusted()
|
|
784
|
+
|
|
785
|
+
if abs(xd) < abs(ud):
|
|
786
|
+
dp = xd.adjusted() - uexp
|
|
787
|
+
xexp = uexp
|
|
788
|
+
else:
|
|
789
|
+
xexp = xd.adjusted()
|
|
500
790
|
else:
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if xexp is None:
|
|
509
|
-
xp = uexp
|
|
791
|
+
xexp = uexp
|
|
792
|
+
|
|
793
|
+
scin = False
|
|
794
|
+
if self.sci_notation is None:
|
|
795
|
+
if xexp > self.sci_notation_high or xexp < self.sci_notation_low:
|
|
796
|
+
scin = True
|
|
510
797
|
else:
|
|
511
|
-
|
|
798
|
+
scin = self.sci_notation
|
|
799
|
+
if xexp > self.max_digits - 1:
|
|
800
|
+
scin = True
|
|
801
|
+
elif xexp < 0 and len(xd.as_tuple().digits) + self.sci_notation_low > self.max_digits - 1:
|
|
802
|
+
scin = True
|
|
803
|
+
elif ud.is_finite() and ud != 0 and xd.is_finite() and (xd.as_tuple()[2] > 0 or len(xd.as_tuple()[1]) < nsig):
|
|
804
|
+
scin = True
|
|
805
|
+
|
|
806
|
+
if not scin:
|
|
807
|
+
xexp = 0
|
|
512
808
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
if (((self.sci_notation is None and (xp > self.sci_notation_high or xp < self.sci_notation_low))
|
|
516
|
-
or self.sci_notation) or psn or (xexp is not None and (uexp > xexp and xexp == 0))):
|
|
517
|
-
xtxt = self._format_mantissa(fmt,x*10**(-xp),-uexp+xp+nsig-1)
|
|
518
|
-
etxt = _format_exp(fmt,xp)
|
|
809
|
+
if xexp == 0:
|
|
810
|
+
txt =_decimal_str(xd,dalign=0,fmt=fmt)
|
|
519
811
|
else:
|
|
520
|
-
|
|
521
|
-
etxt = ''
|
|
812
|
+
txt = _decimal_str(xd,dplace=dp,fmt=fmt)
|
|
522
813
|
|
|
523
|
-
if
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
814
|
+
if xd.is_finite():
|
|
815
|
+
if ud != 0:
|
|
816
|
+
txt += '(' + _decimal_str(ud,dplace=None,fmt=fmt) + ')'
|
|
817
|
+
txt += _format_exp(fmt,xexp)
|
|
818
|
+
else:
|
|
819
|
+
if ud != 0:
|
|
820
|
+
txt += '(' + _decimal_str(ud,dplace=None,fmt=fmt)
|
|
821
|
+
txt += _format_exp(fmt,xexp) + ')'
|
|
822
|
+
return txt
|
|
823
|
+
|
|
528
824
|
except:
|
|
529
825
|
try:
|
|
530
826
|
return(str(self.x) + '{' + str(self.u) + '}' + '??')
|
|
@@ -533,114 +829,9 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
533
829
|
return(str(self.x) + '{??}')
|
|
534
830
|
except:
|
|
535
831
|
return('??')
|
|
536
|
-
|
|
537
|
-
def _format_mantissa(self,fmt,x,sig,parenth=False):
|
|
538
|
-
try:
|
|
539
|
-
if isnan(x):
|
|
540
|
-
return 'nan'
|
|
541
|
-
if isinf(x) and x > 0:
|
|
542
|
-
if fmt == 'html':
|
|
543
|
-
return '∞'
|
|
544
|
-
if fmt == 'latex':
|
|
545
|
-
return r'\infty'
|
|
546
|
-
if fmt == 'ascii':
|
|
547
|
-
return 'inf'
|
|
548
|
-
return '\u221E'
|
|
549
|
-
if isinf(x) and x < 0:
|
|
550
|
-
if fmt == 'html':
|
|
551
|
-
return '-∞'
|
|
552
|
-
if fmt == 'latex':
|
|
553
|
-
return r'-\infty'
|
|
554
|
-
if fmt == 'ascii':
|
|
555
|
-
return '-inf'
|
|
556
|
-
return '-\u221E'
|
|
557
|
-
except:
|
|
558
|
-
pass
|
|
559
832
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
if x == int(x):
|
|
563
|
-
s = str(int(x))
|
|
564
|
-
else:
|
|
565
|
-
xf = float(x)
|
|
566
|
-
e = 15 - _floor(log10(abs(xf)))
|
|
567
|
-
if e < 0:
|
|
568
|
-
e = 0
|
|
569
|
-
n = 10**e*x
|
|
570
|
-
if int(n) != n:
|
|
571
|
-
ellipses = '...'
|
|
572
|
-
s = str(round(xf,e))
|
|
573
|
-
elif sig is None:
|
|
574
|
-
s = str(x)
|
|
575
|
-
else:
|
|
576
|
-
if x != 0 and self.max_digits is not None:
|
|
577
|
-
if mpf is not None and isinstance(x,mpf):
|
|
578
|
-
em = _floor(mp.log10(abs(x)))
|
|
579
|
-
else:
|
|
580
|
-
em = _floor(log10(abs(float(x))))
|
|
581
|
-
e = self.max_digits - em
|
|
582
|
-
if e < 0:
|
|
583
|
-
e = 0
|
|
584
|
-
if sig > e and sig > 0:
|
|
585
|
-
ellipses = '...'
|
|
586
|
-
sig = e
|
|
587
|
-
if sig < 0:
|
|
588
|
-
sig = 0
|
|
589
|
-
if mpf is not None and isinstance(x,mpf):
|
|
590
|
-
s = mp.nstr(x,n=sig+1,strip_zeros=False)
|
|
591
|
-
else:
|
|
592
|
-
s = round(float(x),sig)
|
|
593
|
-
s = ('{:.' + str(sig) + 'f}').format(s)
|
|
594
|
-
|
|
595
|
-
if parenth:
|
|
596
|
-
s = s.replace('.','')
|
|
597
|
-
s = s.lstrip('0')
|
|
598
|
-
if len(s) == 0:
|
|
599
|
-
s = '0'
|
|
600
|
-
return s
|
|
601
|
-
elif self.thousand_spaces:
|
|
602
|
-
# Now insert spaces every three digits
|
|
603
|
-
if fmt == 'html':
|
|
604
|
-
sp = ' '
|
|
605
|
-
elif fmt == 'latex':
|
|
606
|
-
sp = r'\,'
|
|
607
|
-
else:
|
|
608
|
-
sp = ' '
|
|
609
|
-
|
|
610
|
-
d = s.find('.')
|
|
611
|
-
if d != -1:
|
|
612
|
-
i = d + 1
|
|
613
|
-
while i < len(s) and s[i].isdigit():
|
|
614
|
-
i += 1
|
|
615
|
-
nr = i - d - 1
|
|
616
|
-
else:
|
|
617
|
-
nr = 0
|
|
618
|
-
d = len(s)
|
|
619
|
-
|
|
620
|
-
i = d - 1
|
|
621
|
-
while i > 0 and s[i].isdigit():
|
|
622
|
-
i -= 1
|
|
623
|
-
nl = d - i - 1
|
|
624
|
-
|
|
625
|
-
if nr <= 4 and nl <= 4:
|
|
626
|
-
return s + ellipses
|
|
627
|
-
|
|
628
|
-
s = list(s)
|
|
629
|
-
if nr > 4:
|
|
630
|
-
for i in range(int(nr/3)):
|
|
631
|
-
s.insert(d + i + (i+1)*3 + 1, sp)
|
|
632
|
-
if nl > 4:
|
|
633
|
-
for i in range(int(nl/3)):
|
|
634
|
-
s.insert(d - (i+1)*3, sp)
|
|
635
|
-
|
|
636
|
-
s = ''.join(s)
|
|
637
|
-
if s.endswith(sp):
|
|
638
|
-
s = s[:-(len(sp))]
|
|
639
|
-
|
|
640
|
-
return s + ellipses
|
|
641
|
-
|
|
642
|
-
@classmethod
|
|
643
|
-
def _get_dof(cls,dof1,dof2,du1,du2,c):
|
|
833
|
+
#@classmethod
|
|
834
|
+
#def _get_dof(cls,dof1,dof2,du1,du2,c):
|
|
644
835
|
# Use the Welch-Satterhwaite approximation to combine dof1 and dof2.
|
|
645
836
|
# See [R. Willink, Metrologia, 44, 340 (2007)] concerning the use
|
|
646
837
|
# of the Welch-Satterwaite approximation with correlated values.
|
|
@@ -653,23 +844,21 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
653
844
|
# d = (du1**2 + c*du1*du2)**2/dof1 + (du2**2 + c*du1*du2)**2/dof2
|
|
654
845
|
# Willink's formula is used in the _apply method but not here.
|
|
655
846
|
|
|
656
|
-
d = du1**4/dof1 + du2**4/dof2
|
|
657
|
-
d += 2*c**2*(((du1 + du2)**4 - du1**4 - du2**4)/
|
|
658
|
-
((1 - 2*c + 2*c**2)*(dof1+dof2)))
|
|
847
|
+
#d = du1**4/dof1 + du2**4/dof2
|
|
848
|
+
#d += 2*c**2*(((du1 + du2)**4 - du1**4 - du2**4)/
|
|
849
|
+
#((1 - 2*c + 2*c**2)*(dof1+dof2)))
|
|
659
850
|
|
|
660
|
-
if d == 0:
|
|
661
|
-
return float('inf')
|
|
851
|
+
#if d == 0:
|
|
852
|
+
#return float('inf')
|
|
662
853
|
|
|
663
|
-
r = 1/d
|
|
664
|
-
if r > cls.max_dof:
|
|
665
|
-
return float('inf')
|
|
854
|
+
#r = 1/d
|
|
855
|
+
#if r > cls.max_dof:
|
|
856
|
+
#return float('inf')
|
|
666
857
|
|
|
667
|
-
#if r <
|
|
668
|
-
#
|
|
669
|
-
if r < 0:
|
|
670
|
-
r = 1
|
|
858
|
+
#if r < 0:
|
|
859
|
+
#r = 1
|
|
671
860
|
|
|
672
|
-
return r
|
|
861
|
+
#return r
|
|
673
862
|
|
|
674
863
|
@classmethod
|
|
675
864
|
def _apply(cls,function,derivative,*args,fxdx=None):
|
|
@@ -682,57 +871,62 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
682
871
|
if len(args) == 1:
|
|
683
872
|
d = [d]
|
|
684
873
|
|
|
685
|
-
|
|
686
|
-
|
|
874
|
+
xtype = _xtype(fx)
|
|
875
|
+
|
|
876
|
+
try:
|
|
877
|
+
ad = [(a,a._u*p) for a,p in zip(args,d)
|
|
878
|
+
if isinstance(a,ummy) and a._u != 0]
|
|
879
|
+
except TypeError:
|
|
880
|
+
ad = [(a,xtype(a._u)*p) for a,p in zip(args,d)
|
|
881
|
+
if isinstance(a,ummy) and a._u != 0]
|
|
687
882
|
if len(ad) == 0:
|
|
688
883
|
return cls(fx)
|
|
689
884
|
args,du = list(map(list, zip(*ad)))
|
|
690
885
|
|
|
691
886
|
if len(args) == 1:
|
|
692
|
-
|
|
693
|
-
r = cls(fx,u=abs(du[0]),dof=args[0]._ref,utype=refs)
|
|
887
|
+
r = cls(fx,u=du[0],dof=args[0]._ref)
|
|
694
888
|
return r
|
|
695
889
|
|
|
696
890
|
maxdu = max(abs(a) for a in du)
|
|
697
891
|
if maxdu == 0:
|
|
698
892
|
return cls(fx)
|
|
699
|
-
|
|
893
|
+
try:
|
|
894
|
+
dun = [d/maxdu for d in du]
|
|
895
|
+
except TypeError:
|
|
896
|
+
dun = [xtype(d)/xtype(maxdu) for d in du]
|
|
700
897
|
|
|
701
898
|
u = sum(d**2 for d in dun)
|
|
702
|
-
|
|
703
|
-
|
|
899
|
+
try:
|
|
900
|
+
u += 2*sum(sum(args[i]._ref[k]*v for k,v in args[j]._ref.items()
|
|
901
|
+
if k in args[i]._ref)*dun[i]*dun[j]
|
|
902
|
+
for i in range(len(args)) for j in range(i+1,len(args)))
|
|
903
|
+
except TypeError:
|
|
904
|
+
u += 2*sum(xtype(sum(args[i]._ref[k]*v for k,v in args[j]._ref.items()
|
|
905
|
+
if k in args[i]._ref))*dun[i]*dun[j]
|
|
906
|
+
for i in range(len(args)) for j in range(i+1,len(args)))
|
|
704
907
|
|
|
705
908
|
if not isnan(u):
|
|
706
909
|
if u < 0:
|
|
707
910
|
if u < -1e6:
|
|
708
|
-
# something horrible
|
|
911
|
+
# something horrible and unexpected happened
|
|
709
912
|
raise FloatingPointError('u is sqrt of ' + str(maxdu**2*u))
|
|
710
913
|
u = 0
|
|
711
914
|
|
|
712
|
-
|
|
915
|
+
if hasattr(u,'sqrt'):
|
|
916
|
+
u = u.sqrt()
|
|
917
|
+
else:
|
|
918
|
+
u = u**0.5
|
|
713
919
|
u = maxdu*u
|
|
714
920
|
|
|
715
921
|
if u == 0 or isnan(u):
|
|
716
922
|
return cls(fx)
|
|
717
923
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
if dm > 0:
|
|
725
|
-
dof = 1/dm
|
|
726
|
-
|
|
727
|
-
r = cls(fx,u=u,dof=dof)
|
|
728
|
-
|
|
729
|
-
rl = {a._ref for a in args}
|
|
730
|
-
for i,a in enumerate(args):
|
|
731
|
-
s = sum(a.correlation(args[j])*du[j] for j in range(len(du)))
|
|
732
|
-
a._ref.combl(r,float(a._refs*s),float(a._refs*du[i]),rl)
|
|
733
|
-
if r._ref in rl:
|
|
734
|
-
break
|
|
735
|
-
r._check_cor()
|
|
924
|
+
try:
|
|
925
|
+
du = [float(d/u) for d in du]
|
|
926
|
+
except TypeError:
|
|
927
|
+
du = [float(xtype(d)/xtype(u)) for d in du]
|
|
928
|
+
|
|
929
|
+
r = cls(fx,u=u,dof=_combrd(args,du))
|
|
736
930
|
|
|
737
931
|
return r
|
|
738
932
|
|
|
@@ -752,35 +946,80 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
752
946
|
|
|
753
947
|
return cls._apply(function,lambda *x: d,*args,fxdx=fxx)
|
|
754
948
|
|
|
755
|
-
def
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
if
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
949
|
+
def _aop(self,b,f,d1,d2):
|
|
950
|
+
# computes the result of a binary operation f(self,b). d1 and d2 are
|
|
951
|
+
# the derivatives with respect to self and b respectively.
|
|
952
|
+
|
|
953
|
+
# correlation between self and b (/sign(self._u)*sign(b._u))
|
|
954
|
+
if not isinstance(b,ummy) or b._ref is None or self._ref is None:
|
|
955
|
+
c = 0
|
|
956
|
+
else:
|
|
957
|
+
c = sum(b._ref[k]*v for k,v in self._ref.items() if k in b._ref)
|
|
958
|
+
|
|
959
|
+
x = f(self._x,b._x)
|
|
960
|
+
try:
|
|
961
|
+
dua = d1(self._x,b._x)*self._u
|
|
962
|
+
dub = d2(self._x,b._x)*b._u
|
|
963
|
+
u = _combu(dua,dub,c) # combined standard uncertainty
|
|
964
|
+
except TypeError:
|
|
965
|
+
# e.g. if self.x and b.x are Decimal we may need to convert the
|
|
966
|
+
# correlation c which is float to Decimal
|
|
967
|
+
xtype = _xtype(x)
|
|
968
|
+
dua = d1(self._x,b._x)*xtype(self._u)
|
|
969
|
+
dub = d2(self._x,b._x)*xtype(b._u)
|
|
970
|
+
u = _combu(dua,dub,xtype(c))
|
|
971
|
+
|
|
972
|
+
# some special cases where we don't need to bother to work out all the
|
|
973
|
+
# correlations with the independent variables
|
|
974
|
+
if u == 0:
|
|
975
|
+
return type(b)(x)
|
|
976
|
+
|
|
977
|
+
if self._u == 0:
|
|
978
|
+
return type(b)(x,u=u,dof=b._ref)
|
|
979
|
+
|
|
980
|
+
if b._u == 0:
|
|
981
|
+
return type(b)(x,u=u,dof=self._ref)
|
|
982
|
+
|
|
983
|
+
if self._ref is b._ref:
|
|
984
|
+
s = sign(dua + dub)
|
|
985
|
+
return type(b)(x,u=s*u,dof=self._ref)
|
|
986
|
+
|
|
987
|
+
# not a special case, so combine the correlation dictionaries of self
|
|
988
|
+
# and b
|
|
989
|
+
|
|
990
|
+
dua = float(dua/u)
|
|
991
|
+
dub = float(dub/u)
|
|
992
|
+
|
|
993
|
+
ref = _udict(((k,dua*v) for k,v in self._ref.items()))
|
|
994
|
+
for k,v in b._ref.items():
|
|
995
|
+
if k in ref:
|
|
996
|
+
ref[k] += v*dub
|
|
997
|
+
else:
|
|
998
|
+
ref[k] = v*dub
|
|
999
|
+
|
|
1000
|
+
# check that sum squared correlations add to 1 in case rounding errors
|
|
1001
|
+
# have accumulated
|
|
1002
|
+
n = sum([i**2 for i in ref.values()])
|
|
1003
|
+
if abs(n-1) > ummy.correlation_tolerance:
|
|
1004
|
+
n = sqrt(n)
|
|
1005
|
+
for k,v in ref.items():
|
|
1006
|
+
ref[k] /= n
|
|
1007
|
+
|
|
1008
|
+
# round correlations to 1, -1 or 0 if within correlation_tolerance of
|
|
1009
|
+
# these values
|
|
1010
|
+
for k,v in list(ref.items()):
|
|
1011
|
+
c = ref[k]
|
|
1012
|
+
if c < -1 + ummy.correlation_tolerance:
|
|
1013
|
+
ref = _udict(((k,1),))
|
|
1014
|
+
u = -u
|
|
1015
|
+
break
|
|
1016
|
+
elif c > 1 - ummy.correlation_tolerance:
|
|
1017
|
+
ref = _udict(((k,1),))
|
|
1018
|
+
break
|
|
1019
|
+
elif abs(c) < ummy.correlation_tolerance:
|
|
1020
|
+
del ref[k]
|
|
1021
|
+
|
|
1022
|
+
return type(b)(x,u=u,dof=ref)
|
|
784
1023
|
|
|
785
1024
|
def __add__(self,b):
|
|
786
1025
|
if isinstance(b,np.ndarray):
|
|
@@ -792,33 +1031,12 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
792
1031
|
return immy(self) + b
|
|
793
1032
|
|
|
794
1033
|
if not isinstance(b,ummy):
|
|
795
|
-
return type(self)(self._x + b,self._u,dof=self._ref
|
|
796
|
-
|
|
797
|
-
c = self.correlation(b)
|
|
798
|
-
x = self._x + b._x
|
|
799
|
-
u = _combu(self._u,b._u,c)
|
|
1034
|
+
return type(self)(self._x + b,u=self._u,dof=self._ref)
|
|
800
1035
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
return type(b)(x,u,dof=b._ref,utype=b._refs)
|
|
806
|
-
|
|
807
|
-
if b._u == 0:
|
|
808
|
-
return type(b)(x,u,dof=self._ref,utype=self._refs)
|
|
809
|
-
|
|
810
|
-
dua = self._u/u
|
|
811
|
-
dub = b._u/u
|
|
812
|
-
if not isinf(self._ref.dof) or not isinf(b._ref.dof):
|
|
813
|
-
dof = self._get_dof(self._dof,b._dof,dua,dub,c)
|
|
814
|
-
else:
|
|
815
|
-
dof = float('inf')
|
|
816
|
-
|
|
817
|
-
r = type(b)(x,u=u,dof=dof)
|
|
818
|
-
|
|
819
|
-
r._combc(self,b,dua,dub,c)
|
|
820
|
-
|
|
821
|
-
return r
|
|
1036
|
+
return self._aop(b,
|
|
1037
|
+
lambda x1,x2:x1 + x2,
|
|
1038
|
+
lambda x1,x2:1,
|
|
1039
|
+
lambda x1,x2:1)
|
|
822
1040
|
|
|
823
1041
|
def __radd__(self,b):
|
|
824
1042
|
if isinstance(b,np.ndarray):
|
|
@@ -830,7 +1048,7 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
830
1048
|
if isinstance(b,ummy):
|
|
831
1049
|
return b.__add__(self)
|
|
832
1050
|
|
|
833
|
-
return type(self)(self._x + b,self._u,dof=self._ref
|
|
1051
|
+
return type(self)(self._x + b,u=self._u,dof=self._ref)
|
|
834
1052
|
|
|
835
1053
|
def __sub__(self,b):
|
|
836
1054
|
if isinstance(b,np.ndarray):
|
|
@@ -843,33 +1061,12 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
843
1061
|
return immy(self) - b
|
|
844
1062
|
|
|
845
1063
|
if not isinstance(b,ummy):
|
|
846
|
-
return type(self)(self._x - b,self._u,dof=self._ref
|
|
847
|
-
|
|
848
|
-
c = self.correlation(b)
|
|
849
|
-
x = self._x - b._x
|
|
850
|
-
u = _combu(self._u,-b._u,c)
|
|
1064
|
+
return type(self)(self._x - b,u=self._u,dof=self._ref)
|
|
851
1065
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
return type(b)(x,u,dof=b._ref,utype=-b._refs)
|
|
857
|
-
|
|
858
|
-
if b._u == 0:
|
|
859
|
-
return type(b)(x,u,dof=self._ref,utype=self._refs)
|
|
860
|
-
|
|
861
|
-
dua = self._u/u
|
|
862
|
-
dub = -b._u/u
|
|
863
|
-
if not isinf(self._ref.dof) or not isinf(b._ref.dof):
|
|
864
|
-
dof = self._get_dof(self._dof,b._dof,dua,dub,c)
|
|
865
|
-
else:
|
|
866
|
-
dof = float('inf')
|
|
867
|
-
|
|
868
|
-
r = type(b)(x,u=u,dof=dof)
|
|
869
|
-
|
|
870
|
-
r._combc(self,b,dua,dub,c)
|
|
871
|
-
|
|
872
|
-
return r
|
|
1066
|
+
return self._aop(b,
|
|
1067
|
+
lambda x1,x2:x1 - x2,
|
|
1068
|
+
lambda x1,x2:1,
|
|
1069
|
+
lambda x1,x2:-1)
|
|
873
1070
|
|
|
874
1071
|
def __rsub__(self,b):
|
|
875
1072
|
if isinstance(b,np.ndarray):
|
|
@@ -881,7 +1078,7 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
881
1078
|
if isinstance(b,ummy):
|
|
882
1079
|
return b.__sub__(self)
|
|
883
1080
|
|
|
884
|
-
r = type(self)(b - self._x,self._u,dof=self._ref
|
|
1081
|
+
r = type(self)(b - self._x,u=-self._u,dof=self._ref)
|
|
885
1082
|
return r
|
|
886
1083
|
|
|
887
1084
|
def __mul__(self,b):
|
|
@@ -895,38 +1092,20 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
895
1092
|
return immy(self)*b
|
|
896
1093
|
|
|
897
1094
|
if not isinstance(b,ummy):
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
return type(b)(x,u,dof=b._ref,utype=refs)
|
|
913
|
-
|
|
914
|
-
if b._u == 0:
|
|
915
|
-
refs = -self._refs if b._x < 0 else self._refs
|
|
916
|
-
return type(b)(x,u,dof=self._ref,utype=refs)
|
|
917
|
-
|
|
918
|
-
dua = dua/u
|
|
919
|
-
dub = dub/u
|
|
920
|
-
if not isinf(self._ref.dof) or not isinf(b._ref.dof):
|
|
921
|
-
dof = self._get_dof(self._dof,b._dof,dua,dub,c)
|
|
922
|
-
else:
|
|
923
|
-
dof = float('inf')
|
|
924
|
-
|
|
925
|
-
r = type(b)(x,u=u,dof=dof)
|
|
926
|
-
|
|
927
|
-
r._combc(self,b,dua,dub,c)
|
|
928
|
-
|
|
929
|
-
return r
|
|
1095
|
+
x = self._x*b
|
|
1096
|
+
try:
|
|
1097
|
+
ub = self._u*b
|
|
1098
|
+
except TypeError:
|
|
1099
|
+
if isinstance(x,Integral):
|
|
1100
|
+
ub = MFraction.fromnum(self._u)*b
|
|
1101
|
+
else:
|
|
1102
|
+
ub = type(x)(self._u)*b
|
|
1103
|
+
return type(self)(x,u=ub,dof=self._ref)
|
|
1104
|
+
|
|
1105
|
+
return self._aop(b,
|
|
1106
|
+
lambda x1,x2:x1*x2,
|
|
1107
|
+
lambda x1,x2:x2,
|
|
1108
|
+
lambda x1,x2:x1)
|
|
930
1109
|
|
|
931
1110
|
def __rmul__(self,b):
|
|
932
1111
|
if isinstance(b,np.ndarray):
|
|
@@ -938,8 +1117,12 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
938
1117
|
if isinstance(b,ummy):
|
|
939
1118
|
return b.__mul__(self)
|
|
940
1119
|
|
|
941
|
-
|
|
942
|
-
|
|
1120
|
+
x = self._x*b
|
|
1121
|
+
try:
|
|
1122
|
+
ub = self._u*b
|
|
1123
|
+
except TypeError:
|
|
1124
|
+
ub = _xtype(x)(self._u)*b
|
|
1125
|
+
return type(self)(x,u=ub,dof=self._ref)
|
|
943
1126
|
|
|
944
1127
|
def __truediv__(self,b):
|
|
945
1128
|
if isinstance(b,np.ndarray):
|
|
@@ -958,10 +1141,12 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
958
1141
|
x = MFraction(self._x,b)
|
|
959
1142
|
else:
|
|
960
1143
|
x = self._x/b
|
|
961
|
-
|
|
962
|
-
|
|
1144
|
+
try:
|
|
1145
|
+
ub = self._u/b
|
|
1146
|
+
except TypeError:
|
|
1147
|
+
ub = _xtype(x)(self._u)/b
|
|
1148
|
+
return type(self)(x,ub,dof=self._ref)
|
|
963
1149
|
|
|
964
|
-
c = self.correlation(b)
|
|
965
1150
|
if b._x == 0:
|
|
966
1151
|
raise ZeroDivisionError('division by zero')
|
|
967
1152
|
else:
|
|
@@ -971,34 +1156,10 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
971
1156
|
else:
|
|
972
1157
|
x = self._x/b._x
|
|
973
1158
|
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
if u == 0:
|
|
980
|
-
return type(b)(x)
|
|
981
|
-
|
|
982
|
-
if self._u == 0:
|
|
983
|
-
refs = b._refs if self._x < 0 else -b._refs
|
|
984
|
-
return type(b)(x,u,dof=b._ref,utype=refs)
|
|
985
|
-
|
|
986
|
-
if b._u == 0:
|
|
987
|
-
refs = -self._refs if b._x < 0 else self._refs
|
|
988
|
-
return type(b)(x,u,dof=self._ref,utype=refs)
|
|
989
|
-
|
|
990
|
-
dua = dua/u
|
|
991
|
-
dub = dub/u
|
|
992
|
-
if not isinf(self._ref.dof) or not isinf(b._ref.dof):
|
|
993
|
-
dof = self._get_dof(self._dof,b._dof,dua,dub,c)
|
|
994
|
-
else:
|
|
995
|
-
dof = float('inf')
|
|
996
|
-
|
|
997
|
-
r = type(b)(x,u=u,dof=dof)
|
|
998
|
-
|
|
999
|
-
r._combc(self,b,dua,dub,c)
|
|
1000
|
-
|
|
1001
|
-
return r
|
|
1159
|
+
return self._aop(b,
|
|
1160
|
+
lambda x1,x2:x1/x2,
|
|
1161
|
+
lambda x1,x2:1/x2,
|
|
1162
|
+
lambda x1,x2:-x1/x2**2)
|
|
1002
1163
|
|
|
1003
1164
|
def __rtruediv__(self,b):
|
|
1004
1165
|
if isinstance(b,np.ndarray):
|
|
@@ -1018,9 +1179,11 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1018
1179
|
x = MFraction(b,self._x)
|
|
1019
1180
|
else:
|
|
1020
1181
|
x = b/self._x
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1182
|
+
try:
|
|
1183
|
+
ub = -b*self._u/self._x**2
|
|
1184
|
+
except TypeError:
|
|
1185
|
+
ub = -b*_xtype(x)(self._u)/self._x**2
|
|
1186
|
+
return type(self)(x,ub,dof=self._ref)
|
|
1024
1187
|
|
|
1025
1188
|
def __pow__(self,b):
|
|
1026
1189
|
if isinstance(b,np.ndarray):
|
|
@@ -1042,57 +1205,30 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1042
1205
|
x = MFraction(1,self._x**-b)
|
|
1043
1206
|
else:
|
|
1044
1207
|
x = self._x**b
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
if b < 0:
|
|
1051
|
-
sgn = -sgn
|
|
1052
|
-
return type(self)(x,u,dof=self._ref,utype=sgn*self._refs)
|
|
1208
|
+
try:
|
|
1209
|
+
ub = b*self._x**(b-1)*self._u
|
|
1210
|
+
except TypeError:
|
|
1211
|
+
ub = b*self._x**(b-1)*_xtype(x)(self._u)
|
|
1212
|
+
return type(self)(x,ub,dof=self._ref)
|
|
1053
1213
|
|
|
1054
1214
|
if self._x <= 0:
|
|
1055
1215
|
raise ValueError('a negative or zero value cannot raised to a power which has an uncertainty')
|
|
1056
1216
|
|
|
1057
|
-
c = self.correlation(b)
|
|
1058
1217
|
if (isinstance(self._x,Integral) and
|
|
1059
1218
|
isinstance(b._x,Integral) and b._x < 0):
|
|
1060
|
-
|
|
1219
|
+
f = lambda x1,x2:MFraction(1,x1**-x2)
|
|
1061
1220
|
else:
|
|
1062
|
-
|
|
1063
|
-
dua = b._x*self._x**(b._x-1)*self._u
|
|
1064
|
-
lgx = log(self._x)
|
|
1065
|
-
|
|
1066
|
-
# don't exactly remember why I did this,
|
|
1067
|
-
# but it causes problems if x is int.
|
|
1068
|
-
#try:
|
|
1069
|
-
#lgx = type(self._x)(lgx)
|
|
1070
|
-
#except:
|
|
1071
|
-
#pass
|
|
1072
|
-
|
|
1073
|
-
dub = lgx*self._x**b._x*b._u
|
|
1074
|
-
u = _combu(dua,dub,c)
|
|
1075
|
-
|
|
1076
|
-
if u == 0:
|
|
1077
|
-
return type(b)(x)
|
|
1078
|
-
|
|
1079
|
-
if self._u == 0:
|
|
1080
|
-
refs = -b._refs if self._x < 0 else b._refs
|
|
1081
|
-
return type(b)(x,u,dof=b._ref,utype=refs)
|
|
1221
|
+
f = lambda x1,x2:x1**x2
|
|
1082
1222
|
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
if not isinf(self._ref.dof) or not isinf(b._ref.dof):
|
|
1087
|
-
dof = self._get_dof(self._dof,b._dof,dua,dub,c)
|
|
1223
|
+
if hasattr(self._x,'ln'):
|
|
1224
|
+
lgx = self._x.ln()
|
|
1088
1225
|
else:
|
|
1089
|
-
|
|
1226
|
+
lgx = log(self._x)
|
|
1090
1227
|
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
return r
|
|
1228
|
+
return self._aop(b,
|
|
1229
|
+
f,
|
|
1230
|
+
lambda x1,x2:x2*x1**(x2 - 1),
|
|
1231
|
+
lambda x1,x2:lgx*x1**x2)
|
|
1096
1232
|
|
|
1097
1233
|
def __rpow__(self,b):
|
|
1098
1234
|
if isinstance(b,np.ndarray):
|
|
@@ -1118,18 +1254,16 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1118
1254
|
if b < 0:
|
|
1119
1255
|
raise ValueError('a negative number cannot raised to a power which has an uncertainty')
|
|
1120
1256
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
#try:
|
|
1126
|
-
#lgb = type(b)(lgb)
|
|
1127
|
-
#except:
|
|
1128
|
-
#pass
|
|
1257
|
+
if hasattr(b,'ln'):
|
|
1258
|
+
lgb = b.ln()
|
|
1259
|
+
else:
|
|
1260
|
+
lgb = log(b)
|
|
1129
1261
|
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1262
|
+
try:
|
|
1263
|
+
ub = b**self._x*lgb*self._u
|
|
1264
|
+
except TypeError:
|
|
1265
|
+
ub = b**self._x*lgb*_xtype(x)(self._u)
|
|
1266
|
+
return type(self)(x,ub,dof=self._ref)
|
|
1133
1267
|
|
|
1134
1268
|
def _nprnd(self,f):
|
|
1135
1269
|
return type(self)(f(self._x))
|
|
@@ -1144,7 +1278,7 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1144
1278
|
if isinstance(b,Complex) and not isinstance(b,Real):
|
|
1145
1279
|
return immy(self) // b
|
|
1146
1280
|
|
|
1147
|
-
return self.
|
|
1281
|
+
return self.__truediv__(b)._nprnd(floor)
|
|
1148
1282
|
|
|
1149
1283
|
def __rfloordiv__(self,b):
|
|
1150
1284
|
if isinstance(b,np.ndarray):
|
|
@@ -1156,7 +1290,7 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1156
1290
|
if isinstance(b,ummy):
|
|
1157
1291
|
return b.__floordiv__(self)
|
|
1158
1292
|
|
|
1159
|
-
return self.
|
|
1293
|
+
return self.__rtruediv__(b)._nprnd(floor)
|
|
1160
1294
|
|
|
1161
1295
|
def __mod__(self,b):
|
|
1162
1296
|
if isinstance(b,np.ndarray):
|
|
@@ -1169,7 +1303,7 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1169
1303
|
return immy(self) % b
|
|
1170
1304
|
|
|
1171
1305
|
ret = ummy._apply(lambda x1,x2: x1%x2,
|
|
1172
|
-
lambda x1,x2: (1,
|
|
1306
|
+
lambda x1,x2: (1,copysign(abs(x1//x2),x2)),self,b)
|
|
1173
1307
|
return type(self)(ret)
|
|
1174
1308
|
|
|
1175
1309
|
def __rmod__(self,b):
|
|
@@ -1180,14 +1314,20 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1180
1314
|
return b.__mod__(self)
|
|
1181
1315
|
|
|
1182
1316
|
ret = ummy._apply(lambda x1,x2: x1%x2,
|
|
1183
|
-
lambda x1,x2: (1,
|
|
1317
|
+
lambda x1,x2: (1,copysign(abs(x1//x2),x2)),b,self)
|
|
1184
1318
|
return type(self)(ret)
|
|
1319
|
+
|
|
1320
|
+
def __divmod__(self,b):
|
|
1321
|
+
return (self//b,self%b)
|
|
1322
|
+
|
|
1323
|
+
def __rdivmod__(self,b):
|
|
1324
|
+
return (b//self,b%self)
|
|
1185
1325
|
|
|
1186
1326
|
def __neg__(self):
|
|
1187
1327
|
r = self.copy(formatting=False)
|
|
1188
1328
|
r._x = -self._x
|
|
1189
1329
|
if self._ref is not None:
|
|
1190
|
-
r.
|
|
1330
|
+
r._u = -self._u
|
|
1191
1331
|
return r
|
|
1192
1332
|
|
|
1193
1333
|
def __pos__(self):
|
|
@@ -1198,7 +1338,7 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1198
1338
|
if self._x < 0:
|
|
1199
1339
|
r._x = -self._x
|
|
1200
1340
|
if self._ref is not None:
|
|
1201
|
-
r.
|
|
1341
|
+
r._u = -self._u
|
|
1202
1342
|
return r
|
|
1203
1343
|
|
|
1204
1344
|
def __eq__(self,v):
|
|
@@ -1208,7 +1348,7 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1208
1348
|
if isinstance(v,ummy):
|
|
1209
1349
|
if self.u == 0 and v.u == 0:
|
|
1210
1350
|
return self._x == v._x
|
|
1211
|
-
if self.
|
|
1351
|
+
if self.correlation(v) == 1:
|
|
1212
1352
|
return self.x == v.x and self.u == v.u
|
|
1213
1353
|
return False
|
|
1214
1354
|
|
|
@@ -1216,16 +1356,77 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1216
1356
|
return self.x == v
|
|
1217
1357
|
|
|
1218
1358
|
return False
|
|
1359
|
+
|
|
1360
|
+
def __ne__(self, v):
|
|
1361
|
+
return not self == v
|
|
1219
1362
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1363
|
+
def __lt__(self, v):
|
|
1364
|
+
if self.u != 0:
|
|
1365
|
+
raise TypeError('an ummy with non-zero uncertainty cannot be ordered')
|
|
1366
|
+
return self.x < v
|
|
1367
|
+
|
|
1368
|
+
def __le__(self, v):
|
|
1369
|
+
if self.u != 0:
|
|
1370
|
+
raise TypeError('an ummy with non-zero uncertainty cannot be ordered')
|
|
1371
|
+
return self.x <= v
|
|
1222
1372
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1373
|
+
def __gt__(self, v):
|
|
1374
|
+
if self.u != 0:
|
|
1375
|
+
raise TypeError('an ummy with non-zero uncertainty cannot be ordered')
|
|
1376
|
+
return self.x > v
|
|
1225
1377
|
|
|
1226
|
-
|
|
1227
|
-
|
|
1378
|
+
def __ge__(self, v):
|
|
1379
|
+
if self.u != 0:
|
|
1380
|
+
raise TypeError('an ummy with non-zero uncertainty cannot be ordered')
|
|
1381
|
+
return self.x >= v
|
|
1382
|
+
|
|
1383
|
+
def __float__(self):
|
|
1384
|
+
if self.u != 0:
|
|
1385
|
+
raise TypeError('an ummy with non-zero uncertainty cannot be converted to float')
|
|
1386
|
+
return float(self.x)
|
|
1387
|
+
|
|
1388
|
+
def __int__(self):
|
|
1389
|
+
if self.u != 0:
|
|
1390
|
+
raise TypeError('an ummy with non-zero uncertainty cannot be converted to int')
|
|
1391
|
+
return int(self.x)
|
|
1392
|
+
|
|
1393
|
+
def __complex__(self):
|
|
1394
|
+
if self.u != 0:
|
|
1395
|
+
raise TypeError('an ummy with non-zero uncertainty cannot be converted to complex')
|
|
1396
|
+
return complex(self.x)
|
|
1228
1397
|
|
|
1398
|
+
def __bool__(self):
|
|
1399
|
+
return self != 0
|
|
1400
|
+
|
|
1401
|
+
#def __trunc__(self):
|
|
1402
|
+
#try:
|
|
1403
|
+
#return self.x.__trunc__()
|
|
1404
|
+
#except:
|
|
1405
|
+
#return float(self.x).__trunc__()
|
|
1406
|
+
|
|
1407
|
+
#def __floor__(self):
|
|
1408
|
+
#try:
|
|
1409
|
+
#return self.x.__floor__()
|
|
1410
|
+
#except:
|
|
1411
|
+
#return float(self.x).__floor__()
|
|
1412
|
+
|
|
1413
|
+
#def __ceil__(self):
|
|
1414
|
+
#try:
|
|
1415
|
+
#return self.x.__ceil__()
|
|
1416
|
+
#except:
|
|
1417
|
+
#return float(self.x).__ceil__()
|
|
1418
|
+
|
|
1419
|
+
#def __round__(self,ndigits=None):
|
|
1420
|
+
#try:
|
|
1421
|
+
#ret = round(self.x,ndigits)
|
|
1422
|
+
#except:
|
|
1423
|
+
#ret = round(float(self.x),ndigits)
|
|
1424
|
+
|
|
1425
|
+
#if ndigits is None:
|
|
1426
|
+
#return ret
|
|
1427
|
+
#else:
|
|
1428
|
+
#return type(self)(ret)
|
|
1429
|
+
|
|
1229
1430
|
@property
|
|
1230
1431
|
def real(self):
|
|
1231
1432
|
return self
|
|
@@ -1243,46 +1444,48 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1243
1444
|
else:
|
|
1244
1445
|
return type(self)(pi)
|
|
1245
1446
|
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
if self._ref is None
|
|
1254
|
-
return
|
|
1255
|
-
return self._ref._tag.name
|
|
1447
|
+
def __hash__(self):
|
|
1448
|
+
if self._ref is None:
|
|
1449
|
+
return hash(self.x)
|
|
1450
|
+
s = sign(self._u)
|
|
1451
|
+
return hash((tuple((k,s*v) for k,v in self._ref.items()),self.x,self.u))
|
|
1452
|
+
|
|
1453
|
+
def _ufromrefs(self,x):
|
|
1454
|
+
if self._ref is None:
|
|
1455
|
+
return dict()
|
|
1256
1456
|
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
if isinstance(x,ummy):
|
|
1260
|
-
return [x]
|
|
1261
|
-
elif isinstance(x,GummyTag):
|
|
1262
|
-
return x.get_values()
|
|
1263
|
-
elif isinstance(x,str):
|
|
1264
|
-
t = GummyTag.tags.get(x)
|
|
1265
|
-
if t is None:
|
|
1266
|
-
return []
|
|
1267
|
-
x = t.get_values()
|
|
1268
|
-
return x
|
|
1269
|
-
elif hasattr(x,'value') and isinstance(x.value,ummy):
|
|
1270
|
-
return [x.value]
|
|
1271
|
-
|
|
1272
|
-
xl = []
|
|
1273
|
-
for e in x:
|
|
1274
|
-
if hasattr(e,'value') and isinstance(e.value,ummy):
|
|
1275
|
-
xl.append(e.value)
|
|
1276
|
-
elif isinstance(e,ummy):
|
|
1277
|
-
xl.append(e)
|
|
1278
|
-
elif isinstance(e,GummyTag):
|
|
1279
|
-
xl += e.get_values()
|
|
1280
|
-
elif isinstance(e,str):
|
|
1281
|
-
t = GummyTag.tags.get(e)
|
|
1282
|
-
if t is not None:
|
|
1283
|
-
xl += t.get_values()
|
|
1284
|
-
return xl
|
|
1457
|
+
if isinstance(x,ummy) or isinstance(x,str) or isinstance(x,Quantity):
|
|
1458
|
+
x = [x]
|
|
1285
1459
|
|
|
1460
|
+
x = [i.value if isinstance(i,Quantity) else i for i in x]
|
|
1461
|
+
|
|
1462
|
+
d = dict()
|
|
1463
|
+
for g in x:
|
|
1464
|
+
if isinstance(g,str):
|
|
1465
|
+
for k in self._ref:
|
|
1466
|
+
if k.utype == g:
|
|
1467
|
+
if k in d:
|
|
1468
|
+
d[k] += 1
|
|
1469
|
+
else:
|
|
1470
|
+
d[k] = 1
|
|
1471
|
+
elif isinstance(g,ummy):
|
|
1472
|
+
for k,v in g._ref.items():
|
|
1473
|
+
if k in d:
|
|
1474
|
+
d[k] += v**2
|
|
1475
|
+
else:
|
|
1476
|
+
d[k] = v**2
|
|
1477
|
+
|
|
1478
|
+
for k,v in list(d.items()):
|
|
1479
|
+
if k in self._ref and v > 0:
|
|
1480
|
+
if v > 1:
|
|
1481
|
+
d[k] = self._ref[k]**2
|
|
1482
|
+
else:
|
|
1483
|
+
d[k] *= self._ref[k]**2
|
|
1484
|
+
else:
|
|
1485
|
+
del d[k]
|
|
1486
|
+
|
|
1487
|
+
return d
|
|
1488
|
+
|
|
1286
1489
|
|
|
1287
1490
|
def ufrom(self,x):
|
|
1288
1491
|
"""
|
|
@@ -1308,29 +1511,31 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1308
1511
|
>>> d.ufrom('A')
|
|
1309
1512
|
0.53851648071345048
|
|
1310
1513
|
"""
|
|
1311
|
-
|
|
1312
|
-
x =
|
|
1313
|
-
if
|
|
1314
|
-
|
|
1514
|
+
|
|
1515
|
+
#x = ummy._toummylist(x)
|
|
1516
|
+
#x = [i for i in x if self.correlation(i) != 0]
|
|
1517
|
+
#if len(x) == 0:
|
|
1518
|
+
#return 0
|
|
1315
1519
|
|
|
1316
|
-
v = ummy.correlation_matrix(x)
|
|
1520
|
+
#v = ummy.correlation_matrix(x)
|
|
1317
1521
|
|
|
1318
|
-
b = [self.correlation(z) for z in x]
|
|
1319
|
-
s = np.linalg.lstsq(v,b,rcond=None)[0]
|
|
1320
|
-
u = 0
|
|
1321
|
-
|
|
1322
|
-
d = [i*self.u/j.u for i,j in zip(s,x)]
|
|
1323
|
-
for i in range(len(x)):
|
|
1324
|
-
for j in range(len(x)):
|
|
1325
|
-
u += d[i]*d[j]*x[i].correlation(x[j])*x[i].u*x[j].u
|
|
1522
|
+
#b = [self.correlation(z) for z in x]
|
|
1523
|
+
#s = np.linalg.lstsq(v,b,rcond=None)[0]
|
|
1524
|
+
#u = 0
|
|
1525
|
+
|
|
1526
|
+
#d = [i*self.u/j.u for i,j in zip(s,x)]
|
|
1527
|
+
#for i in range(len(x)):
|
|
1528
|
+
#for j in range(len(x)):
|
|
1529
|
+
#u += d[i]*d[j]*x[i].correlation(x[j])*x[i].u*x[j].u
|
|
1326
1530
|
|
|
1327
|
-
return u**0.5
|
|
1531
|
+
#return u**0.5
|
|
1532
|
+
|
|
1533
|
+
return float(self.u*np.sqrt(sum(v for v in self._ufromrefs(x).values())))
|
|
1328
1534
|
|
|
1329
1535
|
def doffrom(self,x):
|
|
1330
1536
|
"""
|
|
1331
1537
|
Gets the degrees of freedom contributed from particular ummys or
|
|
1332
|
-
utypes if all other free variables are held fixed.
|
|
1333
|
-
correlations in the calculations can cause errors in dof calculations.
|
|
1538
|
+
utypes if all other free variables are held fixed.
|
|
1334
1539
|
|
|
1335
1540
|
Parameters
|
|
1336
1541
|
----------
|
|
@@ -1351,193 +1556,43 @@ class ummy(Dfunc,PrettyPrinter):
|
|
|
1351
1556
|
>>> d.doffrom('A')
|
|
1352
1557
|
9.0932962619709627
|
|
1353
1558
|
"""
|
|
1354
|
-
x = ummy._toummylist(x)
|
|
1355
|
-
x = [i for i in x if self.correlation(i) != 0]
|
|
1356
|
-
if len(x) == 0:
|
|
1357
|
-
return float('inf')
|
|
1559
|
+
#x = ummy._toummylist(x)
|
|
1560
|
+
#x = [i for i in x if self.correlation(i) != 0]
|
|
1561
|
+
#if len(x) == 0:
|
|
1562
|
+
#return float('inf')
|
|
1358
1563
|
|
|
1359
|
-
v = ummy.correlation_matrix(x)
|
|
1360
|
-
b = [self.correlation(z) for z in x]
|
|
1361
|
-
s = np.linalg.lstsq(v,b,rcond=None)[0]
|
|
1362
|
-
d = [i*self.u/j.u for i,j in zip(s,x)]
|
|
1363
|
-
usq = 0
|
|
1364
|
-
dm = 0
|
|
1365
|
-
for i in range(len(x)):
|
|
1366
|
-
for j in range(len(x)):
|
|
1367
|
-
usqi = d[i]*d[j]*x[i].correlation(x[j])*x[i].u*x[j].u
|
|
1368
|
-
usq += usqi
|
|
1369
|
-
dm += usqi**2/x[i].dof
|
|
1564
|
+
#v = ummy.correlation_matrix(x)
|
|
1565
|
+
#b = [self.correlation(z) for z in x]
|
|
1566
|
+
#s = np.linalg.lstsq(v,b,rcond=None)[0]
|
|
1567
|
+
#d = [i*self.u/j.u for i,j in zip(s,x)]
|
|
1568
|
+
#usq = 0
|
|
1569
|
+
#dm = 0
|
|
1570
|
+
#for i in range(len(x)):
|
|
1571
|
+
#for j in range(len(x)):
|
|
1572
|
+
#usqi = d[i]*d[j]*x[i].correlation(x[j])*x[i].u*x[j].u
|
|
1573
|
+
#usq += usqi
|
|
1574
|
+
#dm += usqi**2/x[i].dof
|
|
1370
1575
|
|
|
1371
|
-
if dm == 0:
|
|
1576
|
+
#if dm == 0:
|
|
1577
|
+
# return float('inf')
|
|
1578
|
+
#dof = usq**2/dm
|
|
1579
|
+
#if dof > ummy.max_dof:
|
|
1580
|
+
#return float('inf')
|
|
1581
|
+
#if dof < 1:
|
|
1582
|
+
#return 1
|
|
1583
|
+
#return dof
|
|
1584
|
+
|
|
1585
|
+
dof = sum(v**2/k.dof for k,v in self._ufromrefs(x).items() if k.dof is not None)
|
|
1586
|
+
if dof > 0:
|
|
1587
|
+
dof = sum(v for v in self._ufromrefs(x).values())**2/dof
|
|
1588
|
+
else:
|
|
1372
1589
|
return float('inf')
|
|
1373
|
-
dof = usq**2/dm
|
|
1374
1590
|
if dof > ummy.max_dof:
|
|
1375
1591
|
return float('inf')
|
|
1376
1592
|
if dof < 1:
|
|
1377
1593
|
return 1
|
|
1378
|
-
return dof
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
class _GummyRef:
|
|
1382
|
-
# Instances of this class are hashable unlike gummys, so we can use this in
|
|
1383
|
-
# a gummy's ._cor weak key dictionary and a GummyTag's .values weak set.
|
|
1384
|
-
|
|
1385
|
-
_cortol = 1e-15 # correlations smaller than this are rounded to zero
|
|
1386
|
-
_cortolp = 1 - 1e-15 # correlations larger than this are rounded to 1
|
|
1387
|
-
_cortoln = -1 + 1e-15 # correlations smaller than this are rounded to -1
|
|
1388
|
-
|
|
1389
|
-
def __init__(self,dof=float('inf')):
|
|
1390
|
-
self._cor = weakref.WeakKeyDictionary()
|
|
1391
|
-
self._tag = None
|
|
1392
|
-
self._tag_refs = []
|
|
1393
|
-
self._tag_deps = []
|
|
1394
|
-
self.dof = dof
|
|
1395
|
-
|
|
1396
|
-
def cor(self,ref):
|
|
1397
|
-
if ref is self:
|
|
1398
|
-
return 1
|
|
1399
|
-
c = self._cor.get(ref)
|
|
1400
|
-
if c is None:
|
|
1401
|
-
return 0
|
|
1402
|
-
else:
|
|
1403
|
-
return c
|
|
1404
|
-
|
|
1405
|
-
def set_cor(self,g,c):
|
|
1406
|
-
# We allow input values slighly greater than 1 without raising an
|
|
1407
|
-
# exception to give room for rounding errors (but values greater than
|
|
1408
|
-
# 1 will be rounded to 1 below).
|
|
1409
|
-
|
|
1410
|
-
if abs(c) > 1.01:
|
|
1411
|
-
raise ValueError('abs(c) > 1')
|
|
1412
|
-
|
|
1413
|
-
if abs(c) < _GummyRef._cortol:
|
|
1414
|
-
if g._ref in self._cor:
|
|
1415
|
-
del self._cor[g._ref]
|
|
1416
|
-
del g._ref._cor[self]
|
|
1417
|
-
return
|
|
1418
|
-
|
|
1419
|
-
if c > _GummyRef._cortolp:
|
|
1420
|
-
self.copyto(g,1)
|
|
1421
|
-
return
|
|
1422
|
-
|
|
1423
|
-
if c < _GummyRef._cortoln:
|
|
1424
|
-
self.copyto(g,-1)
|
|
1425
|
-
return
|
|
1426
|
-
|
|
1427
|
-
# We can't let an ummy with a utype die and be collected with the garbage
|
|
1428
|
-
# if it is correlated with a living ummy in case ufrom() or doffrom() is
|
|
1429
|
-
# called with the utype as an argument. So we add a strong reference
|
|
1430
|
-
# pointing to the utyped ummy to the GummyRef of all other ummys that
|
|
1431
|
-
# are correlated with it.
|
|
1432
|
-
if self._tag is not None:
|
|
1433
|
-
g._ref._tag_deps.append(self.get_tag_ref())
|
|
1434
|
-
elif len(self._tag_deps) > 0:
|
|
1435
|
-
g._ref._tag_deps += self._tag_deps
|
|
1436
|
-
|
|
1437
|
-
self._cor[g._ref] = c
|
|
1438
|
-
g._ref._cor[self] = c
|
|
1439
|
-
|
|
1440
|
-
def add_cor(self,g,c):
|
|
1441
|
-
if c == 0:
|
|
1442
|
-
return
|
|
1443
|
-
v = self._cor.get(g._ref)
|
|
1444
|
-
if v is None:
|
|
1445
|
-
v = c
|
|
1446
|
-
else:
|
|
1447
|
-
v += c
|
|
1448
|
-
self._cor[g._ref] = v
|
|
1449
|
-
g._ref._cor[self] = v
|
|
1450
|
-
|
|
1451
|
-
@staticmethod
|
|
1452
|
-
def check_cor(r):
|
|
1453
|
-
a = list(r._ref._cor.items())
|
|
1454
|
-
for k,v in a:
|
|
1455
|
-
if k is not None:
|
|
1456
|
-
if abs(v) > 1.01:
|
|
1457
|
-
raise ValueError('abs(correlation) > 1')
|
|
1458
|
-
if abs(v) < _GummyRef._cortol:
|
|
1459
|
-
del k._cor[r._ref]
|
|
1460
|
-
del r._ref._cor[k]
|
|
1461
|
-
return
|
|
1462
|
-
if v > _GummyRef._cortolp:
|
|
1463
|
-
k.copyto(r,1)
|
|
1464
|
-
return
|
|
1465
|
-
if v < _GummyRef._cortoln:
|
|
1466
|
-
k.copyto(r,-1)
|
|
1467
|
-
return
|
|
1468
|
-
|
|
1469
|
-
def copyto(self,g,refs=1):
|
|
1470
|
-
g._ref = self
|
|
1471
|
-
g._refs = refs
|
|
1472
|
-
if self._tag is not None:
|
|
1473
|
-
self._tag_refs.append(weakref.ref(g))
|
|
1474
|
-
|
|
1475
|
-
def combl(self,g,c1,c2,rl=None):
|
|
1476
|
-
self.set_cor(g,c1)
|
|
1477
|
-
if g._ref is not self:
|
|
1478
|
-
a = list(self._cor.items())
|
|
1479
|
-
for k,v in a:
|
|
1480
|
-
if k is not None and k is not g._ref:
|
|
1481
|
-
if rl is None or not any(i is k for i in rl):
|
|
1482
|
-
k.add_cor(g,c2*v)
|
|
1483
|
-
|
|
1484
|
-
def get_tag_ref(self):
|
|
1485
|
-
try:
|
|
1486
|
-
ret = self._tag_refs[-1]()
|
|
1487
|
-
while ret is None:
|
|
1488
|
-
# The GummyRef may be shared by several copys of the original
|
|
1489
|
-
# ummy. Find one that is still alive.
|
|
1490
|
-
self._tag_refs.pop()
|
|
1491
|
-
ret = self._tag_refs[-1]()
|
|
1492
|
-
return ret
|
|
1493
|
-
except IndexError:
|
|
1494
|
-
return None
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
class GummyTag:
|
|
1498
|
-
# Do not create GummyTag objects directly. A GummyTag instance is created
|
|
1499
|
-
# each time a gummy instance with a new and unique utype parameter is created.
|
|
1500
|
-
|
|
1501
|
-
tags = weakref.WeakValueDictionary()
|
|
1502
|
-
|
|
1503
|
-
@staticmethod
|
|
1504
|
-
def set_tag(g,tag):
|
|
1505
|
-
if isinstance(tag,str):
|
|
1506
|
-
tag = tag.strip()
|
|
1507
|
-
if tag in GummyTag.tags:
|
|
1508
|
-
tag = GummyTag.tags[tag]
|
|
1509
|
-
else:
|
|
1510
|
-
tag = GummyTag(tag)
|
|
1511
|
-
|
|
1512
|
-
elif not isinstance(tag,GummyTag):
|
|
1513
|
-
raise ValueError('utype must be either None or a str')
|
|
1514
|
-
|
|
1515
|
-
tag.values.add(g._ref)
|
|
1516
|
-
g._ref._tag = tag
|
|
1517
|
-
g._ref._tag_refs.append(weakref.ref(g))
|
|
1518
|
-
|
|
1519
|
-
def __init__(self,tag_name):
|
|
1520
|
-
GummyTag.tags[tag_name] = self
|
|
1521
|
-
|
|
1522
|
-
self.values = weakref.WeakSet()
|
|
1523
|
-
|
|
1524
|
-
self.name = tag_name
|
|
1525
|
-
|
|
1526
|
-
def get_values(self):
|
|
1527
|
-
vals = list(self.values)
|
|
1528
|
-
|
|
1529
|
-
# Can't put this in a list comprehension since v.ref is a weakref and
|
|
1530
|
-
# it could die in the middle of the loop.
|
|
1531
|
-
ret = []
|
|
1532
|
-
for v in vals:
|
|
1533
|
-
if v is not None:
|
|
1534
|
-
r = v.get_tag_ref()
|
|
1535
|
-
if r is not None:
|
|
1536
|
-
ret.append(r)
|
|
1537
|
-
|
|
1538
|
-
return ret
|
|
1594
|
+
return float(dof)
|
|
1539
1595
|
|
|
1540
|
-
|
|
1541
1596
|
def _der(function,*args):
|
|
1542
1597
|
# computes the numerical derivative of function with respect to args.
|
|
1543
1598
|
# puts zeros for any of args that are not an ummy
|
|
@@ -1576,7 +1631,7 @@ def _der(function,*args):
|
|
|
1576
1631
|
v[i] = a
|
|
1577
1632
|
d = np.zeros(n)
|
|
1578
1633
|
for i,p in enumerate(args):
|
|
1579
|
-
if isinstance(p,ummy) and p._u
|
|
1634
|
+
if isinstance(p,ummy) and p._u != 0:
|
|
1580
1635
|
df = None
|
|
1581
1636
|
s = 2*p.u
|
|
1582
1637
|
x1 = np.array(v)
|
|
@@ -1607,7 +1662,7 @@ def _der(function,*args):
|
|
|
1607
1662
|
return d
|
|
1608
1663
|
|
|
1609
1664
|
|
|
1610
|
-
class MetaImmy(MetaPrettyPrinter):
|
|
1665
|
+
class MetaImmy(MetaPrettyPrinter,ABCMeta):
|
|
1611
1666
|
@property
|
|
1612
1667
|
def style(cls):
|
|
1613
1668
|
"""
|
|
@@ -1640,7 +1695,7 @@ class MetaImmy(MetaPrettyPrinter):
|
|
|
1640
1695
|
def imag_symbol(cls,value):
|
|
1641
1696
|
immy._imag_symbol = str(value)
|
|
1642
1697
|
|
|
1643
|
-
class immy(PrettyPrinter,Dfunc,metaclass=MetaImmy):
|
|
1698
|
+
class immy(PrettyPrinter,Dfunc,Number,metaclass=MetaImmy):
|
|
1644
1699
|
|
|
1645
1700
|
_style = 'cartesian'
|
|
1646
1701
|
_imag_symbol = 'j'
|
|
@@ -1774,20 +1829,20 @@ class immy(PrettyPrinter,Dfunc,metaclass=MetaImmy):
|
|
|
1774
1829
|
def r(self):
|
|
1775
1830
|
"""
|
|
1776
1831
|
read-only
|
|
1777
|
-
Returns the magnitude of the value.
|
|
1832
|
+
Returns the magnitude of the value (`abs(self)`).
|
|
1778
1833
|
"""
|
|
1779
1834
|
if self._r is None:
|
|
1780
|
-
self._r =
|
|
1835
|
+
self._r = (self.real**2 + self.imag**2)**0.5
|
|
1781
1836
|
return self._r
|
|
1782
1837
|
|
|
1783
1838
|
@property
|
|
1784
1839
|
def phi(self):
|
|
1785
1840
|
"""
|
|
1786
1841
|
read-only
|
|
1787
|
-
Returns the polar angle of the value.
|
|
1842
|
+
Returns the polar angle of the value (`self.angle()`)
|
|
1788
1843
|
"""
|
|
1789
1844
|
if self._phi is None:
|
|
1790
|
-
self._phi = self.
|
|
1845
|
+
self._phi = np.arctan2(self.imag,self.real)
|
|
1791
1846
|
return self._phi
|
|
1792
1847
|
|
|
1793
1848
|
def conjugate(self):
|
|
@@ -1798,11 +1853,12 @@ class immy(PrettyPrinter,Dfunc,metaclass=MetaImmy):
|
|
|
1798
1853
|
|
|
1799
1854
|
def angle(self):
|
|
1800
1855
|
"""
|
|
1801
|
-
Returns the polar angle in radians
|
|
1856
|
+
Returns the polar angle in radians (which is also the phi read-only
|
|
1857
|
+
property value).
|
|
1802
1858
|
"""
|
|
1803
|
-
return
|
|
1859
|
+
return self.phi
|
|
1804
1860
|
|
|
1805
|
-
def copy(self,formatting=True,
|
|
1861
|
+
def copy(self,formatting=True,totype=None):
|
|
1806
1862
|
"""
|
|
1807
1863
|
Returns a copy of the jummy. If the `formatting` parameter is
|
|
1808
1864
|
`True` the display formatting information will be copied and if
|
|
@@ -1812,12 +1868,13 @@ class immy(PrettyPrinter,Dfunc,metaclass=MetaImmy):
|
|
|
1812
1868
|
imaginary components will be converted to floats.
|
|
1813
1869
|
"""
|
|
1814
1870
|
if self._ridef:
|
|
1815
|
-
r = self.real.copy(formatting=formatting,
|
|
1816
|
-
i = self.imag.copy(formatting=formatting,
|
|
1871
|
+
r = self.real.copy(formatting=formatting,totype=totype)
|
|
1872
|
+
i = self.imag.copy(formatting=formatting,totype=totype)
|
|
1817
1873
|
return type(self)(real=r,imag=i)
|
|
1874
|
+
|
|
1818
1875
|
else:
|
|
1819
|
-
r = self.r.copy(formatting=formatting,
|
|
1820
|
-
phi = self.phi.copy(formatting=formatting,
|
|
1876
|
+
r = self.r.copy(formatting=formatting,totype=totype)
|
|
1877
|
+
phi = self.phi.copy(formatting=formatting,totype=totype)
|
|
1821
1878
|
return type(self)(r=r,phi=phi)
|
|
1822
1879
|
|
|
1823
1880
|
def tofloat(self):
|
|
@@ -1825,7 +1882,7 @@ class immy(PrettyPrinter,Dfunc,metaclass=MetaImmy):
|
|
|
1825
1882
|
Returns a copy of the gummy with x an u (for both the real and
|
|
1826
1883
|
imaginary components) converted to floats.
|
|
1827
1884
|
"""
|
|
1828
|
-
return self.copy(formatting=False,
|
|
1885
|
+
return self.copy(formatting=False,totype=float)
|
|
1829
1886
|
|
|
1830
1887
|
def splonk(self):
|
|
1831
1888
|
if self.real.u == 0 and self.imag.u == 0:
|
|
@@ -2135,7 +2192,7 @@ class immy(PrettyPrinter,Dfunc,metaclass=MetaImmy):
|
|
|
2135
2192
|
raise TypeError("can't mod immy")
|
|
2136
2193
|
|
|
2137
2194
|
def __abs__(self):
|
|
2138
|
-
return
|
|
2195
|
+
return self.r
|
|
2139
2196
|
|
|
2140
2197
|
def __neg__(self):
|
|
2141
2198
|
return type(self)(real=-self.real.copy(formatting=False),
|
|
@@ -2145,85 +2202,20 @@ class immy(PrettyPrinter,Dfunc,metaclass=MetaImmy):
|
|
|
2145
2202
|
return type(self)(real=self.real.copy(formatting=False),
|
|
2146
2203
|
imag=self.imag.copy(formatting=False))
|
|
2147
2204
|
|
|
2148
|
-
#def __complex__(self):
|
|
2149
|
-
#return complex(self.real.x,self.imag.x)
|
|
2150
|
-
|
|
2151
|
-
#def __float__(self):
|
|
2152
|
-
#raise TypeError("can't convert immy to float")
|
|
2153
|
-
|
|
2154
|
-
#def __int__(self):
|
|
2155
|
-
#raise TypeError("can't convert immy to int")
|
|
2156
|
-
|
|
2157
2205
|
def __eq__(self,v):
|
|
2158
2206
|
return self.real == v.real and self.imag == v.imag
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
class MFraction(Fraction):
|
|
2162
|
-
"""
|
|
2163
|
-
A fraction.Fraction sub-class that works with mpmath.mpf objects
|
|
2164
|
-
"""
|
|
2165
|
-
|
|
2166
|
-
def __new__(cls, *args, **kwargs):
|
|
2167
|
-
if len(args) == 1 and not (isinstance(args[0],str)
|
|
2168
|
-
or isinstance(args[0],Fraction)):
|
|
2169
|
-
return args[0]
|
|
2170
|
-
ret = super(MFraction, cls).__new__(cls, *args, **kwargs)
|
|
2171
|
-
if ret.denominator == 1:
|
|
2172
|
-
return ret.numerator
|
|
2173
|
-
return ret
|
|
2174
2207
|
|
|
2175
|
-
def
|
|
2176
|
-
return
|
|
2208
|
+
def __hash__(self):
|
|
2209
|
+
return hash((self.real,self.imag))
|
|
2177
2210
|
|
|
2178
|
-
def
|
|
2179
|
-
return
|
|
2211
|
+
def __complex__(self):
|
|
2212
|
+
return complex(float(self.real),float(self.imag))
|
|
2180
2213
|
|
|
2181
|
-
def
|
|
2182
|
-
return
|
|
2183
|
-
|
|
2184
|
-
def __sub__(self,v):
|
|
2185
|
-
return MFraction(super().__sub__(v))
|
|
2186
|
-
|
|
2187
|
-
def __rsub__(self,v):
|
|
2188
|
-
return MFraction(super().__rsub__(v))
|
|
2189
|
-
|
|
2190
|
-
def __mul__(self,v):
|
|
2191
|
-
return MFraction(super().__mul__(v))
|
|
2214
|
+
def __bool__(self):
|
|
2215
|
+
return self != 0
|
|
2192
2216
|
|
|
2193
|
-
def
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
def __truediv__(self,v):
|
|
2197
|
-
return MFraction(super().__truediv__(v))
|
|
2198
|
-
|
|
2199
|
-
def __rtruediv__(self,v):
|
|
2200
|
-
return MFraction(super().__rtruediv__(v))
|
|
2201
|
-
|
|
2202
|
-
def __pow__(self,v):
|
|
2203
|
-
return MFraction(super().__pow__(v))
|
|
2204
|
-
|
|
2205
|
-
def __rpow__(self,v):
|
|
2206
|
-
if isinstance(v,Fraction):
|
|
2207
|
-
return MFraction(v).__pow__(self)
|
|
2208
|
-
return MFraction(super().__rpow__(v))
|
|
2209
|
-
|
|
2210
|
-
def __floordiv__(self,v):
|
|
2211
|
-
return MFraction(super().__floordiv__(v))
|
|
2212
|
-
|
|
2213
|
-
def __rfloordiv__(self,v):
|
|
2214
|
-
return MFraction(super().__rfloordiv__(v))
|
|
2217
|
+
#def __float__(self):
|
|
2218
|
+
#raise TypeError("can't convert immy to float")
|
|
2215
2219
|
|
|
2216
|
-
def
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
def __rmod__(self,v):
|
|
2220
|
-
return MFraction(super().__rmod__(v))
|
|
2221
|
-
|
|
2222
|
-
def __abs__(self):
|
|
2223
|
-
return MFraction(super().__abs__())
|
|
2224
|
-
|
|
2225
|
-
def __neg__(self):
|
|
2226
|
-
return MFraction(super().__neg__())
|
|
2227
|
-
|
|
2228
|
-
def __pos__(self):
|
|
2229
|
-
return MFraction(super().__pos__())
|
|
2220
|
+
#def __int__(self):
|
|
2221
|
+
#raise TypeError("can't convert immy to int")
|