7
$\begingroup$

I am trying to implement a heuristic callback to a MILP with BOTH integer variables and continuous variables. After finding a feasible solution, I tried to use setSolution() to send the solution to CPLEX. I don't want CPLEX to spend time evaluating the solution as the feasibility is guaranteed during its generation process. However, according to the online material of setSolution(), I don't know how to update my solution with both integer and continuous variables. I found the following API: enter image description here

enter image description here

Another question is also related to the API. Do I have to convert all of my variables to a one-dimensional array? If so, what should I do if I constructed my model by defining variable matrices?

$\endgroup$
2
  • $\begingroup$ Take a look at this code github.com/My-master-degree/Master-thesis-code/blob/master/src/… $\endgroup$ Dec 13, 2022 at 10:25
  • $\begingroup$ "Do I have to convert all of my variables to a one-dimensional array? If so, what should I do if I constructed my model by defining variable matrices?" Those are orthogonal aspects. This function signature asks for a specific type you have provide. But as all var-types are smart-pointer-like, i mostly ignore cplex helpers and use Eigen (think numpy, but C++), STL types (vectors, maps). Sometimes i collect my variables within boost:graph if it makes sense (TSP-like models). It's your job to transform to that signature then, but often it's a net win as those structures can improve usability. $\endgroup$
    – sascha
    Dec 13, 2022 at 22:20

2 Answers 2

5
$\begingroup$
  1. As @Joni notes, there is a parameter that controls how much (if any) effort CPLEX spends checking solutions you supply.
  2. You can freely mix integer and continuous variables in the call to setSolution, in a single IloNumVarArray argument. Integer variables are special cases of numeric variables. You can put the variables in any order you wish, as long as the second argument (values) has the same order.
  3. Yes, you have to collapse your matrices of variables into 1-D arrays, and then glue together the arrays corresponding to different variables. Just declare an IloNumVarArray and then fill up the slots (and the corresponding slots of the second argument) using loops.
$\endgroup$
1
  • $\begingroup$ Thank you for your answer :) Based on your tips and joni's answer, I have successfully implemented the heuristic callback with setSolution(). $\endgroup$
    – Jasonzjc
    Dec 14, 2022 at 9:07
6
$\begingroup$

Disclaimer: It's been a while since I used Cplex' C++ API.

According to your question, you probably rather want to use a generic callback and inject a feasible (heuristic) solution through Callback::Context::postHeuristicSolution instead of using the legacy callback's setSolution method.

This way you can control how Cplex should complete (partial) solutions by the last argument strat, which is just an SolutionStrategy enum. Consequently, you can inject a feasible solution without any checks by passing IloCplex::SolutionStrategy::Types::NoCheck.

It's worth mentioning that a generic callback can be invoked from different Contexts, i.e.: Branching, Candidate, GlobalProgress, LocalProgress, Relaxation, ThreadDown, ThreadUp. You can find a description of each Context here. (IMHO, it's also worth mentioning that the docs are pretty messy because most of the C++ Docs just link to the C API.)

Each of the contexts has a inContextName() method which returns true if the callback is called in the specific context. For instance, Cplex invokes your callback in the globalProgress context once it has found a feasible solution in one of the local threads and stored it in the global solution structure.

Here's a (untested) code snippet that illustrates this approach for the globalProgress context:

#include <ilcplex/ilocplex.h>


class YourCallback : public IloCplex::Callback::Function{
  private:
        // Your private member's here
  public:
        Yourcallback() = default; // Your other constructors here

        void invoke(IloCplex::Callback::Context const& context) override{
            if(context.inGlobalProgress()){
                // inject your feasible (heuristic) solution and disable
                // any checks by Cplex 
                context.postHeuristicSolution(vars, vals, obj, IloCplex::SolutionStrategy::Types::NoCheck);
            }
        }
};

int main(){
    // Create Cplex model
    IloEnv env;
    IloModel model(env);
    IloNumVarArray x(env);
    IloObjective obj(env);
    IloRangeArray cons(env);
    IloCplex cplex(model);

    // Call default-constructor of your callback
    YourCallback cb;

    // Set the context where your callback gets invoked
    cplex.use(&cb, IloCplex::Callback::Context::Id::GlobalProgress);

    // solve the model
    cplex.solve();
}
```
$\endgroup$
2
  • $\begingroup$ It’s the first time I use stackexchange and I am surprised by the response speed and quality. Thanks for your answer and I will try your solution and let you know my results :) $\endgroup$
    – Jasonzjc
    Dec 13, 2022 at 11:20
  • $\begingroup$ I have implemented the heuristic callback. I use setSolution() and heuristic callback macro though. Thanks again! $\endgroup$
    – Jasonzjc
    Dec 14, 2022 at 9:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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