Skip to content

Commit

Permalink
Merge 6f88f9b into 1ac62d2
Browse files Browse the repository at this point in the history
  • Loading branch information
bqpd committed Feb 17, 2022
2 parents 1ac62d2 + 6f88f9b commit e3ed0f9
Show file tree
Hide file tree
Showing 12 changed files with 59 additions and 39 deletions.
18 changes: 15 additions & 3 deletions docs/source/examples/breakdowns.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
"An example to show off Breakdowns"
import os
import sys
import pickle
import pint
from packaging import version
from gpkit.breakdowns import Breakdowns

if version.parse(pint.__version__) >= version.parse("0.9"):
dirpath = os.path.dirname(os.path.realpath(__file__)) + os.sep
if version.parse(pint.__version__) >= version.parse("0.13"):
sol = pickle.load(open(dirpath+"solar_13.p", "rb"))
elif version.parse(pint.__version__) >= version.parse("0.12"):
sol = pickle.load(open(dirpath+"solar_12.p", "rb"))
elif version.parse(pint.__version__) >= version.parse("0.10"):
sol = pickle.load(open(dirpath+"solar_10.p", "rb"))
elif version.parse(pint.__version__) == version.parse("0.9"):
sol = pickle.load(open(dirpath+"solar.p", "rb"))
else:
sol = None

# our Miniconda windows test platform can't print unicode
if sys.platform[:3] != "win" and sol is not None:
# the code to create solar.p is in ./breakdowns/solartest.py
filepath = os.path.dirname(os.path.realpath(__file__)) + os.sep + "solar.p"
sol = pickle.load(open(filepath, "rb"))
bds = Breakdowns(sol)

print("Cost breakdown (as seen in solution tables)")
Expand Down
2 changes: 1 addition & 1 deletion docs/source/examples/breakdowns/solartest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
M = Mission(Vehicle, latitude=[20])
M.cost = M[M.aircraft.Wtotal]

M.localsolve().save("solar.p")
M.localsolve().save("solar_13.p") # suffix is min pint version worked for
Binary file added docs/source/examples/solar_10.p
Binary file not shown.
Binary file added docs/source/examples/solar_12.p
Binary file not shown.
Binary file added docs/source/examples/solar_13.p
Binary file not shown.
19 changes: 2 additions & 17 deletions gpkit/breakdowns.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,6 @@ def get_free_vks(posy, solution):
return set(vk for vk in posy.vks if vk not in solution["constants"])

def get_model_breakdown(solution):
breakdowns = {"|sensitivity|": 0}
for modelname, senss in solution["sensitivities"]["models"].items():
senss = abs(senss) # for those monomial equalities
*namespace, name = modelname.split(".")
subbd = breakdowns
subbd["|sensitivity|"] += senss
for parent in namespace:
if parent not in subbd:
subbd[parent] = {parent: {}}
subbd = subbd[parent]
if "|sensitivity|" not in subbd:
subbd["|sensitivity|"] = 0
subbd["|sensitivity|"] += senss
subbd[name] = {"|sensitivity|": senss}
# print(breakdowns["HyperloopSystem"]["|sensitivity|"])
breakdowns = {"|sensitivity|": 0}
for constraint, senss in solution["sensitivities"]["constraints"].items():
senss = abs(senss) # for those monomial
Expand Down Expand Up @@ -133,7 +118,7 @@ def get_breakdowns(basically_fixed_variables, solution):
gt, lt = (constraint.left, constraint.right)
for gtvk in gt.vks: # remove RelaxPCCP.C
if (gtvk.name == "C" and gtvk.lineage[0][0] == "RelaxPCCP"
and gtvk not in solution["freevariables"]):
and gtvk not in solution["variables"]):
lt, gt = lt.sub({gtvk: 1}), gt.sub({gtvk: 1})
if len(gt.hmap) > 1:
continue
Expand Down Expand Up @@ -165,7 +150,7 @@ def get_breakdowns(basically_fixed_variables, solution):
gt, lt = (constraint.left, constraint.right)
for gtvk in gt.vks:
if (gtvk.name == "C" and gtvk.lineage[0][0] == "RelaxPCCP"
and gtvk not in solution["freevariables"]):
and gtvk not in solution["variables"]):
lt, gt = lt.sub({gtvk: 1}), gt.sub({gtvk: 1})
if len(gt.hmap) > 1:
continue
Expand Down
15 changes: 13 additions & 2 deletions gpkit/constraints/prog_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ def run_sweep(genfunction, self, solution, skipsweepfailures,
for (var, grid) in zip(sweepvars, sweep_grids)}

if verbosity > 0:
print("Sweeping with %i solves:" % N_passes)
tic = time()

self.program = []
Expand All @@ -170,24 +169,36 @@ def run_sweep(genfunction, self, solution, skipsweepfailures,
program, solvefn = genfunction(self, constants, **kwargs)
program.model = None # so it doesn't try to debug
self.program.append(program) # NOTE: SIDE EFFECTS
if i == 0 and verbosity > 0: # wait for successful program gen
# TODO: use full string when minimum lineage is set automatically
sweepvarsstr = ", ".join([str(var)
for var, val in zip(sweepvars, sweepvals)
if not np.isnan(val).all()])
print("Sweeping %s with %i solves:" % (sweepvarsstr, N_passes))
try:
if verbosity > 1:
print("\nSolve %i:" % i)
result = solvefn(solver, verbosity=verbosity-1, **kwargs)
if kwargs.get("process_result", True):
self.process_result(result)
solution.append(result)
if verbosity == 1:
print(".", end="", flush=True)
except Infeasible as e:
last_error = e
if not skipsweepfailures:
raise RuntimeWarning(
"Solve %i was infeasible; progress saved to m.program."
" To continue sweeping after failures, solve with"
" skipsweepfailures=True." % i) from e
if verbosity > 0:
if verbosity > 1:
print("Solve %i was %s." % (i, e.__class__.__name__))
if verbosity == 1:
print("!", end="", flush=True)
if not solution:
raise RuntimeWarning("All solves were infeasible.") from last_error
if verbosity == 1:
print()

solution["sweepvariables"] = KeyDict()
ksweep = KeyDict(sweep)
Expand Down
7 changes: 6 additions & 1 deletion gpkit/constraints/sgp.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class SequentialGeometricProgram:

def __init__(self, cost, model, substitutions,
*, use_pccp=True, pccp_penalty=2e2, **kwargs):
self.cost = cost
self.pccp_penalty = pccp_penalty
if cost.any_nonpositive_cs:
raise InvalidPosynomial("""an SGP's cost must be Posynomial
Expand Down Expand Up @@ -164,7 +165,9 @@ def localsolve(self, solver=None, *, verbosity=1, x0=None, reltol=1e-4,
if verbosity > 2:
result = gp.generate_result(solver_out, verbosity=verbosity-3)
self._results.append(result)
print(result.table(self.sgpvks))
vartable = result.table(self.sgpvks, tables=["freevariables"])
vartable = "\n" + vartable.replace("Free", "SGP", 1)
print(vartable)
elif verbosity > 1:
print("Solved cost was %.4g." % cost)
if prevcost is None:
Expand Down Expand Up @@ -204,9 +207,11 @@ def localsolve(self, solver=None, *, verbosity=1, x0=None, reltol=1e-4,
" `use_pccp=False` and start it from this model's"
" solution: e.g. `m.localsolve(use_pccp=False, x0="
"m.solution[\"variables\"])`." % self.pccp_penalty)
self.result["cost function"] = self.cost
del self.result["freevariables"][self.slack.key] # pylint: disable=no-member
del self.result["variables"][self.slack.key] # pylint: disable=no-member
del self.result["sensitivities"]["variables"][self.slack.key] # pylint: disable=no-member
del self.result["sensitivities"]["variablerisk"][self.slack.key] # pylint: disable=no-member
slcon = self.gpconstraints[0]
slconsenss = self.result["sensitivities"]["constraints"][slcon]
del self.result["sensitivities"]["constraints"][slcon]
Expand Down
6 changes: 4 additions & 2 deletions gpkit/keydict.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ def __contains__(self, key): # pylint:disable=too-many-return-statements
if super().__contains__(key): # pylint: disable=no-member
if idx:
try:
value = super().__getitem__(key)[idx] # pylint: disable=no-member
return True if is_sweepvar(value) else not isnan(value)
val = super().__getitem__(key)[idx] # pylint: disable=no-member
return True if is_sweepvar(val) else not isnan(val).any()
except TypeError:
raise TypeError("%s has an idx, but its value in this"
" KeyDict is the scalar %s."
Expand Down Expand Up @@ -179,6 +179,8 @@ def __getitem__(self, key):
self._copyonwrite(k)
val = dict.__getitem__(self, k)
if idx:
if len(idx) < len(val.shape):
idx = (...,) + idx # idx from the right, in case of sweep
val = val[idx]
if len(keys) == 1:
return val
Expand Down
5 changes: 4 additions & 1 deletion gpkit/small_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@ def _enray(d_in, d_out):
if len(v) == 1:
v, = v
else:
v = np.array(v)
if isinstance(v[0], list):
v = np.array(v, dtype="object")
else:
v = np.array(v)
d_out[k] = v
return d_out

Expand Down
3 changes: 2 additions & 1 deletion gpkit/solution_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,8 @@ def almost_equal(self, other, reltol=1e-3):
if svks != ovks:
return False
for key in svks:
if np.max(abs(cast(np.divide, svars[key], ovars[key]) - 1)) >= reltol:
reldiff = np.max(abs(cast(np.divide, svars[key], ovars[key]) - 1))
if reldiff >= reltol:
return False
return True

Expand Down
23 changes: 12 additions & 11 deletions gpkit/varkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def __init__(self, name=None, **descr):
self.descr["unitrepr"] = unitrepr

self.key = self
fullstr = self.str_without(["modelnums", "vec"])
fullstr = self.str_without({"hiddenlineage", "modelnums", "vec"})
self.eqstr = fullstr + str(self.lineage) + self.unitrepr
self.hashvalue = hash(self.eqstr)
self.keys = set((self.name, fullstr))
Expand All @@ -46,7 +46,7 @@ def __init__(self, name=None, **descr):
self.veckey = VarKey(**vecdescr)
else:
self.keys.add(self.veckey)
self.keys.add(self.str_without(["idx", "modelnums"]))
self.keys.add(self.str_without({"idx", "modelnums"}))

def __getstate__(self):
"Stores varkey as its metadata dictionary, removing functions"
Expand All @@ -60,7 +60,7 @@ def __setstate__(self, state):
"Restores varkey from its metadata dictionary"
self.__init__(**state)

def str_without(self, excluded=()):
def str_without(self, excluded=()): # pylint:disable=too-many-branches
"Returns string without certain fields (such as 'lineage')."
name = self.name
if "lineage" not in excluded and self.lineage:
Expand All @@ -79,14 +79,15 @@ def str_without(self, excluded=()):
namespace = namespace[1:]
if len(to_replace) > replaced:
namespace.insert(0, "."*(len(to_replace)-replaced))
necessarylineage = self.necessarylineage
if necessarylineage is None and self.veckey:
necessarylineage = self.veckey.necessarylineage
if necessarylineage is not None:
if necessarylineage > 0:
namespace = namespace[-necessarylineage:]
else:
namespace = None
if "hiddenlineage" not in excluded:
necessarylineage = self.necessarylineage
if necessarylineage is None and self.veckey:
necessarylineage = self.veckey.necessarylineage
if necessarylineage is not None:
if necessarylineage > 0:
namespace = namespace[-necessarylineage:]
else:
namespace = None
if namespace:
name = ".".join(namespace) + "." + name
if "idx" not in excluded:
Expand Down

0 comments on commit e3ed0f9

Please sign in to comment.