def testArrayOperations(unit_database_len_time): unit_database = unit_database_len_time m = Quantity.CreateDerived(odict([("Table size", ["m", 1])])) km_city = Quantity.CreateDerived(odict([("City size", ["km", 1])])) s1 = Array.CreateWithQuantity(m, [1]) s2 = Array.CreateWithQuantity(km_city, [0.01]) initial1 = s1.GetQuantity().GetComposingUnits() initial2 = s2.GetQuantity().GetComposingUnits() # Check that they doesn't raise ComposedUnitError s1.GetValues() s2.GetValues() quantity, value = unit_database.Multiply(m, km_city, 1, 0.01) assert initial1 == s1.GetQuantity().GetComposingUnits() assert initial2 == s2.GetQuantity().GetComposingUnits() calculated1 = Array.CreateWithQuantity(quantity, [value]) array = s1 * s2 six.text_type(array) # just to see if it works... assert calculated1 == s1 * s2 quantity, value = unit_database.Sum(m, km_city, 1, 0.01) assert Array.CreateWithQuantity(quantity, [value]) == s1 + s2 quantity, value = unit_database.Subtract(m, km_city, 1, 0.01) assert Array.CreateWithQuantity(quantity, [value]) == s1 - s2
def testDivision(unit_database_len_time): unit_database = unit_database_len_time m = Quantity.CreateDerived(odict([("Table size", ["m", 1])])) km_city = Quantity.CreateDerived(odict([("City size", ["km", 1])])) quantity, value = unit_database.Divide(m, km_city, 1, 0.01) calculated1 = Array.CreateWithQuantity(quantity, [value]) s1 = Array.CreateWithQuantity(m, [1]) s2 = Array.CreateWithQuantity(km_city, [0.01]) assert calculated1 == s1 / s2
def testCreationWithDerivedQuantity(unit_database_len_time): unit_database = unit_database_len_time m = Quantity.CreateDerived(odict([("Table size", ["m", 1])])) km_city = Quantity.CreateDerived(odict([("City size", ["km", 1])])) quantity, value = unit_database.Multiply(m, km_city, 1, 0.01) calculated1 = Scalar.CreateWithQuantity(quantity, value) assert six.text_type(calculated1) s1 = Scalar.CreateWithQuantity(m, 1) s2 = Scalar.CreateWithQuantity(km_city, 0.01) assert calculated1 == s1 * s2
def _ObtainReduced(state): """ :param list state: The value returned from _Quantity.__reduce__ (used in pickle protocol). """ unknown_unit_caption = state.pop(-1) return ObtainQuantity(odict(state), None, unknown_unit_caption)
def testDeepcopy(unit_database_len_time): import copy # Note: the part below is flaky for a test because it relies on getting a refcount to None # which could change if there are other threads running. The code is kept just as a # reference in case we have to debug such a situation again (there was a bug in odict where # it decreased references to None when it shouldn't and it crashed the program later on). # m = odict([('Table size', ['m', 1])]) # import gc # import sys # my_none = None # for i in range(100): # gc.collect() # # Note: # for i in range(200): # gc.collect() # if i < 100: # prev = sys.getrefcount(my_none) # else: # curr = sys.getrefcount(my_none) # self.assert_(curr >= prev, # 'The ref count cannot get lower for None (previous:%s current:%s).' % (prev, curr)) # # # Notice that print sys.getrefcount(None) is always decrementing (this is the error) # m = copy.deepcopy(m) m = Quantity.CreateDerived(odict([("Table size", ["m", 1])])) m0 = copy.deepcopy(m) assert m is m0 # Check if our cache is working.
def testDelWithSlices(): d = odict() d[1] = 1 d[2] = 2 d[3] = 3 del d[1:] assert len(d) == 1 assert d[1] == 1
def testCreateWithQuantity(unit_database_start_units): units.FixedArray.CreateWithQuantity(Quantity.CreateDerived(odict()), [100, 150, 200], dimension=3) quantity = ObtainQuantity("m", "length") a1 = units.FixedArray(3, quantity, values=[1, 2, 3]) a2 = units.FixedArray(3, quantity, values=[1, 2, 3]) assert a1.GetValues("km") == [0.001, 0.002, 0.003] assert a2.GetValues() == [1, 2, 3]
def CreateEmpty(cls): """ Create a quantity without any internal unit. :rtype: Quantity :returns: Returns an empty quantity. """ empty = cls._EMPTY_QUANTITY if empty is None: empty = cls._EMPTY_QUANTITY = ObtainQuantity(odict()) return empty
def GetComposingUnitsJoiningExponents(self): try: return self._composing_units_joining_exponents except AttributeError: ret = odict() for _category, (unit, exp) in six.iteritems( self._category_to_unit_and_exps ): existing = ret.get(unit, 0) ret[unit] = existing + exp self._composing_units_joining_exponents = tuple(ret.items()) return self._composing_units_joining_exponents
def testDerivedQuantities(unit_database_len_time): # define a simple quantity ObtainQuantity(unit="s", category="Time") # see if it works ObtainQuantity(unit="m", category="Table size") # see if it works q3 = Quantity.CreateDerived( odict([("Table size", ["m", 2]), ("Time", ["s", -1])])) q4 = Quantity.CreateDerived( odict([("Table size", ["m", 2]), ("Time", ["s", -2])])) q5 = Quantity.CreateDerived( odict([("Table size", ["m", 1]), ("City size", ["m", 1]), ("Time", ["s", -2])])) q6 = Quantity.CreateDerived(odict([("Time", ["s", -2])])) q7 = Quantity.CreateDerived( odict([("Table size", ["m", 1]), ("Time", ["s", 2])])) with pytest.raises(InvalidUnitError): Quantity.CreateDerived( odict([ ("Table size", ["invalid", 1]), ("City size", ["m", 1]), ("Time", ["s", -2]), ])) assert "(Table size) ** 2 / Time" == q3.GetCategory() assert "m2/s" == q3.GetUnit() assert "(Table size) ** 2 / (Time) ** 2" == q4.GetCategory() assert "m2/s2" == q4.GetUnit() assert "1/s2" == q6.GetUnit() assert "m.s2" == q7.GetUnit() assert (("m", 2), ("s", -2)) == q4.GetComposingUnits() assert (("m", 1), ("m", 1), ("s", -2)) == q5.GetComposingUnits()
def GetCategoryToUnitAndExpsCopy(self, unit_and_exps=None): """ Same as Quantity.GetCategoryToUnitAndExps but returns a copy instead of the internal instance. .. see:: Quantity.GetCategoryToUnitAndExps """ if unit_and_exps is None: unit_and_exps = self._category_to_unit_and_exps return odict( (category, unit_and_exp[:]) for (category, unit_and_exp) in six.iteritems(unit_and_exps) )
def testNumberInteractions(unit_database_len_time): scalar = Scalar("Table size", 1, "m") scalar2 = Scalar.CreateWithQuantity(Quantity.CreateDerived(odict()), 0) assert scalar == scalar + scalar2 assert scalar == scalar + 0 assert scalar == 0 + scalar assert 9 == (10 - scalar).value assert -9 == (scalar - 10).value assert 10 == (10 * scalar).value assert 10 == (scalar * 10).value assert 10 == (10.0 / scalar).value assert 1 / 10.0 == (scalar / 10.0).value
def testQuantityCaption(unit_database_posc_len): unit_database = unit_database_posc_len unit_database.AddUnitBase(UNKNOWN_QUANTITY_TYPE, UNKNOWN_QUANTITY_TYPE, UNKNOWN_UNIT) unit_database.AddCategory(UNKNOWN_QUANTITY_TYPE, UNKNOWN_QUANTITY_TYPE) q0 = ObtainQuantity(unit=UNKNOWN_UNIT, category=UNKNOWN_QUANTITY_TYPE) assert "" == q0.GetUnknownCaption() q = ObtainQuantity(unit=UNKNOWN_UNIT, unknown_unit_caption="Feeeet", category=UNKNOWN_QUANTITY_TYPE) assert "Feeeet" == q.GetUnknownCaption() assert "Feeeet <unknown>" == q.GetUnitCaption() q = Quantity.CreateDerived(odict([("length", ("m", 1))]), unknown_unit_caption="Feeeet") assert "m" == q.GetUnitCaption()
def testConvertionWithDerivedUnits(unit_database_len_time): empty = Quantity.CreateDerived(odict()) m = Quantity.CreateDerived(odict([("Table size", ["m", 1])])) m_city = Quantity.CreateDerived(odict([("City size", ["m", 1])])) cm = Quantity.CreateDerived(odict([("Table size", ["cm", 1])])) km_city = Quantity.CreateDerived(odict([("City size", ["km", 1])])) m2 = Quantity.CreateDerived(odict([("Table size", ["m", 2])])) s = Quantity.CreateDerived(odict([("Time", ["s", -1])])) m2s = Quantity.CreateDerived( odict([("Table size", ["m", 2]), ("Time", ["s", -1])])) cat_mix_m2 = Quantity.CreateDerived( odict([("Table size", ["m", 1]), ("City size", ["m", 1])])) unit_database = unit_database_len_time # multiplication assert (m2, 2) == unit_database.Multiply(m, m, 1, 2) assert (m2s, 2) == unit_database.Multiply(m2, s, 1, 2) assert (m2s, 2) == unit_database.Multiply(m2, s, 1, 2) assert (m2, 1) == unit_database.Multiply(m, cm, 1, 100) assert (cat_mix_m2, 1) == unit_database.Multiply(m, m_city, 1, 1) assert (cat_mix_m2, 1) == unit_database.Multiply(m, km_city, 1, 0.001) # division assert (m, 0.001) == unit_database.Divide(cat_mix_m2, km_city, 1, 1) # check division with cancelling units (and different categories) assert (empty, 1) == unit_database.Divide(m, m_city, 1, 1) # sum assert (m, 1 + 0.01) == unit_database.Sum(m, cm, 1, 1) assert (m, 2) == unit_database.Sum(m, m_city, 1, 1) # subtraction # check with operation so that we have an exact match (without need for almost equals, # as that should be the same exact operation done later) assert (m, 1 - 0.01) == unit_database.Subtract(m, cm, 1, 1) with pytest.raises(InvalidOperationError): unit_database.Subtract(m, s, 1, 1)
def testNumberInteractions(unit_database_len_time): import numpy m = Quantity.CreateDerived(odict([("Table size", ["m", 1])])) s1 = Array.CreateWithQuantity(m, list(range(10))) s2 = Array.CreateWithQuantity(m, [x + x for x in range(10)]) assert s1 == 0 + s1 assert s1 == s1 + 0 assert s2 == s1 + s1 num_arr = Array.CreateWithQuantity(m, numpy.array([x for x in range(10)])) sum_array = num_arr + s1 assert isinstance(sum_array.values, numpy.ndarray) sum_array2 = num_arr + numpy.array([x for x in range(10)]) assert isinstance(sum_array2.values, numpy.ndarray) tup_arr = Array.CreateWithQuantity(m, tuple([x for x in range(10)])) tup_arr = tup_arr + 1 assert isinstance(tup_arr.values, tuple)
def GetUnitName(self): """ :rtype: unicode :returns: A description of the unit showing all the containing parts in the category, units and the exponent for each. This differs from the default unit because the default GetUnit will only return the final name of the unit, composing all the available parts into a single exponent (e.g. if it's composed of more than one 'm', each with exponent 1, it would appear 2 times in this method and would appear 'm2' in the GetUnit). """ repr_and_exp = odict() unit_database = self._unit_database for category, (unit, exp) in six.iteritems(self._category_to_unit_and_exps): quantity_type = unit_database.GetCategoryQuantityType(category) unit_name = unit_database.GetUnitName(quantity_type, unit) existing = repr_and_exp.get(unit_name, 0) repr_and_exp[unit_name] = existing + exp return self._MakeStr(list(repr_and_exp.items()))
def testDerivedUnits(unit_database_empty): unit_database = unit_database_empty unit_database.AddUnitBase("length", "meters", "m") unit_database.AddUnit("length", "milimeters", "mm", "x * 1000.0", "x / 1000.0") unit_database.AddUnit("length", "centimeters", "cm", "x * 100.0", "x / 100.0") unit_database.AddCategory("well-diameter", "length") unit_database.AddUnitBase("time", "seconds", "s") unit_database.AddCategory(category="Time", quantity_type="time") s1 = Scalar("well-diameter", 10, "m") s2 = Scalar("well-diameter", 10, "cm") s3 = Scalar("Time", 10, "s") assert Scalar("well-diameter", 10.10, "m") == s1 + s2 assert Scalar("well-diameter", 9.90, "m") == s1 - s2 quantity = Quantity.CreateDerived(odict()) assert Scalar.CreateWithQuantity(quantity, 100) == s1 / s2 assert Scalar("well-diameter", 9.90, "m") == s1 - s2 with pytest.raises(InvalidOperationError): s1.__sub__(s3)
def testInsert(): d = odict() d[1] = "alpha" d[3] = "charlie" assert list(d.items()) == [(1, "alpha"), (3, "charlie")] d.insert(0, 0, "ZERO") assert list(d.items()) == [(0, "ZERO"), (1, "alpha"), (3, "charlie")] d.insert(2, 2, "bravo") assert list(d.items()) == [(0, "ZERO"), (1, "alpha"), (2, "bravo"), (3, "charlie")] d.insert(99, 4, "echo") assert list(d.items()) == [ (0, "ZERO"), (1, "alpha"), (2, "bravo"), (3, "charlie"), (4, "echo"), ]
def testReadOnlyOperation(unit_database_len_time): unit_database = unit_database_len_time m_ro = ObtainQuantity("m", "Table size") m_rw = ObtainQuantity("m", "Table size") m2 = Quantity.CreateDerived(odict([("Table size", ["m", 2])])) # multiplication assert (m2, 2) == unit_database.Multiply(m_rw, m_rw, 1, 2) assert (m2, 2) == unit_database.Multiply(m_ro, m_rw, 1, 2) assert (m2, 2) == unit_database.Multiply(m_ro, m_ro, 1, 2) assert (m2, 2) == unit_database.Multiply(m_rw, m_ro, 1, 2) quantity, _ = unit_database.Multiply(m_ro, m_ro, 1, 2) assert isinstance(quantity, Quantity) quantity, _ = unit_database.Multiply(m_rw, m_rw, 1, 2) assert isinstance(quantity, Quantity) quantity, _ = unit_database.Multiply(m_rw, m_ro, 1, 2) assert isinstance(quantity, Quantity) quantity, _ = unit_database.Multiply(m_ro, m_rw, 1, 2) assert isinstance(quantity, Quantity)
def __init__(self, category, unit, unknown_unit_caption=None): """ :type category: unicode or odict :param category: The category to which the new quantity should be bound or an odict with information on the derived category/unit (in which case the unit parameter is ignored). :param unicode unit: The unit which the new quantity should have. """ try: # Bail out if unit is already configured: this may happen when the Quantity constructor # is called directly: if Quantity() is called, the protocol will call __new__ and even # if a configured instance is returned, it'll call __init__ again, so, this is a check # for this situation so that we return if the quantity is already configured. self._unknown_unit_caption return except AttributeError: pass # Keep self._unit_database for faster access. unit_database = self._unit_database = UnitDatabase.GetSingleton() if unknown_unit_caption is not None: assert ( unknown_unit_caption.__class__ == six.text_type ), "Unit caption must be a string. Note: the unit database and unit system parameters were removed." self._unknown_unit_caption = unknown_unit_caption else: self._unknown_unit_caption = "" if category.__class__ == odict: assert unit is None self._category_to_unit_and_exps = category self._is_derived = True rep_and_exp = odict() for category, (_unit, exp) in six.iteritems( self._category_to_unit_and_exps ): quantity_type = unit_database.GetCategoryQuantityType(category) existing = rep_and_exp.get(quantity_type, 0) rep_and_exp[quantity_type] = existing + exp self._category = self._MakeStr( [ (category, exp) for category, (_unit, exp) in six.iteritems( self._category_to_unit_and_exps ) ] ) self._quantity_type = self._MakeStr(list(rep_and_exp.items())) self._unit = self._CreateUnitsWithJoinedExponentsString() self._composing_units = tuple( (unit, exp) for _category, (unit, exp) in six.iteritems( self._category_to_unit_and_exps ) ) self._composing_categories = tuple( six.iterkeys(self._category_to_unit_and_exps) ) self._category_info = None return self._is_derived = False # changes to accommodate making operations with derived units (internally the # information must be a dict) if category.__class__ != six.text_type: raise TypeError("Only unicode is accepted. %s is not." % category.__class__) # Getting the category should be enough to check that it's valid. category_info = unit_database.GetCategoryInfo(category) self._category_info = category_info # if no unit was given, assume base unit for this quantity_type if unit is None: unit = category_info.default_unit else: if unit.__class__ != six.text_type: raise TypeError("Only unicode is accepted. %s is not." % unit.__class__) unit_database.CheckCategoryUnit(category, unit) # store it as odict so that we can have a decent order when creating a string from # an operation. category_to_unit_and_exps = odict() category_to_unit_and_exps[category] = [unit, 1] self._category_to_unit_and_exps = category_to_unit_and_exps self._category = category self._quantity_type = unit_database.GetCategoryQuantityType(category) self._unit = unit self._tobase = self._unit_database.GetInfo( self._quantity_type, self._unit, fix_unknown=True ).tobase self._composing_units = unit self._composing_categories = category
def ObtainQuantity(unit, category=None, unknown_unit_caption=None): """ :type unit: unicode or odict(unicode -> list(unicode, int)) :param unit: Either the string representing the unit or an ordered dict with the composing unit information (if composing all the info, including the category will be received in this parameter). :param unicode category: The category for the quantity. If not given it's gotten based on the unit passed. :param unicode unknown_unit_caption: The caption for the unit (used if unknown). :rtype Quantity: """ unit_database = UnitDatabase.GetSingleton() quantities_cache = unit_database.quantities_cache if unit.__class__ in (list, tuple): # It may be a derived unit with list(tuple(unicode, int)) -- in which case the category # must also be a list (of the same size) if len(unit) == 1 and unit[0][1] == 1: # Although passed as composing, it's a simple case unit = unit[0][0] if category.__class__ in (list, tuple): category = category[0] else: assert category.__class__ in (list, tuple) unit = odict( (cat, unit_and_exp) for (cat, unit_and_exp) in zip(category, unit) ) category = None if hasattr(unit, "items"): assert category is None if len(unit) == 1 and next(six.itervalues(unit))[1] == 1: # Although passed as composing, it's a simple case category, (unit, _exp) = next(six.iteritems(unit)) else: key = tuple( (category, tuple(unit_and_exp)) for (category, unit_and_exp) in six.iteritems(unit) ) if unknown_unit_caption: key += (unknown_unit_caption,) try: return quantities_cache[key] except KeyError: quantity = quantities_cache[key] = _Quantity( unit, None, unknown_unit_caption ) return quantity key = (category, unit, unknown_unit_caption) try: return quantities_cache[key] except KeyError: pass # Just go on with the regular flow. assert unit.__class__ != bytes, "unit must be given as unicode string always" if unit.__class__ != six.text_type: if category is None: raise AssertionError("Currently only supporting unit as a string.") else: # Unit is given by the category unit = unit_database.GetDefaultUnit(category) quantity = quantities_cache[key] = _Quantity( category, unit, unknown_unit_caption ) return quantity elif category is None: category = unit_database.GetDefaultCategory(unit) if not category: raise UnitsError("Unable to get default category for: %s" % (unit,)) key_with_resolved_category = (category, unit, unknown_unit_caption) try: return quantities_cache[key_with_resolved_category] except KeyError: quantity = quantities_cache[key_with_resolved_category] = _Quantity( category, unit, unknown_unit_caption ) # Cache it with None category too. quantities_cache[key] = quantity return quantity else: quantities_cache[key] = quantity = _Quantity( category, unit, unknown_unit_caption ) return quantity
def _CreateDerived( cls, category_to_unit_and_exps, resulting_class=None, validate_category_and_units=True, unknown_unit_caption=None, ): """ Create a category that represents a derived quantity (usually resulting from operations among quantities). :type category_to_unit_and_exps: odict(unicode->(list(unicode, int))) :param category_to_unit_and_exps: This odict defines the category as well as the unit in a way that we can specify exponents. :param type resulting_class: Deprecated (no longer used). :param bool validate_category_and_units: If True, the category and units will be validated (otherwise, it'll just create it without trying to validate any value (note that it's meant to be used internally to make creating the quantity faster without having to actually validate it) :rtype: cls :returns: An instance that represents a quantity with the given categories, units and exponents. """ unit_database = UnitDatabase.GetSingleton() if validate_category_and_units: assert hasattr( category_to_unit_and_exps, "items" ), "validate_category_and_units needs to be a dict" for category, (unit, _exp) in six.iteritems(category_to_unit_and_exps): # will do the checkings needed for validation (category/unit) # so that we can later just store the internal information without # any other check. # changes to accomodate making operations with derived units (internally the # information must be a dict) if category.__class__ != six.text_type: raise TypeError( "Only unicode is accepted. %s is not." % category.__class__ ) # Getting the category should be enough to know that it's valid. category_info = unit_database.GetCategoryInfo(category) # if no unit was given, assume base unit for this quantity_type if unit is None: unit = category_info.default_unit else: if unit.__class__ != six.text_type: raise TypeError( "Only unicode is accepted. %s is not." % unit.__class__ ) unit_database.CheckQuantityTypeUnit( category_info.quantity_type, unit ) return ObtainQuantity( odict( (category, unit_and_exp[:]) for (category, unit_and_exp) in six.iteritems(category_to_unit_and_exps) ), unknown_unit_caption=unknown_unit_caption, )