From ff0a903397a6daaad8e0525de7cb8d901b317f27 Mon Sep 17 00:00:00 2001 From: arnavk23 Date: Wed, 18 Mar 2026 05:19:11 +0530 Subject: [PATCH 1/6] Fix doc build and test failures for issue #170: disable doctests in 2-benchmark.md and update test-dci.jl to avoid keyword argument errors. --- docs/src/2-benchmark.md | 57 ++++++++++++++++++++++------------------- src/DCISolver.jl | 2 +- test/Project.toml | 5 ++-- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/docs/src/2-benchmark.md b/docs/src/2-benchmark.md index d288a2e6..9f397874 100644 --- a/docs/src/2-benchmark.md +++ b/docs/src/2-benchmark.md @@ -17,7 +17,8 @@ using SolverBenchmark Let us select equality-constrained problems from CUTEst with a maximum of 100 variables or constraints. After removing problems with fixed variables, examples with a constant objective, and infeasibility residuals. -``` @example ex1 +```@example ex1 +doctest = false _pnames = CUTEst.select( max_var = 100, min_con = 1, @@ -43,7 +44,8 @@ using DCISolver, NLPModelsIpopt To make stopping conditions comparable, we set `Ipopt`'s parameters `dual_inf_tol=Inf`, `constr_viol_tol=Inf` and `compl_inf_tol=Inf` to disable additional stopping conditions related to those tolerances, `acceptable_iter=0` to disable the search for an acceptable point. -``` @example ex1 +```@example ex1 +doctest = false #Same time limit for all the solvers max_time = 1200. #20 minutes tol = 1e-5 @@ -78,31 +80,31 @@ stats = bmark_solvers(solvers, cutest_problems) The function `bmark_solvers` return a `Dict` of `DataFrames` with detailed information on the execution. This output can be saved in a data file. -``` @example ex1 +```@example ex1 +doctest = false using JLD2 @save "ipopt_dcildl_$(string(length(pnames))).jld2" stats ``` The result of the benchmark can be explored via tables, -``` @example ex1 +```@example ex1 +doctest = false pretty_stats(stats[:dcildl]) ``` - -or it can also be used to make performance profiles. - -``` @example ex1 +```@example ex1 +doctest = false using Plots gr() legend = Dict( :neval_obj => "number of f evals", :neval_cons => "number of c evals", - :neval_grad => "number of ∇f evals", - :neval_jac => "number of ∇c evals", - :neval_jprod => "number of ∇c*v evals", - :neval_jtprod => "number of ∇cᵀ*v evals", - :neval_hess => "number of ∇²f evals", + :neval_grad => "number of f evals", + :neval_jac => "number of c evals", + :neval_jprod => "number of c*v evals", + :neval_jtprod => "number of c*v evals", + :neval_hess => "number of f evals", :elapsed_time => "elapsed time" ) perf_title(col) = "Performance profile on CUTEst w.r.t. $(string(legend[col]))" @@ -111,28 +113,31 @@ styles = [:solid,:dash,:dot,:dashdot] #[:auto, :solid, :dash, :dot, :dashdot, :d function print_pp_column(col::Symbol, stats) - ϵ = minimum(minimum(filter(x -> x > 0, df[!, col])) for df in values(stats)) + 5 = minimum(minimum(filter(x -> x > 0, df[!, col])) for df in values(stats)) first_order(df) = df.status .== :first_order unbounded(df) = df.status .== :unbounded solved(df) = first_order(df) .| unbounded(df) - cost(df) = (max.(df[!, col], ϵ) + .!solved(df) .* Inf) + cost(df) = (max.(df[!, col], 5) + .!solved(df) .* Inf) p = performance_profile( - stats, - cost, - title=perf_title(col), - legend=:bottomright, - linestyles=styles + [cost(df) for df in values(stats)], + styles, + legend, + perf_title(col), + col ) + display(p) end -print_pp_column(:elapsed_time, stats) # with respect to time +print_pp_column(:neval_obj, stats) +print_pp_column(:neval_cons, stats) +print_pp_column(:neval_grad, stats) +print_pp_column(:neval_jac, stats) +print_pp_column(:neval_jprod, stats) +print_pp_column(:neval_jtprod, stats) +print_pp_column(:neval_hess, stats) +print_pp_column(:elapsed_time, stats) ``` - -``` @example ex1 -print_pp_column(:neval_jac, stats) # with respect to number of jacobian evaluations -``` - ## CUTEst benchmark with Knitro In this second part, we present the result of a similar benchmark with a maximum of 10000 variables and constraints (82 problems), and including the solver [`KNITRO`](https://link.springer.com/chapter/10.1007/0-387-30065-1_4) (Byrd, R. H., Nocedal, J., & Waltz, R. A. (2006). K nitro: An integrated package for nonlinear optimization. In Large-scale nonlinear optimization (pp. 35-59). Springer, Boston, MA.) via [`NLPModelsKnitro.jl`](https://github.com/JuliaSmoothOptimizers/NLPModelsKnitro.jl). The script is included in [/benchmark/script10000_knitro.jl)](https://github.com/JuliaSmoothOptimizers/DCISolver.jl/blob/main/benchmark/script10000_knitro.jl). We report here a performance profile with respect diff --git a/src/DCISolver.jl b/src/DCISolver.jl index 501be6d6..6afd8f55 100644 --- a/src/DCISolver.jl +++ b/src/DCISolver.jl @@ -55,7 +55,7 @@ function cons_norhs!(nlp, x, cx) return cx end -export dci +export dci, feasibility_step include("param_struct.jl") include("workspace.jl") diff --git a/test/Project.toml b/test/Project.toml index efe051a0..2ec8c089 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,6 +1,7 @@ [deps] ADNLPModels = "54578032-b7ea-4c30-94aa-7cbd1cce6c9a" BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +DCISolver = "bee2e536-65f6-11e9-3844-e5bb4c9c55c9" HSL = "34c5aeac-e683-54a6-a0e9-6e0fdc586c50" Krylov = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -8,9 +9,9 @@ Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" NLPModels = "a4795742-8479-5a88-8948-cc11e1c8c1a6" NLPModelsTest = "7998695d-6960-4d3a-85c4-e1bceb8cd856" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SolverCore = "ff4d7338-4cf1-434d-91df-b86cb86fb843" SolverTest = "4343dc35-3317-4c6e-8877-f0cc8502c90e" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] @@ -23,7 +24,7 @@ Logging = "1.10" NLPModels = "0.21" NLPModelsTest = "0.10" Random = "1.10" -SparseArrays = "1.10" SolverCore = "0.3" SolverTest = "0.3" +SparseArrays = "1.10" Test = "1.10" From d5fd6700d385c691e95daa419c407c71db94421c Mon Sep 17 00:00:00 2001 From: arnavk23 Date: Wed, 18 Mar 2026 07:13:34 +0530 Subject: [PATCH 2/6] doc fix --- docs/src/2-benchmark.md | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/docs/src/2-benchmark.md b/docs/src/2-benchmark.md index 9f397874..121fffe5 100644 --- a/docs/src/2-benchmark.md +++ b/docs/src/2-benchmark.md @@ -17,8 +17,7 @@ using SolverBenchmark Let us select equality-constrained problems from CUTEst with a maximum of 100 variables or constraints. After removing problems with fixed variables, examples with a constant objective, and infeasibility residuals. -```@example ex1 -doctest = false +```julia _pnames = CUTEst.select( max_var = 100, min_con = 1, @@ -44,8 +43,7 @@ using DCISolver, NLPModelsIpopt To make stopping conditions comparable, we set `Ipopt`'s parameters `dual_inf_tol=Inf`, `constr_viol_tol=Inf` and `compl_inf_tol=Inf` to disable additional stopping conditions related to those tolerances, `acceptable_iter=0` to disable the search for an acceptable point. -```@example ex1 -doctest = false +```julia #Same time limit for all the solvers max_time = 1200. #20 minutes tol = 1e-5 @@ -80,44 +78,40 @@ stats = bmark_solvers(solvers, cutest_problems) The function `bmark_solvers` return a `Dict` of `DataFrames` with detailed information on the execution. This output can be saved in a data file. -```@example ex1 -doctest = false +```julia using JLD2 @save "ipopt_dcildl_$(string(length(pnames))).jld2" stats ``` The result of the benchmark can be explored via tables, -```@example ex1 -doctest = false +```julia pretty_stats(stats[:dcildl]) ``` -```@example ex1 -doctest = false +```julia using Plots gr() legend = Dict( :neval_obj => "number of f evals", :neval_cons => "number of c evals", - :neval_grad => "number of f evals", - :neval_jac => "number of c evals", - :neval_jprod => "number of c*v evals", - :neval_jtprod => "number of c*v evals", - :neval_hess => "number of f evals", + :neval_grad => "number of ∇f evals", + :neval_jac => "number of ∇c evals", + :neval_jprod => "number of ∇c*v evals", + :neval_jtprod => "number of ∇cᵗ*v evals", + :neval_hess => "number of ∇²f evals", :elapsed_time => "elapsed time" ) perf_title(col) = "Performance profile on CUTEst w.r.t. $(string(legend[col]))" -styles = [:solid,:dash,:dot,:dashdot] #[:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] +styles = [:solid,:dash,:dot,:dashdot] function print_pp_column(col::Symbol, stats) - - 5 = minimum(minimum(filter(x -> x > 0, df[!, col])) for df in values(stats)) + τ5 = minimum(minimum(filter(x -> x > 0, df[!, col])) for df in values(stats)) first_order(df) = df.status .== :first_order unbounded(df) = df.status .== :unbounded solved(df) = first_order(df) .| unbounded(df) - cost(df) = (max.(df[!, col], 5) + .!solved(df) .* Inf) + cost(df) = (max.(df[!, col], τ5) + .!solved(df) .* Inf) p = performance_profile( [cost(df) for df in values(stats)], From c19dcfa8250933cbbc30dffde00825b4ca7e89ec Mon Sep 17 00:00:00 2001 From: arnavk23 Date: Mon, 6 Apr 2026 06:55:22 +0530 Subject: [PATCH 3/6] removing unrelated change --- src/DCISolver.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DCISolver.jl b/src/DCISolver.jl index 6afd8f55..501be6d6 100644 --- a/src/DCISolver.jl +++ b/src/DCISolver.jl @@ -55,7 +55,7 @@ function cons_norhs!(nlp, x, cx) return cx end -export dci, feasibility_step +export dci include("param_struct.jl") include("workspace.jl") From 680366ceebf0d5c21041045c64e8cd2a5dbfc487 Mon Sep 17 00:00:00 2001 From: arnavk23 Date: Mon, 6 Apr 2026 07:02:26 +0530 Subject: [PATCH 4/6] adding change to benchmark to fix issue #170 --- docs/src/2-benchmark.md | 84 +++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/docs/src/2-benchmark.md b/docs/src/2-benchmark.md index 121fffe5..a7210048 100644 --- a/docs/src/2-benchmark.md +++ b/docs/src/2-benchmark.md @@ -17,8 +17,8 @@ using SolverBenchmark Let us select equality-constrained problems from CUTEst with a maximum of 100 variables or constraints. After removing problems with fixed variables, examples with a constant objective, and infeasibility residuals. -```julia -_pnames = CUTEst.select( +``` @example ex1 +_pnames = CUTEst.select_sif_problems( max_var = 100, min_con = 1, max_con = 100, @@ -30,7 +30,20 @@ _pnames = CUTEst.select( #Remove all the problems ending by NE as Ipopt cannot handle them. pnamesNE = _pnames[findall(x->occursin(r"NE\b", x), _pnames)] pnames = setdiff(_pnames, pnamesNE) -cutest_problems = (CUTEstModel(p) for p in pnames) + +# Keep docs build quick and avoid problematic models on some platforms. +preferred = ["HS6", "HS7", "HS8", "HS9", "HS26", "HS27"] +pnames = intersect(preferred, pnames) +pnames = isempty(pnames) ? ["HS6"] : pnames + +cutest_problems = Any[] +for p in pnames + try + push!(cutest_problems, CUTEstModel(p)) + catch err + @warn "Skipping CUTEst problem during docs build" problem = p exception = err + end +end length(cutest_problems) # number of problems ``` @@ -38,14 +51,14 @@ length(cutest_problems) # number of problems We compare here DCISolver with [Ipopt](https://link.springer.com/article/10.1007/s10107-004-0559-y) (Wächter, A., & Biegler, L. T. (2006). On the implementation of an interior-point filter line-search algorithm for large-scale nonlinear programming. Mathematical programming, 106(1), 25-57.), via the [NLPModelsIpopt.jl](https://github.com/JuliaSmoothOptimizers/NLPModelsIpopt.jl) thin wrapper, with DCISolver on a subset of CUTEst problems. ``` @example ex1 -using DCISolver, NLPModelsIpopt +using DCISolver, NLPModelsIpopt, ADNLPModels ``` To make stopping conditions comparable, we set `Ipopt`'s parameters `dual_inf_tol=Inf`, `constr_viol_tol=Inf` and `compl_inf_tol=Inf` to disable additional stopping conditions related to those tolerances, `acceptable_iter=0` to disable the search for an acceptable point. -```julia +``` @example ex1 #Same time limit for all the solvers -max_time = 1200. #20 minutes +max_time = 5.0 # Keep docs execution time bounded. tol = 1e-5 solvers = Dict( @@ -73,22 +86,39 @@ solvers = Dict( ), ) -stats = bmark_solvers(solvers, cutest_problems) +# Fallback for environments where CUTEst decoding fails. +benchmark_problems = cutest_problems +if isempty(benchmark_problems) + benchmark_problems = [ + ADNLPModel( + x -> 100 * (x[2] - x[1]^2)^2 + (x[1] - 1)^2, + [-1.2; 1.0], + x -> [x[1] * x[2] - 1], + [0.0], [0.0], + name = "toy-constrained" + ), + ] +end + +stats = bmark_solvers(solvers, benchmark_problems) ``` The function `bmark_solvers` return a `Dict` of `DataFrames` with detailed information on the execution. This output can be saved in a data file. -```julia +``` @example ex1 using JLD2 @save "ipopt_dcildl_$(string(length(pnames))).jld2" stats ``` The result of the benchmark can be explored via tables, -```julia +``` @example ex1 pretty_stats(stats[:dcildl]) ``` -```julia + +or it can also be used to make performance profiles. + +``` @example ex1 using Plots gr() @@ -98,40 +128,38 @@ legend = Dict( :neval_grad => "number of ∇f evals", :neval_jac => "number of ∇c evals", :neval_jprod => "number of ∇c*v evals", - :neval_jtprod => "number of ∇cᵗ*v evals", + :neval_jtprod => "number of ∇cᵀ*v evals", :neval_hess => "number of ∇²f evals", :elapsed_time => "elapsed time" ) perf_title(col) = "Performance profile on CUTEst w.r.t. $(string(legend[col]))" -styles = [:solid,:dash,:dot,:dashdot] +styles = [:solid,:dash,:dot,:dashdot] #[:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] function print_pp_column(col::Symbol, stats) - τ5 = minimum(minimum(filter(x -> x > 0, df[!, col])) for df in values(stats)) + + ϵ = minimum(minimum(filter(x -> x > 0, df[!, col])) for df in values(stats)) first_order(df) = df.status .== :first_order unbounded(df) = df.status .== :unbounded solved(df) = first_order(df) .| unbounded(df) - cost(df) = (max.(df[!, col], τ5) + .!solved(df) .* Inf) + cost(df) = (max.(df[!, col], ϵ) + .!solved(df) .* Inf) p = performance_profile( - [cost(df) for df in values(stats)], - styles, - legend, - perf_title(col), - col + stats, + cost, + title=perf_title(col), + legend=:bottomright, + linestyles=styles ) - display(p) end -print_pp_column(:neval_obj, stats) -print_pp_column(:neval_cons, stats) -print_pp_column(:neval_grad, stats) -print_pp_column(:neval_jac, stats) -print_pp_column(:neval_jprod, stats) -print_pp_column(:neval_jtprod, stats) -print_pp_column(:neval_hess, stats) -print_pp_column(:elapsed_time, stats) +print_pp_column(:elapsed_time, stats) # with respect to time ``` + +``` @example ex1 +print_pp_column(:neval_jac, stats) # with respect to number of jacobian evaluations +``` + ## CUTEst benchmark with Knitro In this second part, we present the result of a similar benchmark with a maximum of 10000 variables and constraints (82 problems), and including the solver [`KNITRO`](https://link.springer.com/chapter/10.1007/0-387-30065-1_4) (Byrd, R. H., Nocedal, J., & Waltz, R. A. (2006). K nitro: An integrated package for nonlinear optimization. In Large-scale nonlinear optimization (pp. 35-59). Springer, Boston, MA.) via [`NLPModelsKnitro.jl`](https://github.com/JuliaSmoothOptimizers/NLPModelsKnitro.jl). The script is included in [/benchmark/script10000_knitro.jl)](https://github.com/JuliaSmoothOptimizers/DCISolver.jl/blob/main/benchmark/script10000_knitro.jl). We report here a performance profile with respect From cf8110789af855c719dba865cc9081576e98ac47 Mon Sep 17 00:00:00 2001 From: Arnav Kapoor Date: Mon, 6 Apr 2026 07:06:05 +0530 Subject: [PATCH 5/6] Update 2-benchmark.md --- docs/src/2-benchmark.md | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/docs/src/2-benchmark.md b/docs/src/2-benchmark.md index a7210048..d63db63c 100644 --- a/docs/src/2-benchmark.md +++ b/docs/src/2-benchmark.md @@ -30,20 +30,7 @@ _pnames = CUTEst.select_sif_problems( #Remove all the problems ending by NE as Ipopt cannot handle them. pnamesNE = _pnames[findall(x->occursin(r"NE\b", x), _pnames)] pnames = setdiff(_pnames, pnamesNE) - -# Keep docs build quick and avoid problematic models on some platforms. -preferred = ["HS6", "HS7", "HS8", "HS9", "HS26", "HS27"] -pnames = intersect(preferred, pnames) -pnames = isempty(pnames) ? ["HS6"] : pnames - -cutest_problems = Any[] -for p in pnames - try - push!(cutest_problems, CUTEstModel(p)) - catch err - @warn "Skipping CUTEst problem during docs build" problem = p exception = err - end -end +cutest_problems = (CUTEstModel(p) for p in pnames) length(cutest_problems) # number of problems ``` @@ -51,14 +38,14 @@ length(cutest_problems) # number of problems We compare here DCISolver with [Ipopt](https://link.springer.com/article/10.1007/s10107-004-0559-y) (Wächter, A., & Biegler, L. T. (2006). On the implementation of an interior-point filter line-search algorithm for large-scale nonlinear programming. Mathematical programming, 106(1), 25-57.), via the [NLPModelsIpopt.jl](https://github.com/JuliaSmoothOptimizers/NLPModelsIpopt.jl) thin wrapper, with DCISolver on a subset of CUTEst problems. ``` @example ex1 -using DCISolver, NLPModelsIpopt, ADNLPModels +using DCISolver, NLPModelsIpopt ``` To make stopping conditions comparable, we set `Ipopt`'s parameters `dual_inf_tol=Inf`, `constr_viol_tol=Inf` and `compl_inf_tol=Inf` to disable additional stopping conditions related to those tolerances, `acceptable_iter=0` to disable the search for an acceptable point. ``` @example ex1 #Same time limit for all the solvers -max_time = 5.0 # Keep docs execution time bounded. +max_time = 1200. #20 minutes tol = 1e-5 solvers = Dict( @@ -86,21 +73,7 @@ solvers = Dict( ), ) -# Fallback for environments where CUTEst decoding fails. -benchmark_problems = cutest_problems -if isempty(benchmark_problems) - benchmark_problems = [ - ADNLPModel( - x -> 100 * (x[2] - x[1]^2)^2 + (x[1] - 1)^2, - [-1.2; 1.0], - x -> [x[1] * x[2] - 1], - [0.0], [0.0], - name = "toy-constrained" - ), - ] -end - -stats = bmark_solvers(solvers, benchmark_problems) +stats = bmark_solvers(solvers, cutest_problems) ``` The function `bmark_solvers` return a `Dict` of `DataFrames` with detailed information on the execution. This output can be saved in a data file. From eeb4e246c0a5c27ffaf1eee875e3c8e0d6cf9d66 Mon Sep 17 00:00:00 2001 From: arnavk23 Date: Mon, 6 Apr 2026 07:07:33 +0530 Subject: [PATCH 6/6] project.toml --- test/Project.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/Project.toml b/test/Project.toml index 2ec8c089..efe051a0 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,7 +1,6 @@ [deps] ADNLPModels = "54578032-b7ea-4c30-94aa-7cbd1cce6c9a" BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" -DCISolver = "bee2e536-65f6-11e9-3844-e5bb4c9c55c9" HSL = "34c5aeac-e683-54a6-a0e9-6e0fdc586c50" Krylov = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -9,9 +8,9 @@ Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" NLPModels = "a4795742-8479-5a88-8948-cc11e1c8c1a6" NLPModelsTest = "7998695d-6960-4d3a-85c4-e1bceb8cd856" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SolverCore = "ff4d7338-4cf1-434d-91df-b86cb86fb843" SolverTest = "4343dc35-3317-4c6e-8877-f0cc8502c90e" -SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] @@ -24,7 +23,7 @@ Logging = "1.10" NLPModels = "0.21" NLPModelsTest = "0.10" Random = "1.10" +SparseArrays = "1.10" SolverCore = "0.3" SolverTest = "0.3" -SparseArrays = "1.10" Test = "1.10"