Skip to content

Commit

Permalink
major refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
bqpd committed Mar 7, 2020
1 parent 443d44b commit 96e36c7
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 187 deletions.
2 changes: 1 addition & 1 deletion docs/source/examples/boundschecking.py
Expand Up @@ -49,7 +49,7 @@ def setup(self):
m.solve()
except UnboundedGP:
gp = m.gp(checkbounds=False)
missingbounds = gp.check_bounds(err_on_missing_bounds=False)
missingbounds = gp.check_bounds()

try:
sol = gp.solve(verbosity=0) # Errors on mosek_cli
Expand Down
6 changes: 3 additions & 3 deletions docs/source/examples/external_constraint.py
Expand Up @@ -17,13 +17,13 @@ def as_hmapslt1(self, _):
"Ensures this is treated as an SGP constraint"
raise InvalidGPConstraint("ExternalConstraint cannot solve as a GP.")

def approx_as_posyslt1(self, x0):
def as_gpconstr(self, x0):
"Returns locally-approximating GP constraint"
# Creating a default constraint for the first solve
if self.x not in x0:
return [self.x/self.y]
return (self.y >= self.x)
# Otherwise calls external code at the current position...
x_star = x0[self.x]
res = external_code(x_star)
# ...and returns a linearized posy <= 1
return [res*self.x/x_star/self.y]
return (self.y >= res * self.x/x_star)
38 changes: 9 additions & 29 deletions gpkit/constraints/gp.py
Expand Up @@ -8,7 +8,6 @@
from ..keydict import KeyDict
from ..solution_array import SolutionArray
from .set import ConstraintSet
from .costed import CostedConstraintSet
from ..exceptions import (InvalidPosynomial, Infeasible, UnknownInfeasible,
PrimalInfeasible, DualInfeasible, UnboundedGP)

Expand Down Expand Up @@ -48,7 +47,7 @@ def _get_solver(solver, kwargs):
return solver, optimize


class GeometricProgram(CostedConstraintSet):
class GeometricProgram:
# pylint: disable=too-many-instance-attributes
"""Standard mathematical representation of a GP.
Expand All @@ -69,16 +68,8 @@ class GeometricProgram(CostedConstraintSet):
"""
_result = solve_log = solver_out = model = v_ss = nu_by_posy = None

def __init__(self, cost, constraints, substitutions,
*, checkbounds=True):
# pylint:disable=super-init-not-called,non-parent-init-called
def __init__(self, cost, constraints, substitutions, *, checkbounds=True):
self.cost, self.substitutions = cost, substitutions
if isinstance(constraints, dict):
self.idxlookup = {k: i for i, k in enumerate(constraints)}
constraints = list(constraints.values())
elif isinstance(constraints, ConstraintSet):
constraints = [constraints]
list.__init__(self, constraints)
for key, sub in self.substitutions.items():
if isinstance(sub, FixedScalar):
sub = sub.value
Expand All @@ -90,8 +81,9 @@ def __init__(self, cost, constraints, substitutions,
" %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("cost must be a Posynomial")
self.hmaps = [cost_hmap] + list(self.as_hmapslt1(self.substitutions))
raise InvalidPosynomial("a GP's cost must be Posynomial")
hmapgen = ConstraintSet.as_hmapslt1(constraints, self.substitutions)
self.hmaps = [cost_hmap] + list(hmapgen)
self.gen() # Generate various maps into the posy- and monomials
if checkbounds:
self.check_bounds(err_on_missing_bounds=True)
Expand Down Expand Up @@ -166,8 +158,7 @@ def gen(self):
self.A = CootMatrix(row, col, data)

# pylint: disable=too-many-statements, too-many-locals
def solve(self, solver=None, *, verbosity=1,
process_result=True, gen_result=True, **kwargs):
def solve(self, solver=None, *, verbosity=1, gen_result=True, **kwargs):
"""Solves a GeometricProgram and returns the solution.
Arguments
Expand Down Expand Up @@ -233,10 +224,9 @@ def solve(self, solver=None, *, verbosity=1,

if gen_result: # NOTE: SIDE EFFECTS
self._result = self.generate_result(solver_out,
verbosity=verbosity-2,
process_result=process_result)
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)

Expand All @@ -249,20 +239,17 @@ def result(self):
self._result = self.generate_result(self.solver_out)
return self._result

def generate_result(self, solver_out, *, verbosity=0,
process_result=True, dual_check=True):
def generate_result(self, solver_out, *, verbosity=0, dual_check=True):
"Generates a full SolutionArray and checks it."
if verbosity > 0:
soltime = solver_out["soltime"]
tic = time()

# result packing #
result = self._compile_result(solver_out) # NOTE: SIDE EFFECTS
if verbosity > 0:
print("Result packing took %.2g%% of solve time." %
((time() - tic) / soltime * 100))
tic = time()

# solution checking #
try:
tol = SOLUTION_TOL.get(solver_out["solver"], 1e-5)
Expand All @@ -276,13 +263,6 @@ def generate_result(self, solver_out, *, verbosity=0,
print("Solution checking took %.2g%% of solve time." %
((time() - tic) / soltime * 100))
tic = time()

# result processing #
if process_result:
self.process_result(result)
if verbosity > 0:
print("Processing results took %.2g%% of solve time." %
((time() - tic) / soltime * 100))
return result

def _generate_nula(self, solver_out):
Expand Down
6 changes: 1 addition & 5 deletions gpkit/constraints/model.py
Expand Up @@ -166,11 +166,7 @@ def autosweep(self, sweeps, tol=0.01, samplepoints=100, **solveargs):

# pylint: disable=too-many-locals,too-many-branches,too-many-statements
def debug(self, solver=None, verbosity=1, **solveargs):
"""Attempts to diagnose infeasible models.
If a model debugs but errors in a process_result call, debug again
with `process_results=False`
"""
"Attempts to diagnose infeasible models."
from .relax import ConstantsRelaxed, ConstraintsRelaxed
from .bounded import Bounded

Expand Down
7 changes: 6 additions & 1 deletion gpkit/constraints/prog_factories.py
Expand Up @@ -124,6 +124,8 @@ def solvefn(self, solver=None, *, verbosity=1, skipsweepfailures=False,
else:
self.program, progsolve = genfunction(self)
result = progsolve(solver, verbosity=verbosity, **solveargs)
if solveargs.get("process_result", True):
self.process_result(result)
solution.append(result)
solution.to_arrays()
self.solution = solution
Expand Down Expand Up @@ -162,7 +164,10 @@ def run_sweep(genfunction, self, solution, skipsweepfailures,
program, solvefn = genfunction(self, constants)
self.program.append(program) # NOTE: SIDE EFFECTS
try:
solution.append(solvefn(solver, verbosity=verbosity-1, **solveargs))
result = solvefn(solver, verbosity=verbosity-1, **solveargs)
if solveargs.get("process_result", True):
self.process_result(result)
solution.append(result)
except Infeasible as e:
last_error = e
if not skipsweepfailures:
Expand Down
5 changes: 3 additions & 2 deletions gpkit/constraints/set.py
Expand Up @@ -159,8 +159,9 @@ def constrained_varkeys(self):

def as_hmapslt1(self, subs):
"Yields hmaps<=1 from self.flat()"
yield from chain(*(l.as_hmapslt1(subs)
for l in self.flat(yield_if_hasattr="as_hmapslt1")))
yield from chain(*(c.as_hmapslt1(subs)
for c in flatiter(self,
yield_if_hasattr="as_hmapslt1")))

def process_result(self, result):
"""Does arbitrary computation / manipulation of a program's result
Expand Down

0 comments on commit 96e36c7

Please sign in to comment.