Coverage for gpkit/varkey.py: 100%
88 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-07 22:13 -0500
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-07 22:13 -0500
1"""Defines the VarKey class"""
2from .small_classes import Count
3from .repr_conventions import ReprMixin
4from .units import qty
7class VarKey(ReprMixin): # pylint:disable=too-many-instance-attributes
8 """An object to correspond to each 'variable name'.
10 Arguments
11 ---------
12 name : str, VarKey, or Monomial
13 Name of this Variable, or object to derive this Variable from.
15 **descr :
16 Any additional attributes, which become the descr attribute (a dict).
18 Returns
19 -------
20 VarKey with the given name and descr.
21 """
22 unique_id = Count().next
23 subscripts = ("lineage", "idx")
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
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))
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"}))
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
60 def __setstate__(self, state):
61 "Restores varkey from its metadata dictionary"
62 self.__init__(**state)
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
102 __repr__ = str_without
104 # pylint: disable=multiple-statements
105 def __hash__(self): return self.hashvalue
106 def __getattr__(self, attr): return self.descr.get(attr, None)
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]
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
125 def __eq__(self, other):
126 if not hasattr(other, "descr"):
127 return False
128 return self.eqstr == other.eqstr