Skip to content

Commit

Permalink
CTBase 0.13 (#216)
Browse files Browse the repository at this point in the history
* basic test suite ok

* manual check for constraints / multipliers seems fine

* fix docs

* format code (#218)

* Update README.md

* format code

* fix merge

---------

Co-authored-by: Olivier Cots <[email protected]>

* removed faulty comments in JSON part

* version

---------

Co-authored-by: Olivier Cots <[email protected]>
Co-authored-by: Olivier Cots <[email protected]>
  • Loading branch information
3 people authored Aug 26, 2024
1 parent 8b1b926 commit 0de20e7
Show file tree
Hide file tree
Showing 52 changed files with 1,697 additions and 1,069 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ jobs:
- '1.10'
os:
- ubuntu-latest
- macOS-latest
# - windows-latest #fails with ssh-agent error on windows slave
arch:
- x64
steps:
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/Formatter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Formatter

# Modified from https://github.com/julia-actions/julia-format
on:
schedule:
- cron: '0 0 * * *'

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: julia-actions/cache@v2
- name: Install JuliaFormatter and format
run: |
julia -e 'import Pkg; Pkg.add("JuliaFormatter")'
julia -e 'using JuliaFormatter; format(".")'
# https://github.com/marketplace/actions/create-pull-request
# https://github.com/peter-evans/create-pull-request#reference-example
- name: Create Pull Request
id: cpr
uses: peter-evans/create-pull-request@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: ":robot: Format .jl files"
title: '[AUTO] JuliaFormatter.jl run'
branch: auto-juliaformatter-pr
delete-branch: true
labels: formatting, automated pr, no changelog
- name: Check outputs
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "CTDirect"
uuid = "790bbbee-bee9-49ee-8912-a9de031322d5"
authors = ["Olivier Cots <[email protected]>"]
version = "0.11.2"
version = "0.12.0"

[deps]
ADNLPModels = "54578032-b7ea-4c30-94aa-7cbd1cce6c9a"
Expand Down
83 changes: 61 additions & 22 deletions benchmark/benchmark.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,21 @@ using MadNLPMumps

#######################################################
# load examples library
problem_path = pwd()*"/test/problems"
for problem_file in filter(contains(r".jl$"), readdir(problem_path; join=true))
problem_path = pwd() * "/test/problems"
for problem_file in filter(contains(r".jl$"), readdir(problem_path; join = true))
include(problem_file)
end


function bench(;nlp_solver = :ipopt, linear_solver = nothing,
tol=1e-8, grid_size=1000, precompile = true, display=false, verbose=true)
function bench(;
nlp_solver = :ipopt,
linear_solver = nothing,
tol = 1e-8,
grid_size = 1000,
precompile = true,
display = false,
verbose = true,
)

#######################################################
# set (non) linear solvers and backends
Expand All @@ -30,45 +37,77 @@ function bench(;nlp_solver = :ipopt, linear_solver = nothing,
linear_solver = "UmfpackSolver"
end

verbose && @printf("Profile: NLP Solver %s with linear solver %s\n", nlp_solver, linear_solver)
verbose &&
@printf("Profile: NLP Solver %s with linear solver %s\n", nlp_solver, linear_solver)

# blas backend (cf using MKL above, should be option...)
verbose && @printf("Blas config: %s\n", LinearAlgebra.BLAS.lbt_get_config())

# settings
verbose && @printf("Settings: tol=%g grid_size=%d precompile=%s\n\n", tol, grid_size, precompile)
verbose && @printf(
"Settings: tol=%g grid_size=%d precompile=%s\n\n",
tol,
grid_size,
precompile
)

# load problems for benchmark
names_list = ["beam", "bioreactor_1day", "fuller", "goddard", "jackson", "vanderpol"]
problem_list = []
for problem_name in names_list
ocp_data = getfield(Main, Symbol(problem_name))()
push!(problem_list,ocp_data)
push!(problem_list, ocp_data)
end

#######################################################
# precompile if required
if precompile
t_precomp = 0.
t_precomp = 0.0
verbose && print("Precompilation: ")
for problem in problem_list
verbose && @printf("%s ",problem[:name])
t = @elapsed direct_solve(problem[:ocp], nlp_solver, linear_solver=linear_solver, max_iter=0, display=display)
verbose && @printf("%s ", problem[:name])
t = @elapsed direct_solve(
problem[:ocp],
nlp_solver,
linear_solver = linear_solver,
max_iter = 0,
display = display,
)
t_precomp += t
end
verbose && @printf("\nPrecompilation total time %6.2f\n\n",t_precomp)
verbose && @printf("\nPrecompilation total time %6.2f\n\n", t_precomp)
end

#######################################################
# solve examples with timer and objective check
t_list = []
for problem in problem_list
t = @elapsed sol = direct_solve(problem[:ocp], nlp_solver, init=problem[:init], display=display, linear_solver=linear_solver, grid_size=grid_size, tol=tol)
if !isnothing(problem[:obj]) && !isapprox(sol.objective, problem[:obj], rtol=5e-2)
error("Objective mismatch for ", problem[:name], ": ", sol.objective, " instead of ", problem[:obj])
t = @elapsed sol = direct_solve(
problem[:ocp],
nlp_solver,
init = problem[:init],
display = display,
linear_solver = linear_solver,
grid_size = grid_size,
tol = tol,
)
if !isnothing(problem[:obj]) && !isapprox(sol.objective, problem[:obj], rtol = 5e-2)
error(
"Objective mismatch for ",
problem[:name],
": ",
sol.objective,
" instead of ",
problem[:obj],
)
else
verbose && @printf("%-30s completed in %6.2f s after %4d iterations\n",problem[:name],t,sol.iterations)
append!(t_list,t)
verbose && @printf(
"%-30s completed in %6.2f s after %4d iterations\n",
problem[:name],
t,
sol.iterations
)
append!(t_list, t)
end
end

Expand All @@ -83,16 +122,16 @@ function bench(;nlp_solver = :ipopt, linear_solver = nothing,
end

# +++ put repeat directly in bench()
function bench_average(; repeat=2, verbose=false, kwargs...)
function bench_average(; repeat = 2, verbose = false, kwargs...)

# execute series of benchmark runs
t_list = []
for i in 1:repeat
t = bench(;verbose=verbose, kwargs...)
for i = 1:repeat
t = bench(; verbose = verbose, kwargs...)
append!(t_list, t)
@printf("Run %d / %d: time (s) = %6.2f\n", i, repeat, t)
end

# print / return average total time
avg_time = sum(t_list) / length(t_list)
@printf("Average time (s): %6.2f\n", avg_time)
Expand All @@ -101,11 +140,11 @@ function bench_average(; repeat=2, verbose=false, kwargs...)
end


function bench_series(;grid_size_list=[250, 500, 1000, 2500, 5000, 10000], kwargs...)
function bench_series(; grid_size_list = [250, 500, 1000, 2500, 5000, 10000], kwargs...)
println(grid_size_list)
t_list = []
for grid_size in grid_size_list
t = bench_average(;grid_size=grid_size, kwargs...)
t = bench_average(; grid_size = grid_size, kwargs...)
append!(t_list, t)
@printf("Grid size %d: time (s) = %6.2f\n\n", grid_size, t)
end
Expand Down
72 changes: 42 additions & 30 deletions benchmark/prof.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,48 +21,60 @@ prob = include("../problems/fuller.jl")
#prob = include("../problems/goddard.jl")
ocp = prob[:ocp]
grid_size = 100
docp, nlp = direct_transcription(ocp, grid_size=grid_size)
docp, nlp = direct_transcription(ocp, grid_size = grid_size)
println("Load problem ", prob[:name])

# full solve
if test_time
if precompile
println("Precompilation")
solve(ocp, grid_size=grid_size, display=false, max_iter=2)
end
println("Timed solve")
@timev sol = solve(ocp, grid_size=grid_size, print_level=0)
if precompile
println("Precompilation")
solve(ocp, grid_size = grid_size, display = false, max_iter = 2)
end
println("Timed solve")
@timev sol = solve(ocp, grid_size = grid_size, print_level = 0)
end

if precompile
println("Precompilation")
if test == :objective
CTDirect.DOCP_objective(CTDirect.DOCP_initial_guess(docp), docp)
else
CTDirect.DOCP_constraints!(zeros(docp.dim_NLP_constraints), CTDirect.DOCP_initial_guess(docp), docp)
end
println("Precompilation")
if test == :objective
CTDirect.DOCP_objective(CTDirect.DOCP_initial_guess(docp), docp)
else
CTDirect.DOCP_constraints!(
zeros(docp.dim_NLP_constraints),
CTDirect.DOCP_initial_guess(docp),
docp,
)
end
end

if test_code_warntype
if test == :objective
# NB. Pb with the mayer part: obj is type unstable (Any) because ocp.mayer is Union(Mayer,nothing), even for mayer problems (also, we should not even enter this code part for lagrange problems since has_mayer us defined as const in DOCP oO ...).
@code_warntype CTDirect.DOCP_objective(CTDirect.DOCP_initial_guess(docp), docp)
else
# OK !
@code_warntype CTDirect.DOCP_constraints!(zeros(docp.dim_NLP_constraints), CTDirect.DOCP_initial_guess(docp), docp)
end
if test == :objective
# NB. Pb with the mayer part: obj is type unstable (Any) because ocp.mayer is Union(Mayer,nothing), even for mayer problems (also, we should not even enter this code part for lagrange problems since has_mayer us defined as const in DOCP oO ...).
@code_warntype CTDirect.DOCP_objective(CTDirect.DOCP_initial_guess(docp), docp)
else
# OK !
@code_warntype CTDirect.DOCP_constraints!(
zeros(docp.dim_NLP_constraints),
CTDirect.DOCP_initial_guess(docp),
docp,
)
end
end

if test_jet
if test == :objective
# 4 possible errors
# due to the ocp.mayer type problem cf above
@report_opt CTDirect.DOCP_objective(CTDirect.DOCP_initial_guess(docp), docp)
else
# 50 possible errors: some getindex (Integer vs Int...)
# all variables x,u,v
@report_opt CTDirect.DOCP_constraints!(zeros(docp.dim_NLP_constraints), CTDirect.DOCP_initial_guess(docp), docp)
end
if test == :objective
# 4 possible errors
# due to the ocp.mayer type problem cf above
@report_opt CTDirect.DOCP_objective(CTDirect.DOCP_initial_guess(docp), docp)
else
# 50 possible errors: some getindex (Integer vs Int...)
# all variables x,u,v
@report_opt CTDirect.DOCP_constraints!(
zeros(docp.dim_NLP_constraints),
CTDirect.DOCP_initial_guess(docp),
docp,
)
end
end

#=
Expand All @@ -73,4 +85,4 @@ if test_ipopt
println("\n Discrete solution")
println(dsol)
end
=#
=#
13 changes: 3 additions & 10 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,12 @@ makedocs(
format = Documenter.HTML(
prettyurls = false,
size_threshold_ignore = ["api-ctbase.md"],
assets=[
assets = [
asset("https://control-toolbox.org/assets/css/documentation.css"),
asset("https://control-toolbox.org/assets/js/documentation.js"),
],
),
pages = [
"Introduction" => "index.md",
"API" => "api.md",
"Developers" => "dev-api.md",
]
pages = ["Introduction" => "index.md", "API" => "api.md", "Developers" => "dev-api.md"],
)

deploydocs(
repo = "github.com/control-toolbox/CTDirect.jl.git",
devbranch = "main"
)
deploydocs(repo = "github.com/control-toolbox/CTDirect.jl.git", devbranch = "main")
20 changes: 11 additions & 9 deletions ext/CTDirectExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ $(TYPEDSIGNATURES)
Save OCP solution in JLD2 format
"""
function JLD2.save(sol::OptimalControlSolution; filename_prefix="solution")
function JLD2.save(sol::OptimalControlSolution; filename_prefix = "solution")
save_object(filename_prefix * ".jld2", sol)
return nothing
end

"""
$(TYPEDSIGNATURES)
Load OCP solution in JLD2 format
"""
function JLD2.load(filename_prefix="solution")
function JLD2.load(filename_prefix = "solution")
return load_object(filename_prefix * ".jld2")
end

Expand All @@ -33,9 +33,10 @@ $(TYPEDSIGNATURES)
Export OCP solution in JSON format
"""
function CTDirect.export_ocp_solution(sol::OptimalControlSolution; filename_prefix="solution")
open(filename_prefix * ".json", "w") do io
JSON3.pretty(io, CTDirect.OCPDiscreteSolution(sol))
end
# +++ redo this, start with basics, fuse into save
#open(filename_prefix * ".json", "w") do io
# JSON3.pretty(io, CTDirect.OCPDiscreteSolution(sol))
#end
return nothing
end

Expand All @@ -45,9 +46,10 @@ $(TYPEDSIGNATURES)
Read OCP solution in JSON format
"""
function CTDirect.import_ocp_solution(filename_prefix="solution")
json_string = read(filename_prefix * ".json", String)
return JSON3.read(json_string)
# +++ add constructor from json blob, fuse into load
#json_string = read(filename_prefix * ".json", String)
#return OptimalControlSolution(JSON3.read(json_string))
end


end
end
Loading

0 comments on commit 0de20e7

Please sign in to comment.