55
66# External modules
77import 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
282133if __name__ == "__main__" :
0 commit comments