c++ - 如何将 CPLEX 线性优化从 Matlab 移植到 C++
问题描述
我正在尝试将应用程序从 Matlab 移植到 C++。此应用程序包含以下形式的线性优化问题:
分钟。F * x
st A * x < Rhs
st Lb <= x <= Ub
这需要解决。
CPLEX 的 matlab API 支持将所有系数分别写为向量(F、Rhs、Lb和Ub)或矩阵(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# - 错误 CS1061 'Expence' 不包含 'Expence_Name' 的定义并且没有扩展方法 'Expence_Name'
- java - 在不使用方法的情况下删除数组重复项
- java - 如何解决创建和连接数据库时无法建立与jdbc的连接错误?
- .htaccess - SLIM 3 logn 中间件进入循环
- php - 在 maatwebsite 中换行不适用于 laravel
- react-native - 如何在 SwitchNavigator 下不同 StackNavigator 的屏幕之间导航?
- node.js - 如果 forEach 使用 Promise.all 而不是 async/await
- c# - 点击时没有 URL 参数
- python - 字符串连接如何在 python 中工作?
- junit4 - Maven surefire 插件在最后输出所有错误 - 在大型测试中导致 OOME