// (c) 2024-2025 Fair Isaac Corporation #include // For throwing exceptions #include using namespace xpress; using namespace xpress::objects; using xpress::objects::utils::sum; /** Capital budgeting problem. * Illustrates logical conditions, formulation using logic constraints * and indicator constraints. */ // A project. class Project { public: const std::string name; // The name of this project. const double payout; // Payout for the project. const double investment; // Capital investment for the project. const int personnel; // Number of personnel required for the project. Project(std::string name, double payout, double investment, int personnel) : name(name), payout(payout), investment(investment), personnel(personnel) {} // For printing std::string toString() const { return name; } }; // The projects used in this example. const std::vector projectArray = { Project("Alpha", 124000.0, 104000.0, 22), Project("Beta", 74000.0, 53000.0, 12), Project("Gamma", 42000.0, 29000.0, 7), Project("Delta", 188000.0, 187000.0, 36), Project("Epsilon", 108000.0, 98000.0, 24), Project("Zeta", 56000.0, 32000.0, 10), Project("Eta", 88000.0, 75000.0, 20), Project("Theta", 225000.0, 200000.0, 41)}; // The resource constraints used in this example. const double budget = 478.000; const int workforce = 106; int main() { try { // Create a problem instance with or without verbose messages printed to // Console XpressProblem prob; // prob.callbacks.addMessageCallback(XpressProblem::console); /* VARIABLES */ // Whether each project should be invested in or not std::vector x = prob.addVariables(projectArray.size()) .withType(ColumnType::Binary) .withName("x%d") .toArray(); /* RESOURCE AVAILABILITY CONSTRAINTS */ // Investment limit: sum of investments of all undertaken projects should // not exceed budget Expression requiredInvestment = sum(projectArray.size(), [&](auto i) { return x[i].mul(projectArray[i].investment); }); Inequality investmentLimit = prob.addConstraint(requiredInvestment <= budget); // Workforce limit: sum of personnel committed of all undertaken projects // should not exceed workforce LinExpression requiredWorkforce = LinExpression::create(); for (std::size_t i = 0; i < projectArray.size(); i++) { requiredWorkforce.addTerm(x[i], projectArray[i].personnel); } Inequality workforceLimit = prob.addConstraint(requiredWorkforce <= workforce); // Project alpha can only be done if both gamma and zeta happen prob.addConstraint(x[0] <= x[2]); prob.addConstraint(x[0] <= x[5]); // Project zeta can only be done if project epsilon happens prob.addConstraint(x[5] <= x[4]); // Projects alpha and beta as well as gamma and delta can only happen // together prob.addConstraint(x[0] == x[1]); prob.addConstraint(x[2] == x[3]); // Exactly one of those pairs should be invested in, i.e., if project alpha // is performed, neither gamma nor delta can be invested in, and if project // alpha does not happen, then projects gamma and delta have to be performed prob.addConstraint(x[0].ifThen(sum(x[2], x[3]) == 0.0)); prob.addConstraint(x[0].ifNotThen(sum(x[2], x[3]) == 2)); /* OBJECTIVE */ // Objective function: sum of payouts of all undertaken projects Expression totalProfit = sum(projectArray.size(), [&](auto i) { return x[i].mul(projectArray[i].payout); }); prob.setObjective(totalProfit, xpress::ObjSense::Maximize); /* INSPECT, SOLVE & PRINT */ // Dump the problem to disk so that we can inspect it. prob.writeProb("capbgt2l.lp"); // Solve prob.optimize(); // Check the solution status if (prob.attributes.getSolStatus() != SolStatus::Optimal && prob.attributes.getSolStatus() != SolStatus::Feasible) { std::ostringstream oss; oss << prob.attributes .getSolStatus(); // Convert xpress::SolStatus to String throw std::runtime_error("Optimization failed with status " + oss.str()); } // Get the solution and print it std::vector sol = prob.getSolution(); std::cout << std::endl << "*** Solution ***" << std::endl; std::cout << "Objective: " << prob.attributes.getObjVal() << std::endl; // Print the interesting slacks std::cout << "Remaining Budget: " << investmentLimit.getSlack() << " (out of " << budget << ")" << std::endl; std::cout << "Remaining Workers: " << workforceLimit.getSlack() << " (out of " << workforce << ")" << std::endl; // Print out the variables for (std::size_t i = 0; i < projectArray.size(); ++i) { if (x[i].getValue(sol) > 0,5) { std::cout << "Undertaking project " << projectArray[i].toString() << std::endl; } } return 0; } catch (std::exception &e) { std::cout << "Exception: " << e.what() << std::endl; return -1; } }