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();
}
```
"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$