Coverage for gpkit\nomials\variables.py: 0%
108 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 16:57 -0500
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 16:57 -0500
1"Implement Variable and ArrayVariable classes"
2from collections.abc import Iterable
3import numpy as np
4from .data import NomialData
5from .array import NomialArray
6from .math import Monomial
7from .map import NomialMap
8from ..globals import NamedVariables, Vectorize
9from ..varkey import VarKey
10from ..small_classes import Strings, Numbers, HashVector
11from ..small_scripts import is_sweepvar, veclinkedfn
14def addmodelstodescr(descr, addtonamedvars=None):
15 "Add models to descr, optionally adding the second argument to NAMEDVARS"
16 if NamedVariables.lineage:
17 descr["lineage"] = NamedVariables.lineage
18 if addtonamedvars:
19 NamedVariables.namedvars[descr["lineage"]].append(addtonamedvars)
22class Variable(Monomial):
23 """A described singlet Monomial.
25 Arguments
26 ---------
27 *args : list
28 may contain "name" (Strings)
29 "value" (Numbers + Quantity) or (Iterable) for a sweep
30 "units" (Strings)
31 and/or "label" (Strings)
32 **descr : dict
33 VarKey description
35 Returns
36 -------
37 Monomials containing a VarKey with the name '$name',
38 where $name is the vector's name and i is the VarKey's index.
39 """
40 def __init__(self, *args, **descr):
41 if len(args) == 1 and isinstance(args[0], VarKey):
42 self.key, = args
43 else:
44 for arg in args:
45 if isinstance(arg, Strings) and "name" not in descr:
46 descr["name"] = arg
47 elif (isinstance(arg, Numbers) or hasattr(arg, "__call__")
48 and "value" not in descr):
49 descr["value"] = arg
50 elif (isinstance(arg, Iterable)
51 and not isinstance(arg, Strings)):
52 if is_sweepvar(arg):
53 descr["value"] = arg
54 else:
55 descr["value"] = ("sweep", arg)
56 elif isinstance(arg, Strings) and "units" not in descr:
57 descr["units"] = arg
58 elif isinstance(arg, Strings) and "label" not in descr:
59 descr["label"] = arg
60 addmodelstodescr(descr, addtonamedvars=self)
61 self.key = VarKey(**descr)
62 hmap = NomialMap({HashVector({self.key: 1}): 1.0})
63 hmap.units = self.key.units
64 Monomial.__init__(self, hmap)
65 # NOTE: needed because Signomial.__init__ will change the class
66 self.__class__ = Variable
68 __hash__ = NomialData.__hash__
70 def to(self, units):
71 "Create new Signomial converted to new units"
72 return Monomial(self).to(units) # pylint: disable=no-member
74 def sub(self, *args, **kwargs): # pylint: disable=arguments-differ
75 """Same as nomial substitution, but also allows single-argument calls
77 Example
78 -------
79 x = Variable('x')
80 assert x.sub(3) == Variable('x', value=3)
81 """
82 if len(args) == 1 and "val" not in kwargs:
83 arg, = args
84 if not isinstance(arg, dict):
85 args = [{self: arg}]
86 return Monomial.sub(self, *args, **kwargs)
89class ArrayVariable(NomialArray): # pylint: disable=too-many-locals
90 """A described vector of singlet Monomials.
92 Arguments
93 ---------
94 shape : int or tuple
95 length or shape of resulting array
96 *args :
97 may contain "name" (Strings)
98 "value" (Iterable)
99 "units" (Strings)
100 and/or "label" (Strings)
101 **descr :
102 VarKey description
104 Returns
105 -------
106 NomialArray of Monomials, each containing a VarKey with name '$name_{i}',
107 where $name is the vector's name and i is the VarKey's index.
108 """
109 def __new__(cls, shape, *args, **descr): # pylint: disable=too-many-branches, too-many-statements, arguments-differ
110 if "idx" in descr:
111 raise ValueError("the description field 'idx' is reserved")
113 shape = (shape,) if isinstance(shape, Numbers) else tuple(shape)
114 if Vectorize.vectorization:
115 shape += Vectorize.vectorization
117 descr["shape"] = shape
119 for arg in args:
120 if isinstance(arg, Strings) and "name" not in descr:
121 descr["name"] = arg
122 elif (isinstance(arg, (Numbers, Iterable))
123 and not isinstance(arg, Strings)
124 and "value" not in descr):
125 descr["value"] = arg
126 elif hasattr(arg, "__call__"):
127 descr["value"] = arg
128 elif isinstance(arg, Strings) and "units" not in descr:
129 descr["units"] = arg
130 elif isinstance(arg, Strings) and "label" not in descr:
131 descr["label"] = arg
133 if "name" not in descr:
134 descr["name"] = "\\fbox{%s}" % VarKey.unique_id()
136 values = descr.pop("value", None)
137 if values is not None:
138 if not hasattr(values, "__call__"):
139 if Vectorize.vectorization:
140 if not hasattr(values, "shape"):
141 values = np.full(shape, values, "f")
142 else:
143 values = np.broadcast_to(values, reversed(shape)).T
144 elif not hasattr(values, "shape"):
145 values = np.array(values)
146 if values.shape != shape:
147 raise ValueError("value's shape %s is different from the"
148 " vector's %s." % (values.shape, shape))
150 veckeydescr = descr.copy()
151 addmodelstodescr(veckeydescr)
152 if values is not None:
153 if hasattr(values, "__call__"):
154 veckeydescr["vecfn"] = values
155 veckeydescr["value"] = values
156 veckey = VarKey(**veckeydescr)
158 descr["veckey"] = veckey
159 vl = np.empty(shape, dtype="object")
160 it = np.nditer(vl, flags=['multi_index', 'refs_ok'])
161 while not it.finished:
162 i = it.multi_index
163 it.iternext()
164 descr["idx"] = i
165 if values is not None:
166 if hasattr(values, "__call__"): # a vector function
167 descr["value"] = veclinkedfn(values, i)
168 else:
169 descr["value"] = values[i]
170 vl[i] = Variable(**descr)
172 obj = np.asarray(vl).view(NomialArray)
173 obj.key = veckey
174 obj.units = obj.key.units
175 return obj
178class VectorizableVariable(Variable, ArrayVariable): # pylint: disable=too-many-ancestors
179 "A Variable outside a vectorized environment, an ArrayVariable within."
180 def __new__(cls, *args, **descr):
181 if Vectorize.vectorization:
182 shape = descr.pop("shape", ())
183 return ArrayVariable.__new__(cls, shape, *args, **descr)
184 return Variable(*args, **descr)