Coverage for gpkit/varkey.py: 100%

88 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-07 22:56 -0500

1"""Defines the VarKey class""" 

2from .small_classes import Count 

3from .repr_conventions import ReprMixin 

4from .units import qty 

5 

6 

7class VarKey(ReprMixin): # pylint:disable=too-many-instance-attributes 

8 """An object to correspond to each 'variable name'. 

9 

10 Arguments 

11 --------- 

12 name : str, VarKey, or Monomial 

13 Name of this Variable, or object to derive this Variable from. 

14 

15 **descr : 

16 Any additional attributes, which become the descr attribute (a dict). 

17 

18 Returns 

19 ------- 

20 VarKey with the given name and descr. 

21 """ 

22 unique_id = Count().next 

23 subscripts = ("lineage", "idx") 

24 

25 def __init__(self, name=None, **descr): 

26 # NOTE: Python arg handling guarantees 'name' won't appear in descr 

27 self.descr = descr 

28 self.descr["name"] = name or "\\fbox{%s}" % VarKey.unique_id() 

29 unitrepr = self.unitrepr or self.units 

30 if unitrepr in ["", "-", None]: # dimensionless 

31 self.descr["units"] = None 

32 self.descr["unitrepr"] = "-" 

33 else: 

34 self.descr["units"] = qty(unitrepr) 

35 self.descr["unitrepr"] = unitrepr 

36 

37 self.key = self 

38 fullstr = self.str_without({"hiddenlineage", "modelnums", "vec"}) 

39 self.eqstr = fullstr + str(self.lineage) + self.unitrepr 

40 self.hashvalue = hash(self.eqstr) 

41 self.keys = set((self.name, fullstr)) 

42 

43 if "idx" in self.descr: 

44 if "veckey" not in self.descr: 

45 vecdescr = self.descr.copy() 

46 del vecdescr["idx"] 

47 self.veckey = VarKey(**vecdescr) 

48 else: 

49 self.keys.add(self.veckey) 

50 self.keys.add(self.str_without({"idx", "modelnums"})) 

51 

52 def __getstate__(self): 

53 "Stores varkey as its metadata dictionary, removing functions" 

54 state = self.descr.copy() 

55 for key, value in state.items(): 

56 if getattr(value, "__call__", None): 

57 state[key] = f"unpickleable function {value}" 

58 return state 

59 

60 def __setstate__(self, state): 

61 "Restores varkey from its metadata dictionary" 

62 self.__init__(**state) 

63 

64 def str_without(self, excluded=()): # pylint:disable=too-many-branches 

65 "Returns string without certain fields (such as 'lineage')." 

66 name = self.name 

67 if "lineage" not in excluded and self.lineage: 

68 namespace = self.lineagestr("modelnums" not in excluded).split(".") 

69 for ex in excluded: 

70 if ex[0:7] == ":MAGIC:": 

71 to_replace = ex[7:] 

72 if not to_replace: 

73 continue 

74 to_replace = to_replace.split(".") 

75 replaced = 0 

76 for modelname in to_replace: 

77 if not namespace or namespace[0] != modelname: 

78 break 

79 replaced += 1 

80 namespace = namespace[1:] 

81 if len(to_replace) > replaced: 

82 namespace.insert(0, "."*(len(to_replace)-replaced)) 

83 if "hiddenlineage" not in excluded: 

84 necessarylineage = self.necessarylineage 

85 if necessarylineage is None and self.veckey: 

86 necessarylineage = self.veckey.necessarylineage 

87 if necessarylineage is not None: 

88 if necessarylineage > 0: 

89 namespace = namespace[-necessarylineage:] 

90 else: 

91 namespace = None 

92 if namespace: 

93 name = ".".join(namespace) + "." + name 

94 if "idx" not in excluded: 

95 if self.idx: 

96 # pylint: disable=consider-using-f-string # considered it 

97 name += "[%s]" % ",".join(map(str, self.idx)) 

98 elif "vec" not in excluded and self.shape: 

99 name += "[:]" 

100 return name 

101 

102 __repr__ = str_without 

103 

104 # pylint: disable=multiple-statements 

105 def __hash__(self): return self.hashvalue 

106 def __getattr__(self, attr): return self.descr.get(attr, None) 

107 

108 @property 

109 def models(self): 

110 "Returns a tuple of just the names of models in self.lineage" 

111 return list(zip(*self.lineage))[0] 

112 

113 def latex(self, excluded=()): 

114 "Returns latex representation." 

115 name = self.name 

116 if "vec" not in excluded and "idx" not in excluded and self.shape: 

117 name = "\\vec{%s}" % name 

118 if "idx" not in excluded and self.idx: 

119 name = "{%s}_{%s}" % (name, ",".join(map(str, self.idx))) 

120 if "lineage" not in excluded and self.lineage: 

121 name = "{%s}_{%s}" % (name, 

122 self.lineagestr("modelnums" not in excluded)) 

123 return name 

124 

125 def __eq__(self, other): 

126 if not hasattr(other, "descr"): 

127 return False 

128 return self.eqstr == other.eqstr