Skip to content

Commit ed6639e

Browse files
Fix maxiters warning by implementing solver-specific parameter mapping
- Add _set_maxiters! function to map common maxiters to solver-specific parameters - Support major MOI solvers: Ipopt, Gurobi, CPLEX, SCIP, Mosek, OSQP, ECOS, SCS, COSMO - Implement generic fallback that tries common parameter names - Add comprehensive tests for the new functionality - Resolves #844 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 7e337dc commit ed6639e

2 files changed

Lines changed: 88 additions & 1 deletion

File tree

lib/OptimizationMOI/src/OptimizationMOI.jl

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,56 @@ function _create_new_optimizer(opt::MOI.AbstractOptimizer)
8484
return opt_setup
8585
end
8686

87+
"""
88+
_set_maxiters!(optimizer, maxiters)
89+
90+
Sets the maximum number of iterations for the optimizer using solver-specific parameter names.
91+
Supports common MOI solvers including Ipopt, Gurobi, CPLEX, and SCIP.
92+
"""
93+
function _set_maxiters!(optimizer, maxiters::Number)
94+
optimizer_name = string(typeof(optimizer))
95+
96+
# Try to set maxiters based on common solver patterns
97+
try
98+
if contains(optimizer_name, "Ipopt")
99+
MOI.set(optimizer, MOI.RawOptimizerAttribute("max_iter"), Int(maxiters))
100+
elseif contains(optimizer_name, "Gurobi")
101+
MOI.set(optimizer, MOI.RawOptimizerAttribute("IterationLimit"), Int(maxiters))
102+
elseif contains(optimizer_name, "CPLEX") || contains(optimizer_name, "Cplex")
103+
MOI.set(optimizer, MOI.RawOptimizerAttribute("CPX_PARAM_ITLIM"), Int(maxiters))
104+
elseif contains(optimizer_name, "SCIP") || contains(optimizer_name, "Scip")
105+
MOI.set(optimizer, MOI.RawOptimizerAttribute("limits/iterations"), Int(maxiters))
106+
elseif contains(optimizer_name, "Mosek") || contains(optimizer_name, "MOSEK")
107+
MOI.set(optimizer, MOI.RawOptimizerAttribute("MSK_IPAR_INTPNT_MAX_ITERATIONS"), Int(maxiters))
108+
elseif contains(optimizer_name, "OSQP")
109+
MOI.set(optimizer, MOI.RawOptimizerAttribute("max_iter"), Int(maxiters))
110+
elseif contains(optimizer_name, "ECOS")
111+
MOI.set(optimizer, MOI.RawOptimizerAttribute("maxit"), Int(maxiters))
112+
elseif contains(optimizer_name, "SCS")
113+
MOI.set(optimizer, MOI.RawOptimizerAttribute("max_iters"), Int(maxiters))
114+
elseif contains(optimizer_name, "COSMO")
115+
MOI.set(optimizer, MOI.RawOptimizerAttribute("max_iter"), Int(maxiters))
116+
else
117+
# Generic fallback - try common parameter names
118+
for param_name in ["max_iter", "maxiter", "IterationLimit", "max_iterations"]
119+
try
120+
MOI.set(optimizer, MOI.RawOptimizerAttribute(param_name), Int(maxiters))
121+
return # Success, exit early
122+
catch
123+
continue # Try next parameter name
124+
end
125+
end
126+
# If all attempts fail, show warning with guidance
127+
@warn "common maxiters argument could not be mapped for $(typeof(optimizer)). " *
128+
"Set number of iterations via optimizer specific keyword arguments."
129+
end
130+
catch e
131+
# Catch any errors during parameter setting and show informative warning
132+
@warn "Failed to set maxiters parameter for $(typeof(optimizer)): $(e). " *
133+
"Set number of iterations via optimizer specific keyword arguments."
134+
end
135+
end
136+
87137
function __map_optimizer_args(
88138
cache,
89139
opt::Union{
@@ -109,7 +159,7 @@ function __map_optimizer_args(
109159
@warn "common abstol argument is currently not used by $(optimizer). Set tolerances via optimizer specific keyword arguments."
110160
end
111161
if !isnothing(maxiters)
112-
@warn "common maxiters argument is currently not used by $(optimizer). Set number of iterations via optimizer specific keyword arguments."
162+
_set_maxiters!(optimizer, maxiters)
113163
end
114164
return optimizer
115165
end

lib/OptimizationMOI/test/runtests.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,3 +345,40 @@ end
345345
prob = OptimizationProblem(optprob, x0, _p, lcons = [1.0, 0.5], ucons = [1.0, 0.5])
346346
sol = solve(prob, Ipopt.Optimizer())
347347
end
348+
349+
@testset "common maxiters interface" begin
350+
# Test that the common maxiters interface works without warnings
351+
rosenbrock(x, p) = (p[1] - x[1])^2 + p[2] * (x[2] - x[1]^2)^2
352+
x0 = zeros(2)
353+
_p = [1.0, 100.0]
354+
355+
optprob = OptimizationFunction(rosenbrock, Optimization.AutoZygote())
356+
prob = OptimizationProblem(optprob, x0, _p)
357+
358+
# Test with Ipopt using maxiters parameter
359+
@testset "Ipopt maxiters" begin
360+
# This should not produce a warning and should respect the iteration limit
361+
sol = solve(prob, Ipopt.Optimizer(); maxiters = 5, print_level = 0)
362+
# Should terminate due to iteration limit
363+
@test sol.stats.iterations <= 5
364+
end
365+
366+
# Test with cache interface
367+
@testset "Cache interface maxiters" begin
368+
cache = init(prob, Ipopt.Optimizer(); maxiters = 3, print_level = 0)
369+
sol = solve!(cache)
370+
@test sol.stats.iterations <= 3
371+
end
372+
373+
# Test that unknown solver fallback works gracefully
374+
@testset "Generic fallback" begin
375+
# Mock optimizer that doesn't match any known pattern
376+
struct MockOptimizer <: MathOptInterface.AbstractOptimizer end
377+
378+
# This should not error, but may show a warning for unknown solver
379+
mock_opt = MockOptimizer()
380+
# We can't actually solve with this mock optimizer, but we can test
381+
# that the parameter setting doesn't crash
382+
@test_nowarn OptimizationMOI._set_maxiters!(mock_opt, 10)
383+
end
384+
end

0 commit comments

Comments
 (0)