Coverage for gpkit/tests/t_examples.py: 99%

200 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-07 22:15 -0500

1"""Unit testing of tests in docs/source/examples""" 

2import unittest 

3import os 

4import pickle 

5import json 

6import numpy as np 

7 

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) 

15 

16 

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) 

21 

22 

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. 

29 

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") 

34 

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.: 

37 

38 def test_dummy_example(self, example): 

39 pass 

40 

41 But it's good practice to ensure the example's solution as well, e.g.: 

42 

43 def test_dummy_example(self, example): 

44 self.assertAlmostEqual(example.sol["cost"], 3.121) 

45 """ 

46 

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") 

51 

52 def test_breakdowns(self, example): 

53 pass 

54 

55 def test_issue_1513(self, example): 

56 pass 

57 

58 def test_issue_1522(self, example): 

59 pass 

60 

61 def test_autosweep(self, example): 

62 bst1, tol1 = example.bst1, example.tol1 

63 bst2, tol2 = example.bst2, example.tol2 

64 

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) 

73 

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) 

86 

87 def test_treemap(self, example): 

88 pass 

89 

90 def test_checking_result_changes(self, example): 

91 sol = example.sol 

92 self.assertAlmostEqual(sol["cost"], 0.48, 2) 

93 

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()) 

98 

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) 

103 

104 def test_external_constraint(self, example): 

105 pass 

106 

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))) 

113 

114 def test_external_function(self, example): 

115 external_code = example.external_code 

116 self.assertEqual(external_code(0), 0) 

117 

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) 

122 

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"]) 

130 

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"]) 

135 

136 def test_gettingstarted(self, example): 

137 pass 

138 

139 

140 def test_loose_constraintsets(self, example): 

141 m = example.m 

142 sol = m.solve(verbosity=0) 

143 self.assertAlmostEqual(sol["cost"], 2, 3) 

144 

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 )) 

154 

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) 

161 

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) 

166 

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)) 

174 

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 

180 

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() 

190 

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() 

198 

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()) 

208 

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) 

215 

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 

222 

223 def test_vectorize(self, example): 

224 pass 

225 

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) 

232 

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) 

239 

240 def test_docstringparsing(self, example): 

241 pass 

242 

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) 

252 

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) 

258 

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) 

264 

265 def test_simple_sp(self, example): 

266 pass 

267 

268 def test_simple_box(self, example): 

269 pass 

270 

271 def test_x_greaterthan_1(self, example): 

272 pass 

273 

274 def test_beam(self, example): 

275 self.assertFalse(np.isnan(example.sol("w")).any()) 

276 

277 def test_water_tank(self, example): 

278 pass 

279 

280 def test_sin_approx_example(self, example): 

281 pass 

282 

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) 

317 

318 def test_relaxation(self, example): 

319 pass 

320 

321 def test_unbounded(self, example): 

322 pass 

323 

324 

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 = [] 

332 

333if __name__ == "__main__": # pragma: no cover 

334 # pylint:disable=wrong-import-position 

335 from gpkit.tests.helpers import run_tests 

336 run_tests(TESTS)