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/gummy.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # module gummy
4
4
 
5
- # Copyright (C) 2019 National Research Council Canada
5
+ # Copyright (C) 2025 National Research Council Canada
6
6
  # Author: Harold Parks
7
7
 
8
8
  # This file is part of MetroloPy.
@@ -31,16 +31,15 @@ module. The gummy object, in turn, inherits from the nummy object.
31
31
  """
32
32
 
33
33
  import numpy as np
34
- from .ummy import ummy,immy,_isscalar,_floor,_format_exp
35
- from .nummy import nummy
36
- from .exceptions import IncompatibleUnitsError,NoUnitConversionFoundError
37
- from .unit import Unit,one,Quantity
34
+ from .ummy import ummy,immy,_isscalar,_format_exp,_to_decimal,_decimal_str
35
+ from .nummy import nummy,get_name
36
+ from .exceptions import IncompatibleUnitsError
37
+ from .unit import Unit,one,Quantity,MetaQuantity,MFraction
38
38
  from .distributions import Distribution,MultivariateDistribution
39
39
  from .pmethod import _Pmthd,loc_from_k
40
- from .printing import MetaPrettyPrinter
41
- from math import isnan, isinf,log10
42
- from fractions import Fraction
40
+ from math import isnan, isinf
43
41
  from numbers import Integral,Rational,Real,Complex
42
+ from decimal import Decimal,localcontext,InvalidOperation
44
43
 
45
44
 
46
45
  try:
@@ -51,22 +50,78 @@ except:
51
50
  def _ku(k,u):
52
51
  try:
53
52
  return k*u
54
- except:
55
- return type(u)(k)*u # in case gummy.u is a decimal.Decimal
56
-
57
- def _lg10(x):
58
- if mp is not None and isinstance(x,mp.mpf):
59
- return mp.log10(x)
60
- try:
61
- return x.log10() # in case x is a decimal.Decimal
62
53
  except:
63
54
  try:
64
- return np.log10(x)
55
+ if isinstance(u,Integral):
56
+ raise
57
+ return type(u)(k)*u # in case gummy.u is a decimal.Decimal
65
58
  except:
66
- return log10(float(x)) # in case x is a fraction.Fraction
59
+ return float(k)*float(u)
60
+
61
+ def _add_unit_sp(fmt,unit):
62
+ if unit is None or unit is one:
63
+ return ''
64
+
65
+ if unit == '':
66
+ return ''
67
+
68
+ if unit.startswith('\t'):
69
+ unit = unit[1:]
70
+ else:
71
+ if fmt == 'latex':
72
+ unit = r'\:' + unit
73
+ elif fmt == 'html':
74
+ unit = ' ' + unit
75
+ else:
76
+ unit = ' ' + unit
77
+
78
+ return unit
79
+
80
+ def _p_to_str(fmt,p):
81
+ if fmt == 'latex':
82
+ pct = '\\%'
83
+ else:
84
+ pct = '%'
85
+
86
+ x = 100*Decimal(p)
87
+ if x <= 90:
88
+ return '{:.0f}'.format(x) + pct
89
+
90
+ y = 100 - x
91
+ y = round(y,1-y.adjusted()-1)
92
+ y = round(y,1-y.adjusted()-1)
93
+ x = x.quantize(y)
94
+ return '{:f}'.format(x) + pct
67
95
 
96
+ def _k_to_str(k):
97
+ return '{:.1f}'.format(k)
68
98
 
69
- class MetaGummy(MetaPrettyPrinter):
99
+ def _dof_to_str(dof,fmt=None):
100
+ if isinf(dof) or dof > 99:
101
+ if fmt == 'html':
102
+ return '&infin;'
103
+ if fmt == 'latex':
104
+ return r'\infty'
105
+ if fmt == 'ascii':
106
+ return 'inf'
107
+ return '\u221E'
108
+
109
+ if isinstance(dof,Integral):
110
+ return str(dof)
111
+ return '{:.1f}'.format(dof)
112
+
113
+ def _set_covariance_matrix(gummys, matrix):
114
+ nummys = [g.value for g in gummys]
115
+ nummy._set_covariance_matrix(nummys, matrix)
116
+ for g in gummys:
117
+ g._set_U(None,None)
118
+
119
+ def _set_correlation_matrix(gummys, matrix):
120
+ nummys = [g.value for g in gummys]
121
+ nummy._set_correlation_matrix(nummys, matrix)
122
+
123
+
124
+ class MetaGummy(MetaQuantity):
70
125
  # A metaclass to define some "classproperties" for gummy
71
126
 
72
127
  @property
@@ -320,6 +375,8 @@ class MetaGummy(MetaPrettyPrinter):
320
375
  return ummy.sci_notation
321
376
  @sci_notation.setter
322
377
  def sci_notation(cls,v):
378
+ if v is not None:
379
+ v = bool(v)
323
380
  ummy.sci_notation = v
324
381
 
325
382
  @property
@@ -330,7 +387,7 @@ class MetaGummy(MetaPrettyPrinter):
330
387
  return ummy.sci_notation_high
331
388
  @sci_notation_high.setter
332
389
  def sci_notation_high(cls,v):
333
- ummy.sci_notation_high = v
390
+ ummy.sci_notation_high = int(v)
334
391
 
335
392
  @property
336
393
  def sci_notation_low(cls):
@@ -340,7 +397,7 @@ class MetaGummy(MetaPrettyPrinter):
340
397
  return ummy.sci_notation_low
341
398
  @sci_notation_low.setter
342
399
  def sci_notation_low(cls,v):
343
- ummy.sci_notation_low = v
400
+ ummy.sci_notation_low = int(v)
344
401
 
345
402
  @property
346
403
  def rounding_u(cls):
@@ -366,6 +423,9 @@ class MetaGummy(MetaPrettyPrinter):
366
423
  return ummy.max_digits
367
424
  @max_digits.setter
368
425
  def max_digits(cls,v):
426
+ v = int(v)
427
+ if v < 0:
428
+ raise ValueError('max_digits must be >= 0')
369
429
  ummy.max_digits = v
370
430
 
371
431
  @property
@@ -501,9 +561,8 @@ class gummy(Quantity,metaclass=MetaGummy):
501
561
 
502
562
  if isinstance(x,gummy):
503
563
  self._value = nummy(x.value)
504
- self._value._fp = self._get_p
505
564
  self._unit = x._unit
506
- self._U = self._value._u
565
+ self._U = self._value.u
507
566
  self._value._name = x._value._name
508
567
  self._k = 1
509
568
  self._pm = None
@@ -520,8 +579,7 @@ class gummy(Quantity,metaclass=MetaGummy):
520
579
 
521
580
  if isinstance(x,ummy):
522
581
  self._value = nummy(x)
523
- self._value._fp = self._get_p
524
- self._U = self._value._u
582
+ self._U = self._value.u
525
583
  self._k = 1
526
584
  self._pm = None
527
585
  self._set_k = True
@@ -575,7 +633,7 @@ class gummy(Quantity,metaclass=MetaGummy):
575
633
  u = unit.from_uunit(u,uunit)
576
634
  elif unit.is_dimensionless:
577
635
  if not uunit.is_dimensionless:
578
- raise NoUnitConversionFoundError('no conversion found for unit ' + str(uunit) + ' to one')
636
+ raise IncompatibleUnitsError('no conversion found for unit ' + str(uunit) + ' to one')
579
637
  if uunit is one:
580
638
  u = U.convert(unit).value
581
639
  else:
@@ -583,7 +641,7 @@ class gummy(Quantity,metaclass=MetaGummy):
583
641
  else:
584
642
  try:
585
643
  u = U.convert(unit).value
586
- except NoUnitConversionFoundError:
644
+ except IncompatibleUnitsError:
587
645
  # If no conversion was found for uunit to unit, see
588
646
  # if unit can be converted to one. In this case the u
589
647
  # passed to the intializer was a relative uncertainty.
@@ -592,11 +650,13 @@ class gummy(Quantity,metaclass=MetaGummy):
592
650
  if self._k != 1:
593
651
  try:
594
652
  u = u/self._k
595
- except:
596
- u = u/type(u)(self._k)
653
+ except TypeError:
654
+ if isinstance(u,Integral):
655
+ u = u/MFraction(self._k)
656
+ else:
657
+ u = u/type(u)(self._k)
597
658
 
598
659
  self._value = nummy(x,u=u,dof=dof,utype=utype,name=name)
599
- self._value._fp = self._get_p
600
660
 
601
661
  self._U = None
602
662
  self._set_U(self._k,uunit)
@@ -617,7 +677,7 @@ class gummy(Quantity,metaclass=MetaGummy):
617
677
  sometimes called the "1-sigma" uncertainty. The This property is read-only
618
678
  and returns a float.
619
679
  """
620
- return self._value._u
680
+ return self._value.u
621
681
 
622
682
  @property
623
683
  def dof(self):
@@ -628,15 +688,36 @@ class gummy(Quantity,metaclass=MetaGummy):
628
688
  gummy is based on. If the gummy was created as the result of an
629
689
  operation between two or more other gummys, then the dof is the effective
630
690
  number of degrees of freedom calculated using the Welch-Satterthwaite
631
- approximation. Caution: A variation of the the Welch-Satterthwaite
632
- approximation is used that takes into account correlations, see
633
- [R. Willink, Metrologia, 44, 340 (2007)]. However correlations are
634
- not handled perfectly. So if accurate dof calculations are need, care
635
- should be taken to ensure that correlations are not generated in
636
- intermediate calculations.
691
+ approximation.
637
692
  """
638
693
 
639
694
  return self.value.dof
695
+
696
+ @property
697
+ def isindependent(self):
698
+ """
699
+ `bool`, read-only
700
+
701
+ Returns `True` if the gummy is an independent variable. That is the
702
+ ummy has u > 0 and was not correlated with any other gummy's when it was
703
+ created or is perfectly correlated or anti-correlated (correlation
704
+ coefficeint 1 or -1) with such an ummy.
705
+ """
706
+
707
+ return self.value.isindependent
708
+
709
+ @property
710
+ def independent(self):
711
+ """
712
+ `bool`, read-only
713
+
714
+ Returns `True` if the gummy is an independent variable. That is the
715
+ ummy has u > 0 and was not correlated with any other gummy's when it was
716
+ created or is perfectly correlated or anti-correlated (correlation
717
+ coefficeint 1 or -1) with such an ummy. Identical to the
718
+ isindependent property.
719
+ """
720
+ return self.value.isindependent
640
721
 
641
722
  @property
642
723
  def U(self):
@@ -735,16 +816,22 @@ class gummy(Quantity,metaclass=MetaGummy):
735
816
  if self._unit.linear:
736
817
  try:
737
818
  if self.unit.is_dimensionless:
738
- raise NoUnitConversionFoundError()
819
+ raise IncompatibleUnitsError()
739
820
  return Quantity(_ku(k,u),unit=self.unit).convert(unit)
740
821
 
741
- except NoUnitConversionFoundError:
822
+ except IncompatibleUnitsError:
742
823
  try:
743
- r = abs(_ku(k,u)/self.x)
824
+ try:
825
+ r = abs(_ku(k,u)/self.x)
826
+ except TypeError:
827
+ if isinstance(self.x,Integral):
828
+ r = abs(MFraction.fromnum(_ku(k,u))/self.x)
829
+ else:
830
+ r = abs(type(self.x)(_ku(k,u))/self.x)
744
831
  return Quantity(r).convert(unit)
745
832
  except ZeroDivisionError:
746
833
  if not Unit.unit(unit).is_dimensionless:
747
- raise NoUnitConversionFoundError('no conversion found from unit ' + str(unit) + ' to one')
834
+ raise IncompatibleUnitsError('no conversion found from unit ' + str(unit) + ' to one')
748
835
  return Quantity(float('inf'),unit=unit)
749
836
  else:
750
837
  return Quantity(self.unit.to_uunit(_ku(k,u),unit),unit)
@@ -762,7 +849,7 @@ class gummy(Quantity,metaclass=MetaGummy):
762
849
  `k` properties will affect `Usym`.
763
850
  """
764
851
  if not isinstance(self._U,Quantity):
765
- return self._value.Usim
852
+ return self._value.Usim(self.p)
766
853
 
767
854
  if self.uunit_is_rel:
768
855
  x = self.xsim
@@ -815,7 +902,7 @@ class gummy(Quantity,metaclass=MetaGummy):
815
902
  but changing the `p`, `k`, or `cimethod` properties will affect the return
816
903
  value.
817
904
  """
818
- return self._value.cisim
905
+ return self._value.cisim(self.p)
819
906
 
820
907
  @property
821
908
  def cimethod(self):
@@ -848,7 +935,7 @@ class gummy(Quantity,metaclass=MetaGummy):
848
935
  value = value.lower().strip()
849
936
  if value not in ['shortest','symmetric']:
850
937
  raise ValueError('cimethod ' + str(value) + ' is not recognized')
851
- self.value._cimethod = value
938
+ self.value.cimethod = value
852
939
 
853
940
  @property
854
941
  def simdata(self):
@@ -886,17 +973,7 @@ class gummy(Quantity,metaclass=MetaGummy):
886
973
 
887
974
  Returns ``0.5*(gummy.Usim[0] + gummy.Usim[1])/gummy.usim``
888
975
  """
889
- return self.value.ksim
890
-
891
- @property
892
- def independent(self):
893
- """
894
- `bool`, read-only
895
-
896
- Returns `False` if the owning gummy was created from a operation involving
897
- other gummys or has zero uncertainty and `True` otherwise.
898
- """
899
- return self.value.independent
976
+ return self.value.ksim(self.p)
900
977
 
901
978
  @property
902
979
  def name(self):
@@ -962,6 +1039,7 @@ class gummy(Quantity,metaclass=MetaGummy):
962
1039
  Quantity.unit.fset(self,u)
963
1040
  self._U = None
964
1041
  self._set_U()
1042
+ self._value.clear()
965
1043
 
966
1044
  @property
967
1045
  def uunit(self):
@@ -1239,35 +1317,18 @@ class gummy(Quantity,metaclass=MetaGummy):
1239
1317
  def finfo(self):
1240
1318
  return self.value.finfo
1241
1319
 
1242
- @property
1243
- def real(self):
1244
- """
1245
- returns a copy of the gummy
1246
- """
1247
- return self.copy(formatting=False)
1248
-
1249
- def conjugate(self):
1250
- """
1251
- returns a copy of the gummy
1252
- """
1253
- return self.copy(formatting=False)
1254
-
1255
- def angle(self):
1256
- if self.x >= 0:
1257
- return type(self)(0)
1258
- else:
1259
- return type(self)(np.pi)
1260
-
1261
1320
  @property
1262
1321
  def utype(self):
1263
1322
  """
1264
- `str` or `None`
1265
-
1266
- An arbitrary string value labeling the uncertainty type.
1323
+ `str`, `None` or a list containing strings and possibly `None`
1324
+
1325
+ An arbitrary string value labeling the uncertainty type or or a
1326
+ list of types if the gummy was constructed from independent
1327
+ variables with different utypes.
1267
1328
  """
1268
1329
  return self.value.utype
1269
1330
 
1270
- def ufrom(self,x,sim=False):
1331
+ def ufrom(self,x):
1271
1332
  """
1272
1333
  Gets the standard uncertainty contributed from particular gummys
1273
1334
  or utypes if all other free variables are held fixed.
@@ -1291,18 +1352,7 @@ class gummy(Quantity,metaclass=MetaGummy):
1291
1352
  >>> d.ufrom('A')
1292
1353
  0.53851648071345048
1293
1354
  """
1294
-
1295
- try:
1296
- if isinstance(x,str):
1297
- x = [x]
1298
- x = [i.value if isinstance(i,Quantity) else i for i in x]
1299
- except TypeError:
1300
- # x is probably a gummy and not iterable
1301
- if isinstance(x,Quantity):
1302
- x = [x.value]
1303
- else:
1304
- raise
1305
- return self.value.ufrom(x,sim)
1355
+ return self.value.ufrom(x)
1306
1356
 
1307
1357
  def doffrom(self,x):
1308
1358
  """
@@ -1329,15 +1379,68 @@ class gummy(Quantity,metaclass=MetaGummy):
1329
1379
  >>> d.doffrom('A')
1330
1380
  9.0932962619709627
1331
1381
  """
1332
- try:
1333
- x = [i.value if isinstance(i,Quantity) else i for i in x]
1334
- except TypeError:
1335
- # x is probably a gummy not iterable
1336
- if isinstance(x,Quantity):
1337
- x = [x.value]
1338
- else:
1339
- raise
1340
1382
  return self.value.doffrom(x)
1383
+
1384
+ def ufromsim(self,x):
1385
+ """
1386
+ Gets the standard deviation of the Monte-Carlo data only allowing the
1387
+ independent variables in `x` to vary. Independent istributions not in
1388
+ `x` are held fixed. `sim` or `simulate` must be called to generate
1389
+ Monte-Carlo data before calling this method.
1390
+
1391
+ Parameters
1392
+ ----------
1393
+ x: `gummy`, `str`, or array_like
1394
+ A gummy, a string referencing a utype or a list containing
1395
+ gummys and strings.
1396
+
1397
+ Returns
1398
+ -------
1399
+ `float`
1400
+ """
1401
+ if _isscalar(x):
1402
+ x = [x]
1403
+ x = [i.value if isinstance(i,Quantity) else i for i in x]
1404
+
1405
+ return self.value.ufromsim(x)
1406
+
1407
+ def datafrom(self,x,save=True):
1408
+ """
1409
+ Recomputes the Monte-Carlo `simdata `with only the varaibles in `x`
1410
+ allowed to vary. `sim` or `simulate` must be called to generate
1411
+ Monte-Carlo data before calling this method. This method cannot be
1412
+ called with save == `True` from from a gummy representing an independent
1413
+ variable (that is from a gummy not created by by mathematical operations
1414
+ between two or more other gummy's).
1415
+
1416
+ Parameters
1417
+ ----------
1418
+ ufrom: list containing `gummy` or `str`
1419
+ all independent gummys not in the list or having a utype
1420
+ not in the list are held fixed at their `.x` value
1421
+
1422
+ save: If `save` is `True` the recomputed data is stored in the `simdata`
1423
+ attribute and `None` is returned. If `save` is `False` then the
1424
+ recomputed data is returned and the `simdata` attribute is not
1425
+ overwritten.
1426
+
1427
+ Returns
1428
+ -------
1429
+ 'numpy.array' if `save` is `False`, otherwise returns `None`
1430
+
1431
+ Raises
1432
+ ------
1433
+ `NoSimulatedDataError`:
1434
+ if no simulated data is available from a call to
1435
+ `Distribution.simulate`.
1436
+ `RuntimeError`:
1437
+ if this method is called from an independent `gummy`
1438
+ """
1439
+ if _isscalar(x):
1440
+ x = [x]
1441
+ x = [i.value if isinstance(i,Quantity) else x for i in x]
1442
+
1443
+ return self.value.datafrom(x,save=save)
1341
1444
 
1342
1445
  @property
1343
1446
  def style(self):
@@ -1570,20 +1673,23 @@ class gummy(Quantity,metaclass=MetaGummy):
1570
1673
  return self.value.max_digits
1571
1674
  @max_digits.setter
1572
1675
  def max_digits(self,v):
1676
+ v = int(v)
1677
+ if v < 0:
1678
+ raise ValueError('max_digits must be >= 0')
1573
1679
  self.value.max_digits = v
1574
1680
 
1575
1681
 
1576
- def copy(self,formatting=True,tofloat=False):
1682
+ def copy(self,formatting=True,totype=None):
1577
1683
  """
1578
1684
  Returns a copy of the gummy. If the `formatting` parameter is
1579
1685
  `True` the display formatting information will be copied and if
1580
1686
  `False` the display formatting will be set to the default for a
1581
- new gummy. The default for `formatting` is `True`. If tofloat
1582
- is true the x and u properties will be converted to float values
1687
+ new gummy. The default for `formatting` is `True`. If 'totype`
1688
+ is defined the x and u properties will be converted to type `totype`
1583
1689
  before copying.
1584
1690
  """
1585
1691
 
1586
- r = type(self)(self._value.copy(formatting=formatting,tofloat=tofloat),
1692
+ r = type(self)(self._value.copy(formatting=formatting,totype=totype),
1587
1693
  unit = self._unit)
1588
1694
  r._old = self._old
1589
1695
 
@@ -1603,7 +1709,7 @@ class gummy(Quantity,metaclass=MetaGummy):
1603
1709
  r._k = self._k
1604
1710
  r._pm = self._pm
1605
1711
  r._set_k = self._set_k
1606
- if tofloat:
1712
+ if totype is not None:
1607
1713
  r._set_U(unit=self.uunit)
1608
1714
  else:
1609
1715
  r._U = self._U
@@ -1632,7 +1738,7 @@ class gummy(Quantity,metaclass=MetaGummy):
1632
1738
  instance `one`.
1633
1739
  """
1634
1740
  return self*Unit.unit(unit)/self.unit
1635
-
1741
+
1636
1742
  @staticmethod
1637
1743
  def simulate(gummys,n=100000,ufrom=None):
1638
1744
  """
@@ -1644,6 +1750,9 @@ class gummy(Quantity,metaclass=MetaGummy):
1644
1750
  ----------
1645
1751
  n: `int` > 0, optional
1646
1752
  The number of samples to generate. The default value is 100000.
1753
+
1754
+ gummys: A list or array of `gummy` for which to generate the Monte-Carlo
1755
+ data.
1647
1756
 
1648
1757
  ufrom: `None`, `gummy`, `str` or array_like
1649
1758
  If this is not `None`, then only the gummys referenced here will be
@@ -1652,15 +1761,17 @@ class gummy(Quantity,metaclass=MetaGummy):
1652
1761
  a list containing gummys and strings. The default value is `None`.
1653
1762
  """
1654
1763
  if ufrom is not None:
1655
- ufrom = ummy._toummylist(ufrom)
1764
+ if isinstance(ufrom,(Quantity,ummy,str,Distribution)):
1765
+ ufrom = [ufrom]
1766
+ ufrom = [g.value if isinstance(g,Quantity) else g for g in ufrom]
1656
1767
  gummys = [g.value if isinstance(g,Quantity) else g for g in gummys]
1657
- gummys = ummy._toummylist(gummys)
1658
- return nummy.simulate(gummys,n,ufrom)
1768
+ return nummy.simulate(gummys,n=n,ufrom=ufrom)
1659
1769
 
1660
1770
  def sim(self,n=100000,ufrom=None):
1661
1771
  """
1662
- Generates Monte-Carlo data for this gummy. Calling this method
1663
- erases previously generated Monte-Carlo data for all gummys, so use the
1772
+ Generates Monte-Carlo data for this gummy (as well as for any gummys
1773
+ that this gummy depends on). Calling this method erases previously
1774
+ generated Monte-Carlo data for all gummys, so use the
1664
1775
  `gummy.simulate()` staticmethod if you need Monte-Carlo data for
1665
1776
  several gummys simultaneously.
1666
1777
 
@@ -1675,9 +1786,20 @@ class gummy(Quantity,metaclass=MetaGummy):
1675
1786
  mean values. This can be a gummy, a string referencing a utype or
1676
1787
  a list containing gummys and strings. The default value is `None`.
1677
1788
  """
1678
- if ufrom is not None:
1679
- ufrom = ummy._toummylist(ufrom)
1680
1789
  return gummy.simulate([self],n,ufrom)
1790
+
1791
+ @staticmethod
1792
+ def clear_all():
1793
+ """
1794
+ Clears Monte-Carlo data from all existing gummys.
1795
+ """
1796
+ Distribution.clear_all()
1797
+
1798
+ def clear(self):
1799
+ """
1800
+ Clears the gummys Monte-Carlo data.
1801
+ """
1802
+ self.value.clear()
1681
1803
 
1682
1804
  @classmethod
1683
1805
  def _plotlabel(cls,g,symbol=None,exponent=0,math=None,norm=None,
@@ -1695,13 +1817,17 @@ class gummy(Quantity,metaclass=MetaGummy):
1695
1817
  else:
1696
1818
  slashaxis = gummy.slashaxis
1697
1819
 
1698
- try:
1699
- name = g.name
1700
- except:
1701
- name = None
1702
-
1703
- if name == '':
1704
- name = None
1820
+ if g is None:
1821
+ name = ''
1822
+ else:
1823
+ try:
1824
+ name = g.get_name(fmt='latex',norm=norm)
1825
+ if name is None:
1826
+ name = norm('value')
1827
+ except:
1828
+ name = str(g)
1829
+ if len(name) > 1:
1830
+ name = norm(name.strip())
1705
1831
 
1706
1832
  if symbol is None and isinstance(g,gummy) and g.unit is not one:
1707
1833
  unit = g.unit
@@ -1713,16 +1839,7 @@ class gummy(Quantity,metaclass=MetaGummy):
1713
1839
  if symbol == '':
1714
1840
  symbol = None
1715
1841
 
1716
- xl = ''
1717
- if name is not None:
1718
- if isinstance(name,str) and len(name) > 1:
1719
- name = str(name).replace(' ','\\,').strip()
1720
- xl += norm(name)
1721
- else:
1722
- try:
1723
- xl += name.tolatex()
1724
- except:
1725
- xl += str(name).strip()
1842
+ xl = name
1726
1843
  if symbol is not None:
1727
1844
  if xl != '':
1728
1845
  if slashaxis:
@@ -1847,8 +1964,19 @@ class gummy(Quantity,metaclass=MetaGummy):
1847
1964
  title1 += self.tostring(fmt='latex',style='usim',norm=norm)
1848
1965
  title1 = math(title1)
1849
1966
  title = title0 + '\n' + title1
1967
+
1968
+ if xlabel is not None:
1969
+ plot_options['xlabel'] = xlabel
1970
+ if title is not None:
1971
+ plot_options['title'] = title
1972
+
1973
+ ci = None
1974
+ if 'range' not in plot_options and ci_marker:
1975
+ ci = g.cisim
1976
+ a = (ci[1] - ci[0])/3
1977
+ plot_options['range'] = (ci[0] - a,ci[1] + a)
1850
1978
 
1851
- self.value.hist(xlabel=xlabel,title=title,hold=True,**plot_options)
1979
+ self.value.hist(hold=True,**plot_options)
1852
1980
 
1853
1981
  if mean_marker:
1854
1982
  if 'linewidth' not in mean_marker_options and 'lw' not in mean_marker_options:
@@ -1869,7 +1997,8 @@ class gummy(Quantity,metaclass=MetaGummy):
1869
1997
  if 'zorder' not in ci_marker_options:
1870
1998
  ci_marker_options['zorder'] = 3
1871
1999
 
1872
- ci = g.cisim
2000
+ if ci is None:
2001
+ ci = g.cisim
1873
2002
  plt.axvline(ci[0],**ci_marker_options)
1874
2003
  plt.axvline(ci[1],**ci_marker_options)
1875
2004
 
@@ -2229,16 +2358,16 @@ class gummy(Quantity,metaclass=MetaGummy):
2229
2358
  return('??')
2230
2359
  else:
2231
2360
  try:
2232
- return(str(self.name).strip() + ' = ' + str(self.x) + '{' + str(self.u) + '}' + '??')
2361
+ return(str(self.get_name()).strip() + ' = ' + str(self.x) + '{' + str(self.u) + '}' + '??')
2233
2362
  except:
2234
2363
  try:
2235
- return(str(self.name).strip() + ' = ' + str(self.x) + '{??}')
2364
+ return(str(self.get_name()).strip() + ' = ' + str(self.x) + '{??}')
2236
2365
  except:
2237
2366
  try:
2238
- return(str(self.name).strip() + ' = ??{' + str(self.u) + '}')
2367
+ return(str(self.get_name()).strip() + ' = ??{' + str(self.u) + '}')
2239
2368
  except:
2240
2369
  try:
2241
- return(str(self.name).strip() + ' = ' + '??')
2370
+ return(str(self.get_name()).strip() + ' = ' + '??')
2242
2371
  except:
2243
2372
  return('??')
2244
2373
 
@@ -2335,6 +2464,8 @@ class gummy(Quantity,metaclass=MetaGummy):
2335
2464
  txt = txt.strip()
2336
2465
  return txt
2337
2466
  elif style == 'uunit':
2467
+ if len(v) < 3 or len(v[2]) < 3 or v[2][2] is None:
2468
+ return ''
2338
2469
  return v[2][2]
2339
2470
  elif style == 'ueq':
2340
2471
  if self._ubreakdown is None or len(self._ubreakdown) != len(v) - 2:
@@ -2357,7 +2488,16 @@ class gummy(Quantity,metaclass=MetaGummy):
2357
2488
  else:
2358
2489
  txt = v[1][0] + v[1][1] + v[1][2]
2359
2490
  for i,t in enumerate(v[2:]):
2360
- b = str(self._ubreakdown[i])
2491
+ b = self._ubreakdown[i]
2492
+ if not isinstance(b,str):
2493
+ if isinstance(b,gummy):
2494
+ nm = b.get_name(fmt=fmt,norm=norm)
2495
+ if nm is not None:
2496
+ b = nm
2497
+ else:
2498
+ b = str(i+1)
2499
+ else:
2500
+ b = str(i+1)
2361
2501
  if fmt == 'html':
2362
2502
  if i == 0:
2363
2503
  pm = ' with <i>u</i><sub>'+b+'</sub>&nbsp;=&nbsp;'
@@ -2499,9 +2639,9 @@ class gummy(Quantity,metaclass=MetaGummy):
2499
2639
  else:
2500
2640
  k = self.k
2501
2641
  if fmt == 'html':
2502
- itxt += '<i>k</i>&nbsp;=&nbsp;' + gummy._k_to_str(k)
2642
+ itxt += '<i>k</i>&nbsp;=&nbsp;' + _k_to_str(k)
2503
2643
  else:
2504
- itxt += 'k = ' + gummy._k_to_str(k)
2644
+ itxt += 'k = ' + _k_to_str(k)
2505
2645
 
2506
2646
  txt += itxt
2507
2647
 
@@ -2518,7 +2658,7 @@ class gummy(Quantity,metaclass=MetaGummy):
2518
2658
  else:
2519
2659
  itxt0 = ' with'
2520
2660
 
2521
- ltxt = gummy._p_to_str(fmt,p)
2661
+ ltxt = _p_to_str(fmt,p)
2522
2662
  if ltxt[0] == '1' or ltxt[0] == '8':
2523
2663
  itxt0 += ' an '
2524
2664
  itxt1 = ' ' + self._p_method.text
@@ -2541,12 +2681,12 @@ class gummy(Quantity,metaclass=MetaGummy):
2541
2681
  itxt = ' with '
2542
2682
 
2543
2683
  if fmt == 'html':
2544
- itxt += '<i>&nu;</i>&nbsp;=&nbsp;' + gummy._dof_to_str(self.dof,fmt)
2684
+ itxt += '<i>&nu;</i>&nbsp;=&nbsp;' + _dof_to_str(self.dof,fmt)
2545
2685
  elif fmt == 'latex':
2546
2686
  itxt = norm(itxt)
2547
- itxt += r'\nu = ' + gummy._dof_to_str(self.dof,fmt)
2687
+ itxt += r'\nu = ' + _dof_to_str(self.dof,fmt)
2548
2688
  else:
2549
- itxt += gummy._dof_to_str(self.dof) + ' degrees of freedom'
2689
+ itxt += _dof_to_str(self.dof) + ' degrees of freedom'
2550
2690
 
2551
2691
  txt += itxt
2552
2692
 
@@ -2562,9 +2702,13 @@ class gummy(Quantity,metaclass=MetaGummy):
2562
2702
  def _format_xu(self,fmt,style,norm,nsig,xsig=None,solidus=None,mulsep=None):
2563
2703
  if style is None:
2564
2704
  style = self.style
2565
-
2705
+
2566
2706
  if nsig is None:
2567
2707
  nsig = self.nsig
2708
+ if nsig > self.max_digits - 2:
2709
+ nsig = self.max_digits - 2
2710
+ if nsig < 1:
2711
+ nsig = 1
2568
2712
 
2569
2713
  if solidus is None:
2570
2714
  solidus = self.solidus
@@ -2572,6 +2716,18 @@ class gummy(Quantity,metaclass=MetaGummy):
2572
2716
  if mulsep is None:
2573
2717
  mulsep = self.mulsep
2574
2718
 
2719
+ if style in ['u','uf']:
2720
+ if isinstance(self._U,Quantity):
2721
+ un = self._U.unit
2722
+ u = self._U.value
2723
+ else:
2724
+ un = self.unit
2725
+ u = self._U
2726
+ if style == 'uf':
2727
+ un = 1
2728
+ uret = gummy(u,unit=un)._format_xu(fmt,'x',norm,nsig,xsig=nsig,solidus=solidus,mulsep=mulsep)[1]
2729
+ return (style,('','',''),uret)
2730
+
2575
2731
  if style in ['pmsim','cisim','pmsimi','mcisim','xsim','xfsim','usim','ufsim']:
2576
2732
  sim = True
2577
2733
  if self.simdata is None:
@@ -2582,318 +2738,262 @@ class gummy(Quantity,metaclass=MetaGummy):
2582
2738
  else:
2583
2739
  sim = False
2584
2740
  u = self.u
2585
-
2586
- if u == 0 and style in ['u','uf','usim','ufsim']:
2587
- return (style,('','',''),('0','',''))
2588
-
2589
- if xsig is None and nsig <= 0:
2590
- style = 'x'
2591
- nsig = 1
2741
+
2742
+ if style in ['usim','ufsim']:
2743
+ if isinstance(self._U,Quantity):
2744
+ un = self._U.unit
2745
+ else:
2746
+ un = self.unit
2747
+ u = self.usim
2748
+ if style == 'ufsim':
2749
+ un = 1
2750
+ uret = gummy(u,unit=un)._format_xu(fmt,'x',norm,nsig,xsig=nsig,solidus=solidus,mulsep=mulsep)[1]
2751
+ return (style,('','',''),uret)
2592
2752
 
2593
- xsym = gummy._add_unit_sp(fmt,self.unit.tostring(fmt=fmt,solidus=solidus,mulsep=mulsep))
2753
+ xsym = _add_unit_sp(fmt,self.unit.tostring(fmt=fmt,solidus=solidus,mulsep=mulsep,strip=False))
2594
2754
 
2595
2755
  if sim:
2596
2756
  x = self.xsim
2597
2757
  else:
2598
2758
  x = self.x
2599
-
2600
- xabs = abs(x)
2601
2759
 
2602
- if xabs != 0 and not isinf(xabs) and not isnan(xabs):
2603
- lgx = _lg10(xabs)
2604
- if lgx < 0 and int(lgx) == lgx:
2605
- xexp = _floor(lgx) + 1
2760
+ xd = _to_decimal(x,max_digits=self.max_digits)
2761
+ with localcontext(prec=nsig + 2):
2762
+ ud = _to_decimal(u)*_to_decimal(self._k)
2763
+
2764
+ elip = ''
2765
+ uexp = 0
2766
+ xexp = 0
2767
+ dp = 0
2768
+ with localcontext(prec=self.max_digits):
2769
+ if ud == 0 or not ud.is_finite():
2770
+ if style != 'xf':
2771
+ style = 'x'
2772
+
2773
+ if xd.is_finite():
2774
+ nm = None
2775
+ if xsig is None:
2776
+ xsig = self.max_digits
2777
+ if self.finfo is not None and self.finfo.precision > 0 and self.finfo.precision <= xsig:
2778
+ xsig = self.finfo.precision
2779
+ nm = True
2780
+ else:
2781
+ nm = False
2782
+
2783
+ xd = round(xd,xsig-xd.adjusted()-1)
2784
+ xd = round(xd,xsig-xd.adjusted()-1) # round again in case 9 round up to 10 in the last line
2785
+
2786
+ if xd == 0:
2787
+ xexp = 0
2788
+ else:
2789
+ xexp = xd.adjusted()
2790
+
2791
+ if isinstance(x,Rational) and not isinstance(x,Integral) and x != xd:
2792
+ fstr = str(x.numerator) + '/' + str(x.denominator)
2793
+ if len(fstr) < self.max_digits + 1:
2794
+ return (style,(fstr,'',xsym))
2795
+
2796
+ if (nm is None and x == xd) or nm:
2797
+ xd = xd.normalize()
2798
+ elif nm is None:
2799
+ elip = '...'
2800
+
2606
2801
  else:
2607
- xexp = _floor(lgx)
2608
- oexp = xexp
2609
- else:
2610
- xexp = None
2611
- oexp = 0
2612
-
2613
- if u == 0 or isnan(u) or isinf(u) or (style=='x' and xsig is not None):
2614
- if isinstance(x,Rational) and not isinstance(x,Integral):
2615
- ffstr = str(x)
2616
- fstr = ffstr.split('/')[-1]
2617
- dstr = str(float(x)).split('e')[0].split('E')[0].split('.')[-1]
2618
- dstr.strip().strip('0')
2619
- if len(dstr) > 3 and len(dstr) > len(fstr) and len(ffstr) < (self.max_digits + 10):
2620
- return ('x',(str(x),'',xsym))
2621
- if xexp is None:
2622
- return ('x',(self.value._format_mantissa(fmt,x,None),'',xsym))
2623
- if xsig is not None:
2624
- if xabs > 10**(xexp+1) - 10**(xexp-xsig)/2:
2625
- xexp += 1
2626
- if (xexp is not None and
2627
- ((self.sci_notation is None and (xexp > self.sci_notation_high or xexp < self.sci_notation_low))
2628
- or self.sci_notation)):
2629
- if isinstance(x,Rational) and xexp > 0:
2630
- x = Fraction(x,10**xexp)
2802
+ ud = round(ud,nsig-ud.adjusted()-1)
2803
+ ud = round(ud,nsig-ud.adjusted()-1)
2804
+ uexp = ud.adjusted()
2805
+
2806
+ if xd.is_finite():
2807
+ try:
2808
+ if xsig is None:
2809
+ xd = xd.quantize(ud)
2810
+ else:
2811
+ xd = round(xd,xsig-xd.adjusted()-1)
2812
+ except InvalidOperation:
2813
+ xd = round(xd,self.max_digits-xd.adjusted()-1)
2814
+ xexp = xd.adjusted()
2815
+ elip = '...'
2816
+ if style not in ['x','xf','xsim','xfsim','ueq','u equals']:
2817
+ style = 'pmi'
2818
+
2819
+ if abs(xd) < abs(ud) and style not in ['x','xf','xsim','xfsim']:
2820
+ dp = xd.adjusted() - uexp
2821
+ xexp = uexp
2822
+ else:
2823
+ xexp = xd.adjusted()
2631
2824
  else:
2632
- x = x*10**(-xexp)
2633
- if xsig is not None:
2634
- xsig = xsig - 1
2635
- return ('x',(self.value._format_mantissa(fmt,x,xsig),
2636
- _format_exp(fmt,xexp),
2637
- xsym))
2638
- else:
2639
- if xsig is not None:
2640
- xsig = -xexp + xsig - 1
2641
- return ('x',(self.value._format_mantissa(fmt,x,xsig),'',xsym))
2642
- else:
2643
- # lgadd makes sure the sig figs are displayed correctly if a leading
2644
- # 9 is rounded to a 10.
2645
- lgadd = _lg10(1/(1-10**-nsig/2))+10**-16
2646
- if sim and abs(self.cisim[1]-self.cisim[0]) != 0 and not isinf(self.cisim[0]) and not isinf(self.cisim[1]) and not isnan(self.cisim[0]) and not isnan(self.cisim[1]):
2647
- xcnt = _floor(_lg10(abs((self.cisim[1]-self.cisim[0])/2))+lgadd)
2648
- try:
2649
- xcnt = _floor(_lg10(abs(_ku(self._k,u)))+lgadd)
2650
- except:
2651
- xcnt = _floor(_lg10(abs(_ku(self._k,u)))+type(u)(lgadd))
2652
- uuexp = xcnt - nsig + 1
2825
+ if style in ['concise','pm']:
2826
+ style = 'pmi'
2827
+ elif style == 'pmsim':
2828
+ style == 'pmsimi'
2653
2829
 
2654
- if xexp is not None and xexp - uuexp > self.max_digits and style in ['pm','concise']:
2655
- style = 'pmi'
2656
-
2657
- # Round x to zero if it is smaller that one count in the last
2658
- # digit of the expanded uncertainty.
2659
- if xabs < 10**uuexp/2:
2660
- x = 0
2830
+ scin = False
2831
+ if self.sci_notation is None:
2832
+ if xexp > self.sci_notation_high or xexp < self.sci_notation_low:
2833
+ scin = True
2834
+ else:
2835
+ scin = self.sci_notation
2836
+ if xexp > self.max_digits - 1:
2837
+ scin = True
2838
+ elif xexp < 0 and len(xd.as_tuple().digits) + self.sci_notation_low > self.max_digits - 1:
2839
+ scin = True
2840
+ elif style == 'concise' and xd.is_finite() and (xd.as_tuple()[2] > 0 or len(xd.as_tuple()[1]) < nsig):
2841
+ scin = True
2842
+
2843
+ if not scin:
2844
+ xexp = 0
2845
+
2846
+ if xexp == 0:
2847
+ fstr =_decimal_str(xd,dalign=0,fmt=fmt)
2848
+ else:
2849
+ fstr = _decimal_str(xd,dplace=dp,fmt=fmt)
2850
+
2851
+ xret = (fstr + elip,_format_exp(fmt,xexp),xsym)
2661
2852
 
2662
- if xabs < 10**xcnt or xexp is None:
2663
- xexp = xcnt
2853
+ if style in ['x','xf','xsim','xfsim']:
2854
+ return (style,xret)
2664
2855
 
2665
- if xabs > 10**(xexp+1) - 10**uuexp/2:
2666
- # If a leading 9 will be rounded to a 10, increment xexp by 1
2667
- xexp += 1
2668
-
2669
- ugummy = isinstance(self._U,Quantity) or (self._Ubr is not None and isinstance(self._Ubr[0],Quantity))
2670
- if ugummy and not sim:
2671
- if self._Ubr is None:
2672
- uret = [gummy(self._U)._format_xu(fmt,'x',norm,nsig,xsig=nsig)[1]]
2673
- else:
2674
- uret = [gummy(i)._format_xu(fmt,'x',norm,nsig,xsig=nsig)[1] for i in self._Ubr]
2675
- if style == 'pm' or style == 'concise':
2676
- style = 'pmi'
2677
-
2678
- if (((self.sci_notation is None and
2679
- (xexp > self.sci_notation_high or xexp < self.sci_notation_low))
2680
- or self.sci_notation)):
2681
- x = x*10**(-xexp)
2682
- xret = (self.value._format_mantissa(fmt,x,xexp-uuexp),
2683
- _format_exp(fmt,xexp),
2684
- xsym)
2685
- return tuple([style,xret] + uret)
2686
- else:
2687
- xret = (self.value._format_mantissa(fmt,x,-uuexp),'',xsym)
2688
- return tuple([style,xret] + uret)
2689
- elif ugummy and style in ['pmsim','pmsimi']:
2856
+ ugummy = isinstance(self._U,Quantity) or (self._Ubr is not None and isinstance(self._Ubr[0],Quantity))
2857
+ if ugummy:
2858
+ if sim:
2690
2859
  usm = self.Usim
2691
2860
  uret0 = gummy(usm[0],unit=self._U.unit)._format_xu(fmt,'x',norm,nsig,xsig=nsig,solidus=solidus,mulsep=mulsep)[1]
2692
2861
  uret1 = gummy(usm[1],unit=self._U.unit)._format_xu(fmt,'x',norm,nsig,xsig=nsig,solidus=solidus,mulsep=mulsep)[1]
2693
- if style == 'pmsim':
2694
- style = 'pmsimi'
2695
-
2696
- if (((self.sci_notation is None and
2697
- (xexp > self.sci_notation_high or xexp < self.sci_notation_low))
2698
- or self.sci_notation)):
2699
- x = x*10**(-xexp)
2700
- xret = (self.value._format_mantissa(fmt,x,xexp-uuexp),
2701
- _format_exp(fmt,xexp),
2702
- xsym)
2703
- return (style,xret,uret0,uret1)
2704
- else:
2705
- xret = (self.value._format_mantissa(fmt,x,-uuexp),'',xsym)
2706
- return (style,xret,uret0,uret1)
2862
+ style = 'pmsimi'
2863
+ return (style,xret,uret0,uret1)
2707
2864
  else:
2708
- if sim:
2709
- if style in ['usim','ufsim']:
2710
- u = self.usim
2711
- else:
2712
- u = (self.cisim[1] - self.cisim[0])
2713
- ub = [u]
2714
- elif self._ubreakdown is None:
2715
- u = self._U
2716
- ub = [u]
2865
+ if self._Ubr is None:
2866
+ uret = [gummy(self._U)._format_xu(fmt,'x',norm,nsig,xsig=nsig,solidus=solidus,mulsep=mulsep)[1]]
2717
2867
  else:
2718
- u = self._U
2719
- ub = self._Ubr
2720
-
2868
+ uret = [gummy(i)._format_xu(fmt,'x',norm,nsig,xsig=nsig,solidus=solidus,mulsep=mulsep)[1] for i in self._Ubr]
2869
+ if style == 'pm' or style == 'concise':
2870
+ style = 'pmi'
2871
+ return tuple([style,xret] + uret)
2721
2872
 
2722
- uabs = abs(u)
2723
- if style == 'pm' or style == 'pmsim' or uabs == 0 or isinf(uabs) or isnan(uabs):
2724
- uexp = xexp
2873
+ with localcontext(prec=nsig + 2):
2874
+ if sim:
2875
+ u = abs(_to_decimal(self.cisim[1] - self.cisim[0]))
2876
+ else:
2877
+ u = abs(_to_decimal(self._U))
2878
+
2879
+ if u.is_finite():
2880
+ if xd.is_finite():
2881
+ uu = u.quantize(xd)
2882
+ if uu == 0:
2883
+ u = round(u,nsig-u.adjusted()-1)
2884
+ u = round(u,nsig-u.adjusted()-1)
2885
+ else:
2886
+ u = uu
2725
2887
  else:
2726
- try:
2727
- uexp = _floor(_lg10(uabs)+lgadd)
2728
- except:
2729
- uexp = _floor(_lg10(uabs)+type(uabs)(lgadd))
2730
-
2731
- psn = False
2732
- if style == 'concise':
2733
- if uexp - nsig + 1 > 0 or (uexp > oexp and oexp == 0):
2734
- psn = True
2735
- if style == 'x' and uuexp - nsig + 1 > 0:
2736
- psn = True
2888
+ u = round(u,nsig-u.adjusted()-1)
2889
+ u = round(u,nsig-u.adjusted()-1)
2890
+
2891
+ if self._ubreakdown is None:
2892
+ ub = [u]
2893
+ else:
2894
+ ub = []
2895
+ for b in self._Ubr:
2896
+ bb = abs(_to_decimal(b))
2897
+ if bb.is_finite():
2898
+ if xd.is_finite():
2899
+ bb = bb.quantize(xd)
2900
+ else:
2901
+ bb = round(bb,nsig-bb.adjusted()-1)
2902
+ bb = round(bb,nsig-bb.adjusted()-1)
2903
+ ub.append(bb)
2904
+
2905
+
2906
+ uret = []
2907
+ if style == 'concise':
2908
+ for ue in ub:
2909
+ utxt = _decimal_str(ue,dplace=None,fmt=fmt)
2910
+ uret.append((utxt,'',xsym))
2911
+ elif style == 'pm':
2912
+ for ue in ub:
2913
+ utxt = _decimal_str(ue,fmt=fmt,dalign=xexp)
2914
+ uret.append((utxt,_format_exp(fmt,ue),xsym))
2915
+ else:
2916
+ if style == 'pmsim' or u == 0 or not u.is_finite():
2917
+ uexp = xexp
2918
+ else:
2919
+ uexp = u.adjusted()
2737
2920
 
2738
- if (((self.sci_notation is None and
2739
- (xexp > self.sci_notation_high or xexp < self.sci_notation_low))
2740
- or self.sci_notation) or psn):
2741
- xtxt = self.value._format_mantissa(fmt,x*10**(-xexp),xexp-uuexp)
2742
- xetxt = _format_exp(fmt,xexp)
2743
- xret = (xtxt,xetxt,xsym)
2921
+ if self.sci_notation is None:
2922
+ if uexp > self.sci_notation_high or uexp < self.sci_notation_low:
2923
+ scin = True
2924
+ else:
2925
+ scin = self.sci_notation
2926
+ if uexp > self.max_digits - 1:
2927
+ scin = True
2928
+ elif uexp < 0 and nsig + self.sci_notation_low > self.max_digits - 1:
2929
+ scin = True
2930
+
2931
+ if not scin:
2932
+ uexp = 0
2933
+
2934
+ if style in ['ueq','pmi']:
2935
+ if uexp == 0:
2936
+ for ue in ub:
2937
+ utxt = _decimal_str(ue,fmt=fmt,dalign=0)
2938
+ uret.append((utxt,_format_exp(fmt,uexp),xsym))
2744
2939
  else:
2745
- xtxt = self.value._format_mantissa(fmt,x,-uuexp)
2746
- xret = (xtxt,'',xsym)
2747
-
2748
- uret = []
2749
- if style == 'concise':
2750
2940
  for ue in ub:
2751
- utxt = self.value._format_mantissa(fmt,ue*10**(-uexp),nsig-1,parenth=True)
2752
- uret.append((utxt,'',xsym))
2753
- elif style in ['pmsim','pmsimi']:
2754
- if (((self.sci_notation is None and
2755
- (uexp > self.sci_notation_high or uexp < self.sci_notation_low))
2756
- or self.sci_notation)):
2757
- utxt0 = self.value._format_mantissa(fmt,self.Usim[0]*10**(-uexp),uexp-uuexp)
2758
- utxt1 = self.value._format_mantissa(fmt,self.Usim[1]*10**(-uexp),uexp-uuexp)
2759
- uetxt = _format_exp(fmt,uexp)
2760
- if utxt0 == utxt1:
2761
- if style == 'pmsim':
2762
- style = 'pm'
2763
- else:
2764
- style = 'pmi'
2765
- uret.append((utxt0,uetxt,xsym))
2941
+ utxt = _decimal_str(ue,fmt=fmt,dalign=uexp)
2942
+ uret.append((utxt,_format_exp(fmt,uexp),xsym))
2943
+ elif style in ['pmsim','pmsimi']:
2944
+ u0 = _to_decimal(self.Usim[0]).quantize(u)
2945
+ u1 = _to_decimal(self.Usim[1]).quantize(u)
2946
+ if uexp == 0:
2947
+ utxt0 = _decimal_str(u0,fmt=fmt,dalign=0)
2948
+ utxt1 = _decimal_str(u1,fmt=fmt,dalign=0)
2949
+ if utxt0 == utxt1:
2950
+ if style == 'pmsim':
2951
+ style = 'pm'
2766
2952
  else:
2767
- uret.append((utxt0,uetxt,xsym))
2768
- uret.append((utxt1,uetxt,xsym))
2953
+ style = 'pmi'
2954
+ uret.append((utxt0,'',xsym))
2769
2955
  else:
2770
- utxt0 = self.value._format_mantissa(fmt,self.Usim[0],-uuexp)
2771
- utxt1 = self.value._format_mantissa(fmt,self.Usim[1],-uuexp)
2772
- if utxt0 == utxt1:
2773
- if style == 'pmsim':
2774
- style = 'pm'
2775
- else:
2776
- style = 'pmi'
2777
- uret.append((utxt0,'',xsym))
2956
+ uret.append((utxt0,'',xsym))
2957
+ uret.append((utxt1,'',xsym))
2958
+ else:
2959
+ utxt0 = _decimal_str(u0,fmt=fmt,dalign=uexp)
2960
+ utxt1 = _decimal_str(u1,fmt=fmt,dalign=uexp)
2961
+ uetxt = _format_exp(fmt,uexp)
2962
+ if utxt0 == utxt1:
2963
+ if style == 'pmsim':
2964
+ style = 'pm'
2778
2965
  else:
2779
- uret.append((utxt0,'',xsym))
2780
- uret.append((utxt1,'',xsym))
2781
- elif style in ['cisim','mcisim']:
2782
- if self.cisim[0] != 0 and not isinf(self.cisim[0]) and not isnan(self.cisim[0]):
2783
- x0exp = _floor(_lg10(abs(self.cisim[0])))
2784
- else:
2785
- x0exp = 0
2786
- if (((self.sci_notation is None and
2787
- (x0exp > self.sci_notation_high or x0exp < self.sci_notation_low))
2788
- or self.sci_notation)):
2789
- ci0 = self.value._format_mantissa(fmt,self.cisim[0]*10**(-x0exp),x0exp-uuexp)
2790
- xe0txt = _format_exp(fmt,x0exp)
2791
- uret.append((ci0,xe0txt,xsym))
2966
+ style = 'pmi'
2967
+ uret.append((utxt0,uetxt,xsym))
2792
2968
  else:
2793
- uret.append((self.value._format_mantissa(fmt,self.cisim[0],-uuexp),'',xsym))
2794
-
2795
- if self.cisim[1] != 0 and not isinf(self.cisim[1]) and not isnan(self.cisim[1]):
2796
- x1exp = _floor(_lg10(abs(self.cisim[1])))
2797
- else:
2798
- x1exp = 0
2799
- if (((self.sci_notation is None and
2800
- (x1exp > self.sci_notation_high or x1exp < self.sci_notation_low))
2801
- or self.sci_notation)):
2802
- ci1 = self.value._format_mantissa(fmt,self.cisim[1]*10**(-x1exp),x1exp-uuexp)
2803
- xe1txt = _format_exp(fmt,x1exp)
2804
- uret.append((ci1,xe1txt,xsym))
2969
+ uret.append((utxt0,uetxt,xsym))
2970
+ uret.append((utxt1,uetxt,xsym))
2971
+
2972
+ elif style in ['cisim','mcisim']:
2973
+ for ci in self.cisim:
2974
+ cin = _to_decimal(ci).quantize(u)
2975
+ dp = 0
2976
+ if cin.is_finite():
2977
+ xnexp = cin.adjusted()
2978
+ if cin == 0:
2979
+ xnexp += nsig - 1
2980
+ dp = -nsig + 1
2805
2981
  else:
2806
- uret.append((self.value._format_mantissa(fmt,self.cisim[1],-uuexp),'',xsym))
2807
- else:
2808
- if style == 'ueq':
2809
- uxp = uexp - nsig + 1
2982
+ xnexp = 0
2983
+ if self.sci_notation is None:
2984
+ scin = (xnexp > self.sci_notation_high or xnexp < self.sci_notation_low)
2810
2985
  else:
2811
- uxp = uuexp
2812
- if (((self.sci_notation is None and
2813
- (uexp > self.sci_notation_high or uexp < self.sci_notation_low))
2814
- or self.sci_notation)):
2815
- for ue in ub:
2816
- utxt = self.value._format_mantissa(fmt,ue*10**(-uexp),uexp-uxp)
2817
- uetxt = _format_exp(fmt,uexp)
2818
- uret.append((utxt,uetxt,xsym))
2986
+ scin = self.sci_notation
2987
+ if cin == 0 and xnexp > nsig-1:
2988
+ scin = True
2989
+ if scin:
2990
+ cin = _decimal_str(cin,fmt=fmt,dplace=dp)
2991
+ xentxt = _format_exp(fmt,xnexp)
2992
+ uret.append((cin,xentxt,xsym))
2819
2993
  else:
2820
- for ue in ub:
2821
- utxt = self.value._format_mantissa(fmt,ue,-uxp)
2822
- uret.append((utxt,'',xsym))
2994
+ uret.append(( _decimal_str(cin,fmt=fmt,dalign=0),'',xsym))
2823
2995
 
2824
- return tuple([style,xret]+uret)
2825
-
2826
- @staticmethod
2827
- def _add_unit_sp(fmt,unit):
2828
- if unit is None or unit is one:
2829
- return ''
2830
-
2831
- if unit == '':
2832
- return ''
2833
-
2834
- if unit.startswith('\t'):
2835
- unit = unit[1:]
2836
- else:
2837
- if fmt == 'latex':
2838
- unit = r'\:' + unit
2839
- elif fmt == 'html':
2840
- unit = '&nbsp;' + unit
2841
- else:
2842
- unit = ' ' + unit
2843
-
2844
- return unit
2845
-
2846
- @staticmethod
2847
- def _p_to_str(fmt,p):
2848
- x = p*100
2849
- if fmt == 'latex':
2850
- pct = '\\%'
2851
- else:
2852
- pct = '%'
2853
- if x < 96:
2854
- return '{:.0f}'.format(x) + pct
2855
- if x < 98:
2856
- return '{:.1f}'.format(x) + pct
2857
- if x < 99.9:
2858
- return '{:.2f}'.format(x) + pct
2859
- if x < 99.99:
2860
- return '{:.3f}'.format(x) + pct
2861
- if x < 99.999:
2862
- return '{:.4f}'.format(x) + pct
2863
- if x < 99.9999:
2864
- return '{:.5f}'.format(x) + pct
2865
- return str(x) + pct
2866
-
2867
- @staticmethod
2868
- def _k_to_str(k):
2869
- return '{:.1f}'.format(k)
2870
-
2871
- @staticmethod
2872
- def _dof_to_str(dof,fmt=None):
2873
- if isinf(dof) or dof > 99:
2874
- if fmt == 'html':
2875
- return '&infin;'
2876
- if fmt == 'latex':
2877
- return r'\infty'
2878
- if fmt == 'ascii':
2879
- return 'inf'
2880
- return '\u221E'
2881
-
2882
- if isinstance(dof,Integral):
2883
- return str(dof)
2884
- return '{:.1f}'.format(dof)
2885
-
2886
- @staticmethod
2887
- def _set_covariance_matrix(gummys, matrix):
2888
- nummys = [g.value for g in gummys]
2889
- nummy._set_covariance_matrix(nummys, matrix)
2890
- for g in gummys:
2891
- g._set_U(None,None)
2892
-
2893
- @staticmethod
2894
- def _set_correlation_matrix(gummys, matrix):
2895
- nummys = [g.value for g in gummys]
2896
- nummy._set_correlation_matrix(nummys, matrix)
2996
+ return tuple([style,xret]+uret)
2897
2997
 
2898
2998
  @classmethod
2899
2999
  def create(cls,x,u=0,unit=one,dof=float('inf'),k=1,p=None,uunit=None,
@@ -2974,10 +3074,15 @@ class gummy(Quantity,metaclass=MetaGummy):
2974
3074
 
2975
3075
  return ret
2976
3076
 
2977
- if any([isinstance(v,Distribution) for v in x]):
2978
- if correlation_matrix is not None or covariance_matrix is not None:
3077
+ if correlation_matrix is not None or covariance_matrix is not None:
3078
+ if any([isinstance(v,Distribution) for v in x]):
2979
3079
  raise TypeError('Distribtuion instances may not be used in x if a correlation_matrix nor a covariance_matrix is defined')
2980
-
3080
+ if dof is not None and not _isscalar(dof):
3081
+ raise TypeError('dof cannot be set individually of a correlation of covariance matrix is specified')
3082
+ if utype is not None and not isinstance(utype,str):
3083
+ raise TypeError('utype cannot be set individually of a correlation of covariance matrix is specified')
3084
+
3085
+
2981
3086
  if covariance_matrix is not None:
2982
3087
  if correlation_matrix is not None:
2983
3088
  raise TypeError('correlation_matrix and covariance_matrix cannot both be specified')
@@ -3023,11 +3128,19 @@ class gummy(Quantity,metaclass=MetaGummy):
3023
3128
  ret = [cls(x[i],u=u[i],unit=unit[i],dof=dof[i],k=k[i],p=p[i],
3024
3129
  uunit=uunit[i],utype=utype[i],name=name[i]) for i in range(n)]
3025
3130
 
3026
- if correlation_matrix is not None:
3027
- cls._set_correlation_matrix(ret, correlation_matrix)
3028
-
3029
- if covariance_matrix is not None:
3030
- cls._set_covariance_matrix(ret, covariance_matrix)
3131
+ if correlation_matrix is not None or covariance_matrix is not None:
3132
+ x = [r.x for r in ret]
3133
+ u = [r.u for r in ret]
3134
+ nret = nummy.create(x,u=u,dof=dof[0],utype=utype[0],name=name,
3135
+ correlation_matrix=correlation_matrix,
3136
+ covariance_matrix=covariance_matrix)
3137
+
3138
+ for n,r in zip(nret,ret):
3139
+ r._value = n
3140
+ if r.uunit is None:
3141
+ r._U = _ku(r._k,r._value.u)
3142
+ else:
3143
+ r._set_U(r._k,r.uunit)
3031
3144
 
3032
3145
  return ret
3033
3146
 
@@ -3319,87 +3432,35 @@ class gummy(Quantity,metaclass=MetaGummy):
3319
3432
  return v % np.array(self)
3320
3433
 
3321
3434
  return super().__rmod__(v)
3322
-
3323
- def __eq__(self, v):
3324
- if isinstance(v,gummy):
3325
- try:
3326
- s = self.convert(v._unit)
3327
- except NoUnitConversionFoundError:
3328
- return False
3329
- return self.value == v.value
3330
-
3331
- try:
3332
- s = self.convert(one).value
3333
- except NoUnitConversionFoundError:
3334
- return False
3335
-
3336
- return s == v
3337
3435
 
3338
- def __ne__(self, v):
3339
- try:
3340
- return self < v or self > v
3341
- except IncompatibleUnitsError:
3342
- return True
3343
-
3344
- def __lt__(self, v):
3345
- if isinstance(v,gummy):
3346
- try:
3347
- s = self.convert(v.unit)
3348
- except NoUnitConversionFoundError:
3349
- raise IncompatibleUnitsError('values with incompatible units cannot be compared')
3350
- else:
3351
- if self.unit is not one:
3352
- try:
3353
- s = self.convert(one)
3354
- except NoUnitConversionFoundError:
3355
- raise IncompatibleUnitsError('values with incompatible units cannot be compared ')
3356
- else:
3357
- s = self
3358
-
3359
- df = s - v
3360
- if self._cmp_k is None:
3361
- k = self._p_method.fptok(self._cmp_p,df.dof,df.bayesian)
3362
- else:
3363
- k = self._cmp_k
3364
-
3365
- return (df.x < -k*df.u)
3366
-
3367
- def __le__(self, v):
3368
- return self == v or self < v
3369
-
3370
- def __gt__(self, v):
3371
- if isinstance(v,gummy):
3372
- try:
3373
- s = self.convert(v._unit)
3374
- except NoUnitConversionFoundError:
3375
- raise IncompatibleUnitsError('values with incompatible units cannot be compared')
3376
- else:
3377
- if self._unit is not one:
3378
- try:
3379
- s = self.convert(one)
3380
- except NoUnitConversionFoundError:
3381
- raise IncompatibleUnitsError('values with incompatible units cannot be compared')
3382
- else:
3383
- s = self
3384
-
3385
- df = s - v
3386
- if self._cmp_k is None:
3387
- k = self._p_method.fptok(self._cmp_p,df.dof,df.bayesian)
3388
- else:
3389
- k = self._cmp_k
3390
-
3391
- return (df.x > k*df.u)
3392
-
3393
- def __ge__(self, v):
3394
- return self == v or self > v
3436
+ @property
3437
+ def real(self):
3438
+ """
3439
+ returns a copy of the gummy
3440
+ """
3441
+ return self.copy(formatting=False)
3395
3442
 
3396
3443
  @property
3397
3444
  def imag(self):
3398
- if self._unit.linear:
3399
- return type(self)(0,unit=self._unit)
3445
+ if self.unit.linear:
3446
+ s = self
3447
+ else:
3448
+ s = self.tobaseunit()
3400
3449
 
3401
- return type(self)(self._unit.zero(),unit=self._unit)
3450
+ return type(self)(0,unit=s.unit)
3402
3451
 
3452
+ def conjugate(self):
3453
+ """
3454
+ returns a copy of the gummy
3455
+ """
3456
+ return self.copy(formatting=False)
3457
+
3458
+ def angle(self):
3459
+ if self.x >= 0:
3460
+ return type(self)(0)
3461
+ else:
3462
+ return type(self)(np.pi)
3463
+
3403
3464
 
3404
3465
  class jummy(immy):
3405
3466
 
@@ -3570,27 +3631,7 @@ class jummy(immy):
3570
3631
  raise ValueError('the name must be a string or a length 4 tuple of str')
3571
3632
 
3572
3633
  def get_name(self,fmt='unicode',norm=None):
3573
- if self._name is None:
3574
- return None
3575
-
3576
- if isinstance(self._name,str):
3577
- name = str(self._name).strip()
3578
- if fmt == 'html' and len(name) == 1:
3579
- return '<i>' + name + '</i>'
3580
- if fmt == 'latex' and len(name) > 1:
3581
- if norm is None:
3582
- norm = type(self).latex_norm
3583
- return norm(self.name)
3584
- return self._name
3585
-
3586
- fmt = fmt.strip().lower()
3587
- if fmt == 'unicode':
3588
- return self._name[0]
3589
- if fmt == 'html':
3590
- return self._name[1]
3591
- if fmt == 'latex':
3592
- return self._name[2]
3593
- if fmt == 'ascii':
3594
- return self._name[0]
3595
- raise ValueError('fmt "' + str(fmt) + '" is not recognized')
3634
+ if norm is None:
3635
+ norm = type(self).latex_norm
3636
+ return get_name(self._name,fmt,norm)
3596
3637