7
$\begingroup$

I am trying to implement a user-written lazy constraint callback in concert CPLEX with C++. Although I know there exists a way to implement callbacks in concert CPLEX with C++ via macros, I am looking for a way to implement lazy constraint callback without macros.

At this moment I know that I must create a class that inherits from the class IloCplex.LazyConstraintCallback (the documentation of this class can be found here). I am aware that this type of implementation requires (a kind of) profound understanding of the algorithms used by CPLEX, however, I want to learn how to do it without the using of macros. After spending some days looking for any code in C++ I could not find anything. Therefore, I am here to know if anyone has any template of how it could be done.

Thank you.

$\endgroup$
  • $\begingroup$ Would you see CPLEX optimization forum? $\endgroup$ – A.Omidi Dec 24 '19 at 20:36
  • $\begingroup$ I could not find any topic related to the user-written callback in CPLEX with C++, I could find topics related to python, however, is not the same thing. $\endgroup$ – Matheus Diógenes Andrade Dec 24 '19 at 20:59
10
$\begingroup$

You have it almost all right, with a few caveats. Let's say I want to implement a Foo Lazy Constraint:

#include <ilocplex/ilocplex.h>
#include <ilocplex/ilocplexi.h>

struct Foo : IloCplex::LazyConstraintCallbackI {
  // Your code goes here...
};

There are three main things you need to put inside your class; two of these are documented (more or less) and one comes from my experience:

  1. A reference to the current CPLEX environment and any variable you might want to use, captured via the constructor. Pass the environment up to the parent constructor. This is the part that comes from my experience... indeed, you might think that to access the CPLEX environment object you can just use the inherited getEnv() method, but for me this consistently causes crashes if I do something like IloExpr lhs{getEnv()};. Perhaps some more knowledgeable user can explain why.
#include <ilocplex/ilocplex.h>
#include <ilocplex/ilocplexi.h>

struct Foo : IloCplex::LazyConstraintCallbackI {
  IloEnv& env;
  IloNumVar& x;
  
  Foo(IloEnv& env, IloNumVar& x) :
    LazyConstraintCallbackI{env}, env{env}, x{x} {}
};
  1. Implement a duplicateCallback() method for CPLEX to use internally. This always looks the same, I basically just copy-paste it from project to project.
#include <ilocplex/ilocplex.h>
#include <ilocplex/ilocplexi.h>

struct Foo : IloCplex::LazyConstraintCallbackI {
  IloEnv& env;
  IloNumVar& x;
  
  Foo(IloEnv& env, IloNumVar& x) :
    LazyConstraintCallbackI{env}, env{env}, x{x} {}

  [[nodiscard]] IloCplex::CallbackI* duplicateCallback() const override {
    return new(env) Foo{*this};
  }
};
  1. Implement a main() function that adds the cut using the parent's add() method. This is the juicy part... here you access the current solution, decide whether you need a new cut and, in this case, add it.
#include <ilocplex/ilocplex.h>
#include <ilocplex/ilocplexi.h>

struct Foo : IloCplex::LazyConstraintCallbackI {
  IloEnv& env;
  IloNumVar& x;
  
  Foo(IloEnv& env, IloNumVar& x) :
    LazyConstraintCallbackI{env}, env{env}, x{x} {}

  [[nodiscard]] IloCplex::CallbackI* duplicateCallback() const override {
    return new(env) Foo{*this};
  }

  void main() override {
    const auto x_value = getValue(x);
    
    if(x_value < 2.0) {
      try {
        add(x >= 2.0);
      } catch(IloException& e) {
        std::cerr << "Exception while adding lazy constraint for x = " << x_value << ": " << e.getMessage() << "\n";
        throw;
    }
  }
};

Note how I can use the inherited getValue() to access variables' values, and finally add() to eventually add a cut. If add() is called at least once, CPLEX knows that it needs to re-solve the node; otherwise, it assumes that no cuts are needed and proceeds with branching. If the lazy constraint should not join the "global" pool of constraints, but should only be active in the subtree of the current node, use addLocal() instead of add().

| improve this answer | |
$\endgroup$
4
$\begingroup$

I don't have any C++ code using a lazy constraint callback, although I could show you a Java example if you can't find anything better. The basic approach, IIRC, is to create an instance of class IloCplex::LazyConstraintCallbackI. In it, you implement the main() method, which is what CPLEX will call to invoke the callback. Inside the main method, you call various get methods to get the proposed solution, do whatever calculations you need to do, and call add() or addLocal() once for each cut you want to add. If you exit main() without adding any cuts, you are accepting the proposed solution.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Oh, Nice! It would be very interesting, could you send to me this class and the code passages used for initializing the class and for using the object in the model? $\endgroup$ – Matheus Diógenes Andrade Dec 24 '19 at 23:19
  • $\begingroup$ Since you added the comment, I see that Alberto Santini added sample C++ code. If you still need the Java code, let me know, but Alberto's code is C++, which will spare you some non-obvious conversion steps. $\endgroup$ – prubin Dec 26 '19 at 0:17
  • $\begingroup$ There is no need, but thank you anyway. $\endgroup$ – Matheus Diógenes Andrade Dec 26 '19 at 1:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.