Skip to content

Commit 39dfb6d

Browse files
committed
Finalize display helper integration and fix tests
1 parent c9ff1f1 commit 39dfb6d

File tree

7 files changed

+289
-91
lines changed

7 files changed

+289
-91
lines changed

.extras/test_display.jl

Lines changed: 48 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -193,76 +193,66 @@ function improved_display_ocp_method(
193193

194194
println(io)
195195

196-
# Package information using id()
196+
# Combined configuration + options (Proposition 3)
197197
println(io, "")
198198
println(io, " 📦 Configuration:")
199199

200+
discretizer_pkg = OptimalControl.id(typeof(discretizer))
200201
model_pkg = OptimalControl.id(typeof(modeler))
201202
solver_pkg = OptimalControl.id(typeof(solver))
202-
203-
print(io, " ├─ Modeler: ")
204-
printstyled(io, model_pkg; color=:cyan, bold=true)
205-
println(io)
206-
print(io, " └─ Solver: ")
207-
printstyled(io, solver_pkg; color=:cyan, bold=true)
208-
println(io)
209203

210-
# Options section
211-
if show_options
212-
disc_opts = OptimalControl.options(discretizer)
213-
mod_opts = OptimalControl.options(modeler)
214-
sol_opts = OptimalControl.options(solver)
215-
216-
println(io, "")
217-
println(io, " ⚙️ Options:")
218-
219-
has_disc = !isempty(propertynames(disc_opts.options))
220-
has_mod = !isempty(propertynames(mod_opts.options))
221-
has_sol = !isempty(propertynames(sol_opts.options))
222-
223-
if has_disc
224-
print(io, " ├─ ")
225-
printstyled(io, "Discretizer"; color=:cyan, bold=true)
226-
print(io, ": ")
227-
228-
# Extract individual options from StrategyOptions
229-
items = collect(pairs(disc_opts.options))
230-
for (i, (key, opt)) in enumerate(items)
231-
sep = i == length(items) ? "" : ", "
232-
print(io, string(key), " = ", opt.value, " (", opt.source, ")", sep)
204+
disc_opts = show_options ? OptimalControl.options(discretizer) : nothing
205+
mod_opts = show_options ? OptimalControl.options(modeler) : nothing
206+
sol_opts = show_options ? OptimalControl.options(solver) : nothing
207+
208+
function print_component(line_prefix, label, pkg, opts)
209+
print(io, line_prefix)
210+
printstyled(io, label; bold=true)
211+
print(io, ": ")
212+
printstyled(io, pkg; color=:cyan, bold=true)
213+
if show_options && opts !== nothing
214+
user_items = Tuple{Symbol, Any}[]
215+
for (key, opt) in pairs(opts.options)
216+
if OptimalControl.is_user(opts, key)
217+
push!(user_items, (key, opt))
218+
end
233219
end
234-
println(io)
235-
end
236-
237-
if has_mod
238-
print(io, " ├─ ")
239-
printstyled(io, "Modeler"; color=:cyan, bold=true)
240-
print(io, ": ")
241-
242-
items = collect(pairs(mod_opts.options))
243-
for (i, (key, opt)) in enumerate(items)
244-
sep = i == length(items) ? "" : ", "
245-
print(io, string(key), " = ", opt.value, " (", opt.source, ")", sep)
220+
sort!(user_items, by = x -> string(x[1]))
221+
n = length(user_items)
222+
if n == 0
223+
print(io, " (no user options)")
224+
elseif n <= 2
225+
print(io, " (")
226+
for (i, (key, opt)) in enumerate(user_items)
227+
sep = i == n ? "" : ", "
228+
src = show_sources ? " [" * string(opt.source) * "]" : ""
229+
print(io, string(key), " = ", opt.value, src, sep)
230+
end
231+
print(io, ")")
232+
else
233+
# Multiline with truncation after 3 items
234+
print(io, "\n ")
235+
shown = first(user_items, 3)
236+
for (i, (key, opt)) in enumerate(shown)
237+
sep = i == length(shown) ? "" : ", "
238+
src = show_sources ? " [" * string(opt.source) * "]" : ""
239+
print(io, string(key), " = ", opt.value, src, sep)
240+
end
241+
remaining = n - length(shown)
242+
if remaining > 0
243+
print(io, ", … (+", remaining, ")")
244+
end
246245
end
247-
println(io)
248-
end
249-
250-
if has_sol
251-
print(io, " └─ ")
252-
printstyled(io, "Solver"; color=:cyan, bold=true)
253-
print(io, ": ")
254-
255-
items = collect(pairs(sol_opts.options))
256-
for (i, (key, opt)) in enumerate(items)
257-
sep = i == length(items) ? "" : ", "
258-
print(io, string(key), " = ", opt.value, " (", opt.source, ")", sep)
259-
end
260-
println(io)
261246
end
247+
println(io)
262248
end
263249

250+
print_component(" ├─ ", "Discretizer", discretizer_pkg, disc_opts)
251+
print_component(" ├─ ", "Modeler", model_pkg, mod_opts)
252+
print_component(" └─ ", "Solver", solver_pkg, sol_opts)
253+
264254
println(io)
265-
println(io, "🎯 Ready to solve!")
255+
#println(io, "🎯 Ready to solve!")
266256
return nothing
267257
end
268258

src/OptimalControl.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ include(joinpath(@__DIR__, "imports", "ctmodels.jl"))
1818
include(joinpath(@__DIR__, "imports", "ctparser.jl"))
1919
include(joinpath(@__DIR__, "imports", "ctsolvers.jl"))
2020
include(joinpath(@__DIR__, "imports", "examodels.jl"))
21+
include(joinpath(@__DIR__, "helpers", "print.jl"))
2122

2223
# solve
23-
include(joinpath(@__DIR__, "solve.jl"))
24+
include(joinpath(@__DIR__, "solve", "solve_canonical.jl"))
2425
# export available_methods
2526

2627
end

src/helpers/print.jl

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Display helpers for OptimalControl
2+
3+
import CTDirect
4+
import CTModels
5+
import CTSolvers
6+
7+
"""
8+
display_ocp_configuration(
9+
io::IO,
10+
discretizer::CTDirect.AbstractOptimalControlDiscretizer,
11+
modeler::CTSolvers.AbstractOptimizationModeler,
12+
solver::CTSolvers.AbstractOptimizationSolver;
13+
display::Bool=true,
14+
show_options::Bool=true,
15+
show_sources::Bool=false,
16+
)
17+
18+
Affiche la configuration de résolution (discretizer → modeler → solver) avec les options utilisateur en ligne.
19+
20+
Par défaut l’affichage est compact (`show_sources=false`) et n’affiche que les options marquées utilisateur.
21+
Si `show_options` est `false`, seules les IDs des composants sont affichées.
22+
"""
23+
function display_ocp_configuration(
24+
io::IO,
25+
discretizer::CTDirect.AbstractOptimalControlDiscretizer,
26+
modeler::CTSolvers.AbstractOptimizationModeler,
27+
solver::CTSolvers.AbstractOptimizationSolver;
28+
display::Bool=true,
29+
show_options::Bool=true,
30+
show_sources::Bool=false,
31+
)
32+
display || return nothing
33+
34+
version_str = string(Base.pkgversion(OptimalControl))
35+
36+
# Header with method
37+
print(io, "▫ OptimalControl v", version_str, " solving with: ")
38+
39+
discretizer_id = OptimalControl.id(typeof(discretizer))
40+
modeler_id = OptimalControl.id(typeof(modeler))
41+
solver_id = OptimalControl.id(typeof(solver))
42+
43+
printstyled(io, discretizer_id; color=:cyan, bold=true)
44+
print(io, "")
45+
printstyled(io, modeler_id; color=:cyan, bold=true)
46+
print(io, "")
47+
printstyled(io, solver_id; color=:cyan, bold=true)
48+
49+
# NOTE: if we want to display extra method hints later, re-enable cleaned_method logic.
50+
# cleaned_method = CTBase.remove(method, (discretizer_id, modeler_id, solver_id))
51+
# if !isempty(cleaned_method)
52+
# print(io, " (")
53+
# for (i, m) in enumerate(cleaned_method)
54+
# sep = i == length(cleaned_method) ? "" : ", "
55+
# printstyled(io, string(m) * sep; color=:cyan, bold=true)
56+
# end
57+
# print(io, ")")
58+
# end
59+
60+
println(io)
61+
62+
# Combined configuration + options (compact default)
63+
println(io, "")
64+
println(io, " 📦 Configuration:")
65+
66+
discretizer_pkg = OptimalControl.id(typeof(discretizer))
67+
model_pkg = OptimalControl.id(typeof(modeler))
68+
solver_pkg = OptimalControl.id(typeof(solver))
69+
70+
disc_opts = show_options ? OptimalControl.options(discretizer) : nothing
71+
mod_opts = show_options ? OptimalControl.options(modeler) : nothing
72+
sol_opts = show_options ? OptimalControl.options(solver) : nothing
73+
74+
function print_component(line_prefix, label, pkg, opts)
75+
print(io, line_prefix)
76+
printstyled(io, label; bold=true)
77+
print(io, ": ")
78+
printstyled(io, pkg; color=:cyan, bold=true)
79+
if show_options && opts !== nothing
80+
user_items = Tuple{Symbol, Any}[]
81+
for (key, opt) in pairs(opts.options)
82+
if OptimalControl.is_user(opts, key)
83+
push!(user_items, (key, opt))
84+
end
85+
end
86+
sort!(user_items, by = x -> string(x[1]))
87+
n = length(user_items)
88+
if n == 0
89+
print(io, " (no user options)")
90+
elseif n <= 2
91+
print(io, " (")
92+
for (i, (key, opt)) in enumerate(user_items)
93+
sep = i == n ? "" : ", "
94+
src = show_sources ? " [" * string(opt.source) * "]" : ""
95+
print(io, string(key), " = ", opt.value, src, sep)
96+
end
97+
print(io, ")")
98+
else
99+
# Multiline with truncation after 3 items
100+
print(io, "\n ")
101+
shown = first(user_items, 3)
102+
for (i, (key, opt)) in enumerate(shown)
103+
sep = i == length(shown) ? "" : ", "
104+
src = show_sources ? " [" * string(opt.source) * "]" : ""
105+
print(io, string(key), " = ", opt.value, src, sep)
106+
end
107+
remaining = n - length(shown)
108+
if remaining > 0
109+
print(io, ", … (+", remaining, ")")
110+
end
111+
end
112+
end
113+
println(io)
114+
end
115+
116+
print_component(" ├─ ", "Discretizer", discretizer_pkg, disc_opts)
117+
print_component(" ├─ ", "Modeler", model_pkg, mod_opts)
118+
print_component(" └─ ", "Solver", solver_pkg, sol_opts)
119+
120+
println(io)
121+
return nothing
122+
end
123+
124+
# Convenience without io
125+
function display_ocp_configuration(
126+
discretizer::CTDirect.AbstractOptimalControlDiscretizer,
127+
modeler::CTSolvers.AbstractOptimizationModeler,
128+
solver::CTSolvers.AbstractOptimizationSolver;
129+
display::Bool=true,
130+
show_options::Bool=true,
131+
show_sources::Bool=false,
132+
)
133+
return display_ocp_configuration(
134+
stdout, discretizer, modeler, solver;
135+
display=display, show_options=show_options, show_sources=show_sources,
136+
)
137+
end

src/solve.jl renamed to src/solve/solve_canonical.jl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@ import CTSolvers
1212

1313
# Default options
1414
__display() = true
15-
__initial_guess() = nothing
15+
#__initial_guess() = nothing
1616

1717
# ------------------------------------------------------------------------
1818
# ------------------------------------------------------------------------
1919
# Canonical solve function
2020
function CommonSolve.solve(
2121
ocp::CTModels.AbstractOptimalControlProblem,
22+
initial_guess,
2223
discretizer::CTDirect.AbstractOptimalControlDiscretizer,
2324
modeler::CTSolvers.AbstractOptimizationModeler,
2425
solver::CTSolvers.AbstractOptimizationSolver;
2526
display::Bool=__display(),
26-
initial_guess=__initial_guess(),
2727
)::CTModels.AbstractOptimalControlSolution
2828

2929
# Build and validate initial guess against the optimal control problem before discretization.
@@ -32,6 +32,14 @@ function CommonSolve.solve(
3232
# Discretize the optimal control problem.
3333
discrete_problem = CTDirect.discretize(ocp, discretizer)
3434

35+
# Display configuration (compact, user options only)
36+
if display
37+
OptimalControl.display_ocp_configuration(
38+
stdout, discretizer, modeler, solver;
39+
display=true, show_options=true, show_sources=false,
40+
)
41+
end
42+
3543
# Solve the discretized optimal control problem.
3644
return CommonSolve.solve(
3745
discrete_problem, normalized_init, modeler, solver; display=display

test/suite/reexport/test_ctsolvers.jl

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ function test_ctsolvers()
2424
:nlp_model,
2525
:ocp_solution,
2626
)
27-
@test isdefined(OptimalControl, f)
28-
@test isdefined(CurrentModule, f)
29-
@test getfield(OptimalControl, f) isa Function
27+
@testset "$f" begin
28+
@test isdefined(OptimalControl, f)
29+
@test isdefined(CurrentModule, f)
30+
@test getfield(OptimalControl, f) isa Function
31+
end
3032
end
3133
end
3234
@testset "Modeler Types" begin
@@ -72,9 +74,11 @@ function test_ctsolvers()
7274
:id,
7375
:metadata,
7476
)
75-
@test isdefined(OptimalControl, f)
76-
@test isdefined(CurrentModule, f)
77-
@test getfield(OptimalControl, f) isa Function
77+
@testset "$f" begin
78+
@test isdefined(OptimalControl, f)
79+
@test isdefined(CurrentModule, f)
80+
@test getfield(OptimalControl, f) isa Function
81+
end
7882
end
7983
end
8084
@testset "Strategy Introspection Functions" begin
@@ -91,21 +95,22 @@ function test_ctsolvers()
9195
:is_default,
9296
:is_computed,
9397
)
94-
@test isdefined(OptimalControl, f)
95-
@test isdefined(CurrentModule, f)
96-
@test getfield(OptimalControl, f) isa Function
98+
@testset "$f" begin
99+
@test isdefined(OptimalControl, f)
100+
@test isdefined(CurrentModule, f)
101+
@test getfield(OptimalControl, f) isa Function
102+
end
97103
end
98104
end
99105
@testset "Strategy Utility Functions" begin
100106
for f in (
101-
:filter_options,
102-
:suggest_options,
103-
:options_dict,
104107
:route_to,
105108
)
106-
@test isdefined(OptimalControl, f)
107-
@test isdefined(CurrentModule, f)
108-
@test getfield(OptimalControl, f) isa Function
109+
@testset "$f" begin
110+
@test isdefined(OptimalControl, f)
111+
@test isdefined(CurrentModule, f)
112+
@test getfield(OptimalControl, f) isa Function
113+
end
109114
end
110115
end
111116
end

0 commit comments

Comments
 (0)