Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failure with CachingOptimizer #2384

Closed
blegat opened this issue Jan 2, 2024 · 3 comments · Fixed by #2396
Closed

Failure with CachingOptimizer #2384

blegat opened this issue Jan 2, 2024 · 3 comments · Fixed by #2396
Milestone

Comments

@blegat
Copy link
Member

blegat commented Jan 2, 2024

This seems like a bug in CachingOptimizer or LazyBridgeOptimizer. CSDP passes the tests without this additional cache and when adding this cache (which gives the setting of the JuMP backend, we have a failure). It seems SDPA has the same issue since it's failing on the same tests with the same wrong answers 6 and 0.

using Test
import MathOptInterface as MOI
import CSDP

function tests(cache)
    model = MOI.instantiate(CSDP.Optimizer, with_bridge_type = Float64)
    if cache
        model = MOI.Utilities.CachingOptimizer(
            MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
            model,
        )
    end
    MOI.set(model, MOI.Silent(), true)
    MOI.Test.runtests(
      model,
      MOI.Test.Config(
          rtol = 1e-3,
          atol = 1e-3,
          exclude = Any[
              MOI.ConstraintBasisStatus,
              MOI.VariableBasisStatus,
              MOI.ObjectiveBound,
              MOI.SolverVersion,
          ],
      ),
      include = Regex[
          r"test_variable_solve_with_lowerbound$",
          r"test_modification_delete_variables_in_a_batch$",
      ],
    )
end

@testset "$cache" for cache in [false, true]
   tests(cache)
end

produces

Test Summary: | Pass  Total  Time
false         |   18     18  0.9s
test_modification_delete_variables_in_a_batch: Test Failed at /home/blegat/.julia/packages/MathOptInterface/vrZOD/src/Test/test_modification.jl:827
  Expression: isapprox(MOI.get(model, MOI.ObjectiveValue()), T(2), config)
   Evaluated: isapprox(6.0, 2.0, MathOptInterface.Test.Config{Float64}(0.001, 0.001, MathOptInterface.OPTIMAL, MathOptInterface.INFEASIBLE, Any[MathOptInterface.ConstraintBasisStatus, MathOptInterface.VariableBasisStatus, MathOptInterface.ObjectiveBound, MathOptInterface.SolverVersion]))

Stacktrace:
 [1] macro expansion
   @ ~/.julia/juliaup/julia-1.10.0+0.x64.linux.gnu/share/julia/stdlib/v1.10/Test/src/Test.jl:672 [inlined]
 [2] test_modification_delete_variables_in_a_batch(model::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{CSDP.Optimizer, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, config::MathOptInterface.Test.Config{Float64})
   @ MathOptInterface.Test ~/.julia/packages/MathOptInterface/vrZOD/src/Test/test_modification.jl:827
 [3] macro expansion
   @ ~/.julia/packages/MathOptInterface/vrZOD/src/Test/Test.jl:272 [inlined]
 [4] macro expansion
   @ ~/.julia/juliaup/julia-1.10.0+0.x64.linux.gnu/share/julia/stdlib/v1.10/Test/src/Test.jl:1577 [inlined]
 [5] runtests(model::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{CSDP.Optimizer, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, config::MathOptInterface.Test.Config{Float64}; include::Vector{Regex}, exclude::Vector{String}, warn_unsupported::Bool, verbose::Bool, exclude_tests_after::VersionNumber)
   @ MathOptInterface.Test ~/.julia/packages/MathOptInterface/vrZOD/src/Test/Test.jl:267
test_variable_solve_with_lowerbound: Test Failed at /home/blegat/.julia/packages/MathOptInterface/vrZOD/src/Test/Test.jl:462
  Expression: isapprox(MOI.get(model, MOI.ConstraintPrimal(), index), solution_value, config)
   Evaluated: isapprox(9.973062820023415e-9, 1.0, MathOptInterface.Test.Config{Float64}(0.001, 0.001, MathOptInterface.OPTIMAL, MathOptInterface.INFEASIBLE, Any[MathOptInterface.ConstraintBasisStatus, MathOptInterface.VariableBasisStatus, MathOptInterface.ObjectiveBound, MathOptInterface.SolverVersion]))

Stacktrace:
 [1] macro expansion
   @ ~/.julia/juliaup/julia-1.10.0+0.x64.linux.gnu/share/julia/stdlib/v1.10/Test/src/Test.jl:672 [inlined]
 [2] _test_model_solution(model::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{CSDP.Optimizer, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, config::MathOptInterface.Test.Config{Float64}; objective_value::Float64, variable_primal::Vector{Tuple{MathOptInterface.VariableIndex, Float64}}, constraint_primal::Vector{Tuple{MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex}, Float64}}, constraint_dual::Vector{Tuple{MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex}, Float64}})
   @ MathOptInterface.Test ~/.julia/packages/MathOptInterface/vrZOD/src/Test/Test.jl:462
 [3] _test_model_solution
   @ ~/.julia/packages/MathOptInterface/vrZOD/src/Test/Test.jl:427 [inlined]
 [4] test_variable_solve_with_lowerbound(model::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{CSDP.Optimizer, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, config::MathOptInterface.Test.Config{Float64})
   @ MathOptInterface.Test ~/.julia/packages/MathOptInterface/vrZOD/src/Test/test_variable.jl:244
 [5] macro expansion
   @ ~/.julia/packages/MathOptInterface/vrZOD/src/Test/Test.jl:272 [inlined]
 [6] macro expansion
   @ ~/.julia/juliaup/julia-1.10.0+0.x64.linux.gnu/share/julia/stdlib/v1.10/Test/src/Test.jl:1577 [inlined]
 [7] runtests(model::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{MathOptInterface.Utilities.CachingOptimizer{CSDP.Optimizer, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, config::MathOptInterface.Test.Config{Float64}; include::Vector{Regex}, exclude::Vector{String}, warn_unsupported::Bool, verbose::Bool, exclude_tests_after::VersionNumber)
   @ MathOptInterface.Test ~/.julia/packages/MathOptInterface/vrZOD/src/Test/Test.jl:267
Test Summary:                                   | Pass  Fail  Total  Time
true                                            |   16     2     18  0.7s
  test_modification_delete_variables_in_a_batch |    7     1      8  0.7s
  test_variable_solve_with_lowerbound           |    9     1     10  0.0s
ERROR: LoadError: Some tests did not pass: 16 passed, 2 failed, 0 errored, 0 broken.
in expression starting at /home/blegat/.julia/dev/CSDP/test/bug2.jl:33
@blegat blegat transferred this issue from jump-dev/CSDP.jl Jan 2, 2024
@odow
Copy link
Member

odow commented Jan 5, 2024

Here's a reproducible example:

julia> import MathOptInterface as MOI

julia> import CSDP

julia> inner = MOI.instantiate(CSDP.Optimizer; with_bridge_type = Float64)
MOIB.LazyBridgeOptimizer{MOIU.CachingOptimizer{CSDP.Optimizer, MOIU.UniversalFallback{MOIU.Model{Float64}}}}
with 0 variable bridges
with 0 constraint bridges
with 0 objective bridges
with inner model MOIU.CachingOptimizer{CSDP.Optimizer, MOIU.UniversalFallback{MOIU.Model{Float64}}}
  in state EMPTY_OPTIMIZER
  in mode AUTOMATIC
  with model cache MOIU.UniversalFallback{MOIU.Model{Float64}}
    fallback for MOIU.Model{Float64}
  with optimizer CSDP.Optimizer

julia> MOI.set(inner, MOI.Silent(), true)

julia> model = MOI.Utilities.CachingOptimizer(MOI.Utilities.Model{Float64}(), inner)
MOIU.CachingOptimizer{MOIB.LazyBridgeOptimizer{MOIU.CachingOptimizer{CSDP.Optimizer, MOIU.UniversalFallback{MOIU.Model{Float64}}}}, MOIU.Model{Float64}}
in state EMPTY_OPTIMIZER
in mode AUTOMATIC
with model cache MOIU.Model{Float64}
with optimizer MOIB.LazyBridgeOptimizer{MOIU.CachingOptimizer{CSDP.Optimizer, MOIU.UniversalFallback{MOIU.Model{Float64}}}}
  with 0 variable bridges
  with 0 constraint bridges
  with 0 objective bridges
  with inner model MOIU.CachingOptimizer{CSDP.Optimizer, MOIU.UniversalFallback{MOIU.Model{Float64}}}
    in state EMPTY_OPTIMIZER
    in mode AUTOMATIC
    with model cache MOIU.UniversalFallback{MOIU.Model{Float64}}
      with 1 optimizer attribute
      fallback for MOIU.Model{Float64}
    with optimizer CSDP.Optimizer

julia> x = MOI.add_variables(model, 2)
2-element Vector{MathOptInterface.VariableIndex}:
 MOI.VariableIndex(1)
 MOI.VariableIndex(2)

julia> MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)

julia> f = 1.0 * x[1] + 2.0 * x[2]
0.0 + 1.0 MOI.VariableIndex(1) + 2.0 MOI.VariableIndex(2)

julia> MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)

julia> MOI.add_constraint.(model, x, MOI.GreaterThan(1.0))
2-element Vector{MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.GreaterThan{Float64}}}:
 MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.GreaterThan{Float64}}(1)
 MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.GreaterThan{Float64}}(2)

julia> MOI.optimize!(model)

julia> MOI.delete(model, x[1])

julia> MOI.optimize!(model)

julia> print(inner)
Minimize ScalarAffineFunction{Float64}:
 1.0 + 2.0 v[-2]

Subject to:

VariableIndex-in-GreaterThan{Float64}
 v[-2] >= 1.0

julia> @assert isapprox(MOI.get(model, MOI.ObjectiveValue()), 2.0)
ERROR: AssertionError: isapprox(MOI.get(model, MOI.ObjectiveValue()), 2.0)
Stacktrace:
 [1] top-level scope
   @ REPL[283]:1

Note that the objective of inner is 1.0 + 2.0 v[-2] not 0.0 + 2.0 v[-2].

@odow
Copy link
Member

odow commented Jan 8, 2024

The other issue is:

julia> using Test, Revise

julia> import MathOptInterface as MOI

julia> import CSDP

julia> inner = MOI.instantiate(CSDP.Optimizer, with_bridge_type = Float64)
MOIB.LazyBridgeOptimizer{MOIU.CachingOptimizer{CSDP.Optimizer, MOIU.UniversalFallback{MOIU.Model{Float64}}}}
with 0 variable bridges
with 0 constraint bridges
with 0 objective bridges
with inner model MOIU.CachingOptimizer{CSDP.Optimizer, MOIU.UniversalFallback{MOIU.Model{Float64}}}
  in state EMPTY_OPTIMIZER
  in mode AUTOMATIC
  with model cache MOIU.UniversalFallback{MOIU.Model{Float64}}
    fallback for MOIU.Model{Float64}
  with optimizer CSDP.Optimizer

julia> model = MOI.Utilities.CachingOptimizer(MOI.Utilities.Model{Float64}(), inner)
MOIU.CachingOptimizer{MOIB.LazyBridgeOptimizer{MOIU.CachingOptimizer{CSDP.Optimizer, MOIU.UniversalFallback{MOIU.Model{Float64}}}}, MOIU.Model{Float64}}
in state EMPTY_OPTIMIZER
in mode AUTOMATIC
with model cache MOIU.Model{Float64}
with optimizer MOIB.LazyBridgeOptimizer{MOIU.CachingOptimizer{CSDP.Optimizer, MOIU.UniversalFallback{MOIU.Model{Float64}}}}
  with 0 variable bridges
  with 0 constraint bridges
  with 0 objective bridges
  with inner model MOIU.CachingOptimizer{CSDP.Optimizer, MOIU.UniversalFallback{MOIU.Model{Float64}}}
    in state EMPTY_OPTIMIZER
    in mode AUTOMATIC
    with model cache MOIU.UniversalFallback{MOIU.Model{Float64}}
      fallback for MOIU.Model{Float64}
    with optimizer CSDP.Optimizer

julia> x = MOI.add_variable(model)
MOI.VariableIndex(1)

julia> c1 = MOI.add_constraint(model, x, MOI.GreaterThan(1.0))
MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.GreaterThan{Float64}}(1)

julia> c2 = MOI.add_constraint(model, x, MOI.LessThan(1.0))
MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.LessThan{Float64}}(1)

julia> MOI.optimize!(model)
CSDP 6.2.0
This is a pure primal feasibility problem.
This is a pure dual feasibility problem.
Iter:  0 Ap: 0.00e+00 Pobj:  0.0000000e+00 Ad: 0.00e+00 Dobj:  0.0000000e+00 
Iter:  1 Ap: 9.00e-01 Pobj:  0.0000000e+00 Ad: 1.00e+00 Dobj:  0.0000000e+00 
Success: SDP solved
Primal objective value: 0.0000000e+00 
Dual objective value: 0.0000000e+00 
Relative primal infeasibility: 5.00e-11 
Relative dual infeasibility: 0.00e+00 
Real Relative Gap: 0.00e+00 
XZ Relative Gap: 7.50e-10 
DIMACS error measures: 5.00e-11 0.00e+00 0.00e+00 0.00e+00 0.00e+00 7.50e-10

julia> MOI.get(model, MOI.VariablePrimal(), x)
1.000000000025

julia> MOI.get(model, MOI.ConstraintPrimal(), c1)
1.000000000025

julia> MOI.get(model, MOI.ConstraintPrimal(), c2)
-2.5e-11

The last value is 0 instead of 1.

@odow
Copy link
Member

odow commented Jan 8, 2024

Simplified to one MOIB.Variable.VectorizeBridge:

julia> import MathOptInterface as MOI

julia> import CSDP

julia> cache = MOI.Utilities.Model{Float64}();

julia> x, c1 = MOI.add_constrained_variable(cache, MOI.GreaterThan(1.0));

julia> c2 = MOI.add_constraint(cache, 1.0x, MOI.EqualTo(2.0));

julia> model = MOI.instantiate(CSDP.Optimizer, with_bridge_type = Float64);

julia> index_map, _ = MOI.optimize!(model, cache);
CSDP 6.2.0
This is a pure primal feasibility problem.
Iter:  0 Ap: 0.00e+00 Pobj:  0.0000000e+00 Ad: 0.00e+00 Dobj:  0.0000000e+00 
Iter:  1 Ap: 1.00e+00 Pobj:  0.0000000e+00 Ad: 1.00e+00 Dobj:  9.0000000e+00 
Success: SDP solved
Primal objective value: 0.0000000e+00 
Dual objective value: 0.0000000e+00 
Relative primal infeasibility: 0.00e+00 
Relative dual infeasibility: 5.00e-11 
Real Relative Gap: 0.00e+00 
XZ Relative Gap: 5.00e-11 
DIMACS error measures: 0.00e+00 0.00e+00 5.00e-11 0.00e+00 0.00e+00 5.00e-11

julia> MOI.get(model, MOI.VariablePrimal(), index_map[x])
2.0

julia> MOI.get(model, MOI.ConstraintPrimal(), index_map[c1])
2.0

julia> MOI.get(model, MOI.ConstraintPrimal(), index_map[c2])
1.0

julia> MOI.Bridges.print_active_bridges(model)
 * Supported objective: MOI.ScalarAffineFunction{Float64}
 * Supported constraint: MOI.ScalarAffineFunction{Float64}-in-MOI.EqualTo{Float64}
 * Unsupported variable: MOI.GreaterThan{Float64}
 |  bridged by:
 |    MOIB.Variable.VectorizeBridge{Float64, MOI.Nonnegatives}
 |  may introduce:
 |   * Supported variable: MOI.Nonnegatives

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

2 participants