Skip to content

Commit

Permalink
cleanup, fixes, lint
Browse files Browse the repository at this point in the history
  • Loading branch information
bqpd committed Mar 7, 2020
1 parent 96e36c7 commit 3c61114
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 31 deletions.
5 changes: 0 additions & 5 deletions docs/source/examples/external_constraint.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"Can be found in gpkit/docs/source/examples/external_constraint.py"
from gpkit.exceptions import InvalidGPConstraint
from external_function import external_code


Expand All @@ -13,10 +12,6 @@ def __init__(self, x, y):
self.x = x
self.y = y

def as_hmapslt1(self, _):
"Ensures this is treated as an SGP constraint"
raise InvalidGPConstraint("ExternalConstraint cannot solve as a GP.")

def as_gpconstr(self, x0):
"Returns locally-approximating GP constraint"
# Creating a default constraint for the first solve
Expand Down
3 changes: 2 additions & 1 deletion gpkit/constraints/set.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ def __init__(self, constraints, substitutions=None): # pylint: disable=too-many
for i, constraint in enumerate(self):
if hasattr(constraint, "varkeys"):
self._update(constraint)
elif not hasattr(constraint, "as_hmapslt1"):
elif not (hasattr(constraint, "as_hmapslt1")
or hasattr(constraint, "as_gpconstr")):
try:
for subconstraint in flatiter(constraint, "varkeys"):
self._update(subconstraint)
Expand Down
28 changes: 15 additions & 13 deletions gpkit/constraints/sgp.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ class SequentialGeometricProgram:

def __init__(self, cost, model, substitutions, *,
use_pccp=True, pccp_penalty=2e2, checkbounds=True):
self.substitutions = substitutions
self.pccp_penalty = pccp_penalty
if cost.any_nonpositive_cs:
raise InvalidPosynomial("""an SGP's cost must be Posynomial
Expand All @@ -65,16 +64,20 @@ def __init__(self, cost, model, substitutions, *,
cost *= self.slack**pccp_penalty
self.approxconstraints = []
self.sgpvks = set()
x0 = KeyDict(substitutions)
x0.varkeys = model.varkeys # for string access and so forth
for cs in model.flat():
try:
if not hasattr(cs, "as_hmapslt1"):
raise InvalidGPConstraint(cs)
if not isinstance(cs, PosynomialInequality):
cs.as_hmapslt1(substitutions) # gp-compatible?
self.gpconstraints.append(cs)
except InvalidGPConstraint:
if not hasattr(cs, "as_gpconstr"):
raise InvalidSGPConstraint(cs)
self.sgpconstraints.append(cs)
for hmaplt1 in cs.as_gpconstr({}).as_hmapslt1({}):
for hmaplt1 in cs.as_gpconstr(x0).as_hmapslt1({}):
constraint = (Posynomial(hmaplt1) <= self.slack)
constraint.generated_by = cs
self.approxconstraints.append(constraint)
Expand All @@ -88,8 +91,7 @@ def __init__(self, cost, model, substitutions, *,
self._gp = GeometricProgram(
cost, self.approxconstraints + self.gpconstraints,
substitutions, checkbounds=checkbounds)
self._gp.x0 = KeyDict()
self._gp.x0.varkeys = model.varkeys # for string access, etc.
self._gp.x0 = x0
self.a_idxs = defaultdict(list)
cost_mons = self._gp.k[0]
sp_mons = sum(self._gp.k[:1+len(self.approxconstraints)])
Expand Down Expand Up @@ -182,11 +184,11 @@ def localsolve(self, solver=None, *, verbosity=1, x0=None, reltol=1e-4,
print("Solving took %.3g seconds and %i GP solves."
% (self.result["soltime"], len(self.gps)))
if hasattr(self.slack, "key"):
excess_slack = self.result["variables"][self.slack.key] - 1
excess_slack = self.result["variables"][self.slack.key] - 1 # pylint: disable=no-member
if excess_slack <= EPS:
del self.result["freevariables"][self.slack.key]
del self.result["variables"][self.slack.key]
del self.result["sensitivities"]["variables"][self.slack.key]
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
slackconstraint = self.gpconstraints[0]
del self.result["sensitivities"]["constraints"][slackconstraint]
elif verbosity > -1:
Expand Down Expand Up @@ -214,12 +216,12 @@ def gp(self, x0=None, *, cleanx0=False):
x0 = KeyDict(x0)
self._gp.x0.update({vk: x0[vk] for vk in self.sgpvks if vk in x0})
p_idx = 0
for sp_constraint in self.sgpconstraints:
for hmaplt1 in sp_constraint.as_gpconstr(self._gp.x0).as_hmapslt1({}):
approx_constraint = self.approxconstraints[p_idx]
approx_constraint.unsubbed = [Posynomial(hmaplt1)/self.slack]
for sgpc in self.sgpconstraints:
for hmaplt1 in sgpc.as_gpconstr(self._gp.x0).as_hmapslt1({}):
approxc = self.approxconstraints[p_idx]
approxc.unsubbed = [Posynomial(hmaplt1)/self.slack]
p_idx += 1 # p_idx=0 is the cost; sp constraints are after it
hmap, = approx_constraint.as_hmapslt1(self.substitutions)
hmap, = approxc.as_hmapslt1(self._gp.substitutions)
self._gp.hmaps[p_idx] = hmap
m_idx = self._gp.m_idxs[p_idx].start
a_idxs = list(self.a_idxs[p_idx]) # A's entries we can modify
Expand Down
18 changes: 7 additions & 11 deletions gpkit/nomials/math.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,10 +504,6 @@ def sens_from_dual(self, la, nu, _):
self.v_ss[vk] = nu_i*x + self.v_ss.get(vk, 0)
return self.v_ss, la

def as_gpconstr(self, _):
"The GP version of a Posynomial constraint is itself"
return self


class MonomialEquality(PosynomialInequality):
"A Constraint of the form Monomial == Monomial."
Expand Down Expand Up @@ -542,7 +538,7 @@ def _gen_unsubbed(self, left, right): # pylint: disable=arguments-differ

def as_hmapslt1(self, substitutions):
"Tags posynomials for dual feasibility checking"
out = PosynomialInequality.as_hmapslt1(self, substitutions)
out = super().as_hmapslt1(substitutions)
for h in out:
h.from_meq = True # pylint: disable=attribute-defined-outside-init
return out
Expand All @@ -567,7 +563,7 @@ def sens_from_dual(self, la, nu, _):
class SignomialInequality(ScalarSingleEquationConstraint):
"""A constraint of the general form posynomial >= posynomial
Stored internally (exps, cs) as a single Signomial (0 >= self)"""
Stored at .unsubbed[0] as a single Signomial (0 >= self)"""

def __init__(self, left, oper, right):
ScalarSingleEquationConstraint.__init__(self, left, oper, right)
Expand All @@ -585,7 +581,7 @@ def __init__(self, left, oper, right):
self.nomials.extend(self.unsubbed)
self.bounded = self.as_gpconstr({}).bounded

def as_hmapslt1(self, substitutions=None):
def as_hmapslt1(self, substitutions):
"Returns the posys <= 1 representation of this constraint."
siglt0, = self.unsubbed
siglt0 = siglt0.sub(substitutions, require_positive=False)
Expand Down Expand Up @@ -666,7 +662,7 @@ def subval(posy):
return var_senss, la

def as_gpconstr(self, x0):
"Returns GP approximation of an SP constraint at x0"
"Returns GP-compatible approximation at x0"
siglt0, = self.unsubbed
posy, negy = siglt0.posy_negy()
# default guess of 1.0 for unspecified negy variables
Expand All @@ -684,12 +680,12 @@ def __init__(self, left, right):
self.oper = "="
self.meq_bounded = self.as_gpconstr({}).meq_bounded

def as_hmapslt1(self, substitutions=None):
"Returns the posys <= 1 representation of this constraint."
def as_hmapslt1(self, substitutions):
"SignomialEquality is never considered GP-compatible"
raise InvalidGPConstraint(self)

def as_gpconstr(self, x0):
"Returns GP approximation of an SP constraint at x0"
"Returns GP-compatible approximation at x0"
siglt0, = self.unsubbed
posy, negy = siglt0.posy_negy()
# default guess of 1.0 for unspecified negy variables
Expand Down
2 changes: 1 addition & 1 deletion gpkit/tests/t_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ def test_reassigned_constant_cost(self):
m.localsolve(verbosity=0, solver=self.solver)
del m.substitutions[x_min]
m.cost = 1/x_min
self.assertNotIn(x_min, m.sp().substitutions) # pylint: disable=no-member
self.assertNotIn(x_min, m.sp().gp().substitutions) # pylint: disable=no-member

def test_unbounded_debugging(self):
"Test nearly-dual-feasible problems"
Expand Down

0 comments on commit 3c61114

Please sign in to comment.