@@ -79,6 +79,8 @@ julia> comparison(max_iter=100, test_name=:solution)
7979"""
8080function comparison(; max_iter, test_name)
8181
82+ test_tag(res) = res ? " \0 33[1;32mPASS\0 33[0m" : " \0 33[1;31mFAIL\0 33[0m"
83+
8284 #
8385 available_test_names = [:init, :solution, :iter1]
8486 test_name ∈ available_test_names ||
@@ -97,6 +99,9 @@ function comparison(; max_iter, test_name)
9799 ε_rel_control = 1e-1
98100 ε_abs_control = 1e-6
99101
102+ ε_rel_variable = 1e-1
103+ ε_abs_variable = 1e-6
104+
100105 # options_ipopt for solvers
101106 options_ipopt = Dict(
102107 :print_level => 0 ,
@@ -117,6 +122,96 @@ function comparison(; max_iter, test_name)
117122 :linear_solver => MumpsSolver,
118123 )
119124
125+ #
126+ function test_L2_i(i, times, A, B, A_name, B_name; ε_abs, ε_rel)
127+ yi_oc, yi_jp = [B[k][i] for k in eachindex(times)], [A[k][i] for k in eachindex(times)]
128+ L2_di = L2_norm(times, yi_oc - yi_jp)
129+ L2_bd = max(0.5 * (L2_norm(times, yi_oc)+ L2_norm(times, yi_jp))* ε_rel, ε_abs)
130+ res = @my_test_broken L2_di < L2_bd
131+ r_err = L2_di / (0.5 * (L2_norm(times, yi_oc)+ L2_norm(times, yi_jp)))
132+ DEBUG && @printf(" │ → %s vs %s: r_err=%.3e a_err=%.3e bound=%.3e %s\n " , A_name, B_name, r_err, L2_di, L2_bd, test_tag(res))
133+ return res
134+ end
135+
136+ function test_L2_i(i, times, A, B, A_name, B_name, keep_problem; ε_abs, ε_rel)
137+ res = test_L2_i(i, times, A, B, A_name, B_name; ε_abs= ε_abs, ε_rel= ε_rel)
138+ return keep_problem && res
139+ end
140+
141+ #
142+ function test_abs(A, B, A_name, B_name; ε_abs, ε_rel)
143+ tf_di = abs(A - B)
144+ tf_bd = max(0.5 * (A+ B)* ε_rel, ε_abs)
145+ res = @my_test_broken tf_di < tf_bd
146+ r_err = tf_di / (0.5 * (A+ B))
147+ DEBUG && @printf(" │ → %s: %.3e %s: %.3e r_err=%.3e a_err=%.3e bound=%.3e %s\n " , A_name, A, B_name, B, r_err, tf_di, tf_bd, test_tag(res))
148+ return res
149+ end
150+
151+ function test_abs(A, B, A_name, B_name, keep_problem; ε_abs, ε_rel)
152+ res = test_abs(A, B, A_name, B_name; ε_abs= ε_abs, ε_rel= ε_rel)
153+ return res && keep_problem
154+ end
155+
156+ function test_abs(A, B, A_name, B_name, keep_problem, test_grid_ok; ε_abs, ε_rel)
157+ res = test_abs(A, B, A_name, B_name; ε_abs= ε_abs, ε_rel= ε_rel)
158+ return res && keep_problem, res && test_grid_ok
159+ end
160+
161+ function test_abs_i(i, A, B, A_name, B_name; ε_abs, ε_rel)
162+ return test_abs(A[i], B[i], A_name, B_name; ε_abs, ε_rel)
163+ end
164+
165+ function test_abs_i(i, A, B, A_name, B_name, keep_problem; ε_abs, ε_rel)
166+ return test_abs(A[i], B[i], A_name, B_name, keep_problem; ε_abs, ε_rel)
167+ end
168+
169+ #
170+ function test_int(A, B, A_name, B_name)
171+ res = @my_test_broken A == B
172+ DEBUG && @printf(" │ → %s: %d %s: %d %s\n " , A_name, A, B_name, B, test_tag(res))
173+ return res
174+ end
175+
176+ function test_int(A, B, A_name, B_name, keep_problem)
177+ res = test_int(A, B, A_name, B_name)
178+ return res && keep_problem
179+ end
180+
181+ function test_int(A, B, A_name, B_name, keep_problem, test_grid_ok)
182+ res = test_int(A, B, A_name, B_name)
183+ return res && keep_problem, res && test_grid_ok
184+ end
185+
186+ #
187+ function test_length(A, B, A_name, B_name)
188+ return test_int(length(A), length(B), A_name, B_name)
189+ end
190+
191+ function test_length(A, B, A_name, B_name, keep_problem, test_grid_ok)
192+ res = test_length(A, B, A_name, B_name)
193+ return res && keep_problem, res && test_grid_ok
194+ end
195+
196+ #
197+ function test_grid_max_error(A, B, A_name, B_name, keep_problem, test_grid_ok)
198+ ti_di_max, ti_bd_max, itera_max = 0 , NaN , 0
199+ for i in eachindex(B)
200+ ti_di = B[i] - A[i]
201+ ti_bd = max(0.5 * (abs(B[i])+ abs(A[i]))* ε_rel_grid, ε_abs_grid)
202+ res = @my_test_broken abs(ti_di) < ti_bd
203+ keep_problem = keep_problem && res
204+ test_grid_ok = test_grid_ok && res
205+ if abs(ti_di) ≥ abs(ti_di_max)
206+ ti_di_max, ti_bd_max, itera_max = ti_di, ti_bd, i
207+ end
208+ end
209+ r_err = abs(ti_di_max)/ (0.5 * (abs(B[itera_max])+ abs(A[itera_max])))
210+ res = r_err< 1.0
211+ DEBUG && @printf(" │ → %s vs %s: iter=%d r_err=%.3e a_err=%.3e bound=%.3e %s\n " , A_name, B_name, itera_max, r_err, abs(ti_di_max), ti_bd_max, test_tag(res))
212+ return keep_problem && res, test_grid_ok && res
213+ end
214+
120215 # we loop over the problems
121216 for f in LIST_OF_PROBLEMS
122217 N = metadata[f][:N] # get default N
@@ -148,6 +243,8 @@ function comparison(; max_iter, test_name)
148243 i_jp = iterations(f, nlp_jp)
149244 v_jp = variable(f, nlp_jp)
150245 p_jp = costate(f, nlp_jp). (t_jp)
246+ nb_var_jp = num_variables(nlp_jp)
247+ nb_con_jp = num_constraints(nlp_jp; count_variable_in_set_constraints= false )
151248
152249 # ######### OptimalControl ##########
153250 docp = OptimalControlProblems. eval(f)(OptimalControlBackend(); N= N)
@@ -161,6 +258,8 @@ function comparison(; max_iter, test_name)
161258 o_oc = objective(sol_oc)
162259 i_oc = iterations(sol_oc)
163260 v_oc = variable(sol_oc)
261+ nb_var_oc = get_nvar(nlp_oc)
262+ nb_con_oc = get_ncon(nlp_oc)
164263
165264 # ######### OptimalControl_s ##########
166265 docp = OptimalControlProblems. eval(Symbol(f, :_s))(OptimalControlBackend(), :madnlp, :exa; N= N)
@@ -174,65 +273,49 @@ function comparison(; max_iter, test_name)
174273 o_os = objective(sol_os)
175274 i_os = iterations(sol_os)
176275 v_os = variable(sol_os)
276+ nb_var_os = get_nvar(nlp_os)
277+ nb_con_os = get_ncon(nlp_os)
177278
178279 # ######### Iterations ##########
179- DEBUG && println(" ├─ Iterations → JP: " , i_jp, " , OC: " , i_oc, " , OS: " , i_os)
280+ DEBUG && @printf(" ├─ Iterations\n " )
281+ DEBUG && @printf(" │ → JP: %d OC: %d OS: %d\n " , i_jp, i_oc, i_os)
180282
181283 keep_problem = true
182284
183285 # ######### Variables / Constraints ##########
184286 @testset " nlp" verbose= VERBOSE begin
185- nb_var_oc, nb_var_jp = get_nvar(nlp_oc), num_variables(nlp_jp)
186- res = @my_test_broken nb_var_oc == nb_var_jp
187- keep_problem = keep_problem && res
188- DEBUG && @printf(" ├─ Variables → OC: %d JP: %d %s\n " , nb_var_oc, nb_var_jp,
189- res ? " \0 33[1;32mPASS\0 33[0m" : " \0 33[1;31mFAIL\0 33[0m" )
190-
191- nb_con_oc = get_ncon(nlp_oc)
192- nb_con_jp = num_constraints(nlp_jp; count_variable_in_set_constraints= false )
193- res = @my_test_broken nb_con_oc == nb_con_jp
194- keep_problem = keep_problem && res
195- DEBUG && @printf(" ├─ Constraints → OC: %d JP: %d %s\n " , nb_con_oc, nb_con_jp,
196- res ? " \0 33[1;32mPASS\0 33[0m" : " \0 33[1;31mFAIL\0 33[0m" )
287+ DEBUG && @printf(" ├─ Variables\n " )
288+ keep_problem = test_int(nb_var_jp, nb_var_oc, " JP" , " OC" , keep_problem)
289+ keep_problem = test_int(nb_var_jp, nb_var_os, " JP" , " OS" , keep_problem)
290+
291+ DEBUG && @printf(" ├─ Constraints\n " )
292+ keep_problem = test_int(nb_con_jp, nb_con_oc, " JP" , " OC" , keep_problem)
293+ keep_problem = test_int(nb_con_jp, nb_con_os, " JP" , " OS" , keep_problem)
197294 end
198295
199296 # ######### Time Grid ##########
200297 test_grid_ok = true
201298 @testset " grid" verbose= VERBOSE begin
299+
300+ # ----------------------------
202301 # final time
203- tf_di = abs(t_oc[end ] - t_jp[end ])
204- tf_bd = max(0.5 * (t_oc[end ]+ t_jp[end ])* ε_rel_grid, ε_abs_grid)
205- res = @my_test_broken tf_di < tf_bd
206- r_err = tf_di / (0.5 * (t_oc[end ]+ t_jp[end ]))
207- DEBUG && @printf(" ├─ Final time → OC: %.3e JP: %.3e\n " , t_oc[end ], t_jp[end ])
208- DEBUG && @printf(" │ r_err=%.3e a_err=%.3e bound=%.3e %s\n " ,
209- r_err, tf_di, tf_bd,
210- res ? " \0 33[1;32mPASS\0 33[0m" : " \0 33[1;31mFAIL\0 33[0m" )
302+ DEBUG && @printf(" ├─ Final time\n " )
303+ keep_problem, test_grid_ok = test_abs(t_jp[end ], t_oc[end ], " JP" , " OC" , keep_problem, test_grid_ok; ε_abs= ε_abs_grid, ε_rel= ε_rel_grid)
304+ keep_problem, test_grid_ok = test_abs(t_jp[end ], t_os[end ], " JP" , " OS" , keep_problem, test_grid_ok; ε_abs= ε_abs_grid, ε_rel= ε_rel_grid)
211305
306+ # ----------------------------
212307 # length of the grids
213- res = @my_test_broken length(t_oc) == length(t_jp)
214- keep_problem = keep_problem && res
215- test_grid_ok = test_grid_ok && res
216- DEBUG && @printf(" ├─ Grid length → OC: %d JP: %d %s\n " , length(t_oc), length(t_jp),
217- res ? " \0 33[1;32mPASS\0 33[0m" : " \0 33[1;31mFAIL\0 33[0m" )
308+ DEBUG && @printf(" ├─ Grid length\n " )
309+ keep_problem, test_grid_ok = test_length(t_jp, t_oc, " JP" , " OC" , keep_problem, test_grid_ok)
310+ keep_problem, test_grid_ok = test_length(t_jp, t_os, " JP" , " OS" , keep_problem, test_grid_ok)
218311
312+ # ----------------------------
219313 # max error
220314 if test_grid_ok
221- ti_di_max, ti_bd_max, itera_max = 0 , NaN , 0
222- for i in eachindex(t_oc)
223- ti_di = t_oc[i] - t_jp[i]
224- ti_bd = max(0.5 * (abs(t_oc[i])+ abs(t_jp[i]))* ε_rel_grid, ε_abs_grid)
225- res = @my_test_broken abs(ti_di) < ti_bd
226- keep_problem = keep_problem && res
227- test_grid_ok = test_grid_ok && res
228- if abs(ti_di) ≥ abs(ti_di_max)
229- ti_di_max, ti_bd_max, itera_max = ti_di, ti_bd, i
230- end
231- end
232- r_err = abs(ti_di_max)/ (0.5 * (abs(t_oc[itera_max])+ abs(t_jp[itera_max])))
233- DEBUG && @printf(" ├─ Grid max error → iter=%d r_err=%.3e a_err=%.3e bound=%.3e %s\n " ,
234- itera_max, r_err, abs(ti_di_max), ti_bd_max,
235- r_err< 1.0 ? " \0 33[1;32mPASS\0 33[0m" : " \0 33[1;31mFAIL\0 33[0m" )
315+ DEBUG && @printf(" ├─ Grid max error\n " )
316+ keep_problem, test_grid_ok = test_grid_max_error(t_jp, t_oc, " JP" , " OC" , keep_problem, test_grid_ok)
317+ keep_problem, test_grid_ok = test_grid_max_error(t_jp, t_os, " JP" , " OS" , keep_problem, test_grid_ok)
318+
236319 end
237320 end
238321
@@ -241,16 +324,10 @@ function comparison(; max_iter, test_name)
241324 @testset " state" verbose= VERBOSE begin
242325 DEBUG && println(" ├─ States" )
243326 for i in eachindex(x_vars)
327+ DEBUG && @printf(" │ %-6s\n " , x_vars[i])
244328 @testset " $(x_vars[i]) " verbose= VERBOSE begin
245- xi_oc, xi_jp = [x_oc[k][i] for k in eachindex(t_oc)], [x_jp[k][i] for k in eachindex(t_jp)]
246- L2_di = L2_norm(t_oc, xi_oc - xi_jp)
247- L2_bd = max(0.5 * (L2_norm(t_oc, xi_oc)+ L2_norm(t_oc, xi_jp))* ε_rel_state, ε_abs_state)
248- res = @my_test_broken L2_di < L2_bd
249- keep_problem = keep_problem && res
250- r_err = L2_di / (0.5 * (L2_norm(t_oc, xi_oc)+ L2_norm(t_oc, xi_jp)))
251- DEBUG && @printf(" │ %-6s r_err=%.3e a_err=%.3e bound=%.3e %s\n " ,
252- x_vars[i], r_err, L2_di, L2_bd,
253- res ? " \0 33[1;32mPASS\0 33[0m" : " \0 33[1;31mFAIL\0 33[0m" )
329+ keep_problem = test_L2_i(i, t_jp, x_jp, x_oc, " JP" , " OC" , keep_problem; ε_abs= ε_abs_state, ε_rel= ε_rel_state)
330+ keep_problem = test_L2_i(i, t_jp, x_jp, x_os, " JP" , " OS" , keep_problem; ε_abs= ε_abs_state, ε_rel= ε_rel_state)
254331 end
255332 end
256333 end
@@ -261,16 +338,10 @@ function comparison(; max_iter, test_name)
261338 @testset " control" verbose= VERBOSE begin
262339 DEBUG && println(" ├─ Controls" )
263340 for i in eachindex(u_vars)
341+ DEBUG && @printf(" │ %-6s\n " , u_vars[i])
264342 @testset " $(u_vars[i]) " verbose= VERBOSE begin
265- ui_oc, ui_jp = [u_oc[k][i] for k in eachindex(t_oc)], [u_jp[k][i] for k in eachindex(t_jp)]
266- L2_di = L2_norm(t_oc, ui_oc - ui_jp)
267- L2_bd = max(0.5 * (L2_norm(t_oc, ui_oc)+ L2_norm(t_oc, ui_jp))* ε_rel_control, ε_abs_control)
268- res = @my_test_broken L2_di < L2_bd
269- keep_problem = keep_problem && res
270- r_err = L2_di / (0.5 * (L2_norm(t_oc, ui_oc)+ L2_norm(t_oc, ui_jp)))
271- DEBUG && @printf(" │ %-6s r_err=%.3e a_err=%.3e bound=%.3e %s\n " ,
272- u_vars[i], r_err, L2_di, L2_bd,
273- res ? " \0 33[1;32mPASS\0 33[0m" : " \0 33[1;31mFAIL\0 33[0m" )
343+ keep_problem = test_L2_i(i, t_jp, u_jp, u_oc, " JP" , " OC" , keep_problem; ε_abs= ε_abs_control, ε_rel= ε_rel_control)
344+ keep_problem = test_L2_i(i, t_jp, u_jp, u_os, " JP" , " OS" , keep_problem; ε_abs= ε_abs_control, ε_rel= ε_rel_control)
274345 end
275346 end
276347 end
@@ -281,32 +352,20 @@ function comparison(; max_iter, test_name)
281352 @testset " variable" verbose= VERBOSE begin
282353 DEBUG && println(" ├─ Variables" )
283354 for i in eachindex(v_vars)
355+ DEBUG && @printf(" │ %-6s\n " , v_vars[i])
284356 @testset " $(v_vars[i]) " verbose= VERBOSE begin
285- vi_oc, vi_jp = v_oc[i], v_jp[i]
286- vi_di = abs(vi_oc- vi_jp)
287- vi_bd = max(0.5 * (abs(vi_oc)+ abs(vi_jp))* ε_rel_control, ε_abs_control)
288- res = @my_test_broken vi_di < vi_bd
289- keep_problem = keep_problem && res
290- r_err = vi_di / (0.5 * (abs(vi_oc)+ abs(vi_jp)))
291- DEBUG && @printf(" │ %-6s r_err=%.3e a_err=%.3e bound=%.3e %s\n " ,
292- v_vars[i], r_err, vi_di, vi_bd,
293- res ? " \0 33[1;32mPASS\0 33[0m" : " \0 33[1;31mFAIL\0 33[0m" )
357+ keep_problem = test_abs_i(i, v_jp, v_oc, " JP" , " OC" , keep_problem; ε_abs= ε_abs_variable, ε_rel= ε_rel_variable)
358+ keep_problem = test_abs_i(i, v_jp, v_os, " JP" , " OS" , keep_problem; ε_abs= ε_abs_variable, ε_rel= ε_rel_variable)
294359 end
295360 end
296361 end
297362 end
298363
299364 # ######### Objective ##########
365+ DEBUG && println(" ├─ Objective" )
300366 @testset " objective" verbose= VERBOSE begin
301- o_di = abs(o_oc- o_jp)
302- o_bd = max(0.5 * (abs(o_oc)+ abs(o_jp))* ε_rel_objective, ε_abs_objective)
303- res = @my_test_broken o_di < o_bd
304- keep_problem = keep_problem && res
305- r_err = o_di / (0.5 * (abs(o_oc)+ abs(o_jp)))
306- DEBUG && println(" ├─ Objective" )
307- DEBUG && @printf(" │ r_err=%.3e a_err=%.3e bound=%.3e %s\n " ,
308- r_err, o_di, o_bd,
309- res ? " \0 33[1;32mPASS\0 33[0m" : " \0 33[1;31mFAIL\0 33[0m" )
367+ keep_problem = test_abs(o_jp, o_oc, " JP" , " OC" , keep_problem; ε_abs= ε_abs_objective, ε_rel= ε_rel_objective)
368+ keep_problem = test_abs(o_jp, o_os, " JP" , " OS" , keep_problem; ε_abs= ε_abs_objective, ε_rel= ε_rel_objective)
310369 end
311370
312371 DEBUG && println(" └─" )
@@ -362,9 +421,7 @@ function comparison(; max_iter, test_name)
362421 control_style= (color= color, legend= :none),
363422 path_style= (color= color, legend= :none),
364423 dual_style= (color= color, legend= :none),
365- size= (900 , 220 * (n+ m)),
366424 label= labelOC,
367- leftmargin= 20 mm,
368425 )
369426 for i in 2 : n
370427 plot!(plt[i]; legend= :none)
0 commit comments