Skip to content
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
785a9d7
Setting up CUTEst benchmarks dir structure
alonsoC1s Apr 19, 2024
313ac1c
Unconstrained problem benchmarks
alonsoC1s Apr 21, 2024
2c7dc3a
Fixing problem initialization
alonsoC1s Apr 22, 2024
c4a198a
Starting the analysis
alonsoC1s Aug 23, 2024
810e402
Added preliminary benchmarks for eq/ineq problems with free vars
alonsoC1s Aug 23, 2024
c2babbd
ci: install `gfortran` when the CUTEst optimization benchmarks are run
thazhemadam Aug 27, 2024
64b0e85
fixed variable use in benchmarks
alonsoC1s Aug 27, 2024
bd4c96c
Removing Manifest to fix version errors
alonsoC1s Aug 28, 2024
b0f1c35
Using NLPModels from General Registry
alonsoC1s Sep 3, 2024
b3f7ae1
revert: "ci: install `gfortran` when the CUTEst optimization benchmar…
Sep 26, 2024
82df232
ci: use rootfs image with openmodelica and gfortran pre-installed
Sep 26, 2024
3b2acff
Add SciMLBenchmarks to the manifest and the footer
ChrisRackauckas Sep 26, 2024
d344044
split into benchmark groups
ChrisRackauckas Oct 6, 2024
ca5ef66
Update CUTEst_bounded.jmd
ChrisRackauckas Oct 7, 2024
b91dc7b
Use OptimizationNLPModels from branch
Vaibhavdixit02 Mar 11, 2025
2d27f55
stats pkg
Vaibhavdixit02 Mar 11, 2025
0333356
use julia 1.10 resolved manifest
Vaibhavdixit02 Mar 14, 2025
b417914
omjulia
arnavk23 Jun 29, 2025
292f48f
Update Manifest.toml
arnavk23 Jun 30, 2025
7631605
manifest
arnavk23 Jul 3, 2025
1e9a8a6
formatted using JuliaFormatter
arnavk23 Jul 5, 2025
0554ab0
removing deprecated CUTEst.select
arnavk23 Jul 7, 2025
465f149
Update Project.toml
arnavk23 Jul 7, 2025
a2cd0e4
safe_solvers
arnavk23 Jul 12, 2025
c06bb8e
Update update.jl
arnavk23 Jul 14, 2025
b8bc556
Update make.jl
arnavk23 Jul 14, 2025
39f6a7d
Update pages.jl
arnavk23 Jul 14, 2025
1aeac3d
Update SciMLBenchmarks.jl
arnavk23 Jul 14, 2025
aa51c1b
Improve CUTEst benchmarks with chunked processing and robust error ha…
arnavk23 Jul 14, 2025
026272c
Make CUTEst benchmarks extremely conservative to prevent OOM
arnavk23 Jul 14, 2025
ffb87df
Fix CUTEst benchmark filtering and timeout issues
arnavk23 Jul 14, 2025
40bbd90
changes
arnavk23 Jul 19, 2025
0c9522d
try
arnavk23 Jul 27, 2025
ead71b0
trying again to commit changes to the CUTEst benchmarks in SciMLBench…
arnavk23 Jul 27, 2025
ba7d2bd
new changes to CUTEst benchmarks
arnavk23 Aug 1, 2025
93c308b
building
arnavk23 Aug 2, 2025
61fa6f6
bounded build pass
arnavk23 Aug 2, 2025
73ec8a7
Delete benchmarks/OptimizationCUTEst/benchmarks/OptimizationCUTEst/Pr…
arnavk23 Aug 6, 2025
c3f4c64
Delete benchmarks/OptimizationCUTEst/benchmarks/OptimizationCUTEst/Ma…
arnavk23 Aug 6, 2025
c7b0c87
Update benchmarks/OptimizationCUTEst/CUTEst_bounded.jmd
ChrisRackauckas Aug 6, 2025
a8b9699
Update benchmarks/OptimizationCUTEst/CUTEst_quadratic.jmd
ChrisRackauckas Aug 6, 2025
a71fbfb
Update benchmarks/OptimizationCUTEst/CUTEst_unbounded.jmd
ChrisRackauckas Aug 6, 2025
cf17b46
Update benchmarks/OptimizationCUTEst/CUTEst_unconstrained.jmd
ChrisRackauckas Aug 6, 2025
ac1740a
Update benchmarks/OptimizationCUTEst/CUTEst_safe_solvers.jmd
ChrisRackauckas Aug 6, 2025
6c9d3ea
Update benchmarks/OptimizationCUTEst/Project.toml
ChrisRackauckas Aug 6, 2025
bec7048
Update benchmarks/OptimizationCUTEst/Project.toml
ChrisRackauckas Aug 6, 2025
17d1b73
Update Project.toml
ChrisRackauckas Aug 6, 2025
a1cc6ce
Update Project.toml
ChrisRackauckas Aug 6, 2025
245e7bb
Update Project.toml
ChrisRackauckas Aug 6, 2025
59a11e5
Update Project.toml
ChrisRackauckas Aug 6, 2025
4cfbd62
Regenerate manifest
ChrisRackauckas Aug 6, 2025
84b0d93
Update .buildkite/run_benchmark.yml
ChrisRackauckas Aug 6, 2025
ce841b6
Update .github/workflows/update.jl
ChrisRackauckas Aug 6, 2025
a60a784
Update .github/workflows/update.jl
ChrisRackauckas Aug 6, 2025
14e37f2
ci: use new test rootfs image
thazhemadam Aug 12, 2025
4b2d4d9
ci: sign treehashes
thazhemadam Aug 13, 2025
a8cbec1
Apply suggestions from code review
ChrisRackauckas Aug 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .buildkite/launch_benchmarks.yml.signature
Binary file not shown.
4 changes: 2 additions & 2 deletions .buildkite/run_benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ steps:
- JuliaCI/julia#v1:
version: "1.10"
- staticfloat/sandbox:
rootfs_url: "https://github.com/thazhemadam/openmodelica-rootfs-image/releases/download/v1.23.0/rootfs-openmodelica-v1.23.0.amd64.tar.gz"
rootfs_treehash: "82970243dc4f188e599a976abc20951f4aba2912"
rootfs_url: "https://github.com/thazhemadam/SciMLBenchmarks-rootfs-image/releases/download/v1.0.1/sciml-benchmarks-rootfs-image-v1.0.1.amd64.tar.gz"
rootfs_treehash: "1ca61dc27d89849ed03af6c9babdbd4042ddd4f2"
uid: 1000
gid: 1000
workspaces:
Expand Down
249 changes: 249 additions & 0 deletions benchmarks/OptimizationCUTEst/CUTEst_bounded.jmd
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])
```
Loading
Loading