8.5. C++ API

This section provides MindOpt CPP API reference. Contents of the C++ API are the following:

8.5.18. Examples

Diet problem

There are many different kinds of food, each of which can provide various nutrients. How should we make decisions regarding food intake to minimize the amount spent on purchasing food while ensuring that the daily nutritional requirements of the human body are met?

Sets

  • Food set \(F\)

  • Nutrient set \(N\)

Parameters

  • The content of nutrient \(i \in N\) in one unit of food \(j\) is \(a_{ij}\) .

  • The cost of obtaining one unit of food \(j \in F\) is \(c_j\) .

Decision Variables

  • \(x_j\) is the amount of food \(j \in F\) that a person consumes daily.

Objective Function

  • Minimize the total procurement cost of food: \(\text{minimize}~ \sum_{j \in F} c_{j} x_j\) .

Constraints

  • The nutritional requirement for each nutrient \(i \in N\) must lie between the minimum value \(n_{\min,i}\) and the maximum value \(n_{\max,i}\) : \(n_{\min,i} \le \sum_{j \in F} a_{ij} x_j \le n_{\max,i}, ~~\forall i \in N\) .


#include <iostream>
#include "MindoptCpp.h"
#include <map>

using namespace std;

// Define requirements
map<string, pair<double, double>> req = {
        {"Cal",     {2000, MDO_INFINITY } },
        {"Carbo",   {350,           375 } },
        {"Protein", {55,   MDO_INFINITY } },
        {"VitA",    {100,  MDO_INFINITY } },
        {"VitC",    {100,  MDO_INFINITY } },
        {"Calc",    {100,  MDO_INFINITY } },
        {"Iron",    {100,  MDO_INFINITY } },
        {"Volume",  {-MDO_INFINITY,  75 }}
};

// Define foods
map<string, tuple<double, double, double>> food = {
        {"Cheeseburger",    {0, MDO_INFINITY, 1.84 } },
        {"HamSandwich",     {0, MDO_INFINITY, 2.19 } },
        {"Hamburger",       {0, MDO_INFINITY, 1.84 } },
        {"FishSandwich",    {0, MDO_INFINITY, 1.44 } },
        {"ChickenSandwich", {0, MDO_INFINITY, 2.29 } },
        {"Fries",           {0, MDO_INFINITY, 0.77 } },
        {"SausageBiscuit",  {0, MDO_INFINITY, 1.29 } },
        {"LowfatMilk",      {0, MDO_INFINITY, 0.60 } },
        {"OrangeJuice",     {0, MDO_INFINITY, 0.72 }}
};

// Define requirements and food nutrient values
map<pair<string, string>, double> req_value = {
        {{"Cal",     "Cheeseburger" },    510 },
        {{"Cal",     "HamSandwich" },     370 },
        {{"Cal",     "Hamburger" },       500 },
        {{"Cal",     "FishSandwich" },    370 },
        {{"Cal",     "ChickenSandwich" }, 400 },
        {{"Cal",     "Fries" },           220 },
        {{"Cal",     "SausageBiscuit" },  345 },
        {{"Cal",     "LowfatMilk" },      110 },
        {{"Cal",     "OrangeJuice" },     80 },
        {{"Carbo",   "Cheeseburger" },    34 },
        {{"Carbo",   "HamSandwich" },     35 },
        {{"Carbo",   "Hamburger" },       42 },
        {{"Carbo",   "FishSandwich" },    38 },
        {{"Carbo",   "ChickenSandwich" }, 42 },
        {{"Carbo",   "Fries" },           26 },
        {{"Carbo",   "SausageBiscuit" },  27 },
        {{"Carbo",   "LowfatMilk" },      12 },
        {{"Carbo",   "OrangeJuice" },     20 },
        {{"Protein", "Cheeseburger" },    28 },
        {{"Protein", "HamSandwich" },     24 },
        {{"Protein", "Hamburger" },       25 },
        {{"Protein", "FishSandwich" },    14 },
        {{"Protein", "ChickenSandwich" }, 31 },
        {{"Protein", "Fries" },           3 },
        {{"Protein", "SausageBiscuit" },  15 },
        {{"Protein", "LowfatMilk" },      9 },
        {{"Protein", "OrangeJuice" },     1 },
        {{"VitA",    "Cheeseburger" },    15 },
        {{"VitA",    "HamSandwich" },     15 },
        {{"VitA",    "Hamburger" },       6 },
        {{"VitA",    "FishSandwich" },    2 },
        {{"VitA",    "ChickenSandwich" }, 8 },
        {{"VitA",    "Fries" },           0 },
        {{"VitA",    "SausageBiscuit" },  4 },
        {{"VitA",    "LowfatMilk" },      10 },
        {{"VitA",    "OrangeJuice" },     2 },
        {{"VitC",    "Cheeseburger" },    6 },
        {{"VitC",    "HamSandwich" },     10 },
        {{"VitC",    "Hamburger" },       2 },
        {{"VitC",    "FishSandwich" },    0 },
        {{"VitC",    "ChickenSandwich" }, 15 },
        {{"VitC",    "Fries" },           15 },
        {{"VitC",    "SausageBiscuit" },  0 },
        {{"VitC",    "OrangeJuice" },     4 },
        {{"VitC",    "LowfatMilk" },      120 },
        {{"Calc",    "Cheeseburger" },    20 },
        {{"Calc",    "HamSandwich" },     20 },
        {{"Calc",    "Hamburger" },       25 },
        {{"Calc",    "FishSandwich" },    15 },
        {{"Calc",    "ChickenSandwich" }, 15 },
        {{"Calc",    "Fries" },           0 },
        {{"Calc",    "SausageBiscuit" },  20 },
        {{"Calc",    "LowfatMilk" },      30 },
        {{"Calc",    "OrangeJuice" },     2 },
        {{"Iron",    "Cheeseburger" },    20 },
        {{"Iron",    "HamSandwich" },     20 },
        {{"Iron",    "Hamburger" },       20 },
        {{"Iron",    "FishSandwich" },    10 },
        {{"Iron",    "ChickenSandwich" }, 8 },
        {{"Iron",    "Fries" },           2 },
        {{"Iron",    "SausageBiscuit" },  15 },
        {{"Iron",    "LowfatMilk" },      0 },
        {{"Iron",    "OrangeJuice" },     2 },
        {{"Volume",  "Cheeseburger" },    4 },
        {{"Volume",  "HamSandwich" },     7.5 },
        {{"Volume",  "Hamburger" },       3.5 },
        {{"Volume",  "FishSandwich" },    5 },
        {{"Volume",  "ChickenSandwich" }, 7.3 },
        {{"Volume",  "Fries" },           2.6 },
        {{"Volume",  "SausageBiscuit" },  4.1 },
        {{"Volume",  "LowfatMilk" },      8 },
        {{"Volume",  "OrangeJuice" },     12}
};

int main(int argc, char *argv[]) {
    try {
        // Create a model
        MDOEnv env = MDOEnv();
        MDOModel m = MDOModel(env);

        // Add variables
        map<string, MDOVar> variable;
        map<string, tuple<double, double, double>>::iterator food_it;
        for (food_it = food.begin(); food_it != food.end(); ++food_it) {
            string food_name = food_it->first;
            tuple<double, double, double> food_data = food_it->second;
            variable[food_name] = m.addVar(get<0>(food_data), get<1>(food_data),
                                           0.0, MDO_CONTINUOUS, food_name);
        }

        // Add constraints
        // Ensure that the intake of each nutrient is above the given lower bound and below the given upper bound
        map<string, MDOConstr> cons;
        map<string, pair<double, double>>::iterator req_it;
        for (req_it = req.begin(); req_it != req.end(); ++req_it) {
            string req_name = req_it->first;
            pair<double, double> req_data = req_it->second;
            MDOLinExpr expr = 0;
            for (food_it = food.begin(); food_it != food.end(); ++food_it) {
                string food_name = food_it->first;
                tuple<double, double, double> food_data = food_it->second;
                expr += variable[food_name] * req_value[{req_name, food_name}];
            }
            cons[req_name] = m.addRange(expr, req_data.first, req_data.second);
        }

        // Add objective function
        MDOLinExpr objective = 0;

        for (food_it = food.begin(); food_it != food.end(); ++food_it) {
            string food_name = food_it->first;
            tuple<double, double, double> food_data = food_it->second;
            objective += variable[food_name] * get<2>(food_data);
        }
        m.setObjective(objective, 1);
        m.write("Test_cpp.mps");

        // Optimize the model
        m.optimize();

        // Print the results
        map<string, MDOVar>::iterator it;
        for (it = variable.begin(); it != variable.end(); ++it) {
            string food_name = it->first;
            MDOVar var = it->second;
            cout << "Amount of " << food_name << " intake: " << var.get(MDO_DoubleAttr_X) << endl;
        }
        cout << "Total meal cost: " << m.get(MDO_DoubleAttr_ObjVal) << endl;
        for (req_it = req.begin(); req_it != req.end(); ++req_it) {
            string req_name = req_it->first;
            pair<double, double> req_data = req_it->second;
            MDOLinExpr expr = 0;
            for (food_it = food.begin(); food_it != food.end(); ++food_it) {
                string food_name = food_it->first;
                expr += variable[food_name] * req_value[{req_name, food_name}];
            }
        }
    } catch (MDOException &e) {
        cout << "Error code = " << e.getErrorCode() << endl;
        cout << e.getMessage() << endl;
    } catch (...) {
        cout << "Error during optimization." << endl;
    }
    return 0;
}

Facility problem

Currently, there are two shopping malls, and the locations of the malls have already been determined. There are several alternative locations for constructing warehouses, whose coordinates and construction costs are known. We assume that the transportation cost from the warehouses to the shopping malls is independent of the quantity of goods but is related to the distance between them. Please find the minimum cost scheme for warehouse construction and transportation.

Sets

  • Alternative Warehouses \(F\)

  • Shopping Malls \(M\)

Parameters

  • The transportation cost from warehouse \(i \in F\) to shopping mall \(j \in M\) is \(a_{ij}\) .

  • The cost for constructing warehouse \(i \in F\) is \(c_i\) .

  • The demand for goods at shopping mall \(j \in M\) is \(d_j\) .

Decision Variables

  • \(x_i\) indicates whether a warehouse is constructed at alternative location \(i \in F\) . It can take a value of 0 or 1, where 0 means not to build and 1 means to build.

  • \(y_{ij}\) represents the quantity of goods transported from warehouse \(i \in F\) to shopping mall \(j \in M\) .

Objective Function

Minimize the combined cost of warehouse construction and goods transportation:

\(\text{minimize}~ \sum_{i\in F} c_{i}x_i + \sum_{i\in F,j \in M} a_{ij}y_{ij}\)

Constraints

\(\sum_{i\in F} y_{ij} = d_{j}, ~~ \forall j\in M\)

\(x_i d_j - \sum_{k\in M} y_{ik} = 0, ~~ \forall i \in F, j \in M\)


#include <iostream>
#include "MindoptCpp.h"
#include <map>
#include <vector>
#include <cmath>

using namespace std;
// The objective of this example is to find the minimum cost solution for warehouse construction and transportation

// There are two shopping malls with fixed locations at (0, 1.7) and (1.4, 2.9), requiring 100 and 200 units of goods weight.
map<vector<double>, int> marketInfo = {
        {vector<double>{0.0, 1.7}, 100},
        {vector<double>{1.4, 2.9}, 200}
};

vector<vector<double>> marketKeys = vector<vector<double>>{
        vector<double>{0.0, 1.7},
        vector<double>{1.4, 2.9},
};
size_t marketNum = marketInfo.size();

// Optional location and construction cost of warehouses
map<vector<int>, double> facilitiesInfo = {
        {vector<int>{0, 1}, 3.0},
        {vector<int>{0, 2}, 1.0},
        {vector<int>{1, 0}, 1.5},
        {vector<int>{1, 1}, 1.3},
        {vector<int>{1, 2}, 1.8},
        {vector<int>{2, 0}, 1.6},
        {vector<int>{2, 1}, 1.1},
        {vector<int>{2, 2}, 1.9},
};

vector<vector<int>> facilitiesKeys = {
        vector<int>{0, 1},
        vector<int>{0, 2},
        vector<int>{1, 0},
        vector<int>{1, 1},
        vector<int>{1, 2},
        vector<int>{2, 0},
        vector<int>{2, 1},
        vector<int>{2, 2}
};
int facilitiesNum = facilitiesInfo.size();

double transportFeePerM = 1.23;

int main(int argc, char *argv[]) {
    // Define requirements
    try {
        MDOEnv env;
        MDOModel model(env);
        model.set(MDO_StringAttr_ModelName, "Facility");
        // Add Variables
        vector<MDOVar> xVars(facilitiesNum);
        for (int j = 0; j < facilitiesNum; j++) {
            xVars[j] = model.addVar(0, MDO_INFINITY, 0, MDO_BINARY, "Facility" + to_string(j));
        }
        vector<vector<MDOVar>> yVars(marketNum, vector<MDOVar>(facilitiesNum));
        for (int i = 0; i < marketNum; i++) {
            for (int j = 0; j < facilitiesNum; j++) {
                yVars[i][j] = model.addVar(0, MDO_INFINITY, 0, MDO_CONTINUOUS, to_string(i) + to_string(j));
            }
        }

        // Add Constraints
        for (int i = 0; i < marketNum; i++) {
            // Constraint 1 : ensure that all demand of the shopping centers is satisfied
            MDOLinExpr linExpr;
            vector<double> coeffs;
            vector<MDOVar> vars;
            for (int j = 0; j < facilitiesNum; j++) {
                coeffs.push_back(1);
                vars.push_back(yVars[i][j]);
                MDOLinExpr lhe = 1.0 / marketInfo[marketKeys[i]] * yVars[i][j];
                model.addConstr(lhe - xVars[j], MDO_LESS_EQUAL, 0,
                                "is_built[" + to_string(i) + "," + to_string(j) + "]");
            }
            linExpr.addTerms(coeffs.data(), vars.data(), coeffs.size());
            // Constraint 2 : If this warehouse is not constructed, the amount of goods transported from this location must be 0
            model.addConstr(linExpr, MDO_EQUAL, marketInfo[marketKeys[i]], "is_satisfy_" + to_string(i));
        }

        // Add Objective Function: Minimize the sum of transportation costs and facility construction costs"。
        // Assume that the transportation cost from A to B depends only on the distance and is independent of the weight of the goods
        MDOLinExpr objective;
        vector<double> coeffs;
        vector<MDOVar> vars;
        for (int j = 0; j < facilitiesNum; j++) {
            coeffs.push_back(facilitiesInfo[facilitiesKeys[j]]);
            vars.push_back(xVars[j]);
        }
        for (int j = 0; j < facilitiesNum; j++) {
            for (int i = 0; i < marketNum; i++) {
                double x1 = marketKeys[i][0] - facilitiesKeys[j][0];
                double x2 = marketKeys[i][1] - facilitiesKeys[j][1];
                coeffs.push_back(sqrt(x1 * x1 + x2 * x2) * transportFeePerM);
                vars.push_back(yVars[i][j]);
            }
        }
        objective.addTerms(coeffs.data(), vars.data(), coeffs.size());
        model.setObjective(objective, MDO_MINIMIZE);

        // Start Optimizing
        model.optimize();

        // Print Result
        for (int i = 0; i < facilitiesNum; i++) {
            MDOVar x = xVars[i];
            if (x.get(MDO_DoubleAttr_X) == 1) {
                cout << "The No." << i << " warehouse should be built at (" << facilitiesKeys[i][0] << ", "
                     << facilitiesKeys[i][1] << ")" << endl;
            }
        }
        model.write("TestFacility.mps");
        cout << model.get(MDO_DoubleAttr_ObjVal);
    } catch (MDOException &e) {
        cout << "Error code = " << e.getErrorCode() << endl;
        cout << e.getMessage() << endl;
    } catch (...) {
        cout << "Error during optimization." << endl;
    }
    return 0;
}

WorkForce problem

In a week, the number of workers needed by the factory varies each day. It is currently known how much each worker earns per day and the dates on which they can attend work. The goal is to calculate how to schedule the workers in order to meet the factory’s operational requirements while minimizing wage costs.

Sets:

  • Days of the week \(D = 1, 2, 3, \ldots, 7\)

  • Number of workers needed by the factory \(N\)

  • Workers \(S\)

Parameters:

  • The number of workers needed by the factory on day \(i \in D\) is \(n_i\) .

  • The daily wage of worker \(j \in S\) is \(s_j\) .

  • A sequence indicating the available working times for workers, totaling \(T\) pairs of (worker, day) denoted as \((d_i, t_i)\) , where \(d_i \in S\) and \(t_i \in D\) , for \(i = 1, 2, 3, \ldots, T\) .

Objective Function:

Minimize the total wages paid: \(\text{minimize}~ \sum_{i=1}^{T} x_i s_{d_i}\)

Decision Variables:

  • \(x_i (i = 1, 2, 3, \ldots, T)\) indicates whether the worker in the available working time sequence shows up on that day. Its values must be 0 or 1, where 0 indicates the worker does not attend, and 1 indicates the worker does attend.

Constraints:

\(\sum_{d_i=r} x_i = n_{r}, ~~\forall r\in D\)


#include <iostream>
#include "MindoptCpp.h"
#include "map"

using namespace std;

// Number of required workers for each day
map<string, int> workers_per_day = {
        { "Monday",    3 },
        { "Tuesday",   1 },
        { "Wednesday", 4 },
        { "Thursday",  2 },
        { "Friday",    1 },
        { "Saturday",  3 },
        { "Sunday",    3 }
 };

// Daily wage of each worker
map<string, int> pay = {
        { "Xiaoming", 13 },
        { "Huahua",   10 },
        { "HongHong", 11 },
        { "Dahua",    8 },
        { "Lihua",    9 },
        { "Niuniu",   14 },
        { "Gouzi",    14 }
 };

// Available days for each worker
vector<tuple<string, string>> availability = {
        { "Xiaoming",    "Tuesday" },
        { "Xiaoming",  "Wednesday" },
        { "Xiaoming",     "Friday" },
        { "Xiaoming",     "Sunday" },
        { "Huahua",       "Monday" },
        { "Huahua",      "Tuesday" },
        { "Huahua",       "Friday" },
        { "Huahua",     "Saturday" },
        { "HongHong",  "Wednesday" },
        { "HongHong",   "Thursday" },
        { "HongHong",     "Friday" },
        { "HongHong",     "Sunday" },
        { "Dahua",       "Tuesday" },
        { "Dahua",     "Wednesday" },
        { "Dahua",        "Friday" },
        { "Dahua",      "Saturday" },
        { "Lihua",        "Monday" },
        { "Lihua",       "Tuesday" },
        { "Lihua",     "Wednesday" },
        { "Lihua",      "Thursday" },
        { "Lihua",        "Friday" },
        { "Lihua",        "Sunday" },
        { "Niuniu",       "Monday" },
        { "Niuniu",      "Tuesday" },
        { "Niuniu",    "Wednesday" },
        { "Niuniu",     "Saturday" },
        { "Gouzi",        "Monday" },
        { "Gouzi",       "Tuesday" },
        { "Gouzi",     "Wednesday" },
        { "Gouzi",        "Friday" },
        { "Gouzi",      "Saturday" },
        { "Gouzi",        "Sunday" }
 };


int main(int argc, char *argv[]) {
    try {
        MDOEnv env = MDOEnv();
        MDOModel model = MDOModel(env);

        // Add Variables
        // x[(worker, day)] represents whether this worker is scheduled for this day.
        // Using worker-day pair to initialize variables ensure that each person works only at the time they are available
        map<pair<string, string>, MDOVar> x;
        vector<tuple<string, string>>::iterator availability_it;
        for (availability_it = availability.begin(); availability_it != availability.end(); ++availability_it) {
            string worker = get<0>(*availability_it);
            string day = get<1>(*availability_it);
            x[{ worker, day }] = model.addVar(0.0, 1.0, 0.0, MDO_BINARY, "schedule");
         }

        // Add Constraints
        // Constraint : ensure that each day has enough workforce
        map<string, int>::iterator workers_per_day_it;
        for (workers_per_day_it = workers_per_day.begin(); workers_per_day_it != workers_per_day.end(); ++workers_per_day_it) {
            string day = workers_per_day_it->first;
            int num_workers = workers_per_day_it->second;
            MDOLinExpr expr = 0;

            for (availability_it = availability.begin(); availability_it != availability.end(); ++availability_it) {
                string worker = get<0>(*availability_it);
                string d = get<1>(*availability_it);
                if (d == day) {
                    expr += x[{ worker, day }];
                 }
             }
            model.addConstr(expr == num_workers);
         }

        // Add Objective Function
        MDOLinExpr objective = 0;
        for (availability_it = availability.begin(); availability_it != availability.end(); ++availability_it) {
            string worker = get<0>(*availability_it);
            string day = get<1>(*availability_it);
            objective += pay[worker] * x[{ worker, day }];
         }
        model.setObjective(objective, MDO_MINIMIZE);

        // Start Optimizing
        model.optimize();
        model.write("test_cpp.mps");

        // Print Result
        for (availability_it = availability.begin(); availability_it != availability.end(); ++availability_it) {
            string worker = get<0>(*availability_it);
            string day = get<1>(*availability_it);
            if (x[{ worker, day }].get(MDO_DoubleAttr_X) > 0.5) {
                cout << worker << " should work at " << day << endl;
             }
         }
        cout << "The total cost is " << model.get(MDO_DoubleAttr_ObjVal) << endl;

     } catch (MDOException& e) {
        cout << "Error code = " << e.getErrorCode() << endl;
        cout << e.getMessage() << endl;
     } catch (...) {
        cout << "Error during optimization." << endl;
     }
    return 0;
 }