Skip to content

Commit

Permalink
lint
Browse files Browse the repository at this point in the history
  • Loading branch information
bqpd committed Nov 18, 2020
1 parent f0a03d3 commit 5a27665
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 85 deletions.
4 changes: 2 additions & 2 deletions docs/source/examples/boundschecking_output.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
BoundsChecking
==============

Cost
----
Cost Function
-------------
F

Constraints
Expand Down
4 changes: 2 additions & 2 deletions docs/source/examples/performance_modeling_output.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

Cost
----
Cost Function
-------------
Wfuel[0]

Constraints
Expand Down
16 changes: 8 additions & 8 deletions docs/source/examples/relaxation_output.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Original model
==============

Cost
----
Cost Function
-------------
x

Constraints
Expand All @@ -13,8 +13,8 @@ Constraints
With constraints relaxed equally
================================

Cost
----
Cost Function
-------------
C

Constraints
Expand Down Expand Up @@ -63,8 +63,8 @@ Most Sensitive Constraints
With constraints relaxed individually
=====================================

Cost
----
Cost Function
-------------
C[:].prod()·x^0.01

Constraints
Expand Down Expand Up @@ -115,8 +115,8 @@ Most Sensitive Constraints
With constants relaxed individually
===================================

Cost
----
Cost Function
-------------
[Relax2.x_max, Relax2.x_min].prod()·x^0.01

Constraints
Expand Down
2 changes: 1 addition & 1 deletion gpkit/constraints/bounded.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def __init__(self, constraints, *, eps=1e-30, lower=None, upper=None):

def process_result(self, result):
"Add boundedness to the model's solution"
ConstraintSet.process_result(self, result)
super().process_result(result)
if "boundedness" not in result:
result["boundedness"] = {}
result["boundedness"].update(self.check_boundaries(result))
Expand Down
6 changes: 3 additions & 3 deletions gpkit/constraints/costed.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class CostedConstraintSet(ConstraintSet):
---------
cost : gpkit.Posynomial
constraints : Iterable
substitutions : dict (None)
substitutions : dict
"""
lineage = None

Expand All @@ -35,10 +35,10 @@ def constrained_varkeys(self):
def _rootlines(self, excluded=()):
"String showing cost, to be used when this is the top constraint"
if self.cost.varkeys:
description = ["", "Cost", "----",
description = ["", "Cost Function", "-------------",
" %s" % self.cost.str_without(excluded),
"", "Constraints", "-----------"]
else:
else: # don't print the cost if it's a constant
description = ["", "Constraints", "-----------"]
if self.lineage:
fullname = lineagestr(self)
Expand Down
111 changes: 47 additions & 64 deletions gpkit/constraints/gp.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ class GeometricProgram:
Examples
--------
>>> gp = gpkit.geometric_program.GeometricProgram(
>>> gp = gpkit.constraints.gp.GeometricProgram(
# minimize
x,
[ # subject to
1/x # <= 1, implicitly
x >= 1,
], {})
>>> gp.solve()
"""
Expand All @@ -78,8 +78,8 @@ def __init__(self, cost, constraints, substitutions, *, checkbounds=True):
sub = sub.to(key.units or "dimensionless").magnitude
self.substitutions[key] = sub
if not isinstance(sub, (Numbers, np.ndarray)):
raise ValueError("substitution {%s: %s} has invalid value type"
" %s." % (key, sub, type(sub)))
raise TypeError("substitution {%s: %s} has invalid value type"
" %s." % (key, sub, type(sub)))
cost_hmap = cost.hmap.sub(self.substitutions, cost.varkeys)
if any(c <= 0 for c in cost_hmap.values()):
raise InvalidPosynomial("a GP's cost must be Posynomial")
Expand Down Expand Up @@ -117,18 +117,21 @@ def check_bounds(self, *, err_on_missing_bounds=False):
return missingbounds

def gen(self):
"Generates nomial and solve data (A, p_idxs) from posynomials"
# k [posys]: number of monomials (rows of A) present in each constraint
"""Generates nomial and solve data (A, p_idxs) from posynomials.
k [posys]: number of monomials (rows of A) present in each constraint
m_idxs [mons]: monomial indices of each posynomial
p_idxs [mons]: posynomial index of each monomial
cs, exps [mons]: coefficient and exponents of each monomial
varlocs: {vk: monomial indices of each variables' location}
meq_idxs: {all indices of equality mons} and {the first index of each}
varidxs: {vk: which column corresponds to it in A}
A [mons, vks]: sparse array of each monomials' variables' exponents
"""
self.k = [len(hmap) for hmap in self.hmaps]
# m_idxs [mons]: monomial indices of each posynomial
self.m_idxs = []
# p_idxs [mons]: posynomial index of each monomial
self.p_idxs = []
# cs, exps [mons]: coefficient and exponents of each monomial
self.cs, self.exps = [], []
# varlocs: {vk: monomial indices of each variables' location}
self.m_idxs, self.p_idxs, self.cs, self.exps = [], [], [], []
self.varkeys = self.varlocs = defaultdict(list)
# meq_idxs: {all indices of equality mons} and {just the first halves}
self.meq_idxs = MonoEqualityIndexes()
m_idx = 0
row, col, data = [], [], []
Expand All @@ -149,13 +152,12 @@ def gen(self):
for var in exp:
self.varlocs[var].append(m_idx)
m_idx += 1
self.p_idxs = np.array(self.p_idxs, "int32") # for later use as array
self.p_idxs = np.array(self.p_idxs, "int32") # to use array equalities
self.varidxs = {vk: i for i, vk in enumerate(self.varlocs)}
for j, (var, locs) in enumerate(self.varlocs.items()):
row.extend(locs)
col.extend([j]*len(locs))
data.extend(self.exps[i][var] for i in locs)
# A [mons, vks]: sparse array of each monomials' variables' exponents
self.A = CootMatrix(row, col, data)

# pylint: disable=too-many-statements, too-many-locals
Expand All @@ -170,44 +172,46 @@ def solve(self, solver=None, *, verbosity=1, gen_result=True, **kwargs):
If a function, passes that function cs, A, p_idxs, and k.
verbosity : int (default 1)
If greater than 0, prints solver name and solve time.
gen_result : bool (default True)
If True, makes a human-readable SolutionArray from solver output.
**kwargs :
Passed to solver constructor and solver function.
Returns
-------
result : SolutionArray
SolutionArray (or dict if gen_result is False)
"""
solvername, solverfn = _get_solver(solver, kwargs)
solverargs = DEFAULT_SOLVER_KWARGS.get(solvername, {})
solverargs.update(kwargs)
solver_out = {}

if verbosity > 0:
print("Using solver '%s'" % solvername)
print(" for %i free variables" % len(self.varlocs))
print(" in %i posynomial inequalities." % len(self.k))

solverargs = DEFAULT_SOLVER_KWARGS.get(solvername, {})
solverargs.update(kwargs)
starttime = time()
infeasibility, original_stdout = None, sys.stdout
solver_out, infeasibility, original_stdout = {}, None, sys.stdout
try:
sys.stdout = SolverLog(original_stdout, verbosity=verbosity-2)
solver_out = solverfn(c=self.cs, A=self.A, meq_idxs=self.meq_idxs,
k=self.k, p_idxs=self.p_idxs, **solverargs)
except Infeasible as e:
infeasibility = e
except InvalidLicense as e:
raise InvalidLicense("license for solver \"%s\" is invalid."
% solvername) from e
except Exception as e:
if isinstance(e, InvalidLicense):
raise InvalidLicense("license for solver \"%s\" is invalid."
% solvername) from e
raise UnknownInfeasible("Something unexpected went wrong.") from e
finally:
self.solve_log = "\n".join(sys.stdout)
sys.stdout = original_stdout
self.solver_out = solver_out
solver_out["solver"] = solvername
solver_out["soltime"] = time() - starttime
if verbosity > 0:
print("Solving took %.3g seconds." % solver_out["soltime"])

solver_out["solver"] = solvername
solver_out["soltime"] = time() - starttime
if verbosity > 0:
print("Solving took %.3g seconds." % solver_out["soltime"])

if infeasibility:
if isinstance(infeasibility, PrimalInfeasible):
Expand All @@ -218,23 +222,21 @@ def solve(self, solver=None, *, verbosity=1, gen_result=True, **kwargs):
" bounding the right variables would prevent this.")
elif isinstance(infeasibility, UnknownInfeasible):
msg = "The solver failed for an unknown reason."
if verbosity > 0 and solver_out["soltime"] < 1:
print(msg + "\nSince this model solved in less than a second,"
" let's run `.debug()` automatically to check.\n`")
if (verbosity > 0 and solver_out["soltime"] < 1
and hasattr(self, "model")): # fast, top-level model
print(msg + "\nSince the model solved in less than a second,"
" let's run `.debug()` to analyze what happened.\n`")
return self.model.debug(solver=solver)
# else, raise a clarifying error
msg += (" Running `.debug()` may pinpoint the trouble. You can"
" also try another solver, or increase the verbosity.")
raise infeasibility.__class__(msg) from infeasibility

if gen_result: # NOTE: SIDE EFFECTS
self._result = self.generate_result(solver_out,
verbosity=verbosity-2)
return self.result
# TODO: remove this "generate_result" closure
solver_out["generate_result"] = \
lambda: self.generate_result(solver_out, dual_check=False)

return solver_out
if not gen_result:
return solver_out
# else, generate a human-readable SolutionArray
self._result = self.generate_result(solver_out, verbosity=verbosity-2)
return self.result

@property
def result(self):
Expand All @@ -261,12 +263,11 @@ def generate_result(self, solver_out, *, verbosity=0, dual_check=True):
solver_out["nu"], solver_out["la"], tol)
except Infeasible as chkerror:
chkwarn = str(chkerror)
if dual_check or ("Dual" not in chkwarn and "nu" not in chkwarn):
if not ("Dual" in chkwarn and not dual_check):
print("Solution check warning: %s" % chkwarn)
if verbosity > 0:
print("Solution checking took %.2g%% of solve time." %
((time() - tic) / soltime * 100))
tic = time()
return result

def _generate_nula(self, solver_out):
Expand All @@ -292,24 +293,6 @@ def _generate_nula(self, solver_out):
return la, nu_by_posy

def _compile_result(self, solver_out):
"""Creates a result dict (as returned by solve() from solver output
This internal method is called from within the solve() method, unless
solver_out["status"] is not "optimal", in which case a RuntimeWarning
is raised prior to this method being called. In that case, users
may use this method to attempt to create a results dict from the
output of the failed solve.
Arguments
---------
solver_out: dict
dict in format returned by solverfn within GeometricProgram.solve
Returns
-------
result: dict
dict in format returned by GeometricProgram.solve()
"""
result = {"cost": float(solver_out["objective"])}
primal = solver_out["primal"]
if len(self.varlocs) != len(primal):
Expand Down Expand Up @@ -399,7 +382,7 @@ def almost_equal(num1, num2):
raise Infeasible("Dual solution has negative entries as"
" large as %s." % minnu)
if any(np.abs(A.T.dot(nu)) > tol):
raise Infeasible("Sum of nu^T * A did not vanish.")
raise Infeasible("Dual: sum of nu^T * A did not vanish.")
b = np.log(self.cs)
dual_cost = sum(
self.nu_by_posy[i].dot(
Expand Down Expand Up @@ -428,8 +411,8 @@ def gen_meq_bounds(missingbounds, exps, meq_idxs): # pylint: disable=too-many-l
n_lower.add((key, "lower"))
# (consider x*y/z == 1)
# for a var (e.g. x) to be upper bounded by this monomial equality,
# - vars of the same sign/side (y) must be lower bounded
# - AND vars of the opposite sign/side (z) must be upper bounded
# - vars of the same sign (y) must be lower bounded
# - AND vars of the opposite sign (z) must be upper bounded
p_ub = n_lb = frozenset(n_upper).union(p_lower)
n_ub = p_lb = frozenset(p_upper).union(n_lower)
for keys, ub in ((p_upper, p_ub), (n_upper, n_ub)):
Expand Down
6 changes: 3 additions & 3 deletions gpkit/constraints/relax.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def __init__(self, original_constraints):

def process_result(self, result):
"Warns if any constraints were relaxed"
ConstraintSet.process_result(self, result)
super().process_result(result)
self.check_relaxed(result)

def check_relaxed(self, result):
Expand Down Expand Up @@ -96,7 +96,7 @@ def __init__(self, original_constraints):

def process_result(self, result):
"Warns if any constraints were relaxed"
ConstraintSet.process_result(self, result)
super().process_result(result)
self.check_relaxed(result)

def check_relaxed(self, result):
Expand Down Expand Up @@ -212,7 +212,7 @@ def __init__(self, constraints, *, include_only=None, exclude=None):

def process_result(self, result):
"Transfers constant sensitivities back to the original constants"
ConstraintSet.process_result(self, result)
super().process_result(result)
constant_senss = result["sensitivities"]["variables"]
for new_constant, former_constant in self._derelax_map.items():
constant_senss[former_constant] = constant_senss[new_constant]
Expand Down
3 changes: 2 additions & 1 deletion gpkit/constraints/sgp.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ def localsolve(self, solver=None, *, verbosity=1, x0=None, reltol=1e-4,
def results(self):
"Creates and caches results from the raw solver_outs"
if not self._results:
self._results = [o["generate_result"]() for o in self.solver_outs]
self._results = [gp.generate_result(s_o, dual_check=False)
for gp, s_o in zip(self.gps, self.solver_outs)]
return self._results

def gp(self, x0=None, *, cleanx0=False):
Expand Down
2 changes: 1 addition & 1 deletion gpkit/tests/t_sub.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def test_bad_gp_sub(self):
x = Variable("x")
y = Variable("y")
m = Model(x, [y >= 1], {y: x})
with self.assertRaises(ValueError):
with self.assertRaises(TypeError):
m.solve()

def test_quantity_sub(self):
Expand Down

0 comments on commit 5a27665

Please sign in to comment.