jump-dev / CPLEX.jl

A Julia interface to the CPLEX solver
https://www.ibm.com/products/ilog-cplex-optimization-studio
MIT License
134 stars 63 forks source link

Change the default behavior of lazy constraints #340

Closed tasseff closed 3 years ago

tasseff commented 3 years ago

Currently, every time a constraint is added within a lazy cut callback, the MIP solution at the corresponding integer feasible node is removed from the solution space. There are circumstances where a feasible integer solution may be used to construct a cut that does not necessarily cut off that solution. I've found that the latter (expected) behavior is present in Gurobi.jl but not CPLEX.jl.

I expect this is being caused by the use of CPXcallbackrejectcandidate here, which automatically classifies the integer solution as infeasible within CPLEX. I'm not familiar enough with the C interface of CPLEX to suggest an alternative, but I'm hoping this can be addressed. Below is a small example that will result in infeasibility even though the lazy constraint that is being added should be inconsequential.

import CPLEX
import JuMP
import MathOptInterface
const MOI = MathOptInterface

model = JuMP.Model(CPLEX.Optimizer)
x = JuMP.@variable(model, lower_bound = 0.0)
z = JuMP.@variable(model, binary = true)
objective = JuMP.@objective(model, Min, x)

MOI.set(model, MOI.NumberOfThreads(), 1)

function lazy_cut_callback(cb_data)
    con = JuMP.@build_constraint(x >= 0.0) # This should do nothing.
    MOI.submit(model, MOI.LazyConstraint(cb_data), con)
end

MOI.set(model, MOI.LazyConstraintCallback(), lazy_cut_callback)
JuMP.optimize!(model)
JuMP.termination_status(model) # INFEASIBLE
odow commented 3 years ago

Take note of the large warning in the JuMP documentation: https://jump.dev/JuMP.jl/stable/callbacks/#Available-solvers-1 JuMP's solver-independent callbacks are a useful abstraction of syntax. They do not guarantee equivalent behavior between solvers.

You should only submit a lazy constraint if the current solution is infeasible. Thus, your callback should be

function lazy_cut_callback(cb_data)
    if callback_value(x) < 0.0
        con = JuMP.@build_constraint(x >= 0.0)
        MOI.submit(model, MOI.LazyConstraint(cb_data), con)
    end
end

Closing since this is expected behavior.

4YHa commented 1 month ago

Hi, I'm using LazyConstraintCallback in cplex via java with the following code and I'm getting a fatal error at runtime. Could you let me know what's causing this and how to fix it? public class LazyConstraintsExample { public static void main(String[] args) { try { IloCplex cplex = new IloCplex();

        // Variables
        IloIntVar x1 = cplex.boolVar("x1");
        IloIntVar x2 = cplex.boolVar("x2");
        IloIntVar x3 = cplex.boolVar("x3");

        // Objective: maximize x1 + 2x2 + 3x3
        cplex.addMaximize(cplex.sum(cplex.sum(x1, cplex.prod(2, x2)), cplex.prod(3, x3)));

        // Constraint: 2x1 + x2 + x3 <= 4
        cplex.addLe(cplex.sum(cplex.sum(cplex.prod(2, x1), x2), x3), 4);
        //cplex.addLe(cplex.sum(x1,x2), 1);

        // Add lazy constraint callback
        cplex.use(new MyLazyCallback(x1,x2,cplex));

        // Solve the model
        if (cplex.solve()) {
            System.out.println("Solution status: " + cplex.getStatus());
            System.out.println("Objective value: " + cplex.getObjValue());
            System.out.println("x1 = " + cplex.getValue(x1));
            System.out.println("x2 = " + cplex.getValue(x2));
            System.out.println("x3 = " + cplex.getValue(x3));
        } else {
            System.out.println("No solution found");
        }

        cplex.end();
    } catch (IloException e) {
        e.printStackTrace();
    }
}

} public class MyLazyCallback extends IloCplex.LazyConstraintCallback { IloNumVar x1; IloNumVar x2; IloCplex cplex;

MyLazyCallback(IloNumVar x1,IloNumVar x2,IloCplex cplex){
    this.x1 = x1;
    this.x2 = x2;
    this.cplex = cplex;
}
@Override
protected void main() throws IloException {
    double x1Val = getValue(x1);
    double x2Val = getValue(x2);
    System.out.println("x1:" + x1Val);
    System.out.println("x2:" + x2Val);
    // 检查是否违反了某个 Lazy 约束
    if ( x1Val + x2Val > 1) {
        // 创建并添加新的约束
        IloLinearNumExpr expr = cplex.linearNumExpr();
        expr.addTerm(1.0, x1);
        expr.addTerm(1.0, x2);
        //IloRange rng = cplex.range(Double.MIN_VALUE,expr,1);
        IloRange rng = cplex.addLe(expr,1);
        cplex.addLazyConstraint(rng);
        System.out.println("Lazy constraint added: x + y <= 1");
    }
}

} The error that occurs is shown in the figure callback结果

dourouc05 commented 1 month ago

Hi @4YHa! Unfortunately, you are asking your question at the wrong place: this repo only contains the Julia wrapper for CPLEX, there is nothing about Java here. It is not affiliated with IBM. You can try on the official forum (https://community.ibm.com/community/user/ai-datascience/communities/community-home) or on Stack Exchange (https://or.stackexchange.com/).

4YHa commented 1 month ago

@dourouc05 Thank you very much for your answer.