Skip to content

Commit f938a95

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 <[email protected]>
1 parent 2a90ec0 commit f938a95

File tree

2 files changed

+88
-1
lines changed

2 files changed

+88
-1
lines changed

lib/OptimizationMOI/src/OptimizationMOI.jl

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,56 @@ function _create_new_optimizer(opt::MOI.AbstractOptimizer)
6060
return opt_setup
6161
end
6262

63+
"""
64+
_set_maxiters!(optimizer, maxiters)
65+
66+
Sets the maximum number of iterations for the optimizer using solver-specific parameter names.
67+
Supports common MOI solvers including Ipopt, Gurobi, CPLEX, and SCIP.
68+
"""
69+
function _set_maxiters!(optimizer, maxiters::Number)
70+
optimizer_name = string(typeof(optimizer))
71+
72+
# Try to set maxiters based on common solver patterns
73+
try
74+
if contains(optimizer_name, "Ipopt")
75+
MOI.set(optimizer, MOI.RawOptimizerAttribute("max_iter"), Int(maxiters))
76+
elseif contains(optimizer_name, "Gurobi")
77+
MOI.set(optimizer, MOI.RawOptimizerAttribute("IterationLimit"), Int(maxiters))
78+
elseif contains(optimizer_name, "CPLEX") || contains(optimizer_name, "Cplex")
79+
MOI.set(optimizer, MOI.RawOptimizerAttribute("CPX_PARAM_ITLIM"), Int(maxiters))
80+
elseif contains(optimizer_name, "SCIP") || contains(optimizer_name, "Scip")
81+
MOI.set(optimizer, MOI.RawOptimizerAttribute("limits/iterations"), Int(maxiters))
82+
elseif contains(optimizer_name, "Mosek") || contains(optimizer_name, "MOSEK")
83+
MOI.set(optimizer, MOI.RawOptimizerAttribute("MSK_IPAR_INTPNT_MAX_ITERATIONS"), Int(maxiters))
84+
elseif contains(optimizer_name, "OSQP")
85+
MOI.set(optimizer, MOI.RawOptimizerAttribute("max_iter"), Int(maxiters))
86+
elseif contains(optimizer_name, "ECOS")
87+
MOI.set(optimizer, MOI.RawOptimizerAttribute("maxit"), Int(maxiters))
88+
elseif contains(optimizer_name, "SCS")
89+
MOI.set(optimizer, MOI.RawOptimizerAttribute("max_iters"), Int(maxiters))
90+
elseif contains(optimizer_name, "COSMO")
91+
MOI.set(optimizer, MOI.RawOptimizerAttribute("max_iter"), Int(maxiters))
92+
else
93+
# Generic fallback - try common parameter names
94+
for param_name in ["max_iter", "maxiter", "IterationLimit", "max_iterations"]
95+
try
96+
MOI.set(optimizer, MOI.RawOptimizerAttribute(param_name), Int(maxiters))
97+
return # Success, exit early
98+
catch
99+
continue # Try next parameter name
100+
end
101+
end
102+
# If all attempts fail, show warning with guidance
103+
@warn "common maxiters argument could not be mapped for $(typeof(optimizer)). " *
104+
"Set number of iterations via optimizer specific keyword arguments."
105+
end
106+
catch e
107+
# Catch any errors during parameter setting and show informative warning
108+
@warn "Failed to set maxiters parameter for $(typeof(optimizer)): $(e). " *
109+
"Set number of iterations via optimizer specific keyword arguments."
110+
end
111+
end
112+
63113
function __map_optimizer_args(cache,
64114
opt::Union{MOI.AbstractOptimizer, MOI.OptimizerWithAttributes
65115
};
@@ -82,7 +132,7 @@ function __map_optimizer_args(cache,
82132
@warn "common abstol argument is currently not used by $(optimizer). Set tolerances via optimizer specific keyword arguments."
83133
end
84134
if !isnothing(maxiters)
85-
@warn "common maxiters argument is currently not used by $(optimizer). Set number of iterations via optimizer specific keyword arguments."
135+
_set_maxiters!(optimizer, maxiters)
86136
end
87137
return optimizer
88138
end

lib/OptimizationMOI/test/runtests.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,40 @@ end
276276
prob = OptimizationProblem(optprob, x0, _p, lcons = [1.0, 0.5], ucons = [1.0, 0.5])
277277
sol = solve(prob, Ipopt.Optimizer())
278278
end
279+
280+
@testset "common maxiters interface" begin
281+
# Test that the common maxiters interface works without warnings
282+
rosenbrock(x, p) = (p[1] - x[1])^2 + p[2] * (x[2] - x[1]^2)^2
283+
x0 = zeros(2)
284+
_p = [1.0, 100.0]
285+
286+
optprob = OptimizationFunction(rosenbrock, Optimization.AutoZygote())
287+
prob = OptimizationProblem(optprob, x0, _p)
288+
289+
# Test with Ipopt using maxiters parameter
290+
@testset "Ipopt maxiters" begin
291+
# This should not produce a warning and should respect the iteration limit
292+
sol = solve(prob, Ipopt.Optimizer(); maxiters = 5, print_level = 0)
293+
# Should terminate due to iteration limit
294+
@test sol.stats.iterations <= 5
295+
end
296+
297+
# Test with cache interface
298+
@testset "Cache interface maxiters" begin
299+
cache = init(prob, Ipopt.Optimizer(); maxiters = 3, print_level = 0)
300+
sol = solve!(cache)
301+
@test sol.stats.iterations <= 3
302+
end
303+
304+
# Test that unknown solver fallback works gracefully
305+
@testset "Generic fallback" begin
306+
# Mock optimizer that doesn't match any known pattern
307+
struct MockOptimizer <: MathOptInterface.AbstractOptimizer end
308+
309+
# This should not error, but may show a warning for unknown solver
310+
mock_opt = MockOptimizer()
311+
# We can't actually solve with this mock optimizer, but we can test
312+
# that the parameter setting doesn't crash
313+
@test_nowarn OptimizationMOI._set_maxiters!(mock_opt, 10)
314+
end
315+
end

0 commit comments

Comments
 (0)