8.4. C API

本章节提供MindOpt的C API手册,内容见下文。

8.4.6. Examples

8.4.6.1. 定制食谱

//include <stdio.h>
#include "Mindopt.h"
#include <stdlib.h>

#define CHECK_RESULT(code) { int res = code; if (res != 0) { fprintf(stderr, "Bad code: %d \n", res); exit(res); } }
#define NFOOD 9
#define NNUTRITION 8
#define MODEL_NAME "diet"
#define MODEL_SENSE "ModelSense"
#define STATUS "Status"
#define OBJ_VAL "ObjVal"
#define X "X"

int main(void) {
    MDOenv *env;
    MDOemptyenv(&env);
    MDOstartenv(env);

    // 建立模型
    MDOmodel *m;

    // 初始化数据
    double demand_ub[] = {MDO_INFINITY, 375, MDO_INFINITY, MDO_INFINITY, MDO_INFINITY, MDO_INFINITY, MDO_INFINITY, 75};
    double demand_lb[] = {2000, 350, 55, 100, 100, 100, 100, -MDO_INFINITY};
    const char *nutrition_name[] = {"Calories", "Carbohydrates", "Protein", "VitA", "VitC", "Calcium", "Iron", "Volume"};

    char *food_name[] = {"Cheeseburger", "HamSandwich", "Hamburger", "FishSandwich", "ChickenSandwich", "Fries",
                          "SausageBiscuit", "LowfatMilk", "OrangeJuice"};
    double food_price[] = {1.84, 2.19, 1.84, 1.44, 2.29, 0.77, 1.29, 0.60, 0.72};

    double req_value[NFOOD][NNUTRITION] = {
            {510.0, 34.0, 28.0, 15.0, 6.0,   30.0, 20.0, 4.0},
            {370.0, 35.0, 24.0, 15.0, 10.0,  20.0, 20.0, 7.5},
            {500.0, 42.0, 25.0, 6.0,  2.0,   25.0, 20.0, 3.5},
            {370.0, 38.0, 14.0, 2.0,  0.0,   15.0, 10.0, 5.0},
            {400.0, 42.0, 31.0, 8.0,  15.0,  15.0, 8.0,  7.3},
            {220.0, 26.0, 3.0,  0.0,  15.0,  0.0,  2.0,  2.6},
            {345.0, 27.0, 15.0, 4.0,  0.0,   20.0, 15.0, 4.1},
            {110.0, 12.0, 9.0,  10.0, 120.0, 30.0, 0.0,  8.0},
            {80.0,  20.0, 1.0,  2.0,  4.0,   2.0,  2.0,  12.0}};

    int *cbeg, *cind, idx;
    double *cval;
    int i, j, status;
    double obj, x;
    // 初始化模型同时添加决策变量
    CHECK_RESULT(MDOnewmodel(env, &m, MODEL_NAME, NFOOD, food_price, 0, NULL, NULL, NULL));

    // 添加约束
    // 应满足每日获取的各种营养素在建议的范围内
    cbeg = (int *)malloc(sizeof(int) * NNUTRITION);
    cind = (int *)malloc(sizeof(int) * NNUTRITION * (NFOOD));
    cval = (double *)malloc(sizeof(double) * NNUTRITION * (NFOOD));

    idx = 0;
    for (i = 0; i < NNUTRITION; i++) {
        // Start index of each constraint
        cbeg[i] = idx;
        for (j = 0; j < NFOOD; ++j) {
            cind[idx] = j;
            cval[idx++] = req_value[j][i];
        }
    }

    CHECK_RESULT(MDOaddrangeconstrs(m, NNUTRITION, idx, cbeg, cind, cval, demand_lb, demand_ub, nutrition_name));

    // 添加目标函数
    CHECK_RESULT(MDOsetintattr(m, MODEL_SENSE, MDO_MINIMIZE));
    CHECK_RESULT(MDOoptimize(m));

    // 打印结果
    CHECK_RESULT(MDOgetintattr(m, STATUS, &status));
    if (status == MDO_OPTIMAL) {
        CHECK_RESULT(MDOgetdblattr(m, OBJ_VAL, &obj));

        printf("The total cost is %f \n", obj);
        for (i = 0; i < NFOOD; ++i) {
            CHECK_RESULT(MDOgetdblattrelement(m, X, i, &x));
            printf("You should buy %f unit of %s \n", x, food_name[i]);
        }
    } else {
        printf("No feasible solution exists \n");
    }

    // 释放被分配的内存
    free(cbeg);
    free(cind);
    free(cval);
    MDOfreemodel(m);
    MDOfreeenv(env);
    return 0;
}

8.4.6.2. 设施选址

#include <stdio.h>
#include "Mindopt.h"
#include <stdlib.h>

#define CHECK_RESULT(code) { int res = code; if (res != 0) { fprintf(stderr, "Bad code: %d \n", res); exit(res); } }
#define FACILITY_NUM  8
#define MARKET_NUM  2
#define MODEL_NAME "facility"
#define VAR_TYPE "VType"
#define MODEL_SENSE "ModelSense"
#define STATUS "Status"
#define OBJ "Obj"
#define OBJ_VAL "ObjVal"
#define VAR_NAME "VarName"
#define X "X"

double calculate_transportation_fee(double *pos1, double *pos2, double transport_fee_per_m) {
    double x1 = pos1[0] - pos2[0];
    double x2 = pos1[1] - pos2[1];
    return (x1 * x1 + x2 * x2) * transport_fee_per_m;
}


// 本例子的目标是为了找到最小成本的仓库建造和运输方案
int main(void) {
    MDOenv *env = NULL;
    MDOemptyenv(&env);
    MDOstartenv(env);
    // 建立模型
    MDOmodel *m = NULL;

    // 有两个商场,商场的位置已经确定,分别是(0, 1.7)和(1.4, 2.9), 所需要的货物重量为100单位和200单位
    double market_location[MARKET_NUM][2] = {
            {0.0, 1.7},
            {1.4, 2.9}};
    int market_demand[MARKET_NUM] = {100, 200};

    // 仓库位置和建造成本
    double facility_location[FACILITY_NUM][2] = {
            {0, 1},
            {0, 2},
            {1, 0},
            {1, 1},
            {1, 2},
            {2, 0},
            {2, 1},
            {2, 2}};
    double facility_expense[FACILITY_NUM] = {3.0, 1.0, 1.5, 1.3, 1.8, 1.6, 1.1, 1.9};

    const double transport_fee_per_m = 1.23;

    int *cbeg, *cind, *cbeg2, *cind2, idx, col, i, j, status;
    double *cval, *cval2, *rhs, *rhs2, obj, x;
    char *sense, *sense2, var_name[25];


    // 初始化模型同时添加决策变量
    CHECK_RESULT(MDOnewmodel(env, &m, MODEL_NAME, FACILITY_NUM * (1 + MARKET_NUM), NULL, NULL, NULL, NULL, NULL));

    // 初始化决策变量
    // x代表是否在该地建仓库
    for (i = 0; i < FACILITY_NUM; ++i) {
        CHECK_RESULT(MDOsetcharattrelement(m, VAR_TYPE, i, MDO_BINARY));
        CHECK_RESULT(MDOsetdblattrelement(m, OBJ, i, facility_expense[i] +
        calculate_transportation_fee(market_location[0], facility_location[i], transport_fee_per_m) +
        calculate_transportation_fee(market_location[1], facility_location[i], transport_fee_per_m)));
        sprintf(var_name, "Position%d", i);
        CHECK_RESULT(MDOsetstrattrelement(m, VAR_NAME, i, var_name));
    }

    // y代表从j仓库运向i商场的货物量,值的类型为CONTINUOUS类型,下限为0代表不能从j仓库运送小于0单位的货物到i商场
    for (i = 0; i < FACILITY_NUM; ++i) {
        for (j = 0; j < MARKET_NUM; j++) {
            sprintf(var_name, "Transportation_%d_to_%d", i, j);
            CHECK_RESULT(MDOsetstrattrelement(m, VAR_NAME, FACILITY_NUM * (1 + j) + i, var_name));
        }
    }

    // 增加约束
    cbeg = (int *)malloc(sizeof(int) * FACILITY_NUM * MARKET_NUM);
    cind = (int *)malloc(sizeof(int) * (FACILITY_NUM * MARKET_NUM * 2));
    cval = (double *)malloc(sizeof(double) * (FACILITY_NUM * MARKET_NUM * 2));
    rhs = (double *)malloc(sizeof(double) * FACILITY_NUM * MARKET_NUM);
    sense = (char *)malloc(sizeof(char) * FACILITY_NUM * MARKET_NUM);
    printf("%d \n", FACILITY_NUM * MARKET_NUM);
    idx = 0;

    // 约束1 已经决定建造的仓库必须满足所有商场的货物需求
    for (i = 0; i < FACILITY_NUM; i++) {
        for (j = 0; j < MARKET_NUM; ++j) {
            col = i * MARKET_NUM + j;
            cbeg[col] = idx;
            rhs[col] = 0;
            sense[col] = MDO_LESS_EQUAL;
            cind[idx] = FACILITY_NUM * (1 + j) + i;
            cval[idx++] = 1;
            cind[idx] = i;
            cval[idx++] = -market_demand[j];
        }
    }
    CHECK_RESULT(MDOaddconstrs(m, FACILITY_NUM*MARKET_NUM, idx, cbeg, cind, cval, sense, rhs, NULL));

    // 约束2 如果不建仓库,则此仓库位置运送给所有商场的货物为0
    cbeg2 = (int *)malloc(sizeof(int) * MARKET_NUM);
    cind2 = (int *)malloc(sizeof(int) * MARKET_NUM * FACILITY_NUM);
    cval2 = (double *)malloc(sizeof(double) * MARKET_NUM * FACILITY_NUM);
    rhs2 = (double *)malloc(sizeof(double) * MARKET_NUM);
    sense2 = (char *)malloc(sizeof(char) * MARKET_NUM);

    idx = 0;
    for (j = 0; j < MARKET_NUM; ++j) {
        cbeg2[j] = idx;
        rhs2[j] = market_demand[j];
        sense2[j] = MDO_EQUAL;
        for (i = 0; i < FACILITY_NUM; i++) {
            cind2[idx] = FACILITY_NUM * (1 + j) + i;
            cval2[idx++] = 1;
        }
    }
    CHECK_RESULT(MDOaddconstrs(m, MARKET_NUM, idx, cbeg2, cind2, cval2, sense2, rhs2, NULL));

    // 开始优化
    CHECK_RESULT(MDOsetintattr(m, MODEL_SENSE, MDO_MINIMIZE));
    CHECK_RESULT(MDOwrite(m, "test_c.mps"));
    CHECK_RESULT(MDOoptimize(m));

    CHECK_RESULT(MDOgetintattr(m, STATUS, &status));

    // 打印结果
    if (status == MDO_OPTIMAL) {
        CHECK_RESULT(MDOgetdblattr(m, OBJ_VAL, &obj));
        printf("The total cost is %f \n", obj);
        for (i = 0; i < FACILITY_NUM; ++i) {
            CHECK_RESULT(MDOgetdblattrelement(m, "X", i, &x));
            if (x) {
                printf("The No.%d warehouse should be built. \n", i);
            }
        }
    } else {
        printf("No feasible solution exists \n");
    }

    free(cbeg);
    free(cind);
    free(cval);
    free(rhs);
    free(sense);
    free(cbeg2);
    free(cind2);
    free(cval2);
    free(rhs2);
    free(sense2);
    MDOfreemodel(m);
    MDOfreeenv(env);
    return 0;
}

8.4.6.3. 人力分配

#include <stdio.h>
#include "Mindopt.h"
#include <stdlib.h>

#define CHECK_RESULT(code) { int res = code; if (res != 0) { fprintf(stderr, "Bad code: %d \n", res); exit(res); } }
#define MODEL_NAME "workforce"
#define MODEL_SENSE "ModelSense"
#define STATUS "Status"
#define OBJ_VAL "ObjVal"
#define X "X"
#define WORKERS_NUM 7
#define DAY_NUM 7

int main(void) {
    MDOenv *env = NULL;
    MDOemptyenv(&env);
    MDOstartenv(env);
    MDOmodel *m = NULL;
    int id = 0;
    char var_name[20];
    int i, j;

    // 每天需要的人力数
    const char *day_name[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
    double workers_per_day[] = {3, 1, 4, 2, 1, 3, 3};

    // 每个工人一天的工资
    char *workers_name[] = {"Xiaoming", "Huahua", "HongHong", "Dahua", "Lihua", "Niuniu", "Gouzi"};
    double workers_pay[] = {13, 10, 11, 8, 9, 14, 14};

    // 每个工人可以出勤的时间
    int availability[WORKERS_NUM][DAY_NUM];
    int non_zero_num = 32;
    int non_zero_col[] = {0, 0, 0, 0, 1, 1, 1, 1, 2,2, 2,
                          2, 3, 3, 3, 3, 4,4, 4, 4, 4,
                          4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6};
    int non_zero_row[] = {1, 2, 4, 6, 0, 1, 4, 5, 2, 3, 4,
                          6, 1, 2, 4, 5, 0, 1, 2, 3, 4,
                          6, 0, 1, 2, 5, 0, 1, 2, 4, 5, 6};
    for (j = 0; j < non_zero_num; ++j) {
        availability[non_zero_col[j]][non_zero_row[j]] = 1;
    }

    int *cbeg, *cind, idx, status;
    double *cval, *rhs, obj, x;
    char *sense;


    // 初始化模型同时添加决策变量
    CHECK_RESULT(MDOnewmodel(env, &m, MODEL_NAME, 0, NULL, NULL, NULL, NULL, NULL));

    // 初始化决策变量,并设置其在目标函数中的系数
    for (j = 0; j < DAY_NUM; ++j) {
        for (i = 0; i < WORKERS_NUM; ++i) {
            if (availability[i][j]) {
                sprintf(var_name, "%s.%s", workers_name[i], day_name[j]);
                CHECK_RESULT(MDOaddvar(m, 0, NULL, NULL, workers_pay[i], 0, 1, MDO_BINARY, (const char*)var_name));
                id++;
            }
        }
    }

    // 增加约束
    // 约束: 满足每天的人力需求
    cbeg = (int *)malloc(sizeof(int) * DAY_NUM);
    cind = (int *)malloc(sizeof(int) * id);
    cval = (double *)malloc(sizeof(double) * id);
    rhs = (double *)malloc(sizeof(double) * DAY_NUM);
    sense = (char *)malloc(sizeof(char) * DAY_NUM);
    idx = 0;
    id = 0;

    for (i = 0; i < DAY_NUM; ++i) {
        cbeg[i] = idx;
        rhs[i] = workers_per_day[i];
        sense[i] = MDO_EQUAL;
        for (j = 0; j < WORKERS_NUM; ++j) {
            if (availability[j][i]) {
                cind[idx] = id;
                cval[idx++] = 1;
                id++;
            }
        }
    }

    CHECK_RESULT(MDOaddconstrs(m, DAY_NUM, idx, cbeg, cind, cval, sense, rhs, day_name));

    // 开始优化
    CHECK_RESULT(MDOsetintattr(m, MODEL_SENSE, MDO_MINIMIZE));
    CHECK_RESULT(MDOoptimize(m));
    CHECK_RESULT(MDOwrite(m, "test_c.mps"));

    CHECK_RESULT(MDOgetintattr(m, STATUS, &status));

    // 打印结果
    if (status == MDO_OPTIMAL) {
        CHECK_RESULT(MDOgetdblattr(m, OBJ_VAL, &obj));
        printf("The total cost is %f \n", obj);
        for (j = 0; j < WORKERS_NUM; ++j) {
            char *worker_name = workers_name[j];
            for (i = 0; i < DAY_NUM; ++i) {
                CHECK_RESULT(MDOgetdblattrelement(m, X, i, &x));
                if (x == 1) {
                    printf("%s should work at %s \n", worker_name, day_name[i]);
                }
            }
        }
    } else {
        printf("No feasible solution exists \n");
    }

    // 释放被分配的内存
    free(cbeg);
    free(cind);
    free(cval);
    free(rhs);
    free(sense);
    MDOfreemodel(m);
    MDOfreeenv(env);
    return 0;
}