metrolopy 0.6.5__py3-none-any.whl → 1.0.0__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
95
+
96
+ def _k_to_str(k):
97
+ return '{:.1f}'.format(k)
67
98
 
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)
68
112
 
69
- class MetaGummy(MetaPrettyPrinter):
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
@@ -503,7 +563,7 @@ class gummy(Quantity,metaclass=MetaGummy):
503
563
  self._value = nummy(x.value)
504
564
  self._value._fp = self._get_p
505
565
  self._unit = x._unit
506
- self._U = self._value._u
566
+ self._U = self._value.u
507
567
  self._value._name = x._value._name
508
568
  self._k = 1
509
569
  self._pm = None
@@ -521,7 +581,7 @@ class gummy(Quantity,metaclass=MetaGummy):
521
581
  if isinstance(x,ummy):
522
582
  self._value = nummy(x)
523
583
  self._value._fp = self._get_p
524
- self._U = self._value._u
584
+ self._U = self._value.u
525
585
  self._k = 1
526
586
  self._pm = None
527
587
  self._set_k = True
@@ -575,7 +635,7 @@ class gummy(Quantity,metaclass=MetaGummy):
575
635
  u = unit.from_uunit(u,uunit)
576
636
  elif unit.is_dimensionless:
577
637
  if not uunit.is_dimensionless:
578
- raise NoUnitConversionFoundError('no conversion found for unit ' + str(uunit) + ' to one')
638
+ raise IncompatibleUnitsError('no conversion found for unit ' + str(uunit) + ' to one')
579
639
  if uunit is one:
580
640
  u = U.convert(unit).value
581
641
  else:
@@ -583,7 +643,7 @@ class gummy(Quantity,metaclass=MetaGummy):
583
643
  else:
584
644
  try:
585
645
  u = U.convert(unit).value
586
- except NoUnitConversionFoundError:
646
+ except IncompatibleUnitsError:
587
647
  # If no conversion was found for uunit to unit, see
588
648
  # if unit can be converted to one. In this case the u
589
649
  # passed to the intializer was a relative uncertainty.
@@ -592,8 +652,11 @@ class gummy(Quantity,metaclass=MetaGummy):
592
652
  if self._k != 1:
593
653
  try:
594
654
  u = u/self._k
595
- except:
596
- u = u/type(u)(self._k)
655
+ except TypeError:
656
+ if isinstance(u,Integral):
657
+ u = u/MFraction(self._k)
658
+ else:
659
+ u = u/type(u)(self._k)
597
660
 
598
661
  self._value = nummy(x,u=u,dof=dof,utype=utype,name=name)
599
662
  self._value._fp = self._get_p
@@ -617,7 +680,7 @@ class gummy(Quantity,metaclass=MetaGummy):
617
680
  sometimes called the "1-sigma" uncertainty. The This property is read-only
618
681
  and returns a float.
619
682
  """
620
- return self._value._u
683
+ return self._value.u
621
684
 
622
685
  @property
623
686
  def dof(self):
@@ -628,15 +691,23 @@ class gummy(Quantity,metaclass=MetaGummy):
628
691
  gummy is based on. If the gummy was created as the result of an
629
692
  operation between two or more other gummys, then the dof is the effective
630
693
  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.
694
+ approximation.
637
695
  """
638
696
 
639
697
  return self.value.dof
698
+
699
+ @property
700
+ def isindependent(self):
701
+ """
702
+ `bool`, read-only
703
+
704
+ Returns `True` if the gummy is an independent variable. That is the
705
+ ummy has u > 0 and was not correlated with any other gummy's when it was
706
+ created or is perfectly correlated or anti-correlated (correlation
707
+ coefficeint 1 or -1) with such an ummy.'
708
+ """
709
+
710
+ return self.value.isindependent
640
711
 
641
712
  @property
642
713
  def U(self):
@@ -735,16 +806,22 @@ class gummy(Quantity,metaclass=MetaGummy):
735
806
  if self._unit.linear:
736
807
  try:
737
808
  if self.unit.is_dimensionless:
738
- raise NoUnitConversionFoundError()
809
+ raise IncompatibleUnitsError()
739
810
  return Quantity(_ku(k,u),unit=self.unit).convert(unit)
740
811
 
741
- except NoUnitConversionFoundError:
812
+ except IncompatibleUnitsError:
742
813
  try:
743
- r = abs(_ku(k,u)/self.x)
814
+ try:
815
+ r = abs(_ku(k,u)/self.x)
816
+ except TypeError:
817
+ if isinstance(self.x,Integral):
818
+ r = abs(MFraction.fromnum(_ku(k,u))/self.x)
819
+ else:
820
+ r = abs(type(self.x)(_ku(k,u))/self.x)
744
821
  return Quantity(r).convert(unit)
745
822
  except ZeroDivisionError:
746
823
  if not Unit.unit(unit).is_dimensionless:
747
- raise NoUnitConversionFoundError('no conversion found from unit ' + str(unit) + ' to one')
824
+ raise IncompatibleUnitsError('no conversion found from unit ' + str(unit) + ' to one')
748
825
  return Quantity(float('inf'),unit=unit)
749
826
  else:
750
827
  return Quantity(self.unit.to_uunit(_ku(k,u),unit),unit)
@@ -896,7 +973,7 @@ class gummy(Quantity,metaclass=MetaGummy):
896
973
  Returns `False` if the owning gummy was created from a operation involving
897
974
  other gummys or has zero uncertainty and `True` otherwise.
898
975
  """
899
- return self.value.independent
976
+ return self.value.isindependent
900
977
 
901
978
  @property
902
979
  def name(self):
@@ -1239,35 +1316,18 @@ class gummy(Quantity,metaclass=MetaGummy):
1239
1316
  def finfo(self):
1240
1317
  return self.value.finfo
1241
1318
 
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
1319
  @property
1262
1320
  def utype(self):
1263
1321
  """
1264
- `str` or `None`
1265
-
1266
- An arbitrary string value labeling the uncertainty type.
1322
+ `str`, `None` or a list containing strings and possibly `None`
1323
+
1324
+ An arbitrary string value labeling the uncertainty type or or a
1325
+ list of types if the gummy was constructed from independent
1326
+ variables with different utypes.
1267
1327
  """
1268
1328
  return self.value.utype
1269
1329
 
1270
- def ufrom(self,x,sim=False):
1330
+ def ufrom(self,x):
1271
1331
  """
1272
1332
  Gets the standard uncertainty contributed from particular gummys
1273
1333
  or utypes if all other free variables are held fixed.
@@ -1291,18 +1351,7 @@ class gummy(Quantity,metaclass=MetaGummy):
1291
1351
  >>> d.ufrom('A')
1292
1352
  0.53851648071345048
1293
1353
  """
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)
1354
+ return self.value.ufrom(x)
1306
1355
 
1307
1356
  def doffrom(self,x):
1308
1357
  """
@@ -1329,15 +1378,68 @@ class gummy(Quantity,metaclass=MetaGummy):
1329
1378
  >>> d.doffrom('A')
1330
1379
  9.0932962619709627
1331
1380
  """
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
1381
  return self.value.doffrom(x)
1382
+
1383
+ def ufromsim(self,x):
1384
+ """
1385
+ Gets the standard deviation of the Monte-Carlo data only allowing the
1386
+ independent variables in `x` to vary. Independent istributions not in
1387
+ `x` are held fixed. `sim` or `simulate` must be called to generate
1388
+ Monte-Carlo data before calling this method.
1389
+
1390
+ Parameters
1391
+ ----------
1392
+ x: `gummy`, `str`, or array_like
1393
+ A gummy, a string referencing a utype or a list containing
1394
+ gummys and strings.
1395
+
1396
+ Returns
1397
+ -------
1398
+ `float`
1399
+ """
1400
+ if _isscalar(x):
1401
+ x = [x]
1402
+ x = [i.value if isinstance(i,Quantity) else i for i in x]
1403
+
1404
+ return self.value.ufromsim(x)
1405
+
1406
+ def datafrom(self,x,save=True):
1407
+ """
1408
+ Recomputes the Monte-Carlo `simdata `with only the varaibles in `x`
1409
+ allowed to vary. `sim` or `simulate` must be called to generate
1410
+ Monte-Carlo data before calling this method. This method cannot be
1411
+ called with save == `True` from from a gummy representing an independent
1412
+ variable (that is from a gummy not created by by mathematical operations
1413
+ between two or more other gummy's).
1414
+
1415
+ Parameters
1416
+ ----------
1417
+ ufrom: list containing `gummy` or `str`
1418
+ all independent gummys not in the list or having a utype
1419
+ not in the list are held fixed at their `.x` value
1420
+
1421
+ save: If `save` is `True` the recomputed data is stored in the `simdata`
1422
+ attribute and `None` is returned. If `save` is `False` then the
1423
+ recomputed data is returned and the `simdata` attribute is not
1424
+ overwritten.
1425
+
1426
+ Returns
1427
+ -------
1428
+ 'numpy.array' if `save` is `False`, otherwise returns `None`
1429
+
1430
+ Raises
1431
+ ------
1432
+ `NoSimulatedDataError`:
1433
+ if no simulated data is available from a call to
1434
+ `Distribution.simulate`.
1435
+ `RuntimeError`:
1436
+ if this method is called from an independent `gummy`
1437
+ """
1438
+ if _isscalar(x):
1439
+ x = [x]
1440
+ x = [i.value if isinstance(i,Quantity) else x for i in x]
1441
+
1442
+ return self.value.datafrom(x,save=save)
1341
1443
 
1342
1444
  @property
1343
1445
  def style(self):
@@ -1570,20 +1672,23 @@ class gummy(Quantity,metaclass=MetaGummy):
1570
1672
  return self.value.max_digits
1571
1673
  @max_digits.setter
1572
1674
  def max_digits(self,v):
1675
+ v = int(v)
1676
+ if v < 0:
1677
+ raise ValueError('max_digits must be >= 0')
1573
1678
  self.value.max_digits = v
1574
1679
 
1575
1680
 
1576
- def copy(self,formatting=True,tofloat=False):
1681
+ def copy(self,formatting=True,totype=None):
1577
1682
  """
1578
1683
  Returns a copy of the gummy. If the `formatting` parameter is
1579
1684
  `True` the display formatting information will be copied and if
1580
1685
  `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
1686
+ new gummy. The default for `formatting` is `True`. If 'totype`
1687
+ is defined the x and u properties will be converted to type `totype`
1583
1688
  before copying.
1584
1689
  """
1585
1690
 
1586
- r = type(self)(self._value.copy(formatting=formatting,tofloat=tofloat),
1691
+ r = type(self)(self._value.copy(formatting=formatting,totype=totype),
1587
1692
  unit = self._unit)
1588
1693
  r._old = self._old
1589
1694
 
@@ -1603,7 +1708,7 @@ class gummy(Quantity,metaclass=MetaGummy):
1603
1708
  r._k = self._k
1604
1709
  r._pm = self._pm
1605
1710
  r._set_k = self._set_k
1606
- if tofloat:
1711
+ if totype is not None:
1607
1712
  r._set_U(unit=self.uunit)
1608
1713
  else:
1609
1714
  r._U = self._U
@@ -1632,7 +1737,7 @@ class gummy(Quantity,metaclass=MetaGummy):
1632
1737
  instance `one`.
1633
1738
  """
1634
1739
  return self*Unit.unit(unit)/self.unit
1635
-
1740
+
1636
1741
  @staticmethod
1637
1742
  def simulate(gummys,n=100000,ufrom=None):
1638
1743
  """
@@ -1644,6 +1749,9 @@ class gummy(Quantity,metaclass=MetaGummy):
1644
1749
  ----------
1645
1750
  n: `int` > 0, optional
1646
1751
  The number of samples to generate. The default value is 100000.
1752
+
1753
+ gummys: A list or array of `gummy` for which to generate the Monte-Carlo
1754
+ data.
1647
1755
 
1648
1756
  ufrom: `None`, `gummy`, `str` or array_like
1649
1757
  If this is not `None`, then only the gummys referenced here will be
@@ -1652,15 +1760,17 @@ class gummy(Quantity,metaclass=MetaGummy):
1652
1760
  a list containing gummys and strings. The default value is `None`.
1653
1761
  """
1654
1762
  if ufrom is not None:
1655
- ufrom = ummy._toummylist(ufrom)
1763
+ if isinstance(ufrom,(Quantity,ummy,str,Distribution)):
1764
+ ufrom = [ufrom]
1765
+ ufrom = [g.value if isinstance(g,Quantity) else g for g in ufrom]
1656
1766
  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)
1767
+ return nummy.simulate(gummys,n=n,ufrom=ufrom)
1659
1768
 
1660
1769
  def sim(self,n=100000,ufrom=None):
1661
1770
  """
1662
- Generates Monte-Carlo data for this gummy. Calling this method
1663
- erases previously generated Monte-Carlo data for all gummys, so use the
1771
+ Generates Monte-Carlo data for this gummy (as well as for any gummys
1772
+ that this gummy depends on). Calling this method erases previously
1773
+ generated Monte-Carlo data for all gummys, so use the
1664
1774
  `gummy.simulate()` staticmethod if you need Monte-Carlo data for
1665
1775
  several gummys simultaneously.
1666
1776
 
@@ -1675,9 +1785,20 @@ class gummy(Quantity,metaclass=MetaGummy):
1675
1785
  mean values. This can be a gummy, a string referencing a utype or
1676
1786
  a list containing gummys and strings. The default value is `None`.
1677
1787
  """
1678
- if ufrom is not None:
1679
- ufrom = ummy._toummylist(ufrom)
1680
1788
  return gummy.simulate([self],n,ufrom)
1789
+
1790
+ @staticmethod
1791
+ def clear_all():
1792
+ """
1793
+ Clears Monte-Carlo data from all existing gummys.
1794
+ """
1795
+ Distribution.clear_all()
1796
+
1797
+ def clear(self):
1798
+ """
1799
+ Clears the gummys Monte-Carlo data.
1800
+ """
1801
+ self.value.clear()
1681
1802
 
1682
1803
  @classmethod
1683
1804
  def _plotlabel(cls,g,symbol=None,exponent=0,math=None,norm=None,
@@ -1695,13 +1816,17 @@ class gummy(Quantity,metaclass=MetaGummy):
1695
1816
  else:
1696
1817
  slashaxis = gummy.slashaxis
1697
1818
 
1698
- try:
1699
- name = g.name
1700
- except:
1701
- name = None
1702
-
1703
- if name == '':
1704
- name = None
1819
+ if g is None:
1820
+ name = ''
1821
+ else:
1822
+ try:
1823
+ name = g.get_name(fmt='latex',norm=norm)
1824
+ if name is None:
1825
+ name = norm('value')
1826
+ except:
1827
+ name = str(g)
1828
+ if len(name) > 1:
1829
+ name = norm(name.strip())
1705
1830
 
1706
1831
  if symbol is None and isinstance(g,gummy) and g.unit is not one:
1707
1832
  unit = g.unit
@@ -1713,16 +1838,7 @@ class gummy(Quantity,metaclass=MetaGummy):
1713
1838
  if symbol == '':
1714
1839
  symbol = None
1715
1840
 
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()
1841
+ xl = name
1726
1842
  if symbol is not None:
1727
1843
  if xl != '':
1728
1844
  if slashaxis:
@@ -1847,8 +1963,19 @@ class gummy(Quantity,metaclass=MetaGummy):
1847
1963
  title1 += self.tostring(fmt='latex',style='usim',norm=norm)
1848
1964
  title1 = math(title1)
1849
1965
  title = title0 + '\n' + title1
1966
+
1967
+ if xlabel is not None:
1968
+ plot_options['xlabel'] = xlabel
1969
+ if title is not None:
1970
+ plot_options['title'] = title
1971
+
1972
+ ci = None
1973
+ if 'range' not in plot_options and ci_marker:
1974
+ ci = g.cisim
1975
+ a = (ci[1] - ci[0])/3
1976
+ plot_options['range'] = (ci[0] - a,ci[1] + a)
1850
1977
 
1851
- self.value.hist(xlabel=xlabel,title=title,hold=True,**plot_options)
1978
+ self.value.hist(hold=True,**plot_options)
1852
1979
 
1853
1980
  if mean_marker:
1854
1981
  if 'linewidth' not in mean_marker_options and 'lw' not in mean_marker_options:
@@ -1869,7 +1996,8 @@ class gummy(Quantity,metaclass=MetaGummy):
1869
1996
  if 'zorder' not in ci_marker_options:
1870
1997
  ci_marker_options['zorder'] = 3
1871
1998
 
1872
- ci = g.cisim
1999
+ if ci is None:
2000
+ ci = g.cisim
1873
2001
  plt.axvline(ci[0],**ci_marker_options)
1874
2002
  plt.axvline(ci[1],**ci_marker_options)
1875
2003
 
@@ -2229,16 +2357,16 @@ class gummy(Quantity,metaclass=MetaGummy):
2229
2357
  return('??')
2230
2358
  else:
2231
2359
  try:
2232
- return(str(self.name).strip() + ' = ' + str(self.x) + '{' + str(self.u) + '}' + '??')
2360
+ return(str(self.get_name()).strip() + ' = ' + str(self.x) + '{' + str(self.u) + '}' + '??')
2233
2361
  except:
2234
2362
  try:
2235
- return(str(self.name).strip() + ' = ' + str(self.x) + '{??}')
2363
+ return(str(self.get_name()).strip() + ' = ' + str(self.x) + '{??}')
2236
2364
  except:
2237
2365
  try:
2238
- return(str(self.name).strip() + ' = ??{' + str(self.u) + '}')
2366
+ return(str(self.get_name()).strip() + ' = ??{' + str(self.u) + '}')
2239
2367
  except:
2240
2368
  try:
2241
- return(str(self.name).strip() + ' = ' + '??')
2369
+ return(str(self.get_name()).strip() + ' = ' + '??')
2242
2370
  except:
2243
2371
  return('??')
2244
2372
 
@@ -2335,6 +2463,8 @@ class gummy(Quantity,metaclass=MetaGummy):
2335
2463
  txt = txt.strip()
2336
2464
  return txt
2337
2465
  elif style == 'uunit':
2466
+ if len(v) < 3 or len(v[2]) < 3 or v[2][2] is None:
2467
+ return ''
2338
2468
  return v[2][2]
2339
2469
  elif style == 'ueq':
2340
2470
  if self._ubreakdown is None or len(self._ubreakdown) != len(v) - 2:
@@ -2357,7 +2487,16 @@ class gummy(Quantity,metaclass=MetaGummy):
2357
2487
  else:
2358
2488
  txt = v[1][0] + v[1][1] + v[1][2]
2359
2489
  for i,t in enumerate(v[2:]):
2360
- b = str(self._ubreakdown[i])
2490
+ b = self._ubreakdown[i]
2491
+ if not isinstance(b,str):
2492
+ if isinstance(b,gummy):
2493
+ nm = b.get_name(fmt=fmt,norm=norm)
2494
+ if nm is not None:
2495
+ b = nm
2496
+ else:
2497
+ b = str(i+1)
2498
+ else:
2499
+ b = str(i+1)
2361
2500
  if fmt == 'html':
2362
2501
  if i == 0:
2363
2502
  pm = ' with <i>u</i><sub>'+b+'</sub>&nbsp;=&nbsp;'
@@ -2499,9 +2638,9 @@ class gummy(Quantity,metaclass=MetaGummy):
2499
2638
  else:
2500
2639
  k = self.k
2501
2640
  if fmt == 'html':
2502
- itxt += '<i>k</i>&nbsp;=&nbsp;' + gummy._k_to_str(k)
2641
+ itxt += '<i>k</i>&nbsp;=&nbsp;' + _k_to_str(k)
2503
2642
  else:
2504
- itxt += 'k = ' + gummy._k_to_str(k)
2643
+ itxt += 'k = ' + _k_to_str(k)
2505
2644
 
2506
2645
  txt += itxt
2507
2646
 
@@ -2518,7 +2657,7 @@ class gummy(Quantity,metaclass=MetaGummy):
2518
2657
  else:
2519
2658
  itxt0 = ' with'
2520
2659
 
2521
- ltxt = gummy._p_to_str(fmt,p)
2660
+ ltxt = _p_to_str(fmt,p)
2522
2661
  if ltxt[0] == '1' or ltxt[0] == '8':
2523
2662
  itxt0 += ' an '
2524
2663
  itxt1 = ' ' + self._p_method.text
@@ -2541,12 +2680,12 @@ class gummy(Quantity,metaclass=MetaGummy):
2541
2680
  itxt = ' with '
2542
2681
 
2543
2682
  if fmt == 'html':
2544
- itxt += '<i>&nu;</i>&nbsp;=&nbsp;' + gummy._dof_to_str(self.dof,fmt)
2683
+ itxt += '<i>&nu;</i>&nbsp;=&nbsp;' + _dof_to_str(self.dof,fmt)
2545
2684
  elif fmt == 'latex':
2546
2685
  itxt = norm(itxt)
2547
- itxt += r'\nu = ' + gummy._dof_to_str(self.dof,fmt)
2686
+ itxt += r'\nu = ' + _dof_to_str(self.dof,fmt)
2548
2687
  else:
2549
- itxt += gummy._dof_to_str(self.dof) + ' degrees of freedom'
2688
+ itxt += _dof_to_str(self.dof) + ' degrees of freedom'
2550
2689
 
2551
2690
  txt += itxt
2552
2691
 
@@ -2562,9 +2701,13 @@ class gummy(Quantity,metaclass=MetaGummy):
2562
2701
  def _format_xu(self,fmt,style,norm,nsig,xsig=None,solidus=None,mulsep=None):
2563
2702
  if style is None:
2564
2703
  style = self.style
2565
-
2704
+
2566
2705
  if nsig is None:
2567
2706
  nsig = self.nsig
2707
+ if nsig > self.max_digits - 2:
2708
+ nsig = self.max_digits - 2
2709
+ if nsig < 1:
2710
+ nsig = 1
2568
2711
 
2569
2712
  if solidus is None:
2570
2713
  solidus = self.solidus
@@ -2572,6 +2715,18 @@ class gummy(Quantity,metaclass=MetaGummy):
2572
2715
  if mulsep is None:
2573
2716
  mulsep = self.mulsep
2574
2717
 
2718
+ if style in ['u','uf']:
2719
+ if isinstance(self._U,Quantity):
2720
+ un = self._U.unit
2721
+ u = self._U.value
2722
+ else:
2723
+ un = self.unit
2724
+ u = self._U
2725
+ if style == 'uf':
2726
+ un = 1
2727
+ uret = gummy(u,unit=un)._format_xu(fmt,'x',norm,nsig,xsig=nsig,solidus=solidus,mulsep=mulsep)[1]
2728
+ return (style,('','',''),uret)
2729
+
2575
2730
  if style in ['pmsim','cisim','pmsimi','mcisim','xsim','xfsim','usim','ufsim']:
2576
2731
  sim = True
2577
2732
  if self.simdata is None:
@@ -2582,318 +2737,262 @@ class gummy(Quantity,metaclass=MetaGummy):
2582
2737
  else:
2583
2738
  sim = False
2584
2739
  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
2740
+
2741
+ if style in ['usim','ufsim']:
2742
+ if isinstance(self._U,Quantity):
2743
+ un = self._U.unit
2744
+ else:
2745
+ un = self.unit
2746
+ u = self.usim
2747
+ if style == 'ufsim':
2748
+ un = 1
2749
+ uret = gummy(u,unit=un)._format_xu(fmt,'x',norm,nsig,xsig=nsig,solidus=solidus,mulsep=mulsep)[1]
2750
+ return (style,('','',''),uret)
2592
2751
 
2593
- xsym = gummy._add_unit_sp(fmt,self.unit.tostring(fmt=fmt,solidus=solidus,mulsep=mulsep))
2752
+ xsym = _add_unit_sp(fmt,self.unit.tostring(fmt=fmt,solidus=solidus,mulsep=mulsep,strip=False))
2594
2753
 
2595
2754
  if sim:
2596
2755
  x = self.xsim
2597
2756
  else:
2598
2757
  x = self.x
2599
-
2600
- xabs = abs(x)
2601
2758
 
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
2759
+ xd = _to_decimal(x,max_digits=self.max_digits)
2760
+ with localcontext(prec=nsig + 2):
2761
+ ud = _to_decimal(u)*_to_decimal(self._k)
2762
+
2763
+ elip = ''
2764
+ uexp = 0
2765
+ xexp = 0
2766
+ dp = 0
2767
+ with localcontext(prec=self.max_digits):
2768
+ if ud == 0 or not ud.is_finite():
2769
+ if style != 'xf':
2770
+ style = 'x'
2771
+
2772
+ if xd.is_finite():
2773
+ nm = None
2774
+ if xsig is None:
2775
+ xsig = self.max_digits
2776
+ if self.finfo is not None and self.finfo.precision > 0 and self.finfo.precision <= xsig:
2777
+ xsig = self.finfo.precision
2778
+ nm = True
2779
+ else:
2780
+ nm = False
2781
+
2782
+ xd = round(xd,xsig-xd.adjusted()-1)
2783
+ xd = round(xd,xsig-xd.adjusted()-1) # round again in case 9 round up to 10 in the last line
2784
+
2785
+ if xd == 0:
2786
+ xexp = 0
2787
+ else:
2788
+ xexp = xd.adjusted()
2789
+
2790
+ if isinstance(x,Rational) and not isinstance(x,Integral) and x != xd:
2791
+ fstr = str(x.numerator) + '/' + str(x.denominator)
2792
+ if len(fstr) < self.max_digits + 1:
2793
+ return (style,(fstr,'',xsym))
2794
+
2795
+ if (nm is None and x == xd) or nm:
2796
+ xd = xd.normalize()
2797
+ elif nm is None:
2798
+ elip = '...'
2799
+
2606
2800
  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)
2801
+ ud = round(ud,nsig-ud.adjusted()-1)
2802
+ ud = round(ud,nsig-ud.adjusted()-1)
2803
+ uexp = ud.adjusted()
2804
+
2805
+ if xd.is_finite():
2806
+ try:
2807
+ if xsig is None:
2808
+ xd = xd.quantize(ud)
2809
+ else:
2810
+ xd = round(xd,xsig-xd.adjusted()-1)
2811
+ except InvalidOperation:
2812
+ xd = round(xd,self.max_digits-xd.adjusted()-1)
2813
+ xexp = xd.adjusted()
2814
+ elip = '...'
2815
+ if style not in ['x','xf','xsim','xfsim','ueq','u equals']:
2816
+ style = 'pmi'
2817
+
2818
+ if abs(xd) < abs(ud) and style not in ['x','xf','xsim','xfsim']:
2819
+ dp = xd.adjusted() - uexp
2820
+ xexp = uexp
2821
+ else:
2822
+ xexp = xd.adjusted()
2631
2823
  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
2824
+ if style in ['concise','pm']:
2825
+ style = 'pmi'
2826
+ elif style == 'pmsim':
2827
+ style == 'pmsimi'
2653
2828
 
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
2829
+ scin = False
2830
+ if self.sci_notation is None:
2831
+ if xexp > self.sci_notation_high or xexp < self.sci_notation_low:
2832
+ scin = True
2833
+ else:
2834
+ scin = self.sci_notation
2835
+ if xexp > self.max_digits - 1:
2836
+ scin = True
2837
+ elif xexp < 0 and len(xd.as_tuple().digits) + self.sci_notation_low > self.max_digits - 1:
2838
+ scin = True
2839
+ elif style == 'concise' and xd.is_finite() and (xd.as_tuple()[2] > 0 or len(xd.as_tuple()[1]) < nsig):
2840
+ scin = True
2841
+
2842
+ if not scin:
2843
+ xexp = 0
2844
+
2845
+ if xexp == 0:
2846
+ fstr =_decimal_str(xd,dalign=0,fmt=fmt)
2847
+ else:
2848
+ fstr = _decimal_str(xd,dplace=dp,fmt=fmt)
2849
+
2850
+ xret = (fstr + elip,_format_exp(fmt,xexp),xsym)
2661
2851
 
2662
- if xabs < 10**xcnt or xexp is None:
2663
- xexp = xcnt
2852
+ if style in ['x','xf','xsim','xfsim']:
2853
+ return (style,xret)
2664
2854
 
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']:
2855
+ ugummy = isinstance(self._U,Quantity) or (self._Ubr is not None and isinstance(self._Ubr[0],Quantity))
2856
+ if ugummy:
2857
+ if sim:
2690
2858
  usm = self.Usim
2691
2859
  uret0 = gummy(usm[0],unit=self._U.unit)._format_xu(fmt,'x',norm,nsig,xsig=nsig,solidus=solidus,mulsep=mulsep)[1]
2692
2860
  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)
2861
+ style = 'pmsimi'
2862
+ return (style,xret,uret0,uret1)
2707
2863
  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]
2864
+ if self._Ubr is None:
2865
+ uret = [gummy(self._U)._format_xu(fmt,'x',norm,nsig,xsig=nsig,solidus=solidus,mulsep=mulsep)[1]]
2717
2866
  else:
2718
- u = self._U
2719
- ub = self._Ubr
2720
-
2867
+ uret = [gummy(i)._format_xu(fmt,'x',norm,nsig,xsig=nsig,solidus=solidus,mulsep=mulsep)[1] for i in self._Ubr]
2868
+ if style == 'pm' or style == 'concise':
2869
+ style = 'pmi'
2870
+ return tuple([style,xret] + uret)
2721
2871
 
2722
- uabs = abs(u)
2723
- if style == 'pm' or style == 'pmsim' or uabs == 0 or isinf(uabs) or isnan(uabs):
2724
- uexp = xexp
2872
+ with localcontext(prec=nsig + 2):
2873
+ if sim:
2874
+ u = abs(_to_decimal(self.cisim[1] - self.cisim[0]))
2875
+ else:
2876
+ u = abs(_to_decimal(self._U))
2877
+
2878
+ if u.is_finite():
2879
+ if xd.is_finite():
2880
+ uu = u.quantize(xd)
2881
+ if uu == 0:
2882
+ u = round(u,nsig-u.adjusted()-1)
2883
+ u = round(u,nsig-u.adjusted()-1)
2884
+ else:
2885
+ u = uu
2725
2886
  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
2887
+ u = round(u,nsig-u.adjusted()-1)
2888
+ u = round(u,nsig-u.adjusted()-1)
2889
+
2890
+ if self._ubreakdown is None:
2891
+ ub = [u]
2892
+ else:
2893
+ ub = []
2894
+ for b in self._Ubr:
2895
+ bb = abs(_to_decimal(b))
2896
+ if bb.is_finite():
2897
+ if xd.is_finite():
2898
+ bb = bb.quantize(xd)
2899
+ else:
2900
+ bb = round(bb,nsig-bb.adjusted()-1)
2901
+ bb = round(bb,nsig-bb.adjusted()-1)
2902
+ ub.append(bb)
2903
+
2904
+
2905
+ uret = []
2906
+ if style == 'concise':
2907
+ for ue in ub:
2908
+ utxt = _decimal_str(ue,dplace=None,fmt=fmt)
2909
+ uret.append((utxt,'',xsym))
2910
+ elif style == 'pm':
2911
+ for ue in ub:
2912
+ utxt = _decimal_str(ue,fmt=fmt,dalign=xexp)
2913
+ uret.append((utxt,_format_exp(fmt,ue),xsym))
2914
+ else:
2915
+ if style == 'pmsim' or u == 0 or not u.is_finite():
2916
+ uexp = xexp
2917
+ else:
2918
+ uexp = u.adjusted()
2737
2919
 
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)
2920
+ if self.sci_notation is None:
2921
+ if uexp > self.sci_notation_high or uexp < self.sci_notation_low:
2922
+ scin = True
2923
+ else:
2924
+ scin = self.sci_notation
2925
+ if uexp > self.max_digits - 1:
2926
+ scin = True
2927
+ elif uexp < 0 and nsig + self.sci_notation_low > self.max_digits - 1:
2928
+ scin = True
2929
+
2930
+ if not scin:
2931
+ uexp = 0
2932
+
2933
+ if style in ['ueq','pmi']:
2934
+ if uexp == 0:
2935
+ for ue in ub:
2936
+ utxt = _decimal_str(ue,fmt=fmt,dalign=0)
2937
+ uret.append((utxt,_format_exp(fmt,uexp),xsym))
2744
2938
  else:
2745
- xtxt = self.value._format_mantissa(fmt,x,-uuexp)
2746
- xret = (xtxt,'',xsym)
2747
-
2748
- uret = []
2749
- if style == 'concise':
2750
2939
  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))
2940
+ utxt = _decimal_str(ue,fmt=fmt,dalign=uexp)
2941
+ uret.append((utxt,_format_exp(fmt,uexp),xsym))
2942
+ elif style in ['pmsim','pmsimi']:
2943
+ u0 = _to_decimal(self.Usim[0]).quantize(u)
2944
+ u1 = _to_decimal(self.Usim[1]).quantize(u)
2945
+ if uexp == 0:
2946
+ utxt0 = _decimal_str(u0,fmt=fmt,dalign=0)
2947
+ utxt1 = _decimal_str(u1,fmt=fmt,dalign=0)
2948
+ if utxt0 == utxt1:
2949
+ if style == 'pmsim':
2950
+ style = 'pm'
2766
2951
  else:
2767
- uret.append((utxt0,uetxt,xsym))
2768
- uret.append((utxt1,uetxt,xsym))
2952
+ style = 'pmi'
2953
+ uret.append((utxt0,'',xsym))
2769
2954
  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))
2955
+ uret.append((utxt0,'',xsym))
2956
+ uret.append((utxt1,'',xsym))
2957
+ else:
2958
+ utxt0 = _decimal_str(u0,fmt=fmt,dalign=uexp)
2959
+ utxt1 = _decimal_str(u1,fmt=fmt,dalign=uexp)
2960
+ uetxt = _format_exp(fmt,uexp)
2961
+ if utxt0 == utxt1:
2962
+ if style == 'pmsim':
2963
+ style = 'pm'
2778
2964
  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))
2965
+ style = 'pmi'
2966
+ uret.append((utxt0,uetxt,xsym))
2792
2967
  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))
2968
+ uret.append((utxt0,uetxt,xsym))
2969
+ uret.append((utxt1,uetxt,xsym))
2970
+
2971
+ elif style in ['cisim','mcisim']:
2972
+ for ci in self.cisim:
2973
+ cin = _to_decimal(ci).quantize(u)
2974
+ dp = 0
2975
+ if cin.is_finite():
2976
+ xnexp = cin.adjusted()
2977
+ if cin == 0:
2978
+ xnexp += nsig - 1
2979
+ dp = -nsig + 1
2805
2980
  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
2981
+ xnexp = 0
2982
+ if self.sci_notation is None:
2983
+ scin = (xnexp > self.sci_notation_high or xnexp < self.sci_notation_low)
2810
2984
  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))
2985
+ scin = self.sci_notation
2986
+ if cin == 0 and xnexp > nsig-1:
2987
+ scin = True
2988
+ if scin:
2989
+ cin = _decimal_str(cin,fmt=fmt,dplace=dp)
2990
+ xentxt = _format_exp(fmt,xnexp)
2991
+ uret.append((cin,xentxt,xsym))
2819
2992
  else:
2820
- for ue in ub:
2821
- utxt = self.value._format_mantissa(fmt,ue,-uxp)
2822
- uret.append((utxt,'',xsym))
2993
+ uret.append(( _decimal_str(cin,fmt=fmt,dalign=0),'',xsym))
2823
2994
 
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)
2995
+ return tuple([style,xret]+uret)
2897
2996
 
2898
2997
  @classmethod
2899
2998
  def create(cls,x,u=0,unit=one,dof=float('inf'),k=1,p=None,uunit=None,
@@ -2974,10 +3073,15 @@ class gummy(Quantity,metaclass=MetaGummy):
2974
3073
 
2975
3074
  return ret
2976
3075
 
2977
- if any([isinstance(v,Distribution) for v in x]):
2978
- if correlation_matrix is not None or covariance_matrix is not None:
3076
+ if correlation_matrix is not None or covariance_matrix is not None:
3077
+ if any([isinstance(v,Distribution) for v in x]):
2979
3078
  raise TypeError('Distribtuion instances may not be used in x if a correlation_matrix nor a covariance_matrix is defined')
2980
-
3079
+ if dof is not None and not _isscalar(dof):
3080
+ raise TypeError('dof cannot be set individually of a correlation of covariance matrix is specified')
3081
+ if utype is not None and not isinstance(utype,str):
3082
+ raise TypeError('utype cannot be set individually of a correlation of covariance matrix is specified')
3083
+
3084
+
2981
3085
  if covariance_matrix is not None:
2982
3086
  if correlation_matrix is not None:
2983
3087
  raise TypeError('correlation_matrix and covariance_matrix cannot both be specified')
@@ -3023,11 +3127,19 @@ class gummy(Quantity,metaclass=MetaGummy):
3023
3127
  ret = [cls(x[i],u=u[i],unit=unit[i],dof=dof[i],k=k[i],p=p[i],
3024
3128
  uunit=uunit[i],utype=utype[i],name=name[i]) for i in range(n)]
3025
3129
 
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)
3130
+ if correlation_matrix is not None or covariance_matrix is not None:
3131
+ x = [r.x for r in ret]
3132
+ u = [r.u for r in ret]
3133
+ nret = nummy.create(x,u=u,dof=dof[0],utype=utype[0],name=name,
3134
+ correlation_matrix=correlation_matrix,
3135
+ covariance_matrix=covariance_matrix)
3136
+
3137
+ for n,r in zip(nret,ret):
3138
+ r._value = n
3139
+ if r.uunit is None:
3140
+ r._U = _ku(r._k,r._value.u)
3141
+ else:
3142
+ r._set_U(r._k,r.uunit)
3031
3143
 
3032
3144
  return ret
3033
3145
 
@@ -3319,87 +3431,35 @@ class gummy(Quantity,metaclass=MetaGummy):
3319
3431
  return v % np.array(self)
3320
3432
 
3321
3433
  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
3434
 
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
3435
+ @property
3436
+ def real(self):
3437
+ """
3438
+ returns a copy of the gummy
3439
+ """
3440
+ return self.copy(formatting=False)
3395
3441
 
3396
3442
  @property
3397
3443
  def imag(self):
3398
- if self._unit.linear:
3399
- return type(self)(0,unit=self._unit)
3444
+ if self.unit.linear:
3445
+ s = self
3446
+ else:
3447
+ s = self.tobaseunit()
3400
3448
 
3401
- return type(self)(self._unit.zero(),unit=self._unit)
3449
+ return type(self)(0,unit=s.unit)
3450
+
3451
+ def conjugate(self):
3452
+ """
3453
+ returns a copy of the gummy
3454
+ """
3455
+ return self.copy(formatting=False)
3402
3456
 
3457
+ def angle(self):
3458
+ if self.x >= 0:
3459
+ return type(self)(0)
3460
+ else:
3461
+ return type(self)(np.pi)
3462
+
3403
3463
 
3404
3464
  class jummy(immy):
3405
3465
 
@@ -3570,27 +3630,7 @@ class jummy(immy):
3570
3630
  raise ValueError('the name must be a string or a length 4 tuple of str')
3571
3631
 
3572
3632
  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')
3633
+ if norm is None:
3634
+ norm = type(self).latex_norm
3635
+ return get_name(self._name,fmt,norm)
3596
3636