Skip to content

Commit

Permalink
Add all code in the documentation to unit tests (#1509)
Browse files Browse the repository at this point in the history
  • Loading branch information
bqpd committed Aug 10, 2020
1 parent d7e04ea commit f9a3be6
Show file tree
Hide file tree
Showing 25 changed files with 347 additions and 244 deletions.
108 changes: 8 additions & 100 deletions docs/source/advancedcommands.rst
Expand Up @@ -12,21 +12,7 @@ For example, air density, viscosity, and temperature are functions of altitude.
These can be represented by a substitution or value that is a one-argument function
accepting ``model.substitutions`` (for details, see `Substitutions`_ below).

.. code-block:: python
# code from t_GPSubs.test_calcconst in tests/t_sub.py
x = Variable("x", "hours")
t_day = Variable("t_{day}", 12, "hours")
t_night = Variable("t_{night}", lambda c: 24 - c[t_day], "hours")
# note that t_night has a function as its value
m = Model(x, [x >= t_day, x >= t_night])
sol = m.solve(verbosity=0)
self.assertAlmostEqual(sol(t_night)/gpkit.ureg.hours, 12)
m.substitutions.update({t_day: ("sweep", [8, 12, 16])})
sol = m.solve(verbosity=0)
self.assertEqual(len(sol["cost"]), 3)
npt.assert_allclose(sol(t_day) + sol(t_night), 24)
.. literalinclude:: examples/evaluated_fixed_variables.py

These functions are automatically differentiated with the `ad <https://pypi.org/project/ad/>`_ package to provide more accurate sensitivities. In some cases may require using functions from the ``ad.admath`` instead of their python or numpy equivalents; the `ad documentation <https://pypi.org/project/ad/>`_ contains details on how to do this.

Expand All @@ -40,16 +26,7 @@ variable, but :math:`(1-\nu)` is a valid GP variable, then :math:`\nu` can be ca
These evaluated free variables can be represented by a ``Variable`` with ``evalfn`` metadata.
Note that this variable should not be used in constructing your model!

.. code-block:: python
# code from t_constraints.test_evalfn in tests/t_sub.py
x = Variable("x")
x2 = Variable("x^2", evalfn=lambda v: v[x]**2)
m = Model(x, [x >= 2])
m.unique_varkeys = set([x2.key])
sol = m.solve(verbosity=0)
self.assertAlmostEqual(sol(x2), sol(x)**2)
.. literalinclude:: examples/evaluated_free_variables.py

For evaluated variables that can be used during a solution, see :ref:`sgp`.

Expand Down Expand Up @@ -94,18 +71,7 @@ tight (that is, the right side does not equal the left side) after solving. This
is useful when you know that a constraint *should* be tight for a given model,
but representing it as an equality would be non-convex.

.. code-block:: python
from gpkit import Variable, Model
from gpkit.constraints.tight import Tight
Tight.reltol = 1e-2 # set the global tolerance of Tight
x = Variable('x')
x_min = Variable('x_{min}', 2)
m = Model(x, [Tight([x >= 1], reltol=1e-3), # set the specific tolerance
x >= x_min])
m.solve(verbosity=0) # prints warning
.. literalinclude:: examples/tight_constraintsets.py

Loose ConstraintSets
====================
Expand All @@ -115,18 +81,7 @@ not loose (that is, their sensitivity is above some threshold after solving). Th
is useful when you want a constraint to be inactive for a given model because
it represents an important model assumption (such as a fit only valid over a particular interval).

.. code-block:: python
from gpkit import Variable, Model
from gpkit.constraints.tight import Loose
Tight.reltol = 1e-4 # set the global tolerance of Tight
x = Variable('x')
x_min = Variable('x_{min}', 1)
m = Model(x, [Loose([x >= 2], senstol=1e-4), # set the specific tolerance
x >= x_min])
m.solve(verbosity=0) # prints warning
.. literalinclude:: examples/loose_constraintsets.py

Substitutions
=============
Expand All @@ -138,31 +93,14 @@ Substituting into Posynomials, NomialArrays, and GPs

The examples below all use Posynomials and NomialArrays, but the syntax is identical for GPs (except when it comes to sweep variables).

.. code-block:: python
# adapted from t_sub.py / t_NomialSubs / test_Basic
from gpkit import Variable
x = Variable("x")
p = x**2
assert p.sub(x, 3) == 9
assert p.sub(x.varkeys["x"], 3) == 9
assert p.sub("x", 3) == 9
.. literalinclude:: examples/substitutions.py

Here the variable ``x`` is being replaced with ``3`` in three ways: first by substituting for ``x`` directly, then by substituting for the ``VarKey("x")``, then by substituting the string "x". In all cases the substitution is understood as being with the VarKey: when a variable is passed in the VarKey is pulled out of it, and when a string is passed in it is used as an argument to the Posynomial's ``varkeys`` dictionary.

Substituting multiple values
----------------------------

.. code-block:: python
# adapted from t_sub.py / t_NomialSubs / test_Vector
from gpkit import Variable, VectorVariable
x = Variable("x")
y = Variable("y")
z = VectorVariable(2, "z")
p = x*y*z
assert all(p.sub({x: 1, "y": 2}) == 2*z)
assert all(p.sub({x: 1, y: 2, "z": [1, 2]}) == z.sub(z, [2, 4]))
.. literalinclude:: examples/sub_multi_values.py

To substitute in multiple variables, pass them in as a dictionary where the keys are what will be replaced and values are what it will be replaced with. Note that you can also substitute for VectorVariables by their name or by their NomialArray.

Expand All @@ -171,28 +109,7 @@ Substituting with nonnumeric values

You can also substitute in sweep variables (see Sweeps_), strings, and monomials:

.. code-block:: python
# adapted from t_sub.py / t_NomialSubs
from gpkit import Variable
from gpkit.small_scripts import mag
x = Variable("x", "m")
xvk = x.varkeys.values()[0]
descr_before = x.exp.keys()[0].descr
y = Variable("y", "km")
yvk = y.varkeys.values()[0]
for x_ in ["x", xvk, x]:
for y_ in ["y", yvk, y]:
if not isinstance(y_, str) and type(xvk.units) != str:
expected = 0.001
else:
expected = 1.0
assert abs(expected - mag(x.sub(x_, y_).c)) < 1e-6
if type(xvk.units) != str:
# this means units are enabled
z = Variable("z", "s")
# y.sub(y, z) will raise ValueError due to unit mismatch
.. literalinclude:: examples/substituting_nonnumeric_values.py

Note that units are preserved, and that the value can be either a string (in which case it just renames the variable), a varkey (in which case it changes its description, including the name) or a Monomial (in which case it substitutes for the variable with a new monomial).

Expand All @@ -219,16 +136,7 @@ Freeing Fixed Variables

After creating a Model, it may be useful to "free" a fixed variable and resolve. This can be done using the command ``del m.substitutions["x"]``, where ``m`` is a Model. An example of how to do this is shown below.

.. code-block:: python
from gpkit import Variable, Model
x = Variable("x")
y = Variable("y", 3) # fix value to 3
m = Model(x, [x >= 1 + y, y >= 1])
_ = m.solve() # optimal cost is 4; y appears in sol["constants"]
del m.substitutions["y"]
_ = m.solve() # optimal cost is 2; y appears in Free Variables
.. literalinclude:: examples/freeing_fixed_variables.py

Note that ``del m.substitutions["y"]`` affects ``m`` but not ``y.key``.
``y.value`` will still be 3, and if ``y`` is used in a new model,
Expand Down
6 changes: 3 additions & 3 deletions docs/source/examples.rst
Expand Up @@ -34,7 +34,7 @@ Say we had a fixed mass of water we wanted to contain within a tank, but also wa

.. literalinclude:: examples/water_tank.py

The output is
The output is:

.. literalinclude:: examples/water_tank_output.txt

Expand All @@ -44,7 +44,7 @@ This example comes from Section 3 of `Geometric Programming for Aircraft Design

.. literalinclude:: examples/simpleflight.py

The output is
The output is:

.. literalinclude:: examples/simpleflight_output.txt

Expand All @@ -54,7 +54,7 @@ In this example we consider a beam subjected to a uniformly distributed transver

.. literalinclude:: examples/beam.py

The output is
The output is:

.. literalinclude:: examples/beam_output.txt

Expand Down
2 changes: 1 addition & 1 deletion docs/source/examples/boundschecking.py
@@ -1,4 +1,4 @@
"verifies that bounds are caught through monomials"
"Verifies that bounds are caught through monomials"
from gpkit import Model, parse_variables
from gpkit.exceptions import UnboundedGP, UnknownInfeasible

Expand Down
30 changes: 30 additions & 0 deletions docs/source/examples/checking_result_changes.py
@@ -0,0 +1,30 @@
"Example code for solution saving and differencing."
import pickle
from gpkit import Model, Variable

# build model (dummy)
# decision variable
x = Variable("x")
y = Variable("y")

# objective and constraints
objective = 0.23 + x/y # minimize x and y
constraints = [x + y <= 5, x >= 1, y >= 2]

# create model
m = Model(objective, constraints)

# solve the model
sol = m.solve()

# save the current state of the model
sol.save("last_verified.sol")

# uncomment the line below to verify a new model
last_verified_sol = pickle.load(open("last_verified.sol", mode="rb"))
if not sol.almost_equal(last_verified_sol, reltol=1e-3):
print(last_verified_sol.diff(sol))

# Note you can replace the last three lines above with
# print(sol.diff("last_verified.sol"))
# if you don't mind doing the diff in that direction.
17 changes: 17 additions & 0 deletions docs/source/examples/evaluated_fixed_variables.py
@@ -0,0 +1,17 @@
"Example pre-solve evaluated fixed variable"
from gpkit import Variable, Model

# code from t_GPSubs.test_calcconst in tests/t_sub.py
x = Variable("x", "hours")
t_day = Variable("t_{day}", 12, "hours")
t_night = Variable("t_{night}", lambda c: 24 - c[t_day], "hours")

# note that t_night has a function as its value
m = Model(x, [x >= t_day, x >= t_night])
sol = m.solve(verbosity=0)
assert sol["variables"][t_night] == 12

# call substitutions
m.substitutions.update({t_day: ("sweep", [8, 12, 16])})
sol = m.solve(verbosity=0)
assert (sol["variables"][t_night] == [16, 12, 8]).all()
10 changes: 10 additions & 0 deletions docs/source/examples/evaluated_free_variables.py
@@ -0,0 +1,10 @@
"Example post-solve evaluated variable"
from gpkit import Variable, Model

# code from t_constraints.test_evalfn in tests/t_sub.py
x = Variable("x")
x2 = Variable("x^2", evalfn=lambda v: v[x]**2)
m = Model(x, [x >= 2])
m.unique_varkeys = set([x2.key])
sol = m.solve(verbosity=0)
assert abs(sol(x2) - 4) <= 1e-4
1 change: 1 addition & 0 deletions docs/source/examples/external_constraint.py
Expand Up @@ -22,3 +22,4 @@ def as_gpconstr(self, x0):
res = external_code(x_star)
# ...and returns a linearized posy <= 1
return (self.y >= res * self.x/x_star)

14 changes: 14 additions & 0 deletions docs/source/examples/freeing_fixed_variables.py
@@ -0,0 +1,14 @@
"Example of freeing fixed variables"
from gpkit import Variable, Model
x = Variable("x")
y = Variable("y", 3) # fix value to 3
m = Model(x, [x >= 1 + y, y >= 1])
sol = m.solve() # optimal cost is 4; y appears in sol["constants"]

assert abs(sol["cost"] - 4) <= 1e-4
assert y in sol["constants"]

del m.substitutions["y"]
sol = m.solve() # optimal cost is 2; y appears in Free Variables
assert abs(sol["cost"] - 2) <= 1e-4
assert y in sol["freevariables"]
80 changes: 80 additions & 0 deletions docs/source/examples/gettingstarted.py
@@ -0,0 +1,80 @@
"The getting started page"
from gpkit import Variable, VectorVariable, Model
from gpkit.nomials import Monomial, Posynomial, PosynomialInequality

### Example Free Variables
# Declare a variable, x
x = Variable("x")

# Declare a variable, y, with units of meters
y = Variable("y", "m")

# Declare a variable, z, with units of meters, and a description
z = Variable("z", "m", "A variable called z with units of meters")

### Example Fixed Variables 1
rho = Variable("rho", 1.225, "kg/m^3", "Density of air at sea level")

### Example Fixed Variables 2
#Declare pi equal to 3.14
pi = Variable("pi", 3.14159, "-", constant=True)

### Example Vector Variables
# Declare a 3-element vector variable "x" with units of "m"
x = VectorVariable(3, "x", "m", "Cube corner coordinates")
x_min = VectorVariable(3, "x", [1, 2, 3], "m", "Cube corner minimum")

### Example Creating Monomials and Posynomials 1
# create a Monomial term xy^2/z
x = Variable("x")
y = Variable("y")
z = Variable("z")
m = x * y**2 / z
assert isinstance(m, Monomial)

### Example Creating Monomials and Posynomials 2
# create a Posynomial expression x + xy^2
x = Variable("x")
y = Variable("y")
p = x + x * y**2
assert isinstance(p, Posynomial)

### Example Declaring Constraints
# consider a block with dimensions x, y, z less than 1
# constrain surface area less than 1.0 m^2
x = Variable("x", "m")
y = Variable("y", "m")
z = Variable("z", "m")
S = Variable("S", 1.0, "m^2")
c = (2*x*y + 2*x*z + 2*y*z <= S)
assert isinstance(c, PosynomialInequality)

### Example Formulating a Model
x = Variable("x")
y = Variable("y")
z = Variable("z")
S = 200
objective = 1/(x*y*z)
constraints = [2*x*y + 2*x*z + 2*y*z <= S,
x >= 2*y]
m = Model(objective, constraints)

### Example Solving the Model
sol = m.solve(verbosity=0)

### Printing Results 1
print(sol.table())

### Printing Results 2
print("The optimal value is %s." % sol["cost"])

### Example variable sensitivity usage
x = Variable("x")
x_min = Variable("x_{min}", 2)
sol = Model(x, [x_min <= x]).solve()
sens_x_min = sol["sensitivities"]["variables"][x_min]

x = Variable("x")
x_squared_min = Variable("x^2_{min}", 2)
sol = Model(x, [x_squared_min <= x**2]).solve()
sens_x_min = sol["sensitivities"]["variables"][x_squared_min]
Binary file added docs/source/examples/last_verified.pkl
Binary file not shown.
Binary file added docs/source/examples/last_verified.sol
Binary file not shown.
10 changes: 10 additions & 0 deletions docs/source/examples/loose_constraintsets.py
@@ -0,0 +1,10 @@
"Example Loose ConstraintSet usage"
from gpkit import Variable, Model
from gpkit.constraints.loose import Loose

Loose.reltol = 1e-4 # set the global tolerance of Loose
x = Variable('x')
x_min = Variable('x_{min}', 1)
m = Model(x, [Loose([x >= 2], senstol=1e-4), # set the specific tolerance
x >= x_min])
m.solve(verbosity=0) # prints warning

0 comments on commit f9a3be6

Please sign in to comment.