MathOptInterface.jl icon indicating copy to clipboard operation
MathOptInterface.jl copied to clipboard

isapprox for a VectorNonlinearFunction after simple bridge modifications

Open DimitriAlston opened this issue 1 year ago • 4 comments

I am working on adding support for MOI.ScalarNonlinearFunction to EAGO and I am running into a particular error with some tests.

I cannot provide a MWE since I have not pushed any of my changes yet, but this is how I am running my tests.

import EAGO
import MathOptInterface as MOI

model = MOI.instantiate(
        EAGO.Optimizer;
        with_bridge_type = Float64,
        with_cache_type = Float64,
)
config = MOI.Test.Config(;
             atol = 1e-3,
             rtol = 1e-3,
             exclude = Any[
                 MOI.DualObjectiveValue,
                 MOI.ConstraintBasisStatus,
                 MOI.VariableBasisStatus,
                 MOI.ConstraintDual,
             ],
)
MOI.empty!(model)

MOI.Test.test_basic_VectorNonlinearFunction_CountAtLeast(model, config)

I have excluded the other tests (which are all test_basic_VectorNonlinearFunction_) in this example, but they all return the same error.

ERROR: MethodError: no method matching filter_variables(::MathOptInterface.Utilities.var"#17#18"{Set{MathOptInterface.VariableIndex}}, ::MathOptInterface.VectorNonlinearFunction)
Closest candidates are:
  filter_variables(::F, ::Any, ::Any) where F<:Function
  filter_variables(::Function, ::MathOptInterface.VariableIndex)
  filter_variables(::Function, ::MathOptInterface.VectorOfVariables)
  ...

The way I implemented MOI.ScalarNonlinearFunction is similar to Optim.jl https://github.com/JuliaNLSolvers/Optim.jl/blob/658c39a316bda911cddf442702b11bf086f1b5ba/ext/OptimMOIExt.jl where the functions are added to a MOI.Nonlinear.Model and then used to create a MOI.Nonlinear.Evaluator and MOI.NLPBlockData to continue using the old nonlinear interface.

I am not sure if this issue is because I am not directly storing the functions or something else, but I figured I would check here first for insight before moving forward. Thanks in advance.

DimitriAlston avatar Oct 08 '24 19:10 DimitriAlston

Yeah I hit a bunch of these with KNITRO, but MINLP solvers are pretty rare so I haven't spent much time on them :smile:

Just exclude: https://github.com/jump-dev/KNITRO.jl/blob/7aae642b14973654e6b52341be7b079edb36c4ab/test/MOI_wrapper.jl#L67-L87

odow avatar Oct 08 '24 20:10 odow

Fair enough, I'll exclude those for now. Thanks!

DimitriAlston avatar Oct 08 '24 21:10 DimitriAlston

The remaining issues are due to isapprox:

test_basic_VectorNonlinearFunction_HyperRectangle: Test Failed at /home/blegat/.julia/dev/MathOptInterface/src/Test/test_basic_constraint.jl:262
  Expression: isapprox(MOI.Utilities.canonical(f), constraint_function, config)
   Evaluated: isapprox(┌                                                                                                           ┐
│+(-(+(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))), 0.0), 0.0)│
│+(-(+(MOI.VariableIndex(1)), 0.0), 0.0)                                                                    │
│+(-(+(MOI.VariableIndex(2)), 0.0), 0.0)                                                                    │
└                                                                                                           ┘, ┌                                                                                           ┐
│+(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2)))│
│+(MOI.VariableIndex(1))                                                                    │
│+(MOI.VariableIndex(2))                                                                    │
│+(MOI.VariableIndex(3))                                                                    │
└                                                                                           ┘, MathOptInterface.Test.Config{Float64}(0.001, 0.001, MathOptInterface.OPTIMAL, MathOptInterface.INFEASIBLE, Any[MathOptInterface.DualObjectiveValue, MathOptInterface.ConstraintBasisStatus, MathOptInterface.VariableBasisStatus, MathOptInterface.ConstraintDual]))

Stacktrace:
 [1] macro expansion
   @ ~/.julia/juliaup/julia-1.11.0+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:679 [inlined]
 [2] _basic_constraint_test_helper(model::MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{EAGO.Optimizer{EAGO.Incremental{Cbc.Optimizer}, EAGO.Incremental{Ipopt.Optimizer}, EAGO.DefaultExt}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, config::MathOptInterface.Test.Config{Float64}, ::Type{MathOptInterface.VectorNonlinearFunction}, ::Type{MathOptInterface.HyperRectangle})
   @ MathOptInterface.Test ~/.julia/dev/MathOptInterface/src/Test/test_basic_constraint.jl:262
 [3] test_basic_VectorNonlinearFunction_HyperRectangle(model::MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{EAGO.Optimizer{EAGO.Incremental{Cbc.Optimizer}, EAGO.Incremental{Ipopt.Optimizer}, EAGO.DefaultExt}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, config::MathOptInterface.Test.Config{Float64})
   @ MathOptInterface.Test ~/.julia/dev/MathOptInterface/src/Test/test_basic_constraint.jl:396
 [4] macro expansion
   @ ~/.julia/dev/MathOptInterface/src/Test/Test.jl:272 [inlined]
 [5] macro expansion
   @ ~/.julia/juliaup/julia-1.11.0+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:1700 [inlined]
 [6] runtests(model::MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{EAGO.Optimizer{EAGO.Incremental{Cbc.Optimizer}, EAGO.Incremental{Ipopt.Optimizer}, EAGO.DefaultExt}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, config::MathOptInterface.Test.Config{Float64}; include::Vector{String}, exclude::Vector{String}, warn_unsupported::Bool, verbose::Bool, exclude_tests_after::VersionNumber)
   @ MathOptInterface.Test ~/.julia/dev/MathOptInterface/src/Test/Test.jl:267
test_basic_VectorNonlinearFunction_HyperRectangle: Test Failed at /home/blegat/.julia/dev/MathOptInterface/src/Test/test_basic_constraint.jl:263
  Expression: isapprox(MOI.get(model, MOI.CanonicalConstraintFunction(), c), constraint_function, config)
   Evaluated: isapprox(┌                                                                                                           ┐
│+(-(+(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))), 0.0), 0.0)│
│+(-(+(MOI.VariableIndex(1)), 0.0), 0.0)                                                                    │
│+(-(+(MOI.VariableIndex(2)), 0.0), 0.0)                                                                    │
└                                                                                                           ┘, ┌                                                                                           ┐
│+(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2)))│
│+(MOI.VariableIndex(1))                                                                    │
│+(MOI.VariableIndex(2))                                                                    │
│+(MOI.VariableIndex(3))                                                                    │
└                                                                                           ┘, MathOptInterface.Test.Config{Float64}(0.001, 0.001, MathOptInterface.OPTIMAL, MathOptInterface.INFEASIBLE, Any[MathOptInterface.DualObjectiveValue, MathOptInterface.ConstraintBasisStatus, MathOptInterface.VariableBasisStatus, MathOptInterface.ConstraintDual]))

Stacktrace:
 [1] macro expansion
   @ ~/.julia/juliaup/julia-1.11.0+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:679 [inlined]
 [2] _basic_constraint_test_helper(model::MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{EAGO.Optimizer{EAGO.Incremental{Cbc.Optimizer}, EAGO.Incremental{Ipopt.Optimizer}, EAGO.DefaultExt}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, config::MathOptInterface.Test.Config{Float64}, ::Type{MathOptInterface.VectorNonlinearFunction}, ::Type{MathOptInterface.HyperRectangle})
   @ MathOptInterface.Test ~/.julia/dev/MathOptInterface/src/Test/test_basic_constraint.jl:263
 [3] test_basic_VectorNonlinearFunction_HyperRectangle(model::MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{EAGO.Optimizer{EAGO.Incremental{Cbc.Optimizer}, EAGO.Incremental{Ipopt.Optimizer}, EAGO.DefaultExt}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, config::MathOptInterface.Test.Config{Float64})
   @ MathOptInterface.Test ~/.julia/dev/MathOptInterface/src/Test/test_basic_constraint.jl:396
 [4] macro expansion
   @ ~/.julia/dev/MathOptInterface/src/Test/Test.jl:272 [inlined]
 [5] macro expansion
   @ ~/.julia/juliaup/julia-1.11.0+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:1700 [inlined]
 [6] runtests(model::MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{EAGO.Optimizer{EAGO.Incremental{Cbc.Optimizer}, EAGO.Incremental{Ipopt.Optimizer}, EAGO.DefaultExt}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, config::MathOptInterface.Test.Config{Float64}; include::Vector{String}, exclude::Vector{String}, warn_unsupported::Bool, verbose::Bool, exclude_tests_after::VersionNumber)
   @ MathOptInterface.Test ~/.julia/dev/MathOptInterface/src/Test/Test.jl:267
test_basic_VectorNonlinearFunction_NormInfinityCone: Test Failed at /home/blegat/.julia/dev/MathOptInterface/src/Test/test_basic_constraint.jl:262
  Expression: isapprox(MOI.Utilities.canonical(f), constraint_function, config)
   Evaluated: isapprox(┌                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      ┐
│/(+(+(-(+(MOI.VariableIndex(1), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2)))), +(-(+(MOI.VariableIndex(2), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2)))), +(-(+(MOI.VariableIndex(3), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2)))), +(MOI.VariableIndex(1), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2)))), +(MOI.VariableIndex(2), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2)))), +(MOI.VariableIndex(3), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))), 6.0)│
│/(-(+(MOI.VariableIndex(1), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2)))), +(-(+(MOI.VariableIndex(1), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))), 2.0)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              │
│/(-(+(MOI.VariableIndex(2), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2)))), +(-(+(MOI.VariableIndex(2), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))), 2.0)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              │
│/(-(+(MOI.VariableIndex(3), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2)))), +(-(+(MOI.VariableIndex(3), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))), 2.0)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              │
└                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      ┘, ┌                                                                                                                    ┐
│+(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2)))                         │
│+(MOI.VariableIndex(1), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))│
│+(MOI.VariableIndex(2), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))│
│+(MOI.VariableIndex(3), +(^(MOI.VariableIndex(1), (2)), ^(MOI.VariableIndex(2), (2)), ^(MOI.VariableIndex(3), (2))))│
└                                                                                                                    ┘, MathOptInterface.Test.Config{Float64}(0.001, 0.001, MathOptInterface.OPTIMAL, MathOptInterface.INFEASIBLE, Any[MathOptInterface.DualObjectiveValue, MathOptInterface.ConstraintBasisStatus, MathOptInterface.VariableBasisStatus, MathOptInterface.ConstraintDual]))

Stacktrace:
 [1] macro expansion
   @ ~/.julia/juliaup/julia-1.11.0+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:679 [inlined]
 [2] _basic_constraint_test_helper(model::MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{EAGO.Optimizer{EAGO.Incremental{Cbc.Optimizer}, EAGO.Incremental{Ipopt.Optimizer}, EAGO.DefaultExt}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, config::MathOptInterface.Test.Config{Float64}, ::Type{MathOptInterface.VectorNonlinearFunction}, ::Type{MathOptInterface.NormInfinityCone})
   @ MathOptInterface.Test ~/.julia/dev/MathOptInterface/src/Test/test_basic_constraint.jl:262
 [3] test_basic_VectorNonlinearFunction_NormInfinityCone(model::MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{EAGO.Optimizer{EAGO.Incremental{Cbc.Optimizer}, EAGO.Incremental{Ipopt.Optimizer}, EAGO.DefaultExt}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, config::MathOptInterface.Test.Config{Float64})
   @ MathOptInterface.Test ~/.julia/dev/MathOptInterface/src/Test/test_basic_constraint.jl:396
 [4] macro expansion
   @ ~/.julia/dev/MathOptInterface/src/Test/Test.jl:272 [inlined]
 [5] macro expansion
   @ ~/.julia/juliaup/julia-1.11.0+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:1700 [inlined]
 [6] runtests(model::MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{EAGO.Optimizer{EAGO.Incremental{Cbc.Optimizer}, EAGO.Incremental{Ipopt.Optimizer}, EAGO.DefaultExt}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, config::MathOptInterface.Test.Config{Float64}; include::Vector{String}, exclude::Vector{String}, warn_unsupported::Bool, verbose::Bool, exclude_tests_after::VersionNumber)
   @ MathOptInterface.Test ~/.julia/dev/MathOptInterface/src/Test/Test.jl:267

blegat avatar Oct 09 '24 09:10 blegat

Yeah I think this is where I got stuck and gave up with Xpress...

odow avatar Oct 09 '24 18:10 odow

So MOI.Nonlinear.SymbolicAD.simplify gets us some of the way there, but not for bridges like this:

https://github.com/jump-dev/MathOptInterface.jl/blob/927c58bb2e157813c3d71de754c8bd07374963b6/src/Bridges/Constraint/bridges/NormInfinityBridge.jl#L57-L86

because we don't detect that ((t + x) - (t - x)) / 2 == x.

I wonder if we just need to simplify each problematic bridge?

odow avatar Mar 04 '25 23:03 odow

using Test
import MathOptInterface as MOI
config = MOI.Test.Config()

@testset "All" begin
    @testset "SplitHyperRectangle" begin
        model = MOI.Bridges.Constraint.SplitHyperRectangle{Float64}(MOI.Utilities.Model{Float64}())
        MOI.Test.test_basic_VectorNonlinearFunction_HyperRectangle(model, config)
    end
    @testset "NormInfinity" begin
        model = MOI.Bridges.Constraint.NormInfinity{Float64}(MOI.Utilities.Model{Float64}())
        MOI.Test.test_basic_VectorNonlinearFunction_NormInfinityCone(model, config)
    end
    @testset "NormOne" begin
        model = MOI.Bridges.Constraint.NormOne{Float64}(MOI.Utilities.Model{Float64}())
        MOI.Test.test_basic_VectorNonlinearFunction_NormOneCone(model, config)
    end
end

odow avatar Mar 04 '25 23:03 odow

I'm not actually sure we should try to make this work.

https://github.com/jump-dev/MathOptInterface.jl/blob/927c58bb2e157813c3d71de754c8bd07374963b6/src/Bridges/Constraint/bridges/NormOneBridge.jl#L111-L134

Is it reasonable to be able to simplify this?

odow avatar Mar 05 '25 23:03 odow