diff --git a/lib/OptimizationMOI/Project.toml b/lib/OptimizationMOI/Project.toml index 5ce8950a9..950913362 100644 --- a/lib/OptimizationMOI/Project.toml +++ b/lib/OptimizationMOI/Project.toml @@ -1,6 +1,6 @@ name = "OptimizationMOI" uuid = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" -version = "1.3.1" +version = "1.3.2" authors = ["Vaibhav Dixit and contributors"] [deps] @@ -37,6 +37,7 @@ ModelingToolkitBase = "1" NLopt = "1" OptimizationBase = "5.1" Reexport = "1.2" +ReverseDiff = "1" SciMLBase = "2.130, 3" SciMLStructures = "1" SparseArrays = "1.6" diff --git a/lib/OptimizationMOI/src/OptimizationMOI.jl b/lib/OptimizationMOI/src/OptimizationMOI.jl index fe51c14f6..b59c1360c 100644 --- a/lib/OptimizationMOI/src/OptimizationMOI.jl +++ b/lib/OptimizationMOI/src/OptimizationMOI.jl @@ -88,7 +88,6 @@ function _create_new_optimizer(opt::MOI.AbstractOptimizer) end function __map_optimizer_args( - cache, opt::Union{ MOI.AbstractOptimizer, MOI.OptimizerWithAttributes, }; @@ -96,8 +95,12 @@ function __map_optimizer_args( maxtime::Union{Number, Nothing} = nothing, abstol::Union{Number, Nothing} = nothing, reltol::Union{Number, Nothing} = nothing, + verbose = OptimizationBase.DEFAULT_VERBOSE, kwargs... ) + # `verbose` is consumed here as a named keyword so it isn't forwarded into the + # `kwargs` loop below, which would otherwise try to set it as a raw solver attribute. + opt_verbose = OptimizationBase._process_verbose_param(verbose) optimizer = _create_new_optimizer(opt) for (key, value) in kwargs MOI.set(optimizer, MOI.RawOptimizerAttribute("$(key)"), value) @@ -108,19 +111,19 @@ function __map_optimizer_args( if !isnothing(reltol) @SciMLMessage( lazy"common reltol argument is currently not used by $(optimizer). Set tolerances via optimizer specific keyword arguments.", - cache.verbose, :unsupported_kwargs + opt_verbose, :unsupported_kwargs ) end if !isnothing(abstol) @SciMLMessage( lazy"common abstol argument is currently not used by $(optimizer). Set tolerances via optimizer specific keyword arguments.", - cache.verbose, :unsupported_kwargs + opt_verbose, :unsupported_kwargs ) end if !isnothing(maxiters) @SciMLMessage( lazy"common maxiters argument is currently not used by $(optimizer). Set number of iterations via optimizer specific keyword arguments.", - cache.verbose, :unsupported_kwargs + opt_verbose, :unsupported_kwargs ) end return optimizer diff --git a/lib/OptimizationMOI/src/moi.jl b/lib/OptimizationMOI/src/moi.jl index 442c559d3..e30f9fe99 100644 --- a/lib/OptimizationMOI/src/moi.jl +++ b/lib/OptimizationMOI/src/moi.jl @@ -101,7 +101,6 @@ function SciMLBase.__solve(cache::MOIOptimizationCache) maxiters = OptimizationBase._check_and_convert_maxiters(cache.solver_args.maxiters) maxtime = OptimizationBase._check_and_convert_maxtime(cache.solver_args.maxtime) opt_setup = __map_optimizer_args( - cache, cache.opt; abstol = cache.solver_args.abstol, reltol = cache.solver_args.reltol, diff --git a/lib/OptimizationMOI/src/nlp.jl b/lib/OptimizationMOI/src/nlp.jl index 87d7bc97c..18a06e280 100644 --- a/lib/OptimizationMOI/src/nlp.jl +++ b/lib/OptimizationMOI/src/nlp.jl @@ -555,7 +555,6 @@ function SciMLBase.__solve(cache::MOIOptimizationNLPCache) maxiters = OptimizationBase._check_and_convert_maxiters(cache.solver_args.maxiters) maxtime = OptimizationBase._check_and_convert_maxtime(cache.solver_args.maxtime) opt_setup = __map_optimizer_args( - cache, cache.opt; abstol = cache.solver_args.abstol, reltol = cache.solver_args.reltol, diff --git a/lib/OptimizationMOI/test/core_tests.jl b/lib/OptimizationMOI/test/core_tests.jl index 9cc5baac8..f3c23da5f 100644 --- a/lib/OptimizationMOI/test/core_tests.jl +++ b/lib/OptimizationMOI/test/core_tests.jl @@ -267,6 +267,26 @@ end end end +@testset "verbose kwarg" begin + rosenbrock(x, p) = (p[1] - x[1])^2 + p[2] * (x[2] - x[1]^2)^2 + optprob = OptimizationFunction(rosenbrock, AutoZygote()) + prob = OptimizationProblem(optprob, zeros(2), [1.0, 100.0]; lb = [-5.0, -5.0], ub = [5.0, 5.0]) + + # `verbose` must be consumed as a verbosity setting, not forwarded as a raw + # MOI optimizer attribute. + sol = solve(prob, Ipopt.Optimizer(); verbose = OptimizationVerbosity()) + @test sol.retcode == ReturnCode.Success + + # Passing a common tolerance triggers the `:unsupported_kwargs` message path, + # which previously read a nonexistent `cache.verbose` field and threw. + sol = solve(prob, Ipopt.Optimizer(); abstol = 1.0e-8, verbose = OptimizationVerbosity()) + @test sol.retcode == ReturnCode.Success + + # A `Bool` verbosity must also be accepted. + sol = solve(prob, Ipopt.Optimizer(); abstol = 1.0e-8, verbose = true) + @test sol.retcode == ReturnCode.Success +end + @testset "MOI" begin @parameters c = 0.0 @variables x[1:2] = [0.0, 0.0] [bounds = ([c, c], [Inf, Inf])] diff --git a/lib/OptimizationMOI/test/qa/qa.jl b/lib/OptimizationMOI/test/qa/qa.jl index 337143cb7..67241f196 100644 --- a/lib/OptimizationMOI/test/qa/qa.jl +++ b/lib/OptimizationMOI/test/qa/qa.jl @@ -2,7 +2,17 @@ using OptimizationMOI, Aqua, JET using Test @testset "Aqua" begin - Aqua.test_all(OptimizationMOI) + # OptimizationMOI deliberately extends SciMLBase's solver-trait interface onto the + # MathOptInterface optimizer types, so those methods are intentional, not piracy. + Aqua.test_all( + OptimizationMOI; + piracies = ( + treat_as_own = [ + OptimizationMOI.MOI.AbstractOptimizer, + OptimizationMOI.MOI.OptimizerWithAttributes, + ], + ) + ) end @testset "JET static analysis" begin