-
-
Notifications
You must be signed in to change notification settings - Fork 101
Improve CUTEst benchmarks #1323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 54 commits
Commits
Show all changes
57 commits
Select commit
Hold shift + click to select a range
785a9d7
Setting up CUTEst benchmarks dir structure
alonsoC1s 313ac1c
Unconstrained problem benchmarks
alonsoC1s 2c7dc3a
Fixing problem initialization
alonsoC1s c4a198a
Starting the analysis
alonsoC1s 810e402
Added preliminary benchmarks for eq/ineq problems with free vars
alonsoC1s c2babbd
ci: install `gfortran` when the CUTEst optimization benchmarks are run
thazhemadam 64b0e85
fixed variable use in benchmarks
alonsoC1s bd4c96c
Removing Manifest to fix version errors
alonsoC1s b0f1c35
Using NLPModels from General Registry
alonsoC1s b3f7ae1
revert: "ci: install `gfortran` when the CUTEst optimization benchmar…
82df232
ci: use rootfs image with openmodelica and gfortran pre-installed
3b2acff
Add SciMLBenchmarks to the manifest and the footer
ChrisRackauckas d344044
split into benchmark groups
ChrisRackauckas ca5ef66
Update CUTEst_bounded.jmd
ChrisRackauckas b91dc7b
Use OptimizationNLPModels from branch
Vaibhavdixit02 2d27f55
stats pkg
Vaibhavdixit02 0333356
use julia 1.10 resolved manifest
Vaibhavdixit02 b417914
omjulia
arnavk23 292f48f
Update Manifest.toml
arnavk23 7631605
manifest
arnavk23 1e9a8a6
formatted using JuliaFormatter
arnavk23 0554ab0
removing deprecated CUTEst.select
arnavk23 465f149
Update Project.toml
arnavk23 a2cd0e4
safe_solvers
arnavk23 c06bb8e
Update update.jl
arnavk23 b8bc556
Update make.jl
arnavk23 39f6a7d
Update pages.jl
arnavk23 1aeac3d
Update SciMLBenchmarks.jl
arnavk23 aa51c1b
Improve CUTEst benchmarks with chunked processing and robust error ha…
arnavk23 026272c
Make CUTEst benchmarks extremely conservative to prevent OOM
arnavk23 ffb87df
Fix CUTEst benchmark filtering and timeout issues
arnavk23 40bbd90
changes
arnavk23 0c9522d
try
arnavk23 ead71b0
trying again to commit changes to the CUTEst benchmarks in SciMLBench…
arnavk23 ba7d2bd
new changes to CUTEst benchmarks
arnavk23 93c308b
building
arnavk23 61fa6f6
bounded build pass
arnavk23 73ec8a7
Delete benchmarks/OptimizationCUTEst/benchmarks/OptimizationCUTEst/Pr…
arnavk23 c3f4c64
Delete benchmarks/OptimizationCUTEst/benchmarks/OptimizationCUTEst/Ma…
arnavk23 c7b0c87
Update benchmarks/OptimizationCUTEst/CUTEst_bounded.jmd
ChrisRackauckas a8b9699
Update benchmarks/OptimizationCUTEst/CUTEst_quadratic.jmd
ChrisRackauckas a71fbfb
Update benchmarks/OptimizationCUTEst/CUTEst_unbounded.jmd
ChrisRackauckas cf17b46
Update benchmarks/OptimizationCUTEst/CUTEst_unconstrained.jmd
ChrisRackauckas ac1740a
Update benchmarks/OptimizationCUTEst/CUTEst_safe_solvers.jmd
ChrisRackauckas 6c9d3ea
Update benchmarks/OptimizationCUTEst/Project.toml
ChrisRackauckas bec7048
Update benchmarks/OptimizationCUTEst/Project.toml
ChrisRackauckas 17d1b73
Update Project.toml
ChrisRackauckas a1cc6ce
Update Project.toml
ChrisRackauckas 245e7bb
Update Project.toml
ChrisRackauckas 59a11e5
Update Project.toml
ChrisRackauckas 4cfbd62
Regenerate manifest
ChrisRackauckas 84b0d93
Update .buildkite/run_benchmark.yml
ChrisRackauckas ce841b6
Update .github/workflows/update.jl
ChrisRackauckas a60a784
Update .github/workflows/update.jl
ChrisRackauckas 14e37f2
ci: use new test rootfs image
thazhemadam 4b2d4d9
ci: sign treehashes
thazhemadam a8cbec1
Apply suggestions from code review
ChrisRackauckas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,249 @@ | ||
| --- | ||
| title: CUTEst Bounded Constrained Nonlinear Optimization Benchmarks | ||
| author: Alonso M. Cisneros | ||
| --- | ||
|
|
||
| # Introduction | ||
|
|
||
| CUTEst, the Constraind and Unconstrained Testing Environment is, as the name suggests is a | ||
| collection of around 1500 problems for general nonlinear optimization used to test | ||
| optimization routines. The wrapper | ||
| [CUTEst.jl](https://github.com/JuliaSmoothOptimizers/CUTEst.jl) provides convenient access | ||
| to the problem collection, which we can leverage to test the optimizers made available by | ||
| Optimization.jl. | ||
|
|
||
|
|
||
| ```julia | ||
| using Optimization | ||
| using OptimizationNLPModels | ||
| using CUTEst | ||
| using OptimizationOptimJL | ||
| using OptimizationOptimisers | ||
| using Ipopt | ||
| using OptimizationMOI | ||
| using OptimizationMOI: MOI as MOI | ||
| using DataFrames | ||
| using Plots | ||
| using StatsPlots | ||
| using StatsBase: countmap | ||
|
|
||
| optimizers = [ | ||
| ("Ipopt", MOI.OptimizerWithAttributes(Ipopt.Optimizer, | ||
| "max_iter" => 5000, | ||
| "tol" => 1e-6, | ||
| "print_level" => 5)), | ||
| ] | ||
|
|
||
| function get_stats(sol, optimizer_name) | ||
| # Robustly get solve_time, even if stats or time is missing | ||
| solve_time = try | ||
| hasfield(typeof(sol), :stats) && hasfield(typeof(sol.stats), :time) ? getfield(sol.stats, :time) : NaN | ||
| catch | ||
| NaN | ||
| end | ||
| return (length(sol.u), solve_time, optimizer_name, Symbol(sol.retcode)) | ||
| end | ||
|
|
||
| function run_benchmarks(problems, optimizers; chunk_size=1) | ||
| problem = String[] | ||
| n_vars = Int64[] | ||
| secs = Float64[] | ||
| solver = String[] | ||
| retcode = Symbol[] | ||
| optz = length(optimizers) | ||
| n = length(problems) | ||
| @info "Processing $(n) problems with $(optz) optimizers in chunks of $(chunk_size)" | ||
| broadcast(c -> sizehint!(c, optz * n), [problem, n_vars, secs, solver, retcode]) | ||
| for chunk_start in 1:chunk_size:n | ||
| chunk_end = min(chunk_start + chunk_size - 1, n) | ||
| chunk_problems = problems[chunk_start:chunk_end] | ||
| @info "Processing chunk $(div(chunk_start-1, chunk_size)+1)/$(div(n-1, chunk_size)+1): problems $(chunk_start)-$(chunk_end)" | ||
| for (idx, prob_name) in enumerate(chunk_problems) | ||
| current_problem = chunk_start + idx - 1 | ||
| @info "Problem $(current_problem)/$(n): $(prob_name)" | ||
| nlp_prob = nothing | ||
| try | ||
| nlp_prob = CUTEstModel(prob_name) | ||
| if nlp_prob.meta.nvar > 10000 | ||
| @info " Skipping $(prob_name) (too large: $(nlp_prob.meta.nvar) variables)" | ||
| finalize(nlp_prob) | ||
| continue | ||
| end | ||
| prob = OptimizationNLPModels.OptimizationProblem(nlp_prob, Optimization.AutoForwardDiff()) | ||
| for (optimizer_name, optimizer) in optimizers | ||
| try | ||
| sol = solve(prob, optimizer; maxiters = 1000, maxtime = 30.0) | ||
| @info "✓ Solved $(prob_name) with $(optimizer_name) - Status: $(sol.retcode)" | ||
| vars, time, alg, code = get_stats(sol, optimizer_name) | ||
| push!(problem, prob_name) | ||
| push!(n_vars, vars) | ||
| push!(secs, time) | ||
| push!(solver, alg) | ||
| push!(retcode, code) | ||
| catch e | ||
| push!(problem, prob_name) | ||
| push!(n_vars, nlp_prob !== nothing ? nlp_prob.meta.nvar : -1) | ||
| push!(secs, NaN) | ||
| push!(solver, optimizer_name) | ||
| push!(retcode, :FAILED) | ||
| end | ||
| end | ||
| catch e | ||
| for (optimizer_name, optimizer) in optimizers | ||
| push!(problem, prob_name) | ||
| push!(n_vars, -1) | ||
| push!(secs, NaN) | ||
| push!(solver, optimizer_name) | ||
| push!(retcode, :LOAD_FAILED) | ||
| end | ||
| finally | ||
| if nlp_prob !== nothing | ||
| try | ||
| finalize(nlp_prob) | ||
| catch e | ||
| end | ||
| end | ||
| end | ||
| end | ||
| GC.gc() | ||
| @info "Completed chunk, memory usage cleaned up" | ||
| end | ||
| return DataFrame(problem = problem, n_vars = n_vars, secs = secs, solver = solver, retcode = retcode) | ||
| end | ||
| ``` | ||
|
|
||
| # Benchmarks | ||
|
|
||
| We will be testing the [Ipopt]() and the [LBFGS]() optimizers on these classes of | ||
| problems. | ||
|
|
||
| ``` | ||
|
|
||
| ## Equality/Inequality constrained problems with bounded variables | ||
|
|
||
| Now we analyze the subset of problems with equality/inequality constraints and whose | ||
| variables are bounded. There are 666 such problems for equality constraints and 244 for inequality constraints. | ||
|
|
||
| The following figure shows the results of the same benchmarks previously described for the | ||
| problems on this section. | ||
|
|
||
| ```julia | ||
| @info "before" | ||
|
|
||
| # Select a moderate subset of equality-constrained bounded problems for a realistic mix | ||
| eq_bou_problems = CUTEst.select_sif_problems(min_con=1, only_equ_con=true, only_free_var=false) | ||
| eq_bou_problems = eq_bou_problems[1:min(30, length(eq_bou_problems))] | ||
| # Skip HIER13, BLOWEYA, LUKVLE8, READING2, NINENEW, READING6, DITTERT, CVXQP2, and MSS1 if present | ||
| eq_bou_problems = filter(p -> !(lowercase(p) in ["hier13", "bloweya", "lukvle8", "patternne", "reading2", "ninenew", "reading6", "dittert", "cvxqp2", "mss1"]), eq_bou_problems) | ||
| @info "Testing $(length(eq_bou_problems)) equality-constrained bounded problems (subset)" | ||
|
|
||
| # Analysis | ||
| eq_bou_results = run_benchmarks(eq_bou_problems, optimizers; chunk_size=3) | ||
|
|
||
| total_attempts = nrow(eq_bou_results) | ||
| successful_codes = [:Success, :MaxIters, :MaxTime, :FirstOrderOptimal] | ||
| successful_results = filter(row -> row.retcode in successful_codes, eq_bou_results) | ||
| successful_attempts = nrow(successful_results) | ||
| success_rate = total_attempts > 0 ? round(successful_attempts / total_attempts * 100, digits=1) : 0 | ||
|
|
||
| # Calculate and display success rates | ||
| successful_codes = [:Success, :MaxIters, :MaxTime, :FirstOrderOptimal] | ||
| successful_results = filter(row -> row.retcode in successful_codes, eq_bou_results) | ||
| total_attempts = nrow(eq_bou_results) | ||
| successful_attempts = nrow(successful_results) | ||
| success_rate = total_attempts > 0 ? round(successful_attempts / total_attempts * 100, digits=1) : 0 | ||
|
|
||
| println("Full results table for equality-constrained bounded problems:") | ||
| display(eq_bou_results) | ||
|
|
||
| println("SUCCESS RATE ANALYSIS (Equality Constrained, Bounded):") | ||
| println("Total attempts: ", total_attempts) | ||
| println("Successful attempts: ", successful_attempts) | ||
| println("Success rate: ", success_rate, "%") | ||
| println("Return code distribution:") | ||
| if total_attempts > 0 | ||
| for (code, count) in sort(collect(pairs(countmap(eq_bou_results.retcode))), by=x->x[2], rev=true) | ||
| println(" ", code, ": ", count, " occurrences") | ||
| end | ||
| else | ||
| println(" No results to analyze") | ||
| end | ||
|
|
||
| if nrow(eq_bou_results) > 0 | ||
| @df eq_bou_results scatter(:n_vars, :secs, | ||
| group = :solver, | ||
| xlabel = "n. variables", | ||
| ylabel = "secs.", | ||
| title = "Time to solution by optimizer and number of vars", | ||
| ) | ||
| println("Plotted equality-constrained bounded results.") | ||
| else | ||
| println("No equality-constrained bounded results to plot. DataFrame is empty.") | ||
| println("Attempted problems:") | ||
| println(eq_bou_problems) | ||
| end | ||
| ``` | ||
|
|
||
| Next, we examine the same relationship for inequality-constrained problems. | ||
|
|
||
| ```julia | ||
| @info "after4" | ||
|
|
||
| # Select a moderate subset of inequality-constrained bounded problems for a realistic mix | ||
| neq_bou_problems = CUTEst.select_sif_problems(min_con=1, only_ineq_con=true, only_free_var=false) | ||
| neq_bou_problems = neq_bou_problems[1:min(30, length(neq_bou_problems))] | ||
| # Skip HIER13, BLOWEYA, CHARDIS1, LUKVLE8, READING2, CVPXQ2, and MSS1 if present (in case they appear in this list too) | ||
| neq_bou_problems = filter(p -> !(lowercase(p) in ("chardis1", "hs67", "hs101", "haifal", "himmelp2", "hanging", "synthes1", "lukvli13", "liswet9", "hs85", "lukvli7", "expfita", "s268")), neq_bou_problems) | ||
| @info "Testing $(length(neq_bou_problems)) inequality-constrained bounded problems (subset)" | ||
|
|
||
| # Analysis | ||
| neq_bou_results = run_benchmarks(neq_bou_problems, optimizers; chunk_size=3) | ||
|
|
||
| total_attempts = nrow(neq_bou_results) | ||
| successful_codes = [:Success, :MaxIters, :MaxTime, :FirstOrderOptimal] | ||
| successful_results = filter(row -> row.retcode in successful_codes, neq_bou_results) | ||
| successful_attempts = nrow(successful_results) | ||
| success_rate = total_attempts > 0 ? round(successful_attempts / total_attempts * 100, digits=1) : 0 | ||
|
|
||
| # Calculate and display success rates | ||
| successful_codes = [:Success, :MaxIters, :MaxTime, :FirstOrderOptimal] | ||
| successful_results = filter(row -> row.retcode in successful_codes, neq_bou_results) | ||
| total_attempts = nrow(neq_bou_results) | ||
| successful_attempts = nrow(successful_results) | ||
| success_rate = total_attempts > 0 ? round(successful_attempts / total_attempts * 100, digits=1) : 0 | ||
|
|
||
| println("Full results table for inequality-constrained bounded problems:") | ||
| display(neq_bou_results) | ||
|
|
||
| println("SUCCESS RATE ANALYSIS (Inequality Constrained, Bounded):") | ||
| println("Total attempts: ", total_attempts) | ||
| println("Successful attempts: ", successful_attempts) | ||
| println("Success rate: ", success_rate, "%") | ||
| println("Return code distribution:") | ||
| if total_attempts > 0 | ||
| for (code, count) in sort(collect(pairs(countmap(neq_bou_results.retcode))), by=x->x[2], rev=true) | ||
| println(" ", code, ": ", count, " occurrences") | ||
| end | ||
| else | ||
| println(" No results to analyze") | ||
| end | ||
|
|
||
| if nrow(neq_bou_results) > 0 | ||
| @df neq_bou_results scatter(:n_vars, :secs, | ||
| group = :solver, | ||
| xlabel = "n. variables", | ||
| ylabel = "secs.", | ||
| title = "Time to solution by optimizer and number of vars", | ||
| ) | ||
| println("Plotted inequality-constrained bounded results.") | ||
| else | ||
| println("No inequality-constrained bounded results to plot. DataFrame is empty.") | ||
| println("Attempted problems:") | ||
| println(neq_bou_problems) | ||
| end | ||
| ``` | ||
|
|
||
| ```julia, echo = false | ||
| using SciMLBenchmarks | ||
| SciMLBenchmarks.bench_footer(WEAVE_ARGS[:folder],WEAVE_ARGS[:file]) | ||
| ``` | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.