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. – sascha Dec 13 '22 at 22:20