From 7817d0704494bfd673f0a7ed7d55f1d0b505f31e Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Thu, 12 Sep 2019 14:57:26 -0400 Subject: [PATCH 01/22] Adding a couple of test scripts. --- gpkit/constraints/sp.py | 127 +++++++++++++++++++++++++++++ gpkit/constraints/sp_testscript.py | 47 +++++++++++ 2 files changed, 174 insertions(+) create mode 100644 gpkit/constraints/sp.py create mode 100644 gpkit/constraints/sp_testscript.py diff --git a/gpkit/constraints/sp.py b/gpkit/constraints/sp.py new file mode 100644 index 000000000..897bcb746 --- /dev/null +++ b/gpkit/constraints/sp.py @@ -0,0 +1,127 @@ +"""Implement the SignomialProgram class""" +from __future__ import unicode_literals, print_function +from time import time +from collections import OrderedDict +import numpy as np +from ..exceptions import InvalidGPConstraint +from ..keydict import KeyDict +from ..nomials import Variable, VectorVariable +from .gp import GeometricProgram +from ..nomials import SignomialInequality, PosynomialInequality +from ..nomials import SingleSignomialEquality +from .. import SignomialsEnabled, NamedVariables +from .costed import CostedConstraintSet +from ..small_scripts import mag + + +# pylint: disable=too-many-instance-attributes +class SignomialProgram(CostedConstraintSet): + """Prepares a collection of signomials for a SP solve. + + Arguments + --------- + cost : Posynomial + Objective to minimize when solving + constraints : list of Constraint or SignomialConstraint objects + verbosity : int (optional) + Currently has no effect: SignomialPrograms don't know + anything new after being created, unlike GeometricPrograms. + + Attributes with side effects + ---------------------------- + TODO: DETERMINE WHAT TO SET AFTER MEETING WITH RILEY + `result` is set at the end of a solve + + """ + def __init__(self, cost, constraints, substitutions): + # pylint:disable=super-init-not-called + self.gps = [] + self.solver_outs = [] + self._results = [] + self.result = None + self._spconstrs = [] + self._spvars = set() + self._approx_lt = [] + self._numgpconstrs = None + self._gp = None + + if cost.any_nonpositive_cs: + raise TypeError("""SPs still need Posynomial objectives. + + The equivalent of a Signomial objective can be constructed by constraining + a dummy variable `z` to be greater than the desired Signomial objective `s` + (z >= s) and then minimizing that dummy variable.""") + self.__bare_init__(cost, constraints, substitutions, varkeys=True) + self.externalfn_vars = frozenset(Variable(v) for v in self.varkeys + if v.externalfn) + self.externalfns = bool(self.externalfn_vars) + if not self.externalfns: + self._gp = self.init_gp(self.substitutions) + if self._gp and not self._gp["SP approximations"]: + raise ValueError("""Model valid as a Geometric Program. + + SignomialPrograms should only be created with Models containing + Signomial Constraints, since Models without Signomials have global + solutions and can be solved with 'Model.solve()'.""") + + # pylint: disable=too-many-locals + # pylint: disable=too-many-arguments + # pylint: disable=too-many-statements + def localsolve(self, solver=None, verbosity=1, x0=None, reltol=1e-4, + iteration_limit=50, mutategp=True, **kwargs): + """Locally solves a SequentialGeometricProgram and returns the solution. + + Arguments + --------- + solver : str or function (optional) + By default uses one of the solvers found during installation. + If set to "mosek", "mosek_cli", or "cvxopt", uses that solver. + If set to a function, passes that function cs, A, p_idxs, and k. + verbosity : int (optional) + If greater than 0, prints solve time and number of iterations. + Each GP is created and solved with verbosity one less than this, so + if greater than 1, prints solver name and time for each GP. + x0 : dict (optional) + Initial location to approximate signomials about. + reltol : float + Iteration ends when this is greater than the distance between two + consecutive solve's objective values. + iteration_limit : int + Maximum GP iterations allowed. + mutategp: boolean + Prescribes whether to mutate the previously generated GP + or to create a new GP with every solve. + *args, **kwargs : + Passed to solver function. + + Returns + ------- + result : dict + A dictionary containing the translated solver result. + """ + return + + def generate_matrices(self): + return + + @property + def results(self): + "Creates and caches results from the raw solver_outs" + if not self._results: + self._results = [so["gen_result"]() for so in self.solver_outs] + return self._results + + def _fill_x0(self, x0): + "Returns a copy of x0 with subsitutions added." + x0kd = KeyDict() + x0kd.varkeys = self.varkeys + if x0: + x0kd.update(x0) + for key in self.varkeys: + if key in x0kd: + continue # already specified by input dict + elif key in self.substitutions: + x0kd[key] = self.substitutions[key] + # undeclared variables are handled by individual constraints + return x0kd + diff --git a/gpkit/constraints/sp_testscript.py b/gpkit/constraints/sp_testscript.py new file mode 100644 index 000000000..5950c8134 --- /dev/null +++ b/gpkit/constraints/sp_testscript.py @@ -0,0 +1,47 @@ +"""Implement the SignomialProgram class""" +from __future__ import unicode_literals, print_function +from time import time +from collections import OrderedDict +import numpy as np +from gpkit.nomials import Variable, VectorVariable +from gpkit.nomials import SignomialInequality, PosynomialInequality +from gpkit.nomials import SingleSignomialEquality, MonomialEquality +from gpkit import SignomialsEnabled, NamedVariables +from gpkit.constraints.costed import CostedConstraintSet +from gpkit.small_scripts import mag + +from robust.testing.models import simple_ac + +""" +Deciphering this code... +Each maps consists of two layers of lists. +First layer describes the number of constraints +Second layer contains the hmaps of all of the variables +that are involved in the constraints. + +""" + +m = simple_ac() +constraints = [i for i in m.flat(constraintsets=False)] +n_constr = len(constraints) +varkeys = sorted(m.varkeys) +n_vks = len(varkeys) +maps = {} +signomial_indices = [] +for i in range(n_constr): + constraint = constraints[i] + if isinstance(constraint, MonomialEquality): + maps[i] = [[monomial.hmap] for monomial in constraint.as_posyslt1()] + elif isinstance(constraint, PosynomialInequality): + maps[i] = [[monomial.hmap for monomial in constraint.as_posyslt1()]] + elif isinstance(constraint, SignomialInequality): + signomial_indices.append(i) + with SignomialsEnabled(): + if isinstance(constraint, SingleSignomialEquality): + # Putting constraints in less-than-zero representation + ltzero_rep = [constraint.right-constraint.left, constraint.left-constraint.right] + maps[i] = [[monomial.hmap for monomial in ltzero_rep[0].chop()], + [monomial.hmap for monomial in ltzero_rep[1].chop()]] + else: + ltzero_rep = (constraint.right-constraint.left)*(-1.+2*(constraint.oper=='>=')) + maps[i] = [[monomial.hmap for monomial in ltzero_rep.chop()]] From ef85a4cb2aeb79e0d6c2c4bb0599a208d2c110c7 Mon Sep 17 00:00:00 2001 From: Edward Burnell Date: Wed, 25 Sep 2019 14:43:58 -0400 Subject: [PATCH 02/22] basics to allow x**x --- gpkit/nomials/math.py | 8 ++++++-- gpkit/small_classes.py | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/gpkit/nomials/math.py b/gpkit/nomials/math.py index ddbcd7935..11df09f46 100644 --- a/gpkit/nomials/math.py +++ b/gpkit/nomials/math.py @@ -329,10 +329,14 @@ def __rtruediv__(self, other, rev=True): return self.__rdiv__(other) def __pow__(self, expo): - if isinstance(expo, Numbers): + if isinstance(expo, Numbers+(Signomial,)): (exp, c), = self.hmap.items() exp = exp*expo if expo else EMPTY_HV - hmap = NomialMap({exp: c**expo}) + if c != 1: + newc = c**expo + else: + newc = c + hmap = NomialMap({exp: newc}) if expo and self.hmap.units: hmap.units = self.hmap.units**expo else: diff --git a/gpkit/small_classes.py b/gpkit/small_classes.py index de9117bc7..a29a74cde 100644 --- a/gpkit/small_classes.py +++ b/gpkit/small_classes.py @@ -192,9 +192,8 @@ def __neg__(self): def __pow__(self, other): "Accepts scalars. Return Hashvector with each value put to a power." - if isinstance(other, Numbers): - return self.__class__({key: val**other - for (key, val) in self.items()}) + return self.__class__({key: val**other + for (key, val) in self.items()}) return NotImplemented def __mul__(self, other): @@ -202,7 +201,8 @@ def __mul__(self, other): If the other object inherits from dict, multiplication is element-wise and their key's intersection will form the new keys.""" - if isinstance(other, Numbers): + from gpkit.nomials.math import Signomial + if isinstance(other, Numbers + (Signomial,)): return self.__class__({key: val*other for (key, val) in self.items()}) elif isinstance(other, dict): From 5daf1bbc8583254059f6b46bb8fcb5a7b318a263 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Sat, 28 Sep 2019 16:36:21 -0400 Subject: [PATCH 03/22] Removing junk files from Riley and I, oops. --- gpkit/constraints/sp.py | 127 ----------------------------- gpkit/constraints/sp_testscript.py | 47 ----------- 2 files changed, 174 deletions(-) delete mode 100644 gpkit/constraints/sp.py delete mode 100644 gpkit/constraints/sp_testscript.py diff --git a/gpkit/constraints/sp.py b/gpkit/constraints/sp.py deleted file mode 100644 index 897bcb746..000000000 --- a/gpkit/constraints/sp.py +++ /dev/null @@ -1,127 +0,0 @@ -"""Implement the SignomialProgram class""" -from __future__ import unicode_literals, print_function -from time import time -from collections import OrderedDict -import numpy as np -from ..exceptions import InvalidGPConstraint -from ..keydict import KeyDict -from ..nomials import Variable, VectorVariable -from .gp import GeometricProgram -from ..nomials import SignomialInequality, PosynomialInequality -from ..nomials import SingleSignomialEquality -from .. import SignomialsEnabled, NamedVariables -from .costed import CostedConstraintSet -from ..small_scripts import mag - - -# pylint: disable=too-many-instance-attributes -class SignomialProgram(CostedConstraintSet): - """Prepares a collection of signomials for a SP solve. - - Arguments - --------- - cost : Posynomial - Objective to minimize when solving - constraints : list of Constraint or SignomialConstraint objects - verbosity : int (optional) - Currently has no effect: SignomialPrograms don't know - anything new after being created, unlike GeometricPrograms. - - Attributes with side effects - ---------------------------- - TODO: DETERMINE WHAT TO SET AFTER MEETING WITH RILEY - `result` is set at the end of a solve - - """ - def __init__(self, cost, constraints, substitutions): - # pylint:disable=super-init-not-called - self.gps = [] - self.solver_outs = [] - self._results = [] - self.result = None - self._spconstrs = [] - self._spvars = set() - self._approx_lt = [] - self._numgpconstrs = None - self._gp = None - - if cost.any_nonpositive_cs: - raise TypeError("""SPs still need Posynomial objectives. - - The equivalent of a Signomial objective can be constructed by constraining - a dummy variable `z` to be greater than the desired Signomial objective `s` - (z >= s) and then minimizing that dummy variable.""") - self.__bare_init__(cost, constraints, substitutions, varkeys=True) - self.externalfn_vars = frozenset(Variable(v) for v in self.varkeys - if v.externalfn) - self.externalfns = bool(self.externalfn_vars) - if not self.externalfns: - self._gp = self.init_gp(self.substitutions) - if self._gp and not self._gp["SP approximations"]: - raise ValueError("""Model valid as a Geometric Program. - - SignomialPrograms should only be created with Models containing - Signomial Constraints, since Models without Signomials have global - solutions and can be solved with 'Model.solve()'.""") - - # pylint: disable=too-many-locals - # pylint: disable=too-many-arguments - # pylint: disable=too-many-statements - def localsolve(self, solver=None, verbosity=1, x0=None, reltol=1e-4, - iteration_limit=50, mutategp=True, **kwargs): - """Locally solves a SequentialGeometricProgram and returns the solution. - - Arguments - --------- - solver : str or function (optional) - By default uses one of the solvers found during installation. - If set to "mosek", "mosek_cli", or "cvxopt", uses that solver. - If set to a function, passes that function cs, A, p_idxs, and k. - verbosity : int (optional) - If greater than 0, prints solve time and number of iterations. - Each GP is created and solved with verbosity one less than this, so - if greater than 1, prints solver name and time for each GP. - x0 : dict (optional) - Initial location to approximate signomials about. - reltol : float - Iteration ends when this is greater than the distance between two - consecutive solve's objective values. - iteration_limit : int - Maximum GP iterations allowed. - mutategp: boolean - Prescribes whether to mutate the previously generated GP - or to create a new GP with every solve. - *args, **kwargs : - Passed to solver function. - - Returns - ------- - result : dict - A dictionary containing the translated solver result. - """ - return - - def generate_matrices(self): - return - - @property - def results(self): - "Creates and caches results from the raw solver_outs" - if not self._results: - self._results = [so["gen_result"]() for so in self.solver_outs] - return self._results - - def _fill_x0(self, x0): - "Returns a copy of x0 with subsitutions added." - x0kd = KeyDict() - x0kd.varkeys = self.varkeys - if x0: - x0kd.update(x0) - for key in self.varkeys: - if key in x0kd: - continue # already specified by input dict - elif key in self.substitutions: - x0kd[key] = self.substitutions[key] - # undeclared variables are handled by individual constraints - return x0kd - diff --git a/gpkit/constraints/sp_testscript.py b/gpkit/constraints/sp_testscript.py deleted file mode 100644 index 5950c8134..000000000 --- a/gpkit/constraints/sp_testscript.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Implement the SignomialProgram class""" -from __future__ import unicode_literals, print_function -from time import time -from collections import OrderedDict -import numpy as np -from gpkit.nomials import Variable, VectorVariable -from gpkit.nomials import SignomialInequality, PosynomialInequality -from gpkit.nomials import SingleSignomialEquality, MonomialEquality -from gpkit import SignomialsEnabled, NamedVariables -from gpkit.constraints.costed import CostedConstraintSet -from gpkit.small_scripts import mag - -from robust.testing.models import simple_ac - -""" -Deciphering this code... -Each maps consists of two layers of lists. -First layer describes the number of constraints -Second layer contains the hmaps of all of the variables -that are involved in the constraints. - -""" - -m = simple_ac() -constraints = [i for i in m.flat(constraintsets=False)] -n_constr = len(constraints) -varkeys = sorted(m.varkeys) -n_vks = len(varkeys) -maps = {} -signomial_indices = [] -for i in range(n_constr): - constraint = constraints[i] - if isinstance(constraint, MonomialEquality): - maps[i] = [[monomial.hmap] for monomial in constraint.as_posyslt1()] - elif isinstance(constraint, PosynomialInequality): - maps[i] = [[monomial.hmap for monomial in constraint.as_posyslt1()]] - elif isinstance(constraint, SignomialInequality): - signomial_indices.append(i) - with SignomialsEnabled(): - if isinstance(constraint, SingleSignomialEquality): - # Putting constraints in less-than-zero representation - ltzero_rep = [constraint.right-constraint.left, constraint.left-constraint.right] - maps[i] = [[monomial.hmap for monomial in ltzero_rep[0].chop()], - [monomial.hmap for monomial in ltzero_rep[1].chop()]] - else: - ltzero_rep = (constraint.right-constraint.left)*(-1.+2*(constraint.oper=='>=')) - maps[i] = [[monomial.hmap for monomial in ltzero_rep.chop()]] From d3eeadbe47567f26c04cf8cb662f02795137f780 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Sat, 28 Sep 2019 19:12:54 -0400 Subject: [PATCH 04/22] Now checking exponent units. --- gpkit/nomials/math.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/gpkit/nomials/math.py b/gpkit/nomials/math.py index 11df09f46..c36f48d14 100644 --- a/gpkit/nomials/math.py +++ b/gpkit/nomials/math.py @@ -63,8 +63,17 @@ def __init__(self, hmap=None, cs=1, require_positive=True): # pylint: disable=t self.__class__ = Monomial else: self.__class__ = Posynomial + self.check_exps_units() self.ast = () + def check_exps_units(self): + """Checking that units of signomial exponents are dimensionless.""" + for exp in self.exps: + for k,v in exp.items(): + if isinstance(v, Signomial) and v.units: + raise ValueError("Exponent %s is united. Please normalize" + " or remove units." % v) + def diff(self, var): """Derivative of this with respect to a Variable From fcdeb2e87d393f4c42684077c3e37578a9c70db7 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Sun, 29 Sep 2019 11:41:24 -0400 Subject: [PATCH 05/22] Beginnings of testing, sig exp printing (darn python strings, not working yet). --- gpkit/nomials/core.py | 4 +++- gpkit/tests/t_nomials.py | 10 +++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/gpkit/nomials/core.py b/gpkit/nomials/core.py index 2def32126..693234fc2 100644 --- a/gpkit/nomials/core.py +++ b/gpkit/nomials/core.py @@ -24,7 +24,9 @@ def str_without(self, excluded=()): for (var, x) in exp.items(): if x != 0: varstr = var.str_without(excluded) - if x != 1: + if isinstance(x, Signomial): + varstr += "^(%s)" % x.str_without(excluded) #TODO: fix this string... + elif x != 1: varstr += "^%.2g" % x varstrs.append(varstr) varstrs.sort() diff --git a/gpkit/tests/t_nomials.py b/gpkit/tests/t_nomials.py index dcea3226c..05376c359 100644 --- a/gpkit/tests/t_nomials.py +++ b/gpkit/tests/t_nomials.py @@ -3,7 +3,7 @@ import sys import unittest from gpkit import Variable, Monomial, Posynomial, Signomial, SignomialsEnabled -from gpkit import VectorVariable, NomialArray +from gpkit import VectorVariable, NomialArray, Model from gpkit.nomials import NomialMap from gpkit.small_classes import HashVector from gpkit.exceptions import InvalidPosynomial @@ -273,6 +273,14 @@ def test_eq_ne(self): self.assertEqual(Signomial(-3), -3) self.assertNotEqual(Signomial(-3), 3) + def test_subbed_sig_exp(self): + x = Variable('x') + y = Variable('y', 1, 'm') + z = Variable('z', 2, 'm^2') + with SignomialsEnabled(): + # self.assertRaises(ValueError, x**(y) >= 1) + constraints = [x**(y*z**(-0.5)) >= 1] + m = Model(x, constraints) class TestPosynomial(unittest.TestCase): """TestCase for the Posynomial class""" From 8f4fd1f2e31e9052915ba4866c7bf19661214b3f Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Sun, 29 Sep 2019 12:36:02 -0400 Subject: [PATCH 06/22] Moving varexps check to NomialMap (better for subs). --- gpkit/nomials/map.py | 5 +++-- gpkit/nomials/math.py | 16 ++++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/gpkit/nomials/map.py b/gpkit/nomials/map.py index ac47b8aa6..711f5316f 100644 --- a/gpkit/nomials/map.py +++ b/gpkit/nomials/map.py @@ -20,8 +20,9 @@ class NomialMap(HashVector): x and y are VarKey objects. """ units = None - expmap = None # used for monomial-mapping postsubstitution; see .mmap() - csmap = None # used for monomial-mapping postsubstitution; see .mmap() + expmap = None # used for monomial-mapping postsubstitution; see .mmap() + csmap = None # used for monomial-mapping postsubstitution; see .mmap() + varexps = False # used for signomial exponent operations def copy(self): "Return a copy of this" diff --git a/gpkit/nomials/math.py b/gpkit/nomials/math.py index c36f48d14..2b7e2771f 100644 --- a/gpkit/nomials/math.py +++ b/gpkit/nomials/math.py @@ -63,16 +63,20 @@ def __init__(self, hmap=None, cs=1, require_positive=True): # pylint: disable=t self.__class__ = Monomial else: self.__class__ = Posynomial - self.check_exps_units() + self.check_exps() self.ast = () - def check_exps_units(self): - """Checking that units of signomial exponents are dimensionless.""" + def check_exps(self): + """Checking units and adding varkeys of signomial exponents.""" for exp in self.exps: for k,v in exp.items(): - if isinstance(v, Signomial) and v.units: - raise ValueError("Exponent %s is united. Please normalize" - " or remove units." % v) + if isinstance(v, Signomial): + if v.units: + raise ValueError("Exponent %s is united. Please " + "normalize or remove units." % v) + else: + self.hmap.varexps = True + self.varkeys.update(v.varkeys) def diff(self, var): """Derivative of this with respect to a Variable From fa7a4c103c9beff35216904336d8d4e8ce5ae207 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Wed, 2 Oct 2019 14:11:47 -0400 Subject: [PATCH 07/22] repr_conventions checks exponent type. --- gpkit/repr_conventions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpkit/repr_conventions.py b/gpkit/repr_conventions.py index c2067b108..0023f9930 100644 --- a/gpkit/repr_conventions.py +++ b/gpkit/repr_conventions.py @@ -123,7 +123,7 @@ def parse_ast(self, excluded=("units")): x = values[1] if left == "1": aststr = "1" - elif UNICODE_EXPONENTS and int(x) == x and x >= 2 and x <= 9: + elif isinstance(x, Numbers) and UNICODE_EXPONENTS and int(x) == x and x >= 2 and x <= 9: if int(x) in (2, 3): aststr = "%s%s" % (left, unichr(176+x)) elif int(x) in (4, 5, 6, 7, 8, 9): From 2ddc58e2b0132b17ac54a1493a9ed458c861c529 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Wed, 2 Oct 2019 14:54:48 -0400 Subject: [PATCH 08/22] Hmaps work as intended. Small victories! --- gpkit/nomials/core.py | 7 +++---- gpkit/nomials/math.py | 13 +++++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/gpkit/nomials/core.py b/gpkit/nomials/core.py index 693234fc2..426bacbed 100644 --- a/gpkit/nomials/core.py +++ b/gpkit/nomials/core.py @@ -1,10 +1,9 @@ "The shared non-mathematical backbone of all Nomials" from __future__ import unicode_literals, print_function from .data import NomialData -from ..small_classes import Numbers, FixedScalar +from ..small_classes import Numbers, FixedScalar, HashVector from ..small_scripts import nomial_latex_helper - class Nomial(NomialData): "Shared non-mathematical properties of all nomials" __div__ = None @@ -24,8 +23,8 @@ def str_without(self, excluded=()): for (var, x) in exp.items(): if x != 0: varstr = var.str_without(excluded) - if isinstance(x, Signomial): - varstr += "^(%s)" % x.str_without(excluded) #TODO: fix this string... + if isinstance(x, HashVector): + varstr += "^(%s)" % str(x) #TODO: fix this string... elif x != 1: varstr += "^%.2g" % x varstrs.append(varstr) diff --git a/gpkit/nomials/math.py b/gpkit/nomials/math.py index 2b7e2771f..416908c3a 100644 --- a/gpkit/nomials/math.py +++ b/gpkit/nomials/math.py @@ -344,8 +344,17 @@ def __rtruediv__(self, other, rev=True): def __pow__(self, expo): if isinstance(expo, Numbers+(Signomial,)): (exp, c), = self.hmap.items() - exp = exp*expo if expo else EMPTY_HV - if c != 1: + if isinstance(expo, Signomial): + exp = HashVector({key: expo.hmap*val for key, val in exp.items()}) + else: + exp = exp*expo if expo else EMPTY_HV + if c != 1 and isinstance(expo, Signomial): + raise ValueError("Float %s raised to Signomial %s is " + "not currently supported. Please replace %s " + "with a substituted " + "Variable." % (str(c), expo.str_without(), + str(c))) + elif c != 1: newc = c**expo else: newc = c From 45aa4d1abd9f99cbbda0d5e9d5d98a08a3931d3c Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Fri, 4 Oct 2019 14:32:35 -0400 Subject: [PATCH 09/22] Make sure we aren't losing varkeys in varexps. --- gpkit/nomials/math.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gpkit/nomials/math.py b/gpkit/nomials/math.py index 416908c3a..8238c4747 100644 --- a/gpkit/nomials/math.py +++ b/gpkit/nomials/math.py @@ -70,13 +70,14 @@ def check_exps(self): """Checking units and adding varkeys of signomial exponents.""" for exp in self.exps: for k,v in exp.items(): - if isinstance(v, Signomial): + if isinstance(v, HashVector): if v.units: raise ValueError("Exponent %s is united. Please " "normalize or remove units." % v) else: self.hmap.varexps = True - self.varkeys.update(v.varkeys) + for key,value in v.items(): + self.varkeys.update([k for k in key.keys()]) def diff(self, var): """Derivative of this with respect to a Variable From fc3ea67cad70073f044d08cb08fc7dbaa93ec042 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Fri, 4 Oct 2019 14:38:22 -0400 Subject: [PATCH 10/22] Pint. --- gpkit/nomials/math.py | 5 +++-- gpkit/repr_conventions.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/gpkit/nomials/math.py b/gpkit/nomials/math.py index 8238c4747..50f3fce6e 100644 --- a/gpkit/nomials/math.py +++ b/gpkit/nomials/math.py @@ -69,7 +69,7 @@ def __init__(self, hmap=None, cs=1, require_positive=True): # pylint: disable=t def check_exps(self): """Checking units and adding varkeys of signomial exponents.""" for exp in self.exps: - for k,v in exp.items(): + for _, v in exp.items(): if isinstance(v, HashVector): if v.units: raise ValueError("Exponent %s is united. Please " @@ -346,7 +346,8 @@ def __pow__(self, expo): if isinstance(expo, Numbers+(Signomial,)): (exp, c), = self.hmap.items() if isinstance(expo, Signomial): - exp = HashVector({key: expo.hmap*val for key, val in exp.items()}) + exp = HashVector({key: expo.hmap*val for key, + val in exp.items()}) else: exp = exp*expo if expo else EMPTY_HV if c != 1 and isinstance(expo, Signomial): diff --git a/gpkit/repr_conventions.py b/gpkit/repr_conventions.py index 0023f9930..cf36219dd 100644 --- a/gpkit/repr_conventions.py +++ b/gpkit/repr_conventions.py @@ -123,7 +123,8 @@ def parse_ast(self, excluded=("units")): x = values[1] if left == "1": aststr = "1" - elif isinstance(x, Numbers) and UNICODE_EXPONENTS and int(x) == x and x >= 2 and x <= 9: + elif isinstance(x, Numbers) and UNICODE_EXPONENTS and \ + int(x) == x and x >= 2 and x <= 9: if int(x) in (2, 3): aststr = "%s%s" % (left, unichr(176+x)) elif int(x) in (4, 5, 6, 7, 8, 9): From e6dcc0fca882b987300ab6c7c0a3cc23ecafd15f Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Wed, 16 Oct 2019 11:31:17 -0400 Subject: [PATCH 11/22] Checking for units in signomial exps. --- gpkit/nomials/math.py | 4 ++++ gpkit/tests/t_nomials.py | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/gpkit/nomials/math.py b/gpkit/nomials/math.py index 50f3fce6e..38d5d8ee3 100644 --- a/gpkit/nomials/math.py +++ b/gpkit/nomials/math.py @@ -346,6 +346,10 @@ def __pow__(self, expo): if isinstance(expo, Numbers+(Signomial,)): (exp, c), = self.hmap.items() if isinstance(expo, Signomial): + if expo.units: + raise ValueError("Exponents cannot be united. Please " + "remove units from Signomial %s." % + expo.str_without()) exp = HashVector({key: expo.hmap*val for key, val in exp.items()}) else: diff --git a/gpkit/tests/t_nomials.py b/gpkit/tests/t_nomials.py index 05376c359..806e6b276 100644 --- a/gpkit/tests/t_nomials.py +++ b/gpkit/tests/t_nomials.py @@ -274,11 +274,17 @@ def test_eq_ne(self): self.assertNotEqual(Signomial(-3), 3) def test_subbed_sig_exp(self): + a = Variable('a') + b = Variable('b') + c = Variable('c') x = Variable('x') y = Variable('y', 1, 'm') z = Variable('z', 2, 'm^2') with SignomialsEnabled(): # self.assertRaises(ValueError, x**(y) >= 1) + self.assertRaises(ValueError, (2*a)**c) # float**signomial check + self.assertRaises(ValueError, a**y) # united signomial check + self.assertRaises(ValueError, a**(2*b + z*y)) # dimension check constraints = [x**(y*z**(-0.5)) >= 1] m = Model(x, constraints) From df509038ef63e895f7be717cd89620e367207a08 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Thu, 17 Oct 2019 15:54:03 -0400 Subject: [PATCH 12/22] repr_conventions varexp printing fix. --- gpkit/repr_conventions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpkit/repr_conventions.py b/gpkit/repr_conventions.py index cf36219dd..bda01f59c 100644 --- a/gpkit/repr_conventions.py +++ b/gpkit/repr_conventions.py @@ -120,7 +120,7 @@ def parse_ast(self, excluded=("units")): aststr = "-%s" % parenthesize(strify(values, excluded), mult=False) elif oper == "pow": left = parenthesize(strify(values[0], excluded)) - x = values[1] + x = parenthesize(strify(values[1], excluded)) if left == "1": aststr = "1" elif isinstance(x, Numbers) and UNICODE_EXPONENTS and \ From 47ea4b77c6d2856dcb4842f4958a86de2b13ea64 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Sun, 20 Oct 2019 17:23:04 -0400 Subject: [PATCH 13/22] Updating vks instead of varkeys, lint. --- gpkit/nomials/math.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gpkit/nomials/math.py b/gpkit/nomials/math.py index 38d5d8ee3..14a5d2f37 100644 --- a/gpkit/nomials/math.py +++ b/gpkit/nomials/math.py @@ -76,8 +76,8 @@ def check_exps(self): "normalize or remove units." % v) else: self.hmap.varexps = True - for key,value in v.items(): - self.varkeys.update([k for k in key.keys()]) + for key, _ in v.items(): + self.vks.update([k for k in key.keys()]) def diff(self, var): """Derivative of this with respect to a Variable @@ -350,8 +350,7 @@ def __pow__(self, expo): raise ValueError("Exponents cannot be united. Please " "remove units from Signomial %s." % expo.str_without()) - exp = HashVector({key: expo.hmap*val for key, - val in exp.items()}) + exp = HashVector({k: expo.hmap*v for k, v in exp.items()}) else: exp = exp*expo if expo else EMPTY_HV if c != 1 and isinstance(expo, Signomial): From 106a6df9f7fa70ab48976e3a1751efba760d663d Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Sun, 20 Oct 2019 17:34:10 -0400 Subject: [PATCH 14/22] Replace str_without with try_str_without. --- gpkit/nomials/core.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gpkit/nomials/core.py b/gpkit/nomials/core.py index 426bacbed..3f4aca58c 100644 --- a/gpkit/nomials/core.py +++ b/gpkit/nomials/core.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals, print_function from .data import NomialData from ..small_classes import Numbers, FixedScalar, HashVector -from ..small_scripts import nomial_latex_helper +from ..small_scripts import nomial_latex_helper, try_str_without class Nomial(NomialData): "Shared non-mathematical properties of all nomials" @@ -23,8 +23,8 @@ def str_without(self, excluded=()): for (var, x) in exp.items(): if x != 0: varstr = var.str_without(excluded) - if isinstance(x, HashVector): - varstr += "^(%s)" % str(x) #TODO: fix this string... + if isinstance(x, (HashVector)): + varstr += "^(%s)" % try_str_without(x, []) #TODO: fix this string... elif x != 1: varstr += "^%.2g" % x varstrs.append(varstr) From afa8d3d920c2ffcfd126776c1eb1474b01b7d0a7 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Tue, 22 Oct 2019 11:19:31 -0400 Subject: [PATCH 15/22] genA works, but now sens_from_dual issues. --- gpkit/constraints/gp.py | 31 ++++++++++++++++++++++++++----- gpkit/constraints/sgp.py | 1 + gpkit/nomials/math.py | 6 +++++- gpkit/tests/t_nomials.py | 34 +++++++++++++++++++--------------- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/gpkit/constraints/gp.py b/gpkit/constraints/gp.py index 3bf5cc79e..7e5281e03 100644 --- a/gpkit/constraints/gp.py +++ b/gpkit/constraints/gp.py @@ -5,7 +5,7 @@ from time import time from collections import defaultdict import numpy as np -from ..nomials import NomialData +from ..nomials import NomialData, NomialMap from ..small_classes import CootMatrix, SolverLog, Numbers, FixedScalar from ..keydict import KeyDict from ..small_scripts import mag @@ -122,7 +122,7 @@ def gen(self): self._cs.extend(hmap.values()) self.vks = self.varlocs self.A, self.missingbounds = genA(self.exps, self.varlocs, - self.meq_idxs) + self.meq_idxs, self.substitutions) # pylint: disable=too-many-statements, too-many-locals def solve(self, solver=None, verbosity=1, warn_on_check=False, @@ -394,7 +394,7 @@ def _almost_equal(num1, num2): " cost %s" % (np.exp(dual_cost), cost)) -def genA(exps, varlocs, meq_idxs): # pylint: disable=invalid-name +def genA(exps, varlocs, meq_idxs, substitutions=None): # pylint: disable=invalid-name """Generates A matrix Returns @@ -407,13 +407,34 @@ def genA(exps, varlocs, meq_idxs): # pylint: disable=invalid-name """ missingbounds = {} row, col, data = [], [], [] + bte = set(meq_idxs) # variables bounded through equalities or sig exp substitutions for j, var in enumerate(varlocs): upperbound, lowerbound = False, False row.extend(varlocs[var]) col.extend([j]*len(varlocs[var])) - data.extend(exps[i][var] for i in varlocs[var]) for i in varlocs[var]: - if i not in meq_idxs: + if isinstance(exps[i][var], NomialMap): + varkeyDict = KeyDict({key:key for item in exps[i][var].keys() \ + for key in item.keys()}) + subbed_exp = exps[i][var].sub(substitutions, varkeyDict) + if subbed_exp.keys()[0]: + raise ValueError("Signomial exponent %s has variables that " + "have not been fully substituted. Complete " + "substitutions and try again." % \ + (exps[i])) #TODO: improve error. + data.extend([subbed_exp.values()[0]]) + # Add substituted variables in signomial exponent to bte + # to make them bounded. + bte = bte.union(varkeyDict.keys()) + # Checking boundedness of base variable + if subbed_exp.values()[0] > 0 and \ + not(upperbound and lowerbound): + upperbound = True + else: + lowerbound = True + else: + data.extend([exps[i][var]]) + if i not in bte: if upperbound and lowerbound: break elif exps[i][var] > 0: # pylint:disable=simplifiable-if-statement diff --git a/gpkit/constraints/sgp.py b/gpkit/constraints/sgp.py index d867bdd59..fd40ff7ec 100644 --- a/gpkit/constraints/sgp.py +++ b/gpkit/constraints/sgp.py @@ -194,6 +194,7 @@ def penalty_ccp_solve(self, solver=None, verbosity=1, x0=None, reltol=1e-4, x0, reltol, iteration_limit, mutategp, **kwargs) self.gps = relaxed_model.gps + self.solver_outs = relaxed_model.solver_outs return self.result @property diff --git a/gpkit/nomials/math.py b/gpkit/nomials/math.py index f6434fd1b..b53a27f8c 100644 --- a/gpkit/nomials/math.py +++ b/gpkit/nomials/math.py @@ -133,7 +133,11 @@ def mono_approximation(self, x0): Monomial (unless self(x0) < 0, in which case a Signomial is returned) """ x0, _, _ = parse_subs(self.varkeys, x0) # use only varkey keys - psub = self.hmap.sub(x0, self.varkeys, parsedsubs=True) + if self.hmap.varexps: + raise ValueError("Varexps only works for GPs for now... " + "stick to GPs!") + else: + psub = self.hmap.sub(x0, self.varkeys, parsedsubs=True) if EMPTY_HV not in psub or len(psub) > 1: raise ValueError("Variables %s remained after substituting x0=%s" " into %s" % (psub, x0, self)) diff --git a/gpkit/tests/t_nomials.py b/gpkit/tests/t_nomials.py index 806e6b276..61e34d535 100644 --- a/gpkit/tests/t_nomials.py +++ b/gpkit/tests/t_nomials.py @@ -3,7 +3,7 @@ import sys import unittest from gpkit import Variable, Monomial, Posynomial, Signomial, SignomialsEnabled -from gpkit import VectorVariable, NomialArray, Model +from gpkit import VectorVariable, NomialArray, Model, units from gpkit.nomials import NomialMap from gpkit.small_classes import HashVector from gpkit.exceptions import InvalidPosynomial @@ -273,20 +273,24 @@ def test_eq_ne(self): self.assertEqual(Signomial(-3), -3) self.assertNotEqual(Signomial(-3), 3) - def test_subbed_sig_exp(self): - a = Variable('a') - b = Variable('b') - c = Variable('c') - x = Variable('x') - y = Variable('y', 1, 'm') - z = Variable('z', 2, 'm^2') - with SignomialsEnabled(): - # self.assertRaises(ValueError, x**(y) >= 1) - self.assertRaises(ValueError, (2*a)**c) # float**signomial check - self.assertRaises(ValueError, a**y) # united signomial check - self.assertRaises(ValueError, a**(2*b + z*y)) # dimension check - constraints = [x**(y*z**(-0.5)) >= 1] - m = Model(x, constraints) + # def test_subbed_sig_exp(self): + # a = Variable('a') + # b = Variable('b') + # c = Variable('c') + # x = Variable('x') + # y = Variable('y', 'm') + # z = Variable('z', 'm^2') + # with SignomialsEnabled(): + # self.assertRaises(ValueError, (2*a)**c) # float**signomial check + # self.assertRaises(ValueError, a**y) # united signomial check + # self.assertRaises(ValueError, a**(2*b + z*y)) # dimension check + # mony = a**(2*b + z/y**2) + # constraints = [x**(y*z**(-0.5)) >= 1] + # m = Model(x, constraints) + # m.substitutions.update({y: 1*units('m')}) + # self.assertRaises(ValueError, m.solve()) # substitutions check + # m.substitutions.update({z: 2*units('m^2')}) + class TestPosynomial(unittest.TestCase): """TestCase for the Posynomial class""" From 6843322d2e345b496359584f85407c1f2977ff21 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Wed, 13 Nov 2019 13:25:30 -0500 Subject: [PATCH 16/22] Sens_from_dual should work. --- gpkit/constraints/gp.py | 16 +++++++++------ gpkit/nomials/math.py | 18 ++++++++++++---- gpkit/tests/t_nomials.py | 44 ++++++++++++++++++++++++---------------- 3 files changed, 51 insertions(+), 27 deletions(-) diff --git a/gpkit/constraints/gp.py b/gpkit/constraints/gp.py index 7e5281e03..2059a2820 100644 --- a/gpkit/constraints/gp.py +++ b/gpkit/constraints/gp.py @@ -430,17 +430,18 @@ def genA(exps, varlocs, meq_idxs, substitutions=None): # pylint: disable=invali if subbed_exp.values()[0] > 0 and \ not(upperbound and lowerbound): upperbound = True - else: + elif subbed_exp.values()[0] <= 0: lowerbound = True else: data.extend([exps[i][var]]) - if i not in bte: - if upperbound and lowerbound: - break - elif exps[i][var] > 0: # pylint:disable=simplifiable-if-statement - upperbound = True + if i not in bte: + if exps[i][var] > 0 and not (upperbound and lowerbound): + upperbound = True + elif exps[i][var] <= 0: + lowerbound = True else: lowerbound = True + upperbound = True if not upperbound: missingbounds[(var, "upper")] = "" if not lowerbound: @@ -454,6 +455,9 @@ def genA(exps, varlocs, meq_idxs, substitutions=None): # pylint: disable=invali row.append(i) col.append(0) data.append(0) + if len(row) != len(col) != len(data): + raise ValueError("The A matrix generated does not have the right " + "dimensions.") A = CootMatrix(row, col, data) return A, missingbounds diff --git a/gpkit/nomials/math.py b/gpkit/nomials/math.py index b53a27f8c..d7d0e8b9a 100644 --- a/gpkit/nomials/math.py +++ b/gpkit/nomials/math.py @@ -5,6 +5,7 @@ from .core import Nomial from .array import NomialArray from .. import units +from ..keydict import KeyDict from ..constraints import SingleEquationConstraint from ..globals import SignomialsEnabled from ..small_classes import Numbers @@ -572,10 +573,19 @@ def sens_from_dual(self, la, nu, result): # pylint: disable=unused-argument for idx, percentage in self.const_mmap.items(): nu_[idx] += percentage * la*scale nu = nu_ - return {var: sum([presub.exps[i][var]*nu[i] - for i in presub.varlocs[var]]) - for var in self.varkeys} # Constant sensitivities - + sens_dict = KeyDict({v: 0*v.units for v in self.varkeys if v.units}) + sens_dict.update({v: 0 for v in self.varkeys if not v.units}) + for var in self.varkeys: + for i in presub.varlocs[var]: + if not isinstance(presub.exps[i][var], HashVector): + sens_dict[var] += presub.exps[i][var]*nu[i] + else: + exps = presub.exps[i][var] + varkeyDict = KeyDict({key:key for item in exps.keys() \ + for key in item.keys()}) + subbed_exp = exps.sub(result, varkeyDict).values()[0] + sens_dict[var] += subbed_exp*nu[i] + return sens_dict def as_gpconstr(self, x0): # pylint: disable=unused-argument "The GP version of a Posynomial constraint is itself" return self diff --git a/gpkit/tests/t_nomials.py b/gpkit/tests/t_nomials.py index 61e34d535..e4e5607fb 100644 --- a/gpkit/tests/t_nomials.py +++ b/gpkit/tests/t_nomials.py @@ -2,6 +2,7 @@ import math import sys import unittest +import numpy as np from gpkit import Variable, Monomial, Posynomial, Signomial, SignomialsEnabled from gpkit import VectorVariable, NomialArray, Model, units from gpkit.nomials import NomialMap @@ -273,24 +274,33 @@ def test_eq_ne(self): self.assertEqual(Signomial(-3), -3) self.assertNotEqual(Signomial(-3), 3) - # def test_subbed_sig_exp(self): - # a = Variable('a') - # b = Variable('b') - # c = Variable('c') - # x = Variable('x') - # y = Variable('y', 'm') - # z = Variable('z', 'm^2') - # with SignomialsEnabled(): - # self.assertRaises(ValueError, (2*a)**c) # float**signomial check - # self.assertRaises(ValueError, a**y) # united signomial check - # self.assertRaises(ValueError, a**(2*b + z*y)) # dimension check + def test_subbed_sig_exp(self): + a = Variable('a') + b = Variable('b') + c = Variable('c') + x = Variable('x') + y = Variable('y', 'm') + z = Variable('z', 'm^2') + with SignomialsEnabled(): + with self.assertRaises(ValueError): + mony = (2*a)**c # float**signomial check + mony = a**y # united signomial check + mony = a**(2*b + z*y) # dimension check # mony = a**(2*b + z/y**2) - # constraints = [x**(y*z**(-0.5)) >= 1] - # m = Model(x, constraints) - # m.substitutions.update({y: 1*units('m')}) - # self.assertRaises(ValueError, m.solve()) # substitutions check - # m.substitutions.update({z: 2*units('m^2')}) - + # subs = {a: 3, b: 2, z: 4, y: 0.3} + # self.assertEqual() + constraints = [b*x**(y*z**(-0.5)) >= 1] + constraints_subbed = [3*x**(1*2**(-0.5)) >= 1] + m = Model(x, constraints) + m_subbed = Model(x, constraints_subbed) + m.substitutions.update({y: 1*units('m')}) + self.assertRaises(ValueError, m.solve, verbosity=0) # substitutions check + m.substitutions.update({b:3, z: 2*units('m^2')}) + sol = m.solve(verbosity=0) + self.assertEqual(sol(x), + m_subbed.solve(verbosity=0)(x)) + self.assertAlmostEqual(sol['sensitivities']['constants'][b], + -np.sqrt(2), places=5) class TestPosynomial(unittest.TestCase): """TestCase for the Posynomial class""" From 7451cc176a94aade3a570c60ef3a3d2ba0806e36 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Wed, 13 Nov 2019 14:39:19 -0500 Subject: [PATCH 17/22] Boundedness checking for inequalities. --- gpkit/nomials/math.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gpkit/nomials/math.py b/gpkit/nomials/math.py index d7d0e8b9a..a52290633 100644 --- a/gpkit/nomials/math.py +++ b/gpkit/nomials/math.py @@ -474,9 +474,9 @@ def __init__(self, left, oper, right): if self.unsubbed: for exp in self.unsubbed[0].hmap: for key, e in exp.items(): - if e > 0: + if isinstance(e, Numbers) and e > 0: self.bounded.add((key, "upper")) - if e < 0: + if isinstance(e, Numbers) and e < 0: self.bounded.add((key, "lower")) for key in self.substitutions: for bound in ("upper", "lower"): @@ -686,9 +686,9 @@ def __init__(self, left, oper, right): if self.unsubbed: for exp, c in self.unsubbed[0].hmap.items(): for key, e in exp.items(): - if e*c > 0: + if isinstance(e, Numbers) and e*c > 0: self.bounded.add((key, "upper")) - if e*c < 0: + if isinstance(e, Numbers) and e*c < 0: self.bounded.add((key, "lower")) for key in self.substitutions: for bound in ("upper", "lower"): From 3e65e8411f7b83010a49a3312d2d96be9efc9539 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Wed, 13 Nov 2019 14:41:51 -0500 Subject: [PATCH 18/22] Fixing non-subscriptable keys. --- gpkit/constraints/gp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gpkit/constraints/gp.py b/gpkit/constraints/gp.py index 2059a2820..09f7e58c5 100644 --- a/gpkit/constraints/gp.py +++ b/gpkit/constraints/gp.py @@ -417,12 +417,12 @@ def genA(exps, varlocs, meq_idxs, substitutions=None): # pylint: disable=invali varkeyDict = KeyDict({key:key for item in exps[i][var].keys() \ for key in item.keys()}) subbed_exp = exps[i][var].sub(substitutions, varkeyDict) - if subbed_exp.keys()[0]: + if list(subbed_exp.keys())[0]: raise ValueError("Signomial exponent %s has variables that " "have not been fully substituted. Complete " "substitutions and try again." % \ (exps[i])) #TODO: improve error. - data.extend([subbed_exp.values()[0]]) + data.extend([list(subbed_exp.values())[0]]) # Add substituted variables in signomial exponent to bte # to make them bounded. bte = bte.union(varkeyDict.keys()) From c173a4d6b7d95f66f1b90422808e54523bcc9962 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Wed, 13 Nov 2019 14:47:51 -0500 Subject: [PATCH 19/22] Iterables in py3 are a pain in the backside. --- gpkit/constraints/gp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gpkit/constraints/gp.py b/gpkit/constraints/gp.py index 09f7e58c5..6c073e176 100644 --- a/gpkit/constraints/gp.py +++ b/gpkit/constraints/gp.py @@ -427,10 +427,10 @@ def genA(exps, varlocs, meq_idxs, substitutions=None): # pylint: disable=invali # to make them bounded. bte = bte.union(varkeyDict.keys()) # Checking boundedness of base variable - if subbed_exp.values()[0] > 0 and \ + if list(subbed_exp.values())[0] > 0 and \ not(upperbound and lowerbound): upperbound = True - elif subbed_exp.values()[0] <= 0: + elif list(subbed_exp.values())[0] <= 0: lowerbound = True else: data.extend([exps[i][var]]) From 7a19d362f05dcfc3c40a677a43aefabe723a821e Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Fri, 22 Nov 2019 18:23:06 -0500 Subject: [PATCH 20/22] Push partial fixes to bounding. --- gpkit/constraints/gp.py | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/gpkit/constraints/gp.py b/gpkit/constraints/gp.py index 6c073e176..6773f873f 100644 --- a/gpkit/constraints/gp.py +++ b/gpkit/constraints/gp.py @@ -407,11 +407,13 @@ def genA(exps, varlocs, meq_idxs, substitutions=None): # pylint: disable=invali """ missingbounds = {} row, col, data = [], [], [] - bte = set(meq_idxs) # variables bounded through equalities or sig exp substitutions + bte = set() # variables bounded through equalities or sig exp substitutions for j, var in enumerate(varlocs): + # print(var) upperbound, lowerbound = False, False row.extend(varlocs[var]) col.extend([j]*len(varlocs[var])) + exp_arr = [] for i in varlocs[var]: if isinstance(exps[i][var], NomialMap): varkeyDict = KeyDict({key:key for item in exps[i][var].keys() \ @@ -422,32 +424,36 @@ def genA(exps, varlocs, meq_idxs, substitutions=None): # pylint: disable=invali "have not been fully substituted. Complete " "substitutions and try again." % \ (exps[i])) #TODO: improve error. - data.extend([list(subbed_exp.values())[0]]) + exp_arr.extend([list(subbed_exp.values())[0]]) # Add substituted variables in signomial exponent to bte # to make them bounded. bte = bte.union(varkeyDict.keys()) - # Checking boundedness of base variable - if list(subbed_exp.values())[0] > 0 and \ - not(upperbound and lowerbound): - upperbound = True - elif list(subbed_exp.values())[0] <= 0: - lowerbound = True else: - data.extend([exps[i][var]]) - if i not in bte: - if exps[i][var] > 0 and not (upperbound and lowerbound): - upperbound = True - elif exps[i][var] <= 0: - lowerbound = True + exp_arr.extend([exps[i][var]]) + # print(exp_arr) + data.extend(exp_arr) + for k, i in enumerate(varlocs[var]): + # Checking boundedness of base variable + if i in meq_idxs or var in bte: + # print('meq_idxs') + lowerbound = True + upperbound = True + break + else: + if upperbound and lowerbound: + break + elif exp_arr[k] > 0: + # print('upper') + upperbound = True else: + # print('lower') lowerbound = True - upperbound = True + if not upperbound: missingbounds[(var, "upper")] = "" if not lowerbound: missingbounds[(var, "lower")] = "" - - check_mono_eq_bounds(missingbounds, gen_mono_eq_bounds(exps, meq_idxs)) + check_mono_eq_bounds(missingbounds, gen_mono_eq_bounds(exps, meq_idxs)) #TODO: or bte? # space the matrix out for trailing constant terms for i, exp in enumerate(exps): From cb2a9d61be5265ce9747269adb3c7598ec260046 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Tue, 14 Jan 2020 17:17:02 -0500 Subject: [PATCH 21/22] Fixed bounded bug (wrong logic for meq_idxs). --- gpkit/constraints/gp.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/gpkit/constraints/gp.py b/gpkit/constraints/gp.py index 6773f873f..7500227c5 100644 --- a/gpkit/constraints/gp.py +++ b/gpkit/constraints/gp.py @@ -414,7 +414,8 @@ def genA(exps, varlocs, meq_idxs, substitutions=None): # pylint: disable=invali row.extend(varlocs[var]) col.extend([j]*len(varlocs[var])) exp_arr = [] - for i in varlocs[var]: + # Adding data to A matrix + for k, i in enumerate(varlocs[var]): if isinstance(exps[i][var], NomialMap): varkeyDict = KeyDict({key:key for item in exps[i][var].keys() \ for key in item.keys()}) @@ -432,28 +433,25 @@ def genA(exps, varlocs, meq_idxs, substitutions=None): # pylint: disable=invali exp_arr.extend([exps[i][var]]) # print(exp_arr) data.extend(exp_arr) + # Checking boundedness for k, i in enumerate(varlocs[var]): - # Checking boundedness of base variable - if i in meq_idxs or var in bte: - # print('meq_idxs') + # Checking variables subbed in exponent + if var in bte: lowerbound = True upperbound = True break - else: + elif i not in meq_idxs: if upperbound and lowerbound: break elif exp_arr[k] > 0: - # print('upper') upperbound = True else: - # print('lower') lowerbound = True - if not upperbound: missingbounds[(var, "upper")] = "" if not lowerbound: missingbounds[(var, "lower")] = "" - check_mono_eq_bounds(missingbounds, gen_mono_eq_bounds(exps, meq_idxs)) #TODO: or bte? + check_mono_eq_bounds(missingbounds, gen_mono_eq_bounds(exps, meq_idxs)) # space the matrix out for trailing constant terms for i, exp in enumerate(exps): From eb44a7e614a9361bbb707de1d973ceaa844d3414 Mon Sep 17 00:00:00 2001 From: 1ozturkbe <1ozturkbe@gmail.com> Date: Wed, 15 Jan 2020 10:17:07 -0500 Subject: [PATCH 22/22] lint --- gpkit/constraints/gp.py | 6 +++--- gpkit/nomials/math.py | 4 ++-- gpkit/tests/t_nomials.py | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/gpkit/constraints/gp.py b/gpkit/constraints/gp.py index 7500227c5..186383a4d 100644 --- a/gpkit/constraints/gp.py +++ b/gpkit/constraints/gp.py @@ -417,9 +417,9 @@ def genA(exps, varlocs, meq_idxs, substitutions=None): # pylint: disable=invali # Adding data to A matrix for k, i in enumerate(varlocs[var]): if isinstance(exps[i][var], NomialMap): - varkeyDict = KeyDict({key:key for item in exps[i][var].keys() \ + varkeydict = KeyDict({key:key for item in exps[i][var].keys() \ for key in item.keys()}) - subbed_exp = exps[i][var].sub(substitutions, varkeyDict) + subbed_exp = exps[i][var].sub(substitutions, varkeydict) if list(subbed_exp.keys())[0]: raise ValueError("Signomial exponent %s has variables that " "have not been fully substituted. Complete " @@ -428,7 +428,7 @@ def genA(exps, varlocs, meq_idxs, substitutions=None): # pylint: disable=invali exp_arr.extend([list(subbed_exp.values())[0]]) # Add substituted variables in signomial exponent to bte # to make them bounded. - bte = bte.union(varkeyDict.keys()) + bte = bte.union(varkeydict.keys()) else: exp_arr.extend([exps[i][var]]) # print(exp_arr) diff --git a/gpkit/nomials/math.py b/gpkit/nomials/math.py index a52290633..0ed186731 100644 --- a/gpkit/nomials/math.py +++ b/gpkit/nomials/math.py @@ -581,9 +581,9 @@ def sens_from_dual(self, la, nu, result): # pylint: disable=unused-argument sens_dict[var] += presub.exps[i][var]*nu[i] else: exps = presub.exps[i][var] - varkeyDict = KeyDict({key:key for item in exps.keys() \ + varkeydict = KeyDict({key:key for item in exps.keys() \ for key in item.keys()}) - subbed_exp = exps.sub(result, varkeyDict).values()[0] + subbed_exp = exps.sub(result, varkeydict).values()[0] sens_dict[var] += subbed_exp*nu[i] return sens_dict def as_gpconstr(self, x0): # pylint: disable=unused-argument diff --git a/gpkit/tests/t_nomials.py b/gpkit/tests/t_nomials.py index e4e5607fb..a7a4a3c5d 100644 --- a/gpkit/tests/t_nomials.py +++ b/gpkit/tests/t_nomials.py @@ -283,6 +283,7 @@ def test_subbed_sig_exp(self): z = Variable('z', 'm^2') with SignomialsEnabled(): with self.assertRaises(ValueError): + #pylint: disable=unused-variable mony = (2*a)**c # float**signomial check mony = a**y # united signomial check mony = a**(2*b + z*y) # dimension check