Coverage for gpkit/varkey.py: 100%
87 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-28 11:52 -0400
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-28 11:52 -0400
1"""Defines the VarKey class"""
2from .small_classes import Count, qty
3from .repr_conventions import ReprMixin
6class VarKey(ReprMixin): # pylint:disable=too-many-instance-attributes
7 """An object to correspond to each 'variable name'.
9 Arguments
10 ---------
11 name : str, VarKey, or Monomial
12 Name of this Variable, or object to derive this Variable from.
14 **descr :
15 Any additional attributes, which become the descr attribute (a dict).
17 Returns
18 -------
19 VarKey with the given name and descr.
20 """
21 unique_id = Count().next
22 subscripts = ("lineage", "idx")
24 def __init__(self, name=None, **descr):
25 # NOTE: Python arg handling guarantees 'name' won't appear in descr
26 self.descr = descr
27 self.descr["name"] = name or "\\fbox{%s}" % VarKey.unique_id()
28 unitrepr = self.unitrepr or self.units
29 if unitrepr in ["", "-", None]: # dimensionless
30 self.descr["units"] = None
31 self.descr["unitrepr"] = "-"
32 else:
33 self.descr["units"] = qty(unitrepr)
34 self.descr["unitrepr"] = unitrepr
36 self.key = self
37 fullstr = self.str_without({"hiddenlineage", "modelnums", "vec"})
38 self.eqstr = fullstr + str(self.lineage) + self.unitrepr
39 self.hashvalue = hash(self.eqstr)
40 self.keys = set((self.name, fullstr))
42 if "idx" in self.descr:
43 if "veckey" not in self.descr:
44 vecdescr = self.descr.copy()
45 del vecdescr["idx"]
46 self.veckey = VarKey(**vecdescr)
47 else:
48 self.keys.add(self.veckey)
49 self.keys.add(self.str_without({"idx", "modelnums"}))
51 def __getstate__(self):
52 "Stores varkey as its metadata dictionary, removing functions"
53 state = self.descr.copy()
54 for key, value in state.items():
55 if getattr(value, "__call__", None):
56 state[key] = "unpickleable function %s" % value
57 return state
59 def __setstate__(self, state):
60 "Restores varkey from its metadata dictionary"
61 self.__init__(**state)
63 def str_without(self, excluded=()): # pylint:disable=too-many-branches
64 "Returns string without certain fields (such as 'lineage')."
65 name = self.name
66 if "lineage" not in excluded and self.lineage:
67 namespace = self.lineagestr("modelnums" not in excluded).split(".")
68 for ex in excluded:
69 if ex[0:7] == ":MAGIC:":
70 to_replace = ex[7:]
71 if not to_replace:
72 continue
73 to_replace = to_replace.split(".")
74 replaced = 0
75 for modelname in to_replace:
76 if not namespace or namespace[0] != modelname:
77 break
78 replaced += 1
79 namespace = namespace[1:]
80 if len(to_replace) > replaced:
81 namespace.insert(0, "."*(len(to_replace)-replaced))
82 if "hiddenlineage" not in excluded:
83 necessarylineage = self.necessarylineage
84 if necessarylineage is None and self.veckey:
85 necessarylineage = self.veckey.necessarylineage
86 if necessarylineage is not None:
87 if necessarylineage > 0:
88 namespace = namespace[-necessarylineage:]
89 else:
90 namespace = None
91 if namespace:
92 name = ".".join(namespace) + "." + name
93 if "idx" not in excluded:
94 if self.idx:
95 name += "[%s]" % ",".join(map(str, self.idx))
96 elif "vec" not in excluded and self.shape:
97 name += "[:]"
98 return name
100 __repr__ = str_without
102 # pylint: disable=multiple-statements
103 def __hash__(self): return self.hashvalue
104 def __getattr__(self, attr): return self.descr.get(attr, None)
106 @property
107 def models(self):
108 "Returns a tuple of just the names of models in self.lineage"
109 return list(zip(*self.lineage))[0]
111 def latex(self, excluded=()):
112 "Returns latex representation."
113 name = self.name
114 if "vec" not in excluded and "idx" not in excluded and self.shape:
115 name = "\\vec{%s}" % name
116 if "idx" not in excluded and self.idx:
117 name = "{%s}_{%s}" % (name, ",".join(map(str, self.idx)))
118 if "lineage" not in excluded and self.lineage:
119 name = "{%s}_{%s}" % (name,
120 self.lineagestr("modelnums" not in excluded))
121 return name
123 def __eq__(self, other):
124 if not hasattr(other, "descr"):
125 return False
126 return self.eqstr == other.eqstr