Lazy constraints & issues with presolve

Good morning community,
I’m trying to use lazy constraints, but I’m finding some issues. I would like to solve a problem iterative
(benders decomposition) where in each iteration an additional constraint is added as lazy constraint. The issue that I’m facing is that in the first iteration the PRESOLVE is eliminating some variables that in the next iteration are going to be used in the lazy constraint.
I attached a dummy example where I got the same problem, It only works if the PRESOLVE is deactivated.
Any idea how to solve this issue? Maybe by telling the PRESOLVE not to eliminate a specific variable?

Thanks for everything.


model = Model(()->Xpress.Optimizer())
@variable(model, x >= 0)
@variable(model, y >= 0)
@variable(model, v >= 0)
@variable(model, z >= 0,Int)
@constraint(model, x+y >= 2)
@objective(model, Min, x+y+z+v)
set_optimizer_attributes(model, "HEURSTRATEGY"  => 0) 
#set_optimizer_attributes(model, "PRESOLVE"  => 0) 
optimize!(model);
println(" Execution is " * string(termination_status(model)))
println("x: ",JuMP.value(x), " y: ",JuMP.value(y)," z: ",JuMP.value(z)," v: ",JuMP.value(v)) 

function good_callback_function(cb_data)
    v_val = callback_value(cb_data,v) 

    if v_val < 10
        con = @build_constraint(v >= 10 )
        MOI.submit(model, MOI.LazyConstraint(cb_data), con)
    end
end


MOI.set(model, MOI.LazyConstraintCallback(), good_callback_function)
optimize!(model);
println(" Execution is " * string(termination_status(model)))
println("x: ",JuMP.value(x), " y: ",JuMP.value(y)," z: ",JuMP.value(z)," v: ",JuMP.value(v)) 

The logs:

FICO Xpress v8.8.0, Hyper, solve started 11:01:41, Feb 20, 2024
Heap usage: 2389KB (peak 2847KB, 5348KB system)
Minimizing MILP 
Original problem has:
         1 rows            4 cols            2 elements         1 globals
Presolved problem has:
         0 rows            0 cols            0 elements         0 globals
LP relaxation tightened
Presolve finished in 0 seconds
Heap usage: 2391KB (peak 2847KB, 5348KB system)
Will try to keep branch and bound tree memory usage below 11.0GB
Starting concurrent solve with dual

 Concurrent-Solve,   0s
            Dual        
    objective   dual inf
 D  2.0000000   .0000000
------- optimal --------
Concurrent statistics:
      Dual: 0 simplex iterations, 0.00s
Optimal solution found
 
   Its         Obj Value      S   Ninf  Nneg   Sum Dual Inf  Time
     0          2.000000      D      0     0        .000000     0
Dual solved problem
  0 simplex iterations in 0s

Final objective                       : 2.000000000000000e+00
  Max primal violation      (abs/rel) :       0.0 /       0.0
  Max dual violation        (abs/rel) :       0.0 /       0.0
  Max complementarity viol. (abs/rel) :       0.0 /       0.0
All values within tolerances

Starting root cutting & heuristics
 
 Its Type    BestSoln    BestBound   Sols    Add    Del     Gap     GInf   Time
 *** Search unfinished ***    Time:     0 Nodes:          0
Number of integer feasible solutions found is 0
Best bound is     2.000000
Uncrunching matrix
ERROR: XpressError(32): Subroutine not completed successfully, possibly due to invalid argument. Xpress internal error:

405 Error: Invalid column number passed to XPRSstorecuts.
Column number 2 is invalid.

Stacktrace:
 [1] _check(prob::Xpress.XpressProblem, val::Int32)
   @ Xpress ~/.julia/packages/Xpress/IPJtC/src/utils.jl:144
 [2] macro expansion
   @ ~/.julia/packages/Xpress/IPJtC/src/utils.jl:137 [inlined]
 [3] optimize!(model::Xpress.Optimizer)
   @ Xpress ~/.julia/packages/Xpress/IPJtC/src/MOI/MOI_wrapper.jl:2742
 [4] optimize!
   @ ~/.julia/packages/MathOptInterface/tWT4o/src/Bridges/bridge_optimizer.jl:376 [inlined]
 [5] optimize!(m::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{Xpress.Optimizer}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}})
   @ MathOptInterface.Utilities ~/.julia/packages/MathOptInterface/tWT4o/src/Utilities/cachingoptimizer.jl:325
 [6] optimize!(model::Model; ignore_optimize_hook::Bool, _differentiation_backend::MathOptInterface.Nonlinear.SparseReverseMode, kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ JuMP ~/.julia/packages/JuMP/KiENn/src/optimizer_interface.jl:439
 [7] optimize!(model::Model)
   @ JuMP ~/.julia/packages/JuMP/KiENn/src/optimizer_interface.jl:409
 [8] top-level scope
   @ REPL[151]:1

This looks to be the same bug as Possible bug in lazy constraints · Issue #191 · jump-dev/Xpress.jl · GitHub.

It only works if the PRESOLVE is deactivated.
Any idea how to solve this issue?

Just disable presolve, or do set_attribute(model, "MIPDUALREDUCTIONS", 0)

Thanks for the quick answer, my problem is that I’m not able to disable Presolve, as the real problem that I’m solving is too big. I have tried set_attribute(model, "MIPDUALREDUCTIONS", 0) but the solver says the problem is INFEASABLE when I set the lazy constraints when in reality it is feasible.

For now, I don’t have any other suggestions. I need to take a look at how callbacks are managed in Xpress.jl. It seems there are a few issues.