首页 > 解决方案 > 如何将 CPLEX 线性优化从 Matlab 移植到 C++

问题描述

我正在尝试将应用程序从 Matlab 移植到 C++。此应用程序包含以下形式的线性优化问题:

分钟。F * x

st A * x < Rhs

st Lb <= x <= Ub

这需要解决。

CPLEX 的 matlab API 支持将所有系数分别写为向量(FRhsLbUb)或矩阵(A)。然而,C++ 接口没有。相反,问题需要按行或按列构造。

在这里我被卡住了,因为即使对于一个小玩具问题,我似乎也无法在 Matlab 和 C++ 中得到相同的结果。

t_v_l = [1 0 0 1 0 0 1 0 0]';
t_v_m = [0 1 0 0 1 0 0 1 0]';
t_v_r = [0 0 1 0 0 1 0 0 1]';

t_h_o = [1 1 1 0 0 0 0 0 0]';
t_h_m = [0 0 0 1 1 1 0 0 0]';
t_h_u = [0 0 0 0 0 0 1 1 1]';

target = [0 1 0 1 1 1 0 1 0];

dict = [t_v_l, t_v_m, t_v_r, t_h_o, t_h_m, t_h_u];

M = size(dict, 1);
N = size(dict, 2);

F = zeros(N + M, 1);
F(N + 1 : N + M) = ones(M,1); 

Lb = sparse(zeros(N + M, 1));
Lb(N + 1 : N + M) = 0;

Ub = ones(N + M, 1);
Ub(N + 1 : N + M) = 999;

Rhs = zeros(2 * M, 1);
Rhs(    1 :     M) = -target;
Rhs(M + 1 : 2 * M) =  target;

Lhs = -999 * ones(2 * M, 1);

A = zeros(2 * M, N + M);
A(    1 :     M,     1 : N    ) = -dict;
A(M + 1 : 2 * M,     1 : N    ) =  dict;
A(    1 :     M, N + 1 : N + M) = -eye(M);
A(M + 1 : 2 * M, N + 1 : N + M) = -eye(M);

plex = Cplex('myModel');

plex.addCols(F, [], Lb, Ub);
plex.addRows(Lhs, A, Rhs);
            
plex.writeModel('ex.lp'); % Optional

plex.solve();

X = plex.Solution.x;

fprintf("Solution:\n")
disp(X');

从概念上讲,我正在尝试从不同的二进制模板重建二进制目标向量。解决方案中的前 6 个条目显示必须选择哪些模板(在本例中为模板 2 和 5)。其余条目对应于松弛变量,其中由于目标中的重叠或噪声,重建并不完全准确。上面的代码有效并给了我正确的结果。

但是,当我尝试按照示例(如 Ilodiet.cpp)在 Concert API 中产生相同的结果时,优化不再起作用。

#ifndef IL_STD
#define IL_STD
#endif
#include <cstring>
#include <ilcplex/ilocplex.h>
#include <vector>
ILOSTLBEGIN

void main()
{
    // Input Data
    std::vector<vector<float>> dict =
    {
        { 1, 0, 0, 1, 0, 0, 1, 0, 0 },
        { 0, 1, 0, 0, 1, 0, 0, 1, 0 },
        { 0, 0, 1, 0, 0, 1, 0, 0, 1 },
        { 1, 1, 1, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 1, 1, 1, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 1, 1, 1 }
    };

    std::vector<float> target =
    { 0, 1, 0, 1, 1, 1, 0, 1, 0 };

    const int M = target.size();
    const int N = dict.size();

    // Build Matrix A
    std::vector<std::vector<float>> A;
    for(int r = 0; r < M; ++r)
    {
        std::vector<float> row(N + M, 0);
        for(int c = 0; c < N; ++c)
        {
            row[c] = -dict[c][r];
        }
        row[N + r] = -1;
        A.push_back(row);
    }

    for (int r = M; r < 2 * M; ++r)
    {
        std::vector<float> row(N + M, 0);
        for (int c = 0; c < N; ++c)
        {
            row[c] = dict[c][r - M];
        }
        row[N + r - M] = -1;
        A.push_back(row);
    }

    // Build Model
    IloEnv env;
    IloModel mod(env);

    IloNumArray F(env, N + M);
    IloNumArray Lb(env, N + M);
    IloNumArray Ub(env, N + M);

    for (int i = 0; i < N; ++i)
    {
        F[i] = 0.f;

        Lb[i] = 0.f;
        Ub[i] = 1.f;
    }

    for (int i = N; i < N + M; ++i)
    {
        Lb[i] = 0.f;
        Ub[i] = 999.f;// IloInfinity;

        F[i] = 1.f;
    }

    IloNumArray Rhs(env, 2 * M);
    IloNumArray Lhs(env, 2 * M);

    for (int i = 0; i < M; ++i)
    {
        Rhs[i] = -target[i];
        Lhs[i] = -999.f;// -IloInfinity;

        Rhs[M + i] = target[i];
        Lhs[M + i] = -999.f;// -IloInfinity;
    }

    IloNumVarArray x(env, Lb, Ub, ILOFLOAT);

    IloObjective minimize = IloMinimize(env, IloScalProd(x, F));
    mod.add(IloExtractable(minimize));

    for (int r = 0; r < A.size(); ++r) 
    {
        IloExpr expr(env);
        for (int c = 0; c < A[r].size(); ++c) {
            expr += F[c] * A[r][c];
        }
        mod.add(expr <= Rhs[r]);
        expr.end();
    }

    IloCplex cplex(mod);
    cplex.exportModel("ex.lp");
    cplex.solve();
    
    std::cout << "Solution status = " << cplex.getStatus() << endl;
    std::cout << "Cost = " << cplex.getObjValue() << endl;

    std::vector<int> ret;
    for (int i = 0; i < N; i++) 
    {
        try 
        {
            std::cout << "Template i: " << cplex.getValue(x[i]) << std::endl;
        }
        catch(...)
        {
            std::cout << "Template i: 0 (optimized away)" << std::endl;
        }
    }

    for (int i = N; i < N + M; i++)
    {
        try
        {
            std::cout << "Slack i: " << cplex.getValue(x[i]) << std::endl;
        }
        catch (...)
        {
            std::cout << "Slack i: 0 (optimized away)" << std::endl;
        }
    }

    env.end();
}

输出告诉我预求解器消除了所有行和列,我的解决方案是空的。导出的问题文件看起来也完全不同。但是,如果我看一下各个数组,它们看起来完全一样。所以我想我在将信息传递给 Concert API 时做错了什么。

我的错误在哪里?任何帮助表示赞赏!


在 Matlab 中打印变量的代码(因为 IloArrays 在调试器中很难查看):

fprintf("F:\n");
disp(F');

fprintf("Lb:\n");
disp(Lb');

fprintf("Ub:\n");
disp(Ub');

fprintf("Lhs:\n");
disp(Lhs');

fprintf("Rhs:\n");
disp(Rhs');

fprintf("A:\n");
disp(A);

在 C++ 中:

std::cout << "F:" << std::endl;
for (int i = 0; i < N + M; ++i)
{
    std::cout << F[i] << ", ";
}

std::cout << "\n\nLb:" << std::endl;
for (int i = 0; i < N + M; ++i)
{
    std::cout << Lb[i] << ", ";
}

std::cout << "\n\nUb:" << std::endl;
for (int i = 0; i < N + M; ++i)
{
    std::cout << Ub[i] << ", ";
}

std::cout << "\n\nLhs:" << std::endl;
for (int i = 0; i < 2 * M; ++i)
{
    std::cout << Lhs[i] << ", ";
}

std::cout << "\n\nRhs:" << std::endl;
for (int i = 0; i < 2 * M; ++i)
{
    std::cout << Rhs[i] << ", ";
}

std::cout << "\n\nA:" << std::endl;
for (int r = 0; r < 2 * M; ++r)
{
    for(int c = 0; c < N + M; ++c)
    {
        std::cout.width(2);
        std::cout.fill(' ');
        std::cout << A[r][c] << ", ";
    }
    std::cout << "\n";
}

标签: c++matlabcplex

解决方案


推荐阅读