FICO
FICO Xpress Optimization Examples Repository
FICO Optimization Community FICO Xpress Optimization Home
Back to examples browserPrevious exampleNext example

Capital budgeting - Using multi-objective optimization

Description
Capital budgeting example, solved using three multi-objective approaches:
  • Lexicographic approach, solving first to minimize capital expended and second to maximize return
  • Blended approach, solving a weighted combination of both objectives simultaneously
  • Lexicographic approach, with the objective priorities reversed
The model version Capbgt2l demonstrates the formuation of logical constraints.

capbgt_cpp.zip[download all files]

Source Files
By clicking on a file name, a preview is opened at the bottom of this page.
CapitalBudgeting.cpp[download]
Capbgt2l.cpp[download]





CapitalBudgeting.cpp

#include <xpress.hpp>
#include <stdexcept>  // For throwing exceptions

using namespace xpress;
using namespace xpress::objects;
using xpress::objects::utils::sum;
using xpress::objects::utils::scalarProduct;

/*
 * Capital budgeting example, showing multi-objective optimization.
 * The problem is solved using three multi-objective approaches:
 *  - Lexicographic approach, solving first to minimize capital expended and second to maximize return
 *  - Blended approach, solving a weighted combination of both objectives simultaneously
 *  - Lexicographic approach, with the objective priorities reversed
 */


class CapitalBudgeting {
public:
    // Required capital for each project
    const std::vector<double> CAPITAL = { 104000, 53000, 29000, 187000, 98000, 32000, 75000, 200000 };
    // Required personnel for each project
    const std::vector<double> PERSONNEL = { 22, 12, 7, 36, 24, 10, 20, 41 };
    // Expected return of each project
    const std::vector<double> RETURN = { 124000, 74000, 42000, 188000, 108000, 56000, 88000, 225000 };

    const double CAPITAL_TARGET = 478000;   // Target capital to invest
    const double ROI_TARGET = 550000;       // Target return on investment
    const int PERSONNEL_MAX = 106;          // Available personnel

    CapitalBudgeting() {};          // Class constructor
    void model();                   // Function to make variables, constraints, and objective
    void solveThreeTimes();         // To optimize with different variations of objectives
    void printSolution(std::string title);     // To print the solution to console

private:
    XpressProblem prob;

    // Binary decision variables indicating which projects will be implemented
    std::vector<Variable> selectProject;

    Inequality personnel;  // Constraint for the number of personnel available
    LinExpression capital; // Primary objective: minimize capital expended
    LinExpression roi;     // Secondary objective: maximize return on investment
};

void CapitalBudgeting::model() {

    /* VARIABLES */

    // Binary decision variables indicating which projects will be implemented
    selectProject = prob.addVariables(static_cast<int>(CAPITAL.size()))
                        .withType(ColumnType::Binary)
                        .withName("selectProject_%d")
                        .toArray();

    /* CONSTRAINTS */

    // Constraint: at least 3 projects must be implemented
    prob.addConstraint(sum(selectProject) >= 3);

    // Constraint for the number of personnel available
    personnel = prob.addConstraint(scalarProduct(selectProject, PERSONNEL) <= PERSONNEL_MAX);

    /* OBJECTIVES */

    // Primary objective: minimize capital expended
    capital = scalarProduct(selectProject, CAPITAL);
    prob.setObjective(capital, ObjSense::Minimize);

    // Secondary objective: maximize return on investment
    roi = scalarProduct(selectProject, RETURN);
    // We add the second objective with priority=0 (same as default given
    // to the first objective) and weight=-1 (to maximize this expression)
    prob.addObjective(roi, 0, -1);
};

void CapitalBudgeting::solveThreeTimes() {
    // Set the first objective (with id=0) to priority=1 to give higher priority than 2nd objective
    prob.setObjIntControl(0, ObjControl::Priority, 1);
    // Optimize & print
    prob.optimize();
    printSolution("*** Higher priority for 'Minimize Capital' objective ***");

    // Now set the same priority for both objectives (i.e. we set the the second
    // objective (with id=1) to have priority=1). This will result in a single
    // solve using the weighted sum of the two objectives.
    prob.setObjIntControl(1, ObjControl::Priority, 1);
    // Optimize & print
    prob.optimize();
    printSolution("*** Equal priority for Both objectives ***");

    // Finally, give the first objective (with id=0) a lower priority (=0)
    prob.setObjIntControl(0, ObjControl::Priority, 0);
    // Optimize & print
    prob.optimize();
    printSolution("*** Higher priority for 'Maximize Return' objective ***");
}

void CapitalBudgeting::printSolution(std::string title) {
    std::cout << std::endl << title << std::endl;

    // Check the solution status
    if (prob.attributes.getSolveStatus() != SolveStatus::Completed) {
        std::cout << "Problem not solved" << std::endl;
    }
    else if (prob.attributes.getSolStatus() != SolStatus::Optimal && prob.attributes.getSolStatus() != SolStatus::Feasible) {
        throw std::runtime_error("Optimization failed with status " + to_string(prob.attributes.getSolStatus()));
    }

    std::cout << "  Objectives: " << prob.attributes.getObjectives() << "\t";
    std::cout << "solved objectives: " << prob.attributes.getSolvedObjs() << std::endl;

    std::vector<double> sol = prob.getSolution();
    double capitalUsed = capital.evaluate(sol);
    double roiGained = roi.evaluate(sol);

    if (capitalUsed > CAPITAL_TARGET) std::cout << "  Unable to meet Capital target" << std::endl;
    if (roiGained < ROI_TARGET)       std::cout << "  Unable to meet Return target" << std::endl;

    std::cout << "  Projects undertaken:";
    for (std::size_t p=0; p<selectProject.size(); p++) {
        if (selectProject[p].getSolution() == 1.0) {
            std::cout << " " << p;
        }
    }
    std::cout << std::endl;

    std::cout << "  Used capital: $" << capitalUsed
              << (CAPITAL_TARGET >= capitalUsed ? " unused: $" : " overused: $")
              << std::abs(CAPITAL_TARGET - capitalUsed) << std::endl;
    std::cout << "  Total return: $" << roiGained << std::endl;
    std::cout << "  Unused personnel: " << int(personnel.getSlack()) << " persons" << std::endl;
};

int main() {
    try {
        CapitalBudgeting capbudget_problem;
        capbudget_problem.model();
        capbudget_problem.solveThreeTimes();
        return 0;
    }
    catch (std::exception& e) {
        std::cout << "Exception: " << e.what() << std::endl;
        return -1;
    }
}

Back to examples browserPrevious exampleNext example