/* Copyright 2024, Gurobi Optimization, LLC */
/* We find alternative epsilon-optimal solutions to a given knapsack
* problem by using PoolSearchMode */
#include "gurobi_c++.h"
#include <sstream>
#include <iomanip>
using namespace std;
int main(void)
{
GRBEnv *env = 0;
GRBVar *Elem = 0;
int e, status, nSolutions;
try {
// Sample data
const int groundSetSize = 10;
double objCoef[10] =
{32, 32, 15, 15, 6, 6, 1, 1, 1, 1};
double knapsackCoef[10] =
{16, 16, 8, 8, 4, 4, 2, 2, 1, 1};
double Budget = 33;
// Create environment
env = new GRBEnv("poolsearch_c++.log");
// Create initial model
GRBModel model = GRBModel(*env);
model.set(GRB_StringAttr_ModelName, "poolsearch_c++");
// Initialize decision variables for ground set:
// x[e] == 1 if element e is chosen
Elem = model.addVars(groundSetSize, GRB_BINARY);
model.set(GRB_DoubleAttr_Obj, Elem, objCoef, groundSetSize);
for (e = 0; e < groundSetSize; e++) {
ostringstream vname;
vname << "El" << e;
Elem[e].set(GRB_StringAttr_VarName, vname.str());
}
// Constraint: limit total number of elements to be picked to be at most
// Budget
GRBLinExpr lhs;
lhs = 0;
for (e = 0; e < groundSetSize; e++) {
lhs += Elem[e] * knapsackCoef[e];
}
model.addConstr(lhs <= Budget, "Budget");
// set global sense for ALL objectives
model.set(GRB_IntAttr_ModelSense, GRB_MAXIMIZE);
// Limit how many solutions to collect
model.set(GRB_IntParam_PoolSolutions, 1024);
// Limit the search space by setting a gap for the worst possible solution that will be accepted
model.set(GRB_DoubleParam_PoolGap, 0.10);
// do a systematic search for the k-best solutions
model.set(GRB_IntParam_PoolSearchMode, 2);
// save problem
model.write("poolsearch_c++.lp");
// Optimize
model.optimize();
// Status checking
status = model.get(GRB_IntAttr_Status);
if (status == GRB_INF_OR_UNBD ||
status == GRB_INFEASIBLE ||
status == GRB_UNBOUNDED ) {
cout << "The model cannot be solved " <<
"because it is infeasible or unbounded" << endl;
return 1;
}
if (status != GRB_OPTIMAL) {
cout << "Optimization was stopped with status " << status << endl;
return 1;
}
// Print best selected set
cout << "Selected elements in best solution:" << endl << "\t";
for (e = 0; e < groundSetSize; e++) {
if (Elem[e].get(GRB_DoubleAttr_X) < .9) continue;
cout << " El" << e;
}
cout << endl;
// Print number of solutions stored
nSolutions = model.get(GRB_IntAttr_SolCount);
cout << "Number of solutions found: " << nSolutions << endl;
// Print objective values of solutions
for (e = 0; e < nSolutions; e++) {
model.set(GRB_IntParam_SolutionNumber, e);
cout << model.get(GRB_DoubleAttr_PoolObjVal) << " ";
if (e%15 == 14) cout << endl;
}
cout << endl;
// print fourth best set if available
if (nSolutions >= 4) {
model.set(GRB_IntParam_SolutionNumber, 3);
cout << "Selected elements in fourth best solution:" << endl << "\t";
for (e = 0; e < groundSetSize; e++) {
if (Elem[e].get(GRB_DoubleAttr_Xn) < .9) continue;
cout << " El" << e;
}
cout << endl;
}
}
catch (GRBException e) {
cout << "Error code = " << e.getErrorCode() << endl;
cout << e.getMessage() << endl;
}
catch (...) {
cout << "Exception during optimization" << endl;
}
// Free environment/vars
delete[] Elem;
delete env;
return 0;
}