From 94283c55df67b088460200ef3a3282e1f772a366 Mon Sep 17 00:00:00 2001 From: bqpd Date: Fri, 2 Dec 2016 13:05:50 -0500 Subject: [PATCH] v0.5.0 (#954) * preparation for version 0.5 * remove unused gitmodules file * update release notes * following coverage, remove unused code * add mailing list / chatroom links * pylint * gpkit version 0.5.0.2 as uploaded to pypi * pylint * update release notes * unitstr -> latex_unitstr, small refactors (#973) * rename varkey.unitstr to varkey.latex_unitstr * minor improvements from #965 review --- .gitmodules | 0 MANIFEST | 26 ++++++++++++++------------ docs/source/citinggpkit.rst | 2 +- docs/source/conf.py | 4 ++-- docs/source/index.rst | 2 ++ docs/source/installation.rst | 4 ++-- docs/source/releasenotes.rst | 33 +++++++++++++++++++++++++++------ gpkit/__init__.py | 2 +- gpkit/build.py | 13 ++++++------- gpkit/constraints/relax.py | 6 ------ gpkit/geometric_program.py | 13 +++++++------ gpkit/interactive/widgets.py | 2 +- gpkit/nomials/array.py | 6 +----- gpkit/nomials/data.py | 11 ----------- gpkit/nomials/nomial_core.py | 18 ------------------ gpkit/nomials/nomial_math.py | 25 ++++--------------------- gpkit/nomials/substitution.py | 4 ---- gpkit/solution_array.py | 32 ++++++++++---------------------- gpkit/tools/tools.py | 5 +---- gpkit/varkey.py | 2 +- setup.py | 2 +- 21 files changed, 81 insertions(+), 131 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29bb..000000000 diff --git a/MANIFEST b/MANIFEST index 3bf68ceee..cf0585b2d 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,20 +1,9 @@ -setup.py gpkit/__init__.py gpkit/_cvxopt.py -gpkit/build.py -gpkit/feasibility.py -gpkit/geometric_program.py -gpkit/keydict.py -gpkit/exceptions.py -gpkit/modified_ctypesgen.py -gpkit/repr_conventions.py -gpkit/small_classes.py -gpkit/small_scripts.py -gpkit/solution_array.py -gpkit/varkey.py gpkit/_mosek/__init__.py gpkit/_mosek/cli_expopt.py gpkit/_mosek/expopt.py +gpkit/build.py gpkit/constraints/__init__.py gpkit/constraints/array.py gpkit/constraints/bounded.py @@ -22,16 +11,23 @@ gpkit/constraints/costed.py gpkit/constraints/linked.py gpkit/constraints/model.py gpkit/constraints/prog_factories.py +gpkit/constraints/relax.py gpkit/constraints/set.py +gpkit/constraints/sigeq.py gpkit/constraints/signomial_program.py gpkit/constraints/single_equation.py gpkit/constraints/tight.py +gpkit/exceptions.py +gpkit/geometric_program.py gpkit/interactive/__init__.py gpkit/interactive/chartjs.py +gpkit/interactive/linking_diagram.py gpkit/interactive/plotting.py gpkit/interactive/ractor.py gpkit/interactive/sensitivity_map.py gpkit/interactive/widgets.py +gpkit/keydict.py +gpkit/modified_ctypesgen.py gpkit/nomials/__init__.py gpkit/nomials/array.py gpkit/nomials/data.py @@ -40,6 +36,10 @@ gpkit/nomials/nomial_math.py gpkit/nomials/substitution.py gpkit/nomials/variables.py gpkit/pint/usd_cpi.txt +gpkit/repr_conventions.py +gpkit/small_classes.py +gpkit/small_scripts.py +gpkit/solution_array.py gpkit/tests/__init__.py gpkit/tests/helpers.py gpkit/tests/run_tests.py @@ -57,3 +57,5 @@ gpkit/tests/t_vars.py gpkit/tools/__init__.py gpkit/tools/fmincon.py gpkit/tools/tools.py +gpkit/varkey.py +setup.py diff --git a/docs/source/citinggpkit.rst b/docs/source/citinggpkit.rst index d506d1660..f328554aa 100644 --- a/docs/source/citinggpkit.rst +++ b/docs/source/citinggpkit.rst @@ -8,5 +8,5 @@ If you use GPkit, please cite it with the following bibtex:: title={GPkit software for geometric programming}, howpublished={\url{https://github.com/hoburg/gpkit}}, year={2016}, - note={Version 0.4.2} + note={Version 0.5.0} } diff --git a/docs/source/conf.py b/docs/source/conf.py index 156e33472..daf1350d0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -55,9 +55,9 @@ # built documents. # # The short X.Y version. -version = '0.4' +version = '0.5' # The full version, including alpha/beta/rc tags. -release = '0.4.2' +release = '0.5.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/index.rst b/docs/source/index.rst index 4073c5e53..b8208c2df 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -16,6 +16,8 @@ Supported solvers are `MOSEK `_ and `CVXOPT `_. +Join our `mailing list `_ and/or `chatroom `_ for support and examples. + Table of contents ==================== .. toctree:: diff --git a/docs/source/installation.rst b/docs/source/installation.rst index fc34fbeb4..1979d76b3 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -113,7 +113,7 @@ Install GPkit - Run ``pip install gpkit`` at the command line (for system python installs, use ``sudo pip``) - Run ``pip install ipywidgets`` for interactive control of models (recommended) - Run ``python -c "import gpkit.tests; gpkit.tests.run()"`` to run the tests; if any tests do not pass, please email ``gpkit@mit.edu`` or `raise a GitHub issue `_. - - *Optional:* to install gpkit into an isolated python environment, install ``virtualenv``, run ``virtualenv $DESTINATION_DIR`` then activate it with ``source $DESTINATION_DIR/bin/activate`` + - Join our `mailing list `_ and/or `chatroom `_ for support and examples. Debugging installation @@ -124,7 +124,7 @@ You may need to rebuild GPkit if any of the following occur: - You delete the ``.gpkit`` folder from your home directory - You see ``Could not load settings file.`` when importing GPkit, or - ``Could not load MOSEK library: ImportError('$HOME/.gpkit/expopt.so not found.')`` -To rebuild GPkit, do the following: +To rebuild GPkit, first try running ``python -c "from gpkit.build import rebuild; rebuild()"``. If that doesn't work then try the following: - Run ``pip uninstall gpkit`` - Run ``pip install --no-cache-dir --no-deps gpkit`` - Run ``python -c "import gpkit.tests; gpkit.tests.run()"`` diff --git a/docs/source/releasenotes.rst b/docs/source/releasenotes.rst index 197e9b889..f97904f9f 100644 --- a/docs/source/releasenotes.rst +++ b/docs/source/releasenotes.rst @@ -3,8 +3,29 @@ Release Notes This page lists the changes made in each point version of gpkit. +Version 0.5.0 +============= + * No longer recommend the use of linked variables and subinplace + * Switched default solver to MOSEK + * Added Linked Variable diagram (PR #915) + * Dropped operator overloading with pint (PR #938) + * Added and documented debugging tools (PR #933) + * Added and documented vectorization tools + * Documented modular model construction + * 3200 lines of code, 1800 lines of tests, 1700 lines of docstring. (not counting `interactive`) + +Changes to named models / Model inheritance +------------------------------------------- +We are deprecating the creation of named submodels with custom ``__init__`` methods. Previously, variables created during ``__init__`` in any class inheriting from Model were replaced by a copy with ``__class__.__name__`` added as varkey metadata. This was slow, a bit irregular, and hacky. + +We're moving to an explicitly-irregular ``setup`` method, which (if declared for a class inheriting from Model) is automatically called during ``Model.__init__`` inside a ``NamedVariables(self.__class__.__name__)`` environment. This 1) handles the naming of variables more explicitly and efficiently, and 2) allows us to capture variables created within ``setup``, so that constants that are not a part of any constraint can be used directly (several examples of such template models are in the new `Building Complex Models` documentation). + +``Model.__init__`` calls ``setup`` with the arguments given to the constructor, with the exception of ``substitutions``, which is a reserved keyword. This allows for the easy creation of a named model with custom parameter values (as in the documentation's Beam example). ``setup`` methods should return an iterable (list, tuple, ConstraintSet, ...) of constraints or nothing if the model contains no constraints. To declare a submodel cost, set ``self.cost`` during ``setup``. However, we often find declaring a model's cost explicitly just before solving to be a more legible practice. + +In addition to permitting us to name variables at creation, and include unconstrained variables in a model, we hope that ``setup`` methods will clarify the side effects of named model creation. + Version 0.4.2 -------------- +============= * prototype handling of SignomialEquality constraints * fix an issue where solution tables printed incorrect units (despite the units being correct in the ``SolutionArray`` data structure) * fix ``controlpanel`` slider display for newer versions of ipywidgets @@ -22,14 +43,14 @@ Version 0.4.2 * SP sweep compatibility Version 0.4.0 -------------- +============= * New model for considering constraints: all constraints are considered as sets of constraints which may contain other constraints, and are asked for their substitutions / posynomial less than 1 representation as late as possible. * Support for calling external code during an SP solve. * New class KeyDict to allow referring to variables by name or with objects. * Many many other bug fixes, speed ups, and refactors under the hood. Version 0.3.4 -------------- +============= * Modular / model composition fixes and improvements * Working controlpanel() for Model * ipynb and numpy dependency fixes @@ -38,7 +59,7 @@ Version 0.3.4 * slider widgets now have units Version 0.3.2 -------------- +============= * Assorted bug fixes * Assorted internal improvements and simplifications * Refactor signomial constraints, resulting in smarter SP heuristic @@ -46,7 +67,7 @@ Version 0.3.2 * Not counting submodules, went from 2400 to 2500 lines of code and from 1050 to 1170 lines of docstrings and comments. Version 0.3 ------------ +=========== * Integrated GP and SP creation under the Model class * Improved and simplified under-the-hood internals of GPs and SPs * New experimental SP heuristic @@ -63,7 +84,7 @@ Version 0.3 * Simplified solver logging and printing, making it easier to access solver output. Version 0.2 ------------ +=========== * Various bug fixes * Python 3 compatibility diff --git a/gpkit/__init__.py b/gpkit/__init__.py index b219ac2ac..274e1d73a 100644 --- a/gpkit/__init__.py +++ b/gpkit/__init__.py @@ -19,7 +19,7 @@ from os.path import dirname as os_path_dirname SETTINGS_PATH = os_sep.join([os_path_dirname(__file__), "env", "settings"]) -__version__ = "0.4.2" +__version__ = "0.5.0" UNIT_REGISTRY = None SIGNOMIALS_ENABLED = False diff --git a/gpkit/build.py b/gpkit/build.py index b1e43b2ad..7526e35bf 100644 --- a/gpkit/build.py +++ b/gpkit/build.py @@ -268,16 +268,15 @@ def build(self): return True +def rebuild(): + "Changes to the installed gpkit directory and runs build_gpkit()" + import gpkit + log("# Moving to the directory from which GPkit was imported.") + os.chdir(gpkit.__path__[0]) + build_gpkit() def build_gpkit(): "Builds GPkit" - try: - import gpkit - log("# Moving to the directory from which GPkit was imported.") - os.chdir(gpkit.__path__[0]) - except ImportError: - pass - if isfile("__init__.py"): #call("ls") log("# Don't want to be in a folder with __init__.py, going up!") diff --git a/gpkit/constraints/relax.py b/gpkit/constraints/relax.py index 3e121987a..96c2f3f0a 100644 --- a/gpkit/constraints/relax.py +++ b/gpkit/constraints/relax.py @@ -28,8 +28,6 @@ class ConstraintsRelaxedEqually(ConstraintSet): """ def __init__(self, constraints): - if not isinstance(constraints, ConstraintSet): - constraints = ConstraintSet(constraints) substitutions = dict(constraints.substitutions) posynomials = constraints.as_posyslt1() with NamedVariables("Relax"): @@ -64,8 +62,6 @@ class ConstraintsRelaxed(ConstraintSet): """ def __init__(self, constraints): - if not isinstance(constraints, ConstraintSet): - constraints = ConstraintSet(constraints) substitutions = dict(constraints.substitutions) posynomials = constraints.as_posyslt1() N = len(posynomials) @@ -105,8 +101,6 @@ class ConstantsRelaxed(ConstraintSet): def __init__(self, constraints, include_only=None, exclude=None): exclude = frozenset(exclude) if exclude else frozenset() include_only = frozenset(include_only) if include_only else frozenset() - if not isinstance(constraints, ConstraintSet): - constraints = ConstraintSet(constraints) substitutions = KeyDict(constraints.substitutions) constants, _, _ = parse_subs(constraints.varkeys, constraints.substitutions) diff --git a/gpkit/geometric_program.py b/gpkit/geometric_program.py index f6958c86b..a4ed3fe81 100644 --- a/gpkit/geometric_program.py +++ b/gpkit/geometric_program.py @@ -3,9 +3,9 @@ from time import time import numpy as np from .nomials import NomialData -from .small_classes import CootMatrix, HashVector +from .small_classes import CootMatrix, HashVector, SolverLog from .keydict import FastKeyDict -from .small_classes import SolverLog +from .small_scripts import mag DEFAULT_SOLVER_KWARGS = {"cvxopt": {"kktsolver": "ldl"}} @@ -58,9 +58,6 @@ def __init__(self, cost, constraints, substitutions=None, verbosity=1): for constraint in constraints: constr_posys = constraint.as_posyslt1(self.substitutions) - if not all(constr_posys): - raise ValueError("%s is an invalid constraint for a" - " GeometricProgram" % constraint) start_idx, ps_added = len(self.posynomials), len(constr_posys) self.constr_idxs.append(range(start_idx, start_idx + ps_added)) self.posynomials.extend(constr_posys) @@ -266,7 +263,11 @@ def _compile_result(self, solver_out): else: # use self.posynomials[0] because the cost may have had constants freev = result["freevariables"] - result["cost"] = self.posynomials[0].subsummag(freev) + cost = self.posynomials[0].sub(freev) + if cost.varkeys: + raise ValueError("cost contains unsolved variables %s" + % cost.varkeys.keys()) + result["cost"] = mag(cost.c) ## Get sensitivities result["sensitivities"] = {"nu": nu, "la": la} diff --git a/gpkit/interactive/widgets.py b/gpkit/interactive/widgets.py index 588cd707d..8213b6df7 100644 --- a/gpkit/interactive/widgets.py +++ b/gpkit/interactive/widgets.py @@ -106,7 +106,7 @@ def modelcontrolpanel(model, *args, **kwargs): sliderboxes = [] for sl in sliders.children: cb = widgets.Checkbox(value=True) - unit_latex = sl.varkey.unitstr() + unit_latex = sl.varkey.latex_unitstr() if unit_latex: unit_latex = r"$\scriptsize"+unit_latex+"$" units = widgets.Latex(value=unit_latex) diff --git a/gpkit/nomials/array.py b/gpkit/nomials/array.py index dda9795e5..4dedb8df3 100644 --- a/gpkit/nomials/array.py +++ b/gpkit/nomials/array.py @@ -32,11 +32,7 @@ def wrapped_func(self, other): "Creates array constraint from vectorized operator." if not self.shape: return func(self.flatten()[0], other) - if isinstance(other, Quantity): - veccable_other = np.array([other], dtype=object) - else: - veccable_other = other - result = vecfunc(self, veccable_other) + result = vecfunc(self, other) left = self.key if hasattr(self, "key") else self right = other.key if hasattr(other, "key") else other return ArrayConstraint(result, left, symbol, right) diff --git a/gpkit/nomials/data.py b/gpkit/nomials/data.py index 144f1689d..ab3284d10 100644 --- a/gpkit/nomials/data.py +++ b/gpkit/nomials/data.py @@ -19,10 +19,6 @@ class NomialData(object): """ # pylint: disable=too-many-instance-attributes def __init__(self, exps=None, cs=None, simplify=True): - if exps is None and cs is None: - # pass through for classmethods to get a NomialData object, - # which they will then call __init__ on - return if simplify: exps, cs = simplify_exps_and_cs(exps, cs) self.exps, self.cs = exps, cs @@ -54,13 +50,6 @@ def __hash__(self): [str(self.units)])) return self._hashvalue - @classmethod - def fromnomials(cls, nomials): - """Construct a NomialData from an iterable of Signomial objects""" - nd = cls() # use pass-through version of __init__ - nd.init_from_nomials(nomials) - return nd - @property def varkeys(self): "The NomialData's varkeys, created when necessary for a substitution." diff --git a/gpkit/nomials/nomial_core.py b/gpkit/nomials/nomial_core.py index af5499b7a..18565fe0b 100644 --- a/gpkit/nomials/nomial_core.py +++ b/gpkit/nomials/nomial_core.py @@ -6,24 +6,6 @@ from ..repr_conventions import _str, _repr, _repr_latex_ -def fast_monomial_str(exp, c): - "Quickly generates a unitless monomial string." - varstrs = [] - for (var, x) in exp.items(): - if x != 0: - varstr = str(var) - if x != 1: - varstr += "**%.2g" % x - varstrs.append(varstr) - c = mag(c) - cstr = "%.3g" % c - if cstr == "-1" and varstrs: - return "-" + "*".join(varstrs) - else: - cstr = [cstr] if (cstr != "1" or not varstrs) else [] - return "*".join(cstr + varstrs) - - class Nomial(NomialData): "Shared non-mathematical properties of all nomials" __div__ = None diff --git a/gpkit/nomials/nomial_math.py b/gpkit/nomials/nomial_math.py index ee3e3eb84..b7ad7106d 100644 --- a/gpkit/nomials/nomial_math.py +++ b/gpkit/nomials/nomial_math.py @@ -170,10 +170,6 @@ def mono_approximation(self, x0): ------- Monomial (unless self(x0) < 0, in which case a Signomial is returned) """ - if not x0: - for i, exp in enumerate(self.exps): - if exp == {}: - return Monomial({}, self.cs[i]) x0, _, _ = parse_subs(self.varkeys, x0) # use only varkey keys exp = HashVector() psub = self.sub(x0) @@ -219,26 +215,18 @@ def subinplace(self, substitutions): _, exps, cs, _ = substitution(self, substitutions) super(Signomial, self).__init__(exps, cs) - def subsummag(self, substitutions): - "Returns the sum of the magnitudes of the substituted Nomial." - _, exps, cs, _ = substitution(self, substitutions) - if any(exps): - keys = set() - for exp in exps: - keys.update(exp) - raise ValueError("could not substitute for %s" % keys) - return mag(cs).sum() - def __le__(self, other): if isinstance(other, (Numbers, Signomial)): return SignomialInequality(self, "<=", other) - return NotImplemented + else: + return NotImplemented def __ge__(self, other): if isinstance(other, (Numbers, Signomial)): # by default all constraints take the form left >= right return SignomialInequality(self, ">=", other) - return NotImplemented + else: + return NotImplemented # posynomial arithmetic def __add__(self, other): @@ -570,11 +558,6 @@ def as_gpconstr(self, x0): "GP version of a Posynomial constraint is itself" return self - # pylint: disable=unused-argument - def sens_from_gpconstr(self, posyapprox, pa_sens, var_senss): - "Returns sensitivities as parsed from an approximating GP constraint." - return pa_sens - class MonomialEquality(PosynomialInequality): "A Constraint of the form Monomial == Monomial." diff --git a/gpkit/nomials/substitution.py b/gpkit/nomials/substitution.py index ee92165fb..0f9455810 100644 --- a/gpkit/nomials/substitution.py +++ b/gpkit/nomials/substitution.py @@ -158,10 +158,6 @@ def substitution(nomial, substitutions): else: mag(cs_)[i] = np.nan # if sub is 0 and x is 0, pass - elif isinstance(sub, np.ndarray): - if not sub.shape: - c, = sub.flatten() - cs_[i] *= c**x elif isinstance(sub, Strings): descr = dict(var.descr) del descr["name"] diff --git a/gpkit/solution_array.py b/gpkit/solution_array.py index a3cb936b3..21ad1c441 100644 --- a/gpkit/solution_array.py +++ b/gpkit/solution_array.py @@ -2,7 +2,7 @@ from collections import Iterable import numpy as np from .nomials import NomialArray -from .small_classes import Strings, DictOfLists +from .small_classes import DictOfLists from .small_scripts import unitstr, mag @@ -55,13 +55,7 @@ def __len__(self): def __call__(self, posy): posy_subbed = self.subinto(posy) - if hasattr(posy_subbed, "exp") and not posy_subbed.exp: - # it's a constant monomial - return posy_subbed.c - elif hasattr(posy_subbed, "c"): - # it's a posyarray, which'll throw an error if non-constant... - return posy_subbed.c - return posy_subbed + return getattr(posy_subbed, "c", posy_subbed) def subinto(self, posy): "Returns NomialArray of each solution substituted into posy." @@ -97,8 +91,6 @@ def table(self, tables=("cost", "sweepvariables", "freevariables", ------- str """ - if isinstance(tables, Strings): - tables = [tables] strs = [] for table in tables: subdict = self.get(table, None) @@ -190,13 +182,9 @@ def results_table(data, title, minval=0, printunits=True, fixedcols=True, b = isinstance(v, Iterable) and bool(v.shape) kmodels = k.descr.get("models", []) kmodelnums = k.descr.get("modelnums", []) - model = "" - for i, kmodel in enumerate(kmodels): - if model: - model += "/" - model += kmodel - if kmodelnums[i] != 0: - model += ".%i" % kmodelnums[i] + model = "/".join([kstr + (".%i" % knum if knum != 0 else "") + for kstr, knum in zip(kmodels, kmodelnums) + if kstr]) models.add(model) s = k.str_without("models") if not sortbyvals: @@ -242,13 +230,14 @@ def results_table(data, title, minval=0, printunits=True, fixedcols=True, else: varstr = "$%s$" % varstr.replace(" : ", "") if latex == 1: # normal results table - lines.append([varstr, valstr, "$%s$" % var.unitstr(), label]) + lines.append([varstr, valstr, "$%s$" % var.latex_unitstr(), + label]) coltitles = [title, "Value", "Units", "Description"] elif latex == 2: # no values - lines.append([varstr, "$%s$" % var.unitstr(), label]) + lines.append([varstr, "$%s$" % var.latex_unitstr(), label]) coltitles = [title, "Units", "Description"] elif latex == 3: # no description - lines.append([varstr, valstr, "$%s$" % var.unitstr()]) + lines.append([varstr, valstr, "$%s$" % var.latex_unitstr()]) coltitles = [title, "Value", "Units"] else: raise ValueError("Unexpected latex option, %s." % latex) @@ -266,8 +255,7 @@ def results_table(data, title, minval=0, printunits=True, fixedcols=True, line = [fmts[0].format(" | "), line[1]] else: line = [fmt.format(s) for fmt, s in zip(fmts, line)] - line = "".join(line).rstrip() # pylint:disable=redefined-variable-type - lines[i] = line + lines[i] = "".join(line).rstrip() lines = [title] + ["-"*len(title)] + lines + [""] elif lines: colfmt = {1: "llcl", 2: "lcl", 3: "llc"} diff --git a/gpkit/tools/tools.py b/gpkit/tools/tools.py index 41ebf3325..4068010b1 100644 --- a/gpkit/tools/tools.py +++ b/gpkit/tools/tools.py @@ -115,10 +115,7 @@ def composite_objective(*objectives, **kwargs): "Creates a cost function that sweeps between multiple objectives." objectives = list(objectives) n = len(objectives) - if "k" in kwargs: - k = kwargs["k"] - else: - k = 4 + k = kwargs.get("k", 4) if "sweep" in kwargs: sweeps = [kwargs["sweep"]]*(n-1) elif "sweeps" in kwargs: diff --git a/gpkit/varkey.py b/gpkit/varkey.py index 4a3921ca6..ee911bf42 100644 --- a/gpkit/varkey.py +++ b/gpkit/varkey.py @@ -101,7 +101,7 @@ def str_without(self, excluded=None): def __getattr__(self, attr): return self.descr.get(attr, None) - def unitstr(self): + def latex_unitstr(self): "Returns latex unitstr" us = unitstr(self.units, r"~\mathrm{%s}", "L~") utf = us.replace("frac", "tfrac").replace(r"\cdot", r"\cdot ") diff --git a/setup.py b/setup.py index 7b1ad2f08..861c1267c 100644 --- a/setup.py +++ b/setup.py @@ -53,7 +53,7 @@ author_email="gpkit@mit.edu", url="https://www.github.com/hoburg/gpkit", install_requires=["numpy >= 1.8.1", "pint", "scipy"], - version="0.4.2.1", + version="0.5.0.2", packages=["gpkit", "gpkit._mosek", "gpkit.tests", "gpkit.interactive", "gpkit.nomials", "gpkit.constraints", "gpkit.tools"], package_data={"gpkit": ["pint/*", "env/*"],