I am trying to use ctypes (Python) to run an optimization model that was written in C++ with the CP Optimizer of CPLEX. I have successfully done this in MacOS, however, I am trying to do it in Linux and I am facing some errors that I cannot understand.
At this point I am simply trying to run an example from CPLEX, namely the teambuilding.cpp. I have made just some changes to the original file for it to be compatible with ctypes. I'll leave it down below but the main change to the original code is to put the main function between extern "C"{}, and to comment out the lines "NameVars(...)".
#include <ilcp/cp.h>
#include "util.h"
const IloInt nbPersons = 60;
const IloInt nbTeams = 10;
const IloInt teamSize = 6;
const IloInt nbServices = 6;
IloInt coaching[nbPersons];
// MakeTeamTuples return a IloIntTupleSet containing all the possible
// configurations of a team. The team members in a tuple are ordered
// to break symmetry.
IloIntTupleSet MakeTeamTuples(IloEnv globalEnv) {
IloEnv env;
IloModel model(env);
IloIntArray newEmployee(env, nbPersons);
IloIntArray service(env, nbPersons);
for (IloInt i = 0; i < nbPersons; i++) {
newEmployee[i] = ((i % 2) == 0);
if (i < 20) service[i] = 0;
else if (i < 40) service[i] = 1;
else if (i < 45) service[i] = 2;
else if (i < 50) service[i] = 3;
else if (i < 55) service[i] = 4;
else service[i] = 5;
}
IloIntTupleSet ts(globalEnv, teamSize);
IloIntVarArray teamMembers(env, teamSize, 0, nbPersons-1);
//NameVars(teamMembers, "TM");
//number of new employees among the teamMembers = number of teamMembers / 2
IloIntExpr nbNewEmployees(env);
for (IloInt i = 0; i < teamSize; i++)
nbNewEmployees += newEmployee[teamMembers[i]];
model.add(nbNewEmployees == teamSize / 2);
//a new employee and his coach must be in the same team
for (IloInt i = 0; i < 60; i += 2) {
if (coaching[i] >= 0)
model.add(IloCount(teamMembers, i) == IloCount(teamMembers, coaching[i]));
}
IloIntVarArray serviceVar(env, teamSize, 0, nbServices - 1);
//NameVars(serviceVar, "S");
for (IloInt i = 0; i < teamSize; i++)
model.add(serviceVar[i] == service[teamMembers[i]]);
// at most 4 people of the same service
for (IloInt i = 0; i < nbServices; i++)
model.add(IloCount(serviceVar, i) <= 4);
// Persons of Services A and B cannot be in the same team
// Persons of Services E and F cannot be in the same team
model.add(IloCount(serviceVar, 0) == 0 || IloCount(serviceVar, 1) == 0);
model.add(IloCount(serviceVar, 4) == 0 || IloCount(serviceVar, 5) == 0);
// order the teamMembers to break symmetry
for (IloInt i = 0; i < teamSize-1; i++)
model.add(teamMembers[i] < teamMembers[i+1]);
IloIntArray tuple(globalEnv, teamSize);
IloCP cp(model);
cp.setParameter(IloCP::LogVerbosity, IloCP::Quiet);
cp.setParameter(IloCP::SearchType, IloCP::DepthFirst);
cp.setParameter(IloCP::Workers, 1);
cp.startNewSearch();
while (cp.next()) {
for (IloInt i = 0; i < teamSize; i++)
tuple[i] = cp.getValue(teamMembers[i]);
ts.add(tuple);
}
cp.end();
env.end();
return ts;
}
extern "C"{
int main(int, const char* []) {
for (IloInt i = 0; i < nbPersons; i++)
coaching[i] = -1;
// the 12 first of Service A are couples of coached/coach
for (IloInt i = 0; i < 12; i += 2) {
coaching[i] = i+1;
coaching[i+1] = i;
}
// the 12 first of Service B are couples of coached/coach
for (IloInt i = 20; i < 32; i += 2) {
coaching[i] = i+1;
coaching[i+1] = i;
}
// the 4 first of Services C, D, E, F are couples of coached/coach
for (IloInt i = 40; i < nbPersons; i += 5) {
coaching[i] = i+1;
coaching[i+1 ]= i;
coaching[i+2] = i+3;
coaching[i+3] = i+2;
}
IloEnv env;
try {
//compute the possible solutions of a team
IloIntTupleSet tupleSet = MakeTeamTuples(env);
IloModel model(env);
// groups[i] represents the ordered set of people in the team i
IloArray<IloIntVarArray> groups(env, nbTeams);
for (IloInt i = 0; i < nbTeams; i++) {
groups[i] = IloIntVarArray(env, teamSize, 0, nbPersons-1);
model.add(IloAllowedAssignments(env, groups[i], tupleSet));
}
//NameVars(groups, "G");
IloIntVarArray allVars(env, nbPersons);
IloInt s = 0, w, p;
for (w = 0; w < nbTeams; ++w) {
for (p = 0; p < teamSize; ++p) {
allVars[s] = groups[w][p];
++s;
}
}
model.add(IloAllDiff(env, allVars));
// team[i] represents the number of the team of people number i
IloIntVarArray team(env, nbPersons, 0, nbTeams);
//NameVars(team, "T");
for (w = 0; w < nbTeams; ++w) {
for (p = 0; p < teamSize; ++p) {
model.add(team[groups[w][p]] == w);
}
}
// Additional constraints
// to improve efficiency we could force the following
// first three constraints directly in MakeTeamTuples but the fourth
// constraint cannot be expressed as a restriction of
// the tuple set, since it is not local to a tuple
model.add(team[5] == team[41] || team[5] == team[51]);
model.add(team[15] == team[40] || team[15] == team[51]);
model.add(team[25] == team[40] || team[25] == team[50]);
model.add(team[20] == team[24] || team[22] == team[50]);
// break symmetry: the teams are ordered according to the smallest
// in each team
for (IloInt i = 0; i < nbTeams-1; i++)
model.add(groups[i][0] < groups[i+1][0]);
IloCP cp(model);
cp.setParameter(IloCP::AllDiffInferenceLevel, IloCP::Extended);
if (cp.solve()) {
cp.out() << std::endl << "SOLUTION" << std::endl;
for (p = 0; p < nbTeams; ++p) {
cp.out() << "team " << p << " : ";
for (w = 0; w < teamSize; ++w) {
cp.out() << cp.getValue(groups[p][w]) << " ";
}
cp.out() << std::endl;
}
}
else
cp.out() << "**** NO SOLUTION ****" << std::endl;
cp.end();
}
catch (IloException& ex) {
env.out() << "Caught: " << ex << std::endl;
}
env.end();
return 0;
}
}
After this, I tried to compile the .cpp (my file is named main.cpp) to obtain the .so file using the following code:
g++ -I/home/admin/cplex/CPLEX_Studio2211/cplex/include -I/home/admin/cplex/CPLEX_Studio2211/cpoptimizer/include -I/home/admin/cplex/CPLEX_Studio2211/concert/include -L/home/admin/cplex/CPLEX_Studio2211/cplex/lib/x86-64_linux/static_pic -L/home/admin/cplex/CPLEX_Studio2211/cpoptimizer/lib/x86-64_linux/static_pic -L/home/admin/cplex/CPLEX_Studio2211/concert/lib/x86-64_linux/static_pic -lilocplex -lconcert -lcplex -lcp -lm -lpthread -fPIC -shared -o main.so main.cpp
It compiled with no errors and I obtained the .so file. Moving to Python, I am using the following code:
import ctypes
clibrary = ctypes.CDLL("/Users/paulo/Desktop/IntegrationCP/IntegrationCP/main.so")
X = clibrary.main()
When running the Python code, I am obtaining the following error:
Traceback (most recent call last):
File "Trying.py", line 3, in <module>
clibrary = ctypes.CDLL("/home/admin/Downloads/IntegrationCP/main.so")
File "/usr/lib/python3.8/ctypes/__init__.py", line 373, in __init__
self._handle = _dlopen(self._name, mode)
OSError: /home/admin/Downloads/IntegrationCP/main.so: undefined symbol: _ZTI12IloException
Thinking that the code I have used to compile the .so file could be wrong, I change it to this:
g++ -Wall -fPIC -shared -o main.so main.cpp -I/home/admin/cplex/CPLEX_Studio2211/cplex/include -I/home/admin/cplex/CPLEX_Studio2211/cpoptimizer/include -I/home/admin/cplex/CPLEX_Studio2211/concert/include -L/home/admin/cplex/CPLEX_Studio2211/cplex/lib/x86-64_linux/static_pic -L/home/admin/cplex/CPLEX_Studio2211/cpoptimizer/lib/x86-64_linux/static_pic -L/home/admin/cplex/CPLEX_Studio2211/concert/lib/x86-64_linux/static_pic -lilocplex -lconcert -lcplex -lcp -lm -lpthread
Once again, the .so file compiled with no errors, but when running the Python code, now I obtain this error:
Traceback (most recent call last):
File "Trying.py", line 3, in <module>
clibrary = ctypes.CDLL("/home/admin/Downloads/IntegrationCP/main.so")
File "/usr/lib/python3.8/ctypes/__init__.py", line 373, in __init__
self._handle = _dlopen(self._name, mode)
OSError: /home/admin/Downloads/IntegrationCP/main.so: undefined symbol: _ZTI10IloXmlInfo
I did some research but I didn't really find any information about these errors. I am pretty much following exactly what I did on MacOS and it worked, but for some reason it is not working in Linux. Does anyone have any idea why this might be happening or any suggestions? Can it be any problem with the CPLEX or even Python installation?
Thank you in advance for taking the time to read this.
After trying multiple things, here is what worked for me
g++ -shared -o main.so -O -DNDEBUG -I/home/admin/CPLEX_Studio2211/cpoptimizer/include -I /home/admin/CPLEX_Studio2211/concert/include -fPIC -std=c++11 -pedantic -fstrict-aliasing -fexceptions -frounding-math -Wall -Wextra -Wno-long-long -m64 main.cpp -L/home/admin/CPLEX_Studio2211/cplex/lib/x86-64_linux/static_pic -lcp -L/home/admin/CPLEX_Studio2211/cpoptimizer/lib/x86-64_linux/static_pic -lcplex -L/home/admin/CPLEX_Studio2211/concert/lib/x86-64_linux/static_pic -lconcert -lpthread -lm -ldl