Coverage for gpkit/tests/t_examples.py: 99%
200 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-05 22:33 -0500
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-05 22:33 -0500
1"""Unit testing of tests in docs/source/examples"""
2import unittest
3import os
4import pickle
5import json
6import numpy as np
8from gpkit import settings, Model, Variable, ureg
9from gpkit.tests.helpers import generate_example_tests
10from gpkit.small_scripts import mag
11from gpkit.small_classes import Quantity
12from gpkit.constraints.loose import Loose
13from gpkit.exceptions import (UnknownInfeasible,
14 PrimalInfeasible, DualInfeasible, UnboundedGP)
17def assert_logtol(first, second, logtol=1e-6):
18 "Asserts that the logs of two arrays have a given abstol"
19 np.testing.assert_allclose(np.log(mag(first)), np.log(mag(second)),
20 atol=logtol, rtol=0)
23# pylint: disable=too-many-public-methods
24class TestExamples(unittest.TestCase):
25 """
26 To test a new example, add a function called `test_$EXAMPLENAME`, where
27 $EXAMPLENAME is the name of your example in docs/source/examples without
28 the file extension.
30 This function should accept two arguments (e.g. 'self' and 'example').
31 The imported example script will be passed to the second: anything that
32 was a global variable (e.g, "sol") in the original script is available
33 as an attribute (e.g., "example.sol")
35 If you don't want to perform any checks on the example besides making
36 sure it runs, just put "pass" as the function's body, e.g.:
38 def test_dummy_example(self, example):
39 pass
41 But it's good practice to ensure the example's solution as well, e.g.:
43 def test_dummy_example(self, example):
44 self.assertAlmostEqual(example.sol["cost"], 3.121)
45 """
47 # TODO: allow enabling plotting examples, make plots in correct folder...
48 # def test_plot_sweep1d(self, _):
49 # import matplotlib.pyplot as plt
50 # plt.close("all")
52 def test_breakdowns(self, example):
53 pass
55 def test_issue_1513(self, example):
56 pass
58 def test_issue_1522(self, example):
59 pass
61 def test_autosweep(self, example):
62 bst1, tol1 = example.bst1, example.tol1
63 bst2, tol2 = example.bst2, example.tol2
65 l_ = np.linspace(1, 10, 100)
66 for bst in [bst1, example.bst1_loaded]:
67 sol1 = bst.sample_at(l_)
68 assert_logtol(sol1("l"), l_)
69 assert_logtol(sol1("A"), l_**2 + 1, tol1)
70 assert_logtol(sol1["cost"], (l_**2 + 1)**2, tol1)
71 self.assertEqual(Quantity(1.0, sol1("A").units),
72 Quantity(1.0, ureg.m)**2)
74 ndig = -int(np.log10(tol2))
75 self.assertAlmostEqual(bst2.cost_at("cost", 3), 1.0, ndig)
76 # before corner
77 a_bc = np.linspace(1, 3, 50)
78 sol_bc = bst2.sample_at(a_bc)
79 assert_logtol(sol_bc("A"), (a_bc/3)**0.5, tol2)
80 assert_logtol(sol_bc["cost"], a_bc/3, tol2)
81 # after corner
82 a_ac = np.linspace(3, 10, 50)
83 sol_ac = bst2.sample_at(a_ac)
84 assert_logtol(sol_ac("A"), (a_ac/3)**2, tol2)
85 assert_logtol(sol_ac["cost"], (a_ac/3)**4, tol2)
87 def test_treemap(self, example):
88 pass
90 def test_checking_result_changes(self, example):
91 sol = example.sol
92 self.assertAlmostEqual(sol["cost"], 0.48, 2)
94 def test_evaluated_fixed_variables(self, example):
95 sol = example.sol
96 t_night = example.t_night
97 self.assertTrue((sol["variables"][t_night] == [16, 12, 8]).all())
99 def test_evaluated_free_variables(self, example):
100 x2 = example.x2
101 sol = example.sol
102 self.assertTrue(abs(sol(x2) - 4) <= 1e-4)
104 def test_external_constraint(self, example):
105 pass
107 def test_migp(self, example):
108 if settings["default_solver"] == "mosek_conif":
109 assert_logtol(example.sol(example.x), [1]*3 + [2]*6 + [3]*2)
110 else:
111 assert_logtol(example.sol(example.x),
112 np.sqrt(example.sol(example.num)))
114 def test_external_function(self, example):
115 external_code = example.external_code
116 self.assertEqual(external_code(0), 0)
118 def test_external_sp(self, example):
119 m = example.m
120 sol = m.localsolve(verbosity=0)
121 self.assertAlmostEqual(sol["cost"], 0.707, places=3)
123 def test_freeing_fixed_variables(self, example):
124 x = example.x
125 y = Variable("y", 3)
126 m = Model(x, [x >= 1 + y, y >= 1])
127 sol = m.solve(verbosity=0)
128 self.assertTrue(abs(sol["cost"] - 4) <= 1e-4)
129 self.assertTrue(y in sol["constants"])
131 del m.substitutions["y"]
132 sol = m.solve(verbosity=0)
133 self.assertTrue(abs(sol["cost"] - 2) <= 1e-4)
134 self.assertTrue(y in sol["freevariables"])
136 def test_gettingstarted(self, example):
137 pass
140 def test_loose_constraintsets(self, example):
141 m = example.m
142 sol = m.solve(verbosity=0)
143 self.assertAlmostEqual(sol["cost"], 2, 3)
145 def test_sub_multi_values(self, example):
146 x = example.x
147 y = example.y
148 z = example.z
149 p = example.p
150 self.assertTrue(all(p.sub({x: 1, "y": 2}) == 2*z))
151 self.assertTrue(all(
152 p.sub({x: 1, y: 2, "z": [1, 2]}) == z.sub({z: [2, 4]})
153 ))
155 def test_substitutions(self, example):
156 x = example.x
157 p = example.p
158 self.assertTrue(p.sub({x: 3}) == 9)
159 self.assertTrue(p.sub({x.key: 3}) == 9)
160 self.assertTrue(p.sub({"x": 3}) == 9)
162 def test_tight_constraintsets(self, example):
163 m = example.m
164 sol = m.solve(verbosity=0)
165 self.assertAlmostEqual(sol["cost"], 2, places=2)
167 def test_vectorization(self, example):
168 x = example.x
169 y = example.y
170 z = example.z
171 self.assertEqual(y.shape, (5, 3))
172 self.assertEqual(x.shape, (2, 5, 3))
173 self.assertEqual(z.shape, (7, 3))
175 def test_model_var_access(self, example):
176 model = example.PS
177 _ = model["E"]
178 with self.assertRaises(ValueError):
179 _ = model["m"] # multiple variables called m
181 def test_performance_modeling(self, example):
182 m = Model(example.M.cost, Loose(example.M), example.M.substitutions)
183 sol = m.solve(verbosity=0)
184 sol.table()
185 sol.save("solution.pkl")
186 sol.table()
187 with open("solution.pkl", "rb") as f:
188 sol_loaded = pickle.load(f)
189 sol_loaded.table()
191 sweepsol = m.sweep({example.AC.fuse.W: (50, 100, 150)}, verbosity=0)
192 sweepsol.table()
193 sweepsol.save("sweepsolution.pkl")
194 sweepsol.table()
195 with open("sweepsolution.pkl", "rb") as f:
196 sol_loaded = pickle.load(f)
197 sol_loaded.table()
199 # testing savejson
200 sol.savejson("solution.json")
201 json_dict = {}
202 with open("solution.json", "r", encoding="UTF-8") as rf:
203 json_dict = json.load(rf)
204 for var in sol["variables"]:
205 self.assertTrue(np.all(json_dict[str(var.key)]['v']
206 == sol["variables"][var.key]))
207 self.assertEqual(json_dict[str(var.key)]['u'], var.unitstr())
209 def test_sp_to_gp_sweep(self, example):
210 sol = example.sol
211 cost = sol["cost"]
212 self.assertAlmostEqual(cost[0], 4628.21, places=2)
213 self.assertAlmostEqual(cost[1], 6226.60, places=2)
214 self.assertAlmostEqual(cost[2], 7362.77, places=2)
216 def test_boundschecking(self, example): # pragma: no cover
217 if "mosek_cli" in settings["default_solver"]:
218 with self.assertRaises(UnknownInfeasible):
219 example.gp.solve(verbosity=0)
220 else:
221 example.gp.solve(verbosity=0) # mosek_conif and cvxopt solve it
223 def test_vectorize(self, example):
224 pass
226 def test_primal_infeasible_ex1(self, example):
227 primal_or_unknown = PrimalInfeasible
228 if "cvxopt" in settings["default_solver"]: # pragma: no cover
229 primal_or_unknown = UnknownInfeasible
230 with self.assertRaises(primal_or_unknown):
231 example.m.solve(verbosity=0)
233 def test_primal_infeasible_ex2(self, example):
234 primal_or_unknown = PrimalInfeasible
235 if "cvxopt" in settings["default_solver"]: # pragma: no cover
236 primal_or_unknown = UnknownInfeasible
237 with self.assertRaises(primal_or_unknown):
238 example.m.solve(verbosity=0)
240 def test_docstringparsing(self, example):
241 pass
243 def test_debug(self, example):
244 dual_or_primal = DualInfeasible
245 if "mosek_conif" == settings["default_solver"]: # pragma: no cover
246 dual_or_primal = PrimalInfeasible
247 with self.assertRaises(UnboundedGP):
248 example.m.gp()
249 with self.assertRaises(dual_or_primal):
250 gp = example.m.gp(checkbounds=False)
251 gp.solve(verbosity=0)
253 primal_or_unknown = PrimalInfeasible
254 if "cvxopt" == settings["default_solver"]: # pragma: no cover
255 primal_or_unknown = UnknownInfeasible
256 with self.assertRaises(primal_or_unknown):
257 example.m2.solve(verbosity=0)
259 with self.assertRaises(UnboundedGP):
260 example.m3.gp()
261 with self.assertRaises(DualInfeasible):
262 gp3 = example.m3.gp(checkbounds=False)
263 gp3.solve(verbosity=0)
265 def test_simple_sp(self, example):
266 pass
268 def test_simple_box(self, example):
269 pass
271 def test_x_greaterthan_1(self, example):
272 pass
274 def test_beam(self, example):
275 self.assertFalse(np.isnan(example.sol("w")).any())
277 def test_water_tank(self, example):
278 pass
280 def test_sin_approx_example(self, example):
281 pass
283 def test_simpleflight(self, example):
284 self.assertTrue(example.sol.almost_equal(example.sol_loaded))
285 for sol in [example.sol, example.sol_loaded]:
286 freevarcheck = {
287 "A": 8.46,
288 "C_D": 0.0206,
289 "C_f": 0.0036,
290 "C_L": 0.499,
291 "Re": 3.68e+06,
292 "S": 16.4,
293 "W": 7.34e+03,
294 "V": 38.2,
295 "W_w": 2.40e+03
296 }
297 # sensitivity values from p. 34 of W. Hoburg's thesis
298 senscheck = {
299 r"(\frac{S}{S_{wet}})": 0.4300,
300 "e": -0.4785,
301 "V_{min}": -0.3691,
302 "k": 0.4300,
303 r"\mu": 0.0860,
304 "(CDA0)": 0.0915,
305 "C_{L,max}": -0.1845,
306 r"\tau": -0.2903,
307 "N_{ult}": 0.2903,
308 "W_0": 1.0107,
309 r"\rho": -0.2275
310 }
311 for key, val in freevarcheck.items():
312 sol_rat = mag(sol["variables"][key])/val
313 self.assertTrue(abs(1-sol_rat) < 1e-2)
314 for key, val in senscheck.items():
315 sol_rat = sol["sensitivities"]["variables"][key]/val
316 self.assertTrue(abs(1-sol_rat) < 1e-2)
318 def test_relaxation(self, example):
319 pass
321 def test_unbounded(self, example):
322 pass
325FILE_DIR = os.path.dirname(os.path.realpath(__file__))
326EXAMPLE_DIR = os.path.abspath(FILE_DIR + '../../../docs/source/examples')
327SOLVERS = settings["installed_solvers"]
328if os.path.isdir(EXAMPLE_DIR):
329 TESTS = generate_example_tests(EXAMPLE_DIR, [TestExamples], SOLVERS)
330else: # pragma: no cover
331 TESTS = []
333if __name__ == "__main__": # pragma: no cover
334 # pylint:disable=wrong-import-position
335 from gpkit.tests.helpers import run_tests
336 run_tests(TESTS)