30 enum VarType {REAL, INTEGER, BOOLEAN};
32 enum Status {OPTIMAL, NONOPTIMAL, UNFEASIBLE, UNBOUNDED, UNKNOWN, ERROR};
36 using Term = pair<double, int>;
91 int numConstraints()
const {
103 double lower_bound = 0.0,
104 double upper_bound = -1.0) {
105 return newVar(name, REAL, lower_bound, upper_bound);
116 double lower_bound = 0,
117 double upper_bound = -1) {
118 return newVar(name, INTEGER, lower_bound, upper_bound);
127 return newVar(name, BOOLEAN, 0, 1);
150 assert (i >= 0 and i < Vars.size());
151 return Vars[i].value;
160 assert (Name2Var.count(name) > 0);
161 return Vars[Name2Var.at(name)].value;
170 assert (i >= 0 and i < Vars.size() and Vars[i].type == BOOLEAN);
171 return Vars[i].value > 0.5;
180 return not isTrue(i);
190 int newRow(
char type =
'<',
double rhs = 0.0,
const string& name =
"") {
192 if (type ==
'<') t = LEQ;
193 else if (type ==
'>') t = GEQ;
194 else if (type ==
'=') t = EQ;
196 Matrix.push_back(Row {name, t, rhs});
197 return Matrix.size() - 1;
208 int newRow(
const vecTerms& terms,
char type =
'<',
double rhs = 0.0,
const string& name =
"") {
209 int r = newRow(type, rhs, name);
210 for (
auto t: terms) newTerm(r, t.first, t.second);
220 void newTerm(
int rowIndex,
double coeff,
int varIndex) {
221 assert (rowIndex >= 0);
222 assert (rowIndex < Matrix.size());
223 assert (varIndex >= 0);
224 assert (varIndex < Vars.size());
225 assert (rowIndex >= 0 and rowIndex < Matrix.size() and
226 varIndex >= 0 and varIndex < Vars.size());
227 Matrix[rowIndex].vecRow.push_back( {coeff, varIndex});
236 assert (rowIndex >= 0 and rowIndex < Matrix.size());
237 Matrix[rowIndex].rhs = rhs;
247 int r = newRow(
'<', 1.0, name);
248 for (
int v: vars) newTerm(r, 1.0, v);
259 int r = newRow(
'>', 1.0, name);
260 for (
int v: vars) newTerm(r, 1.0, v);
271 int r = newRow(
'=', 1.0, name);
272 for (
int v: vars) newTerm(r, 1.0, v);
286 int r = newRow(
'=', 0.0, name);
287 for (
int v: x) newTerm(r, 1.0, v);
288 for (
int v: y) newTerm(r, -1.0, v);
300 assert (v.size() > 1);
301 int row = newRow( {{1, v[0]}, {-1, v[1]}},
'=');
302 for (
unsigned int i = 2; i < v.size(); ++i) newRow( {{1, v[0]}, {-1, v[i]}},
'=');
314 int r = newRow(
'>', 0.0);
316 for (
int v: y) newTerm(r, 1.0, v);
330 int r = newRow(
'>', 0.0);
331 if (first < 0) first = r;
345 assert (varIndex >= 0 and varIndex < Vars.size());
346 Cost.push_back( {coeff, varIndex});
357 if (not f.is_open()) {
358 setError(
"Could not open file " + filename +
".");
373 if (solver !=
"cbc" and solver !=
"glpsol" and solver !=
"gurobi_cl") {
374 setError(
"Unkonwn solver " + solver +
".");
378 string exec =
"which " + solver +
" >/dev/null 2>&1";
379 if (system(exec.c_str())) {
380 setError(
"Solver " + solver +
" is not available.");
385 string lpfile = createTempFilename(
"MILP_lpmodel",
".lp");
387 if(solver ==
"gurobi_cl")
388 outfile = createTempFilename(
"MILP_solution",
".sol");
390 outfile = createTempFilename(
"MILP_solution",
".gsol");
391 string command = writeCommand(solver, lpfile, outfile, timelimit) +
" >/dev/null 2>&1";
394 int status = system(command.c_str());
397 deleteTempFilename(outfile);
398 setError(
"Error when executing " + solver +
".");
402 if (solver ==
"cbc") opt_status = readCbcSolution(outfile);
403 else if (solver ==
"glpsol") opt_status = readGlpsolSolution(outfile);
404 else if (solver ==
"gurobi_cl") opt_status = readGurobiSolution(outfile);
408 cout <<
"*ERROR* MILP solution is UNFEASIBLE or UNBOUNDED" << endl;
420 bool init(
const string& solver =
"") {
423 numRealVars=numIntegerVars=numBooleanVars=0;
433 return find_solver(solver);
442 file.open(file_name);
443 for (
auto const& values: Name2delays){
444 file << values.second <<
" " << Vars[values.first].value << endl;
448 file.open(
"tmp_delays.txt");
449 int i, ord = Vars.size();
450 for (i = 0; i< ord; i++){
451 file << Vars[i].name <<
" " << Vars[i].value << endl;
461 return Vars[id].name;
487 vector<int>appearanceOrder;
488 vector<bool>appeared;
494 map<string, int> Name2Var;
499 map<int, string> Name2delays;
501 bool find_solver(
const string& s) {
503 if (not s.empty() and s !=
"cbc" and s !=
"glpsol" and s !=
"gurobi_cl") {
504 setError(
"Unkonwn solver " + solver +
".");
509 if (s.empty() or s ==
"cbc") {
510 string exec =
"which cbc >/dev/null 2>&1";
511 if (system(exec.c_str()) == 0) {
517 setError(
"MILP solver cbc not found.");
523 if (s.empty() or s ==
"glpsol") {
524 string exec =
"which glpsol >/dev/null 2>&1";
525 if (system(exec.c_str()) == 0) {
531 setError(
"MILP solver glpsol not found.");
537 if (s.empty() or s ==
"gurobi_cl") {
538 string exec =
"which gurobi_cl >/dev/null 2>&1";
539 if (system(exec.c_str()) == 0) {
540 solver =
"gurobi_cl";
544 if (s ==
"gurobi_cl") {
545 setError(
"MILP solver gurobi_cl not found.");
551 setError(
"No MILP solver found.");
563 int newVar(
const string& name, VarType type,
double lower_bound,
double upper_bound) {
564 string n = name.empty() ?
"x" + to_string(Vars.size()) : name;
565 if (Name2Var.count(n) > 0) {
566 setError(
"Variable " + n +
" multiply defined.");
569 Name2Var[n] = Vars.size();
570 Vars.push_back(Var {n, type, lower_bound, upper_bound, 0});
571 if (type == REAL) numRealVars++;
572 else if (type == INTEGER) numIntegerVars++;
573 else numBooleanVars++;
574 return Vars.size() - 1;
583 int newOutDelay(
int ind,
string output) {
584 if (Name2delays.count(ind) > 0)
586 Name2delays[ind] = output;
596 for (Row& r: Matrix) normalizeRow(r.vecRow);
606 sort(r.begin(), r.end(), [](
Term& t1,
Term& t2) {
607 return t1.second < t2.second;
613 while (i < last - 1) {
614 int var = r[i].second;
615 unsigned int k = i + 1;
616 while (k < last and r[k].second == var) {
617 r[i].first += r[k].first;
626 for (i = 0; i < last; ++i) {
627 if (abs(r[i].first) >= epsilon) r[k++] = r[i];
637 void writeTerms(ofstream& f,
const vecTerms& terms) {
638 assert (terms.size() > 0);
639 double coeff = terms[0].first;
640 if (abs(coeff) != 1.0) f << coeff <<
' ';
641 else if (coeff < 0) f <<
'-';
642 int idx = terms[0].second;
644 if (not appeared[idx]) {
645 appearanceOrder.push_back(idx);
646 appeared[idx] =
true;
647 regex regexp (
"timePath_(.)+_out[0-9]+");
648 if (regex_search(Vars[idx].name, regexp)){
649 newOutDelay(idx, Vars[idx].name);
653 for (
unsigned int i = 1; i < terms.size(); ++i) {
654 coeff = terms[i].first;
655 f <<
' ' << (coeff < 0 ?
'-' :
'+') <<
' ';
656 if (abs(coeff) != 1) f << abs(coeff) <<
' ';
657 idx = terms[i].second;
659 if (not appeared[idx]) {
660 appearanceOrder.push_back(idx);
661 appeared[idx] =
true;
662 regex regexp (
"timePath_(.)+_out[0-9]+");
663 if (regex_search(Vars[idx].name, regexp)){
664 newOutDelay(idx, Vars[idx].name);
675 void writeRow(ofstream& f,
const Row& r) {
676 if (r.vecRow.empty()) {
681 if (not r.name.empty()) f << r.name <<
": ";
682 writeTerms(f, r.vecRow);
683 if (r.type == EQ) f <<
" = ";
684 else if (r.type == GEQ) f <<
" >= ";
693 void writeLP(ofstream& f) {
695 appearanceOrder.clear();
696 appeared = vector<bool>(Vars.size(),
false);
699 if (Cost.empty()) newCostTerm(0, 0);
702 f << (MinMax ?
"Minimize" :
"Maximize") << endl;
707 f <<
"Subject to" << endl;
708 for (
const Row& r: Matrix) writeRow(f, r);
711 for (
bool b: appeared)
if (b) ++numUsedVars;
713 bool need_bounds =
false;
714 for (
const Var& v: Vars) {
715 if (v.type != BOOLEAN and v.lower_bound <= v.upper_bound) {
722 f <<
"Bounds" << endl;
723 for (
const Var& v: Vars) {
724 if (v.type != BOOLEAN and v.lower_bound <= v.upper_bound) {
725 f <<
" " << v.lower_bound <<
" <= " << v.name <<
" <= " << v.upper_bound << endl;
730 if (numIntegerVars > 0) {
731 f <<
"General" << endl <<
' ';
732 for (
const Var& v: Vars) {
733 if (v.type == INTEGER) f <<
' ' << v.name;
738 if (numBooleanVars > 0) {
739 f <<
"Binary" << endl <<
' ';
740 for (
const Var& v: Vars) {
741 if (v.type == BOOLEAN) f <<
' ' << v.name;
755 static int readLine(ifstream& f, vector<string>& items) {
758 istringstream myStream(line);
761 while (myStream >> s) items.push_back(s);
770 bool readCbcSolution(
const string& filename) {
773 if (not f.is_open())
return false;
775 vector<string> values;
776 int nv = readLine(f, values);
779 int nrows = stoi(values[0]);
780 int ncols = stoi(values[1]);
782 assert (nrows == Matrix.size() - numEmptyRows + 1);
783 assert (ncols == numUsedVars);
788 nv = readLine(f, values);
790 int st = stoi(values[0]);
808 if (stat != OPTIMAL and stat != NONOPTIMAL) {
813 obj = stod(values.back());
814 if (not MinMax) obj = -obj;
817 for (
unsigned int i = 0; i < nrows; ++i) readLine(f, values);
820 for (
unsigned int i = 0; i < ncols; ++i) {
821 nv = readLine(f, values);
822 int loc = nv > 1 ? 1 : 0;
823 Vars[appearanceOrder[i]].value = stod(values[loc]);
835 bool readGlpsolSolution(
const string& filename) {
838 if (not f.is_open())
return false;
840 bool cost_read =
false;
843 while (readLine(f, line) > 0) {
845 if (c !=
's' and c !=
'j') {
848 }
else if (c ==
'j') {
850 int nvar = stoi(line[1]);
851 int loc = line.size() == 3 ? 2 : 3;
852 Vars[appearanceOrder[nvar-1]].value = stod(line[loc]);
854 assert (not cost_read);
857 int nrows = stoi(line[2]);
858 int ncols = stoi(line[3]);
859 char st = line[4][0];
860 obj = stod(line.back());
862 assert (nrows == Matrix.size() - numEmptyRows);
863 assert (ncols == numUsedVars);
893 bool readGurobiSolution(
const string& filename) {
896 if (not f.is_open())
return false;
898 vector<string> values;
903 while(getline(f, line)){
904 istringstream myStream(line);
907 while (myStream >> s) values.push_back(s);
908 Vars[Name2Var[values[0]]].value = stod(values[1]);
929 string writeCommand(
const string& solver,
const string& lpfile,
const string& solfile,
931 ostringstream command;
932 command << solver <<
' ';
933 if (solver ==
"cbc") {
935 if (timeout > 0) command <<
" sec " << timeout;
936 command <<
" solve gsolution " << solfile;
937 }
else if (solver ==
"glpsol") {
938 command <<
" --lp " << lpfile;
939 if (timeout > 0) command <<
" --tmlim " << timeout;
940 command <<
" -w " << solfile;
941 }
else if (solver ==
"gurobi_cl") {
942 if (timeout > 0) command <<
" TimeLimit=" << timeout;
943 command <<
" ResultFile=" << solfile;
944 command <<
" " << lpfile;
948 return command.str();
958 static std::string createTempFilename(
const std::string& prefix,
const std::string& suffix =
"") {
960 strcat(strcpy(fname, prefix.c_str()),
".XXXXXX");
961 if (not suffix.empty()) strcat(fname, suffix.c_str());
962 int file = mkstemps(fname, suffix.size());
963 if (file == -1)
return "";
971 static void deleteTempFilename(
const std::string& filename) {
972 unlink(filename.c_str());
975 #endif // MILP_INTERFACE_H vector< int > vecVars
Vector of terms.
Definition: MILP_Model.h:38
int atMostOne(const vecVars &vars, const string &name="")
Creates an at-most-one constraint (v1+...+vn <= 1).
Definition: MILP_Model.h:246
double operator[](int i) const
Returns the value of a variable.
Definition: MILP_Model.h:149
int exactlyOne(const vecVars &vars, const string &name="")
Creates an exactly-one constraint (v1+...+vn = 1).
Definition: MILP_Model.h:270
pair< double, int > Term
Type of solution.
Definition: MILP_Model.h:36
int numVariables() const
Definition: MILP_Model.h:87
double operator[](const string &name) const
Returns the value of a variable.
Definition: MILP_Model.h:159
void newTerm(int rowIndex, double coeff, int varIndex)
Adds a new term to a constraint.
Definition: MILP_Model.h:220
string getVarName(int id)
Get variables names.
Definition: MILP_Model.h:460
void setRHS(int rowIndex, double rhs)
Defines the RHS of a constraint.
Definition: MILP_Model.h:235
void setMinimize()
Defines a model to minimize the cost function.
Definition: MILP_Model.h:65
bool init(const string &solver="")
Initializes the MILP model.
Definition: MILP_Model.h:420
Status getStatus() const
Definition: MILP_Model.h:133
void setError(const string &err="")
Defines the error message of the object (no error by default).
Definition: MILP_Model.h:58
void setMaximize()
Defines a model to maximize the cost function.
Definition: MILP_Model.h:72
Status
Type of constraints.
Definition: MILP_Model.h:32
void writeOutDelays(const string &file_name)
Writes a output file to save output pin timing to retrieve critical path.
Definition: MILP_Model.h:440
const string & getError() const
Definition: MILP_Model.h:50
void setEpsilon(double eps)
Defines the min epsilon value to distinguish from zero.
Definition: MILP_Model.h:80
double getObj() const
Definition: MILP_Model.h:140
Definition: MILP_Model.h:26
int newRow(const vecTerms &terms, char type='<', double rhs=0.0, const string &name="")
Creates a new row in the constraint matrix from a vector of terms.
Definition: MILP_Model.h:208
bool writeLP(const string &filename)
Write the LP model into a file in CPLEX LP format.
Definition: MILP_Model.h:354
int newRow(char type='<', double rhs=0.0, const string &name="")
Creates a new row in the constraint matrix.
Definition: MILP_Model.h:190
void newCostTerm(double coeff, int varIndex)
Adds a new term to the cost function.
Definition: MILP_Model.h:344
bool isFalse(int i) const
Checks whether the value of a boolean variable is false.
Definition: MILP_Model.h:179
Milp_Model(const string &solver="")
Vector of variables.
Definition: MILP_Model.h:43
int equalSum(const vecVars &x, const vecVars &y, const string &name="")
Creates a constraint indicating that the sum of a set of variables must be equal to the sum of anothe...
Definition: MILP_Model.h:285
bool solve(int timelimit=-1)
Calls the MILP solver.
Definition: MILP_Model.h:371
int implies(const vecVars &x, int y)
Creates a set of constraints representing the Boolean implication x1 + ... + xn => y...
Definition: MILP_Model.h:327
int newBooleanVar(string name="")
Creates a new Boolean variable.
Definition: MILP_Model.h:126
int allEqual(const vecVars &v)
Creates a set of constraints that enforces a set of variables to have the same value.
Definition: MILP_Model.h:299
int newRealVar(string name="", double lower_bound=0.0, double upper_bound=-1.0)
Creates a new real variable.
Definition: MILP_Model.h:102
vector< Term > vecTerms
Type to represent a term (coefficient, var index)
Definition: MILP_Model.h:37
int newIntegerVar(string name="", double lower_bound=0, double upper_bound=-1)
Creates a new integer variable.
Definition: MILP_Model.h:115
int implies(int x, const vecVars &y)
Creates a constraint representing the Boolean implication x => y1 + ... + ym.
Definition: MILP_Model.h:313
int atLeastOne(const vecVars &vars, const string &name="")
Creates an at-least-one constraint (v1+...+vn >= 1).
Definition: MILP_Model.h:258
bool isTrue(int i) const
Checks whether the value of a boolean variable is true.
Definition: MILP_Model.h:169
RowType
Types of variables.
Definition: MILP_Model.h:31