Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
52 changes: 51 additions & 1 deletion lib/OptimizationMOI/src/OptimizationMOI.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,56 @@ function _create_new_optimizer(opt::MOI.AbstractOptimizer)
return opt_setup
end

"""
_set_maxiters!(optimizer, maxiters)

Sets the maximum number of iterations for the optimizer using solver-specific parameter names.
Supports common MOI solvers including Ipopt, Gurobi, CPLEX, and SCIP.
"""
function _set_maxiters!(optimizer, maxiters::Number)
optimizer_name = string(typeof(optimizer))

# Try to set maxiters based on common solver patterns
try
if contains(optimizer_name, "Ipopt")
MOI.set(optimizer, MOI.RawOptimizerAttribute("max_iter"), Int(maxiters))
elseif contains(optimizer_name, "Gurobi")
MOI.set(optimizer, MOI.RawOptimizerAttribute("IterationLimit"), Int(maxiters))
elseif contains(optimizer_name, "CPLEX") || contains(optimizer_name, "Cplex")
MOI.set(optimizer, MOI.RawOptimizerAttribute("CPX_PARAM_ITLIM"), Int(maxiters))
elseif contains(optimizer_name, "SCIP") || contains(optimizer_name, "Scip")
MOI.set(optimizer, MOI.RawOptimizerAttribute("limits/iterations"), Int(maxiters))
elseif contains(optimizer_name, "Mosek") || contains(optimizer_name, "MOSEK")
MOI.set(optimizer, MOI.RawOptimizerAttribute("MSK_IPAR_INTPNT_MAX_ITERATIONS"), Int(maxiters))
elseif contains(optimizer_name, "OSQP")
MOI.set(optimizer, MOI.RawOptimizerAttribute("max_iter"), Int(maxiters))
elseif contains(optimizer_name, "ECOS")
MOI.set(optimizer, MOI.RawOptimizerAttribute("maxit"), Int(maxiters))
elseif contains(optimizer_name, "SCS")
MOI.set(optimizer, MOI.RawOptimizerAttribute("max_iters"), Int(maxiters))
elseif contains(optimizer_name, "COSMO")
MOI.set(optimizer, MOI.RawOptimizerAttribute("max_iter"), Int(maxiters))
else
# Generic fallback - try common parameter names
for param_name in ["max_iter", "maxiter", "IterationLimit", "max_iterations"]
try
MOI.set(optimizer, MOI.RawOptimizerAttribute(param_name), Int(maxiters))
return # Success, exit early
catch
continue # Try next parameter name
end
end
# If all attempts fail, show warning with guidance
@warn "common maxiters argument could not be mapped for $(typeof(optimizer)). " *
"Set number of iterations via optimizer specific keyword arguments."
end
catch e
# Catch any errors during parameter setting and show informative warning
@warn "Failed to set maxiters parameter for $(typeof(optimizer)): $(e). " *
"Set number of iterations via optimizer specific keyword arguments."
end
end

function __map_optimizer_args(cache,
opt::Union{MOI.AbstractOptimizer, MOI.OptimizerWithAttributes
};
Expand All @@ -82,7 +132,7 @@ function __map_optimizer_args(cache,
@warn "common abstol argument is currently not used by $(optimizer). Set tolerances via optimizer specific keyword arguments."
end
if !isnothing(maxiters)
@warn "common maxiters argument is currently not used by $(optimizer). Set number of iterations via optimizer specific keyword arguments."
_set_maxiters!(optimizer, maxiters)
end
return optimizer
end
Expand Down
37 changes: 37 additions & 0 deletions lib/OptimizationMOI/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,40 @@ end
prob = OptimizationProblem(optprob, x0, _p, lcons = [1.0, 0.5], ucons = [1.0, 0.5])
sol = solve(prob, Ipopt.Optimizer())
end

@testset "common maxiters interface" begin
# Test that the common maxiters interface works without warnings
rosenbrock(x, p) = (p[1] - x[1])^2 + p[2] * (x[2] - x[1]^2)^2
x0 = zeros(2)
_p = [1.0, 100.0]

optprob = OptimizationFunction(rosenbrock, Optimization.AutoZygote())
prob = OptimizationProblem(optprob, x0, _p)

# Test with Ipopt using maxiters parameter
@testset "Ipopt maxiters" begin
# This should not produce a warning and should respect the iteration limit
sol = solve(prob, Ipopt.Optimizer(); maxiters = 5, print_level = 0)
# Should terminate due to iteration limit
@test sol.stats.iterations <= 5
end

# Test with cache interface
@testset "Cache interface maxiters" begin
cache = init(prob, Ipopt.Optimizer(); maxiters = 3, print_level = 0)
sol = solve!(cache)
@test sol.stats.iterations <= 3
end

# Test that unknown solver fallback works gracefully
@testset "Generic fallback" begin
# Mock optimizer that doesn't match any known pattern
struct MockOptimizer <: MathOptInterface.AbstractOptimizer end

# This should not error, but may show a warning for unknown solver
mock_opt = MockOptimizer()
# We can't actually solve with this mock optimizer, but we can test
# that the parameter setting doesn't crash
@test_nowarn OptimizationMOI._set_maxiters!(mock_opt, 10)
end
end
Loading