Skip to content

Commit bf140e2

Browse files
authored
Refactor tests (#251)
* added test util file * refactored into a separate testing class * renamed check to assert * simplified some code * updated test_sparse * renamed utils to testing_utils * moved optimize_with_hotstart and check_hist_file to test class * updated tp109 * changed sol.fStar to a dictionary format * add support for multiple solutions in asserts * updated hs015 * generalized testing functions * changed obj back to scalar instead of dict * updated base class API * only skip tests if non built-in optimizer * updated isort * Update .flake8 * added isort.cfg * moved test_large_snopt to test_large_sparse * switched to parameterized tests * made sparse tests smaller * added version check for SNOPT < 7.7.7 * updated NSGA2 test * stopped storing opt in testing class, was causing obscure pickle error * isort * removed unnecessary code from ALPSO wrapper * switched from packaging to pkg_resources * switched to using self.id() to access the test name * removed duplicate code * added some comments
1 parent b955064 commit bf140e2

14 files changed

+934
-899
lines changed

.flake8

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[flake8]
2-
exclude =
2+
extend-exclude =
33
# OptView and related files need to be fixed eventually
44
pyoptsparse/postprocessing/OptView.py
55
pyoptsparse/postprocessing/OptView_baseclass.py

.isort.cfg

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[isort]
2+
profile=black
3+
line_length=120
4+
force_sort_within_sections=true
5+
import_heading_stdlib=Standard Python modules
6+
import_heading_thirdparty=External modules
7+
import_heading_firstparty=First party modules
8+
import_heading_localfolder=Local modules
9+
skip_glob=**__init__.py,**setup.py
10+
known_local_folder=testing_utils

pyoptsparse/pyALPSO/pyALPSO.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,6 @@ def objconfunc(x):
181181

182182
# Create the optimization solution
183183
sol = self._createSolution(optTime, sol_inform, opt_f, opt_x)
184-
for key in sol.objectives.keys():
185-
sol.objectives[key].value = opt_f
186184
else: # We are not on the root process so go into waiting loop:
187185
self._waitLoop()
188186
sol = None

test/test_hs015.py

Lines changed: 53 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55

66
# External modules
77
import numpy as np
8-
from numpy.testing import assert_allclose
8+
from parameterized import parameterized
99

1010
# First party modules
11-
from pyoptsparse import OPT, History, Optimization
12-
from pyoptsparse.pyOpt_error import Error
11+
from pyoptsparse import History, Optimization
1312

13+
# Local modules
14+
from testing_utils import OptTest
1415

15-
class TestHS15(unittest.TestCase):
16+
17+
class TestHS15(OptTest):
1618

1719
## Solve test problem HS15 from the Hock & Schittkowski collection.
1820
#
@@ -27,6 +29,29 @@ class TestHS15(unittest.TestCase):
2729
# at (-0.79212, -1.26243), with final objective = 360.4.
2830
##
2931

32+
name = "HS015"
33+
DVs = {"xvars"}
34+
cons = {"con"}
35+
objs = {"obj"}
36+
extras = {"extra1", "extra2"}
37+
fStar = [
38+
306.5,
39+
360.379767,
40+
]
41+
xStar = [
42+
{"xvars": (0.5, 2.0)},
43+
{"xvars": (-0.79212322, -1.26242985)},
44+
]
45+
tol = {
46+
"SLSQP": 1e-8,
47+
"NLPQLP": 1e-12,
48+
"IPOPT": 1e-4,
49+
"ParOpt": 1e-6,
50+
"CONMIN": 1e-10,
51+
"PSQP": 5e-12,
52+
}
53+
optOptions = {}
54+
3055
def objfunc(self, xdict):
3156
self.nf += 1
3257
x = xdict["xvars"]
@@ -53,190 +78,30 @@ def sens(self, xdict, funcs):
5378
fail = False
5479
return funcsSens, fail
5580

56-
def optimize(self, optName, tol, optOptions={}, storeHistory=False, hotStart=None, x0=[-2, 1.0]):
57-
self.nf = 0 # number of function evaluations
58-
self.ng = 0 # number of gradient evaluations
81+
def setup_optProb(self):
5982
# Optimization Object
60-
optProb = Optimization("HS15 Constraint Problem", self.objfunc)
83+
self.optProb = Optimization("HS15 Constraint Problem", self.objfunc)
6184

6285
# Design Variables
6386
lower = [-5.0, -5.0]
6487
upper = [0.5, 5.0]
65-
optProb.addVarGroup("xvars", 2, lower=lower, upper=upper, value=x0)
88+
value = [-2, 1.0]
89+
self.optProb.addVarGroup("xvars", 2, lower=lower, upper=upper, value=value)
6690

6791
# Constraints
6892
lower = [1.0, 0.0]
6993
upper = [None, None]
70-
optProb.addConGroup("con", 2, lower=lower, upper=upper)
94+
self.optProb.addConGroup("con", 2, lower=lower, upper=upper)
7195

7296
# Objective
73-
optProb.addObj("obj")
74-
75-
# Check optimization problem:
76-
print(optProb)
77-
78-
# Optimizer
79-
try:
80-
opt = OPT(optName, options=optOptions)
81-
except Error:
82-
raise unittest.SkipTest("Optimizer not available:", optName)
83-
84-
# Solution
85-
if storeHistory is not None:
86-
if storeHistory is True:
87-
self.histFileName = "%s_hs015_Hist.hst" % (optName.lower())
88-
elif isinstance(storeHistory, str):
89-
self.histFileName = storeHistory
90-
else:
91-
self.histFileName = None
92-
93-
sol = opt(optProb, sens=self.sens, storeHistory=self.histFileName, hotStart=hotStart)
94-
95-
# Test printing solution to screen
96-
print(sol)
97-
98-
# Check Solution
99-
self.fStar1 = 306.5
100-
self.fStar2 = 360.379767
101-
102-
self.xStar1 = (0.5, 2.0)
103-
self.xStar2 = (-0.79212322, -1.26242985)
104-
105-
dv = sol.getDVs()
106-
sol_xvars = [sol.variables["xvars"][i].value for i in range(2)]
107-
assert_allclose(sol_xvars, dv["xvars"], atol=tol, rtol=tol)
108-
# we check either optimum via try/except
109-
try:
110-
if optName == "SNOPT" and opt.version != "7.7.7":
111-
assert_allclose(sol.objectives["obj"].value, self.fStar1, atol=tol, rtol=tol)
112-
else:
113-
assert_allclose(sol.fStar, self.fStar1, atol=tol, rtol=tol)
114-
assert_allclose(dv["xvars"], self.xStar1, atol=tol, rtol=tol)
115-
except AssertionError:
116-
if optName == "SNOPT" and opt.version != "7.7.7":
117-
assert_allclose(sol.objectives["obj"].value, self.fStar2, atol=tol, rtol=tol)
118-
else:
119-
assert_allclose(sol.fStar, self.fStar2, atol=tol, rtol=tol)
120-
assert_allclose(dv["xvars"], self.xStar2, atol=tol, rtol=tol)
121-
122-
def check_hist_file(self, optimizer, tol):
123-
"""
124-
We check the history file here along with the API
125-
"""
126-
hist = History(self.histFileName, flag="r")
127-
# Metadata checks
128-
metadata = hist.getMetadata()
129-
self.assertEqual(metadata["optimizer"], optimizer)
130-
metadata_def_keys = [
131-
"optName",
132-
"optOptions",
133-
"nprocs",
134-
"startTime",
135-
"endTime",
136-
"optTime",
137-
"version",
138-
"optVersion",
139-
]
140-
for key in metadata_def_keys:
141-
self.assertIn(key, metadata)
142-
# we test that SNOPT version is stored correctly
143-
if optimizer == "SNOPT" and key == "optVersion":
144-
self.assertNotEqual(metadata[key], None)
145-
hist.getOptProb()
146-
147-
# Info checks
148-
self.assertEqual(hist.getDVNames(), ["xvars"])
149-
self.assertEqual(hist.getConNames(), ["con"])
150-
self.assertEqual(hist.getObjNames(), ["obj"])
151-
dvInfo = hist.getDVInfo()
152-
self.assertEqual(len(dvInfo), 1)
153-
self.assertEqual(dvInfo["xvars"], hist.getDVInfo(key="xvars"))
154-
conInfo = hist.getConInfo()
155-
self.assertEqual(len(conInfo), 1)
156-
self.assertEqual(conInfo["con"], hist.getConInfo(key="con"))
157-
objInfo = hist.getObjInfo()
158-
self.assertEqual(len(objInfo), 1)
159-
self.assertEqual(objInfo["obj"], hist.getObjInfo(key="obj"))
160-
for key in ["lower", "upper", "scale"]:
161-
self.assertIn(key, dvInfo["xvars"])
162-
self.assertIn(key, conInfo["con"])
163-
self.assertIn("scale", objInfo["obj"])
164-
165-
# callCounter checks
166-
callCounters = hist.getCallCounters()
167-
last = hist.read("last") # 'last' key should be present
168-
self.assertIn(last, callCounters)
169-
170-
# iterKeys checks
171-
iterKeys = hist.getIterKeys()
172-
for key in ["xuser", "fail", "isMajor"]:
173-
self.assertIn(key, iterKeys)
174-
175-
# extraFuncsNames checks
176-
extraFuncsNames = hist.getExtraFuncsNames()
177-
for key in ["extra1", "extra2"]:
178-
self.assertIn(key, extraFuncsNames)
179-
180-
# getValues checks
181-
val = hist.getValues()
182-
183-
# this check is only used for optimizers that guarantee '0' and 'last' contain funcs
184-
if optimizer in ["SNOPT", "PSQP"]:
185-
val = hist.getValues(callCounters=["0", "last"], stack=True)
186-
self.assertEqual(val["isMajor"].size, 2)
187-
self.assertTrue(val["isMajor"][0]) # the first callCounter must be a major iteration
188-
self.assertTrue(val["isMajor"][-1]) # the last callCounter must be a major iteration
189-
# check optimum stored in history file against xstar
190-
assert_allclose(val["xuser"][-1], self.xStar1, atol=tol, rtol=tol)
191-
192-
def optimize_with_hotstart(self, optName, tol, optOptions={}):
193-
"""
194-
This code will perform 4 optimizations, one real opt and three restarts.
195-
In this process, it will check various combinations of storeHistory and hotStart filenames.
196-
It will also call `check_hist_file` after the first optimization.
197-
"""
198-
self.optimize(optName, tol, storeHistory=True, optOptions=optOptions, x0=[-2.001, 1.001])
199-
self.assertGreater(self.nf, 0)
200-
self.assertGreater(self.ng, 0)
201-
self.check_hist_file(optName, tol)
202-
203-
# re-optimize with hotstart
204-
self.optimize(optName, tol, storeHistory=False, hotStart=self.histFileName, optOptions=optOptions)
205-
# we should have zero actual function/gradient evaluations
206-
self.assertEqual(self.nf, 0)
207-
self.assertEqual(self.ng, 0)
208-
# another test with hotstart, this time with storeHistory = hotStart
209-
self.optimize(optName, tol, storeHistory=True, hotStart=self.histFileName, optOptions=optOptions)
210-
# we should have zero actual function/gradient evaluations
211-
self.assertEqual(self.nf, 0)
212-
self.assertEqual(self.ng, 0)
213-
# another test with hotstart, this time with a non-existing history file
214-
# this will perform a cold start
215-
self.optimize(optName, tol, storeHistory=True, hotStart="notexisting.hst", optOptions=optOptions)
216-
self.assertGreater(self.nf, 0)
217-
self.assertGreater(self.ng, 0)
218-
self.check_hist_file(optName, tol)
219-
# final test with hotstart, this time with a different storeHistory
220-
self.optimize(
221-
optName,
222-
tol,
223-
storeHistory="{}_new_hotstart.hst".format(optName),
224-
hotStart=self.histFileName,
225-
optOptions=optOptions,
226-
)
227-
# we should have zero actual function/gradient evaluations
228-
self.assertEqual(self.nf, 0)
229-
self.assertEqual(self.ng, 0)
97+
self.optProb.addObj("obj")
23098

23199
def test_snopt(self):
232-
test_name = "hs015_SNOPT"
100+
self.optName = "SNOPT"
101+
self.setup_optProb()
233102
store_vars = ["step", "merit", "feasibility", "optimality", "penalty", "Hessian", "condZHZ", "slack", "lambda"]
234-
optOptions = {
235-
"Save major iteration variables": store_vars,
236-
"Print file": "{}.out".format(test_name),
237-
"Summary file": "{}_summary.out".format(test_name),
238-
}
239-
self.optimize_with_hotstart("SNOPT", 1e-12, optOptions=optOptions)
103+
optOptions = {"Save major iteration variables": store_vars}
104+
self.optimize_with_hotstart(1e-12, optOptions=optOptions)
240105

241106
hist = History(self.histFileName, flag="r")
242107
data = hist.getValues(callCounters=["last"])
@@ -249,34 +114,20 @@ def test_snopt(self):
249114
self.assertEqual(data["feasibility"].shape, (1, 1))
250115
self.assertEqual(data["slack"].shape, (1, 2))
251116
self.assertEqual(data["lambda"].shape, (1, 2))
252-
253-
def test_slsqp(self):
254-
optOptions = {"IFILE": "hs015_SLSQP.out"}
255-
self.optimize_with_hotstart("SLSQP", 1e-8, optOptions=optOptions)
256-
257-
def test_nlpqlp(self):
258-
optOptions = {"iFile": "hs015_NLPQLP.out"}
259-
self.optimize_with_hotstart("NLPQLP", 1e-12, optOptions=optOptions)
260-
261-
def test_ipopt(self):
262-
optOptions = {"output_file": "hs015_IPOPT.out"}
263-
self.optimize_with_hotstart("IPOPT", 1e-4, optOptions=optOptions)
264-
265-
def test_paropt(self):
266-
optOptions = {"output_file": "hs015_ParOpt.out"}
267-
self.optimize_with_hotstart("ParOpt", 1e-6, optOptions=optOptions)
268-
269-
def test_conmin(self):
270-
optOptions = {
271-
"DELFUN": 1e-10,
272-
"DABFUN": 1e-10,
273-
"IFILE": "hs015_CONMIN.out",
274-
}
275-
self.optimize_with_hotstart("CONMIN", 1e-10, optOptions=optOptions)
276-
277-
def test_psqp(self):
278-
optOptions = {"IFILE": "hs015_PSQP.out"}
279-
self.optimize_with_hotstart("PSQP", 5e-12, optOptions=optOptions)
117+
# dv = sol.getDVs()
118+
# sol_xvars = [sol.variables["xvars"][i].value for i in range(2)]
119+
# assert_allclose(sol_xvars, dv["xvars"], atol=tol, rtol=tol)
120+
121+
@parameterized.expand(["IPOPT", "SLSQP", "PSQP", "CONMIN", "NLPQLP", "ParOpt"])
122+
def test_optimization(self, optName):
123+
self.optName = optName
124+
self.setup_optProb()
125+
optOptions = self.optOptions.pop(optName, None)
126+
sol = self.optimize(optOptions=optOptions)
127+
# Check Solution
128+
self.assert_solution_allclose(sol, self.tol[optName])
129+
# Check informs
130+
self.assert_inform_equal(sol)
280131

281132

282133
if __name__ == "__main__":

0 commit comments

Comments
 (0)