在 GUROBI 的 C API 中添加约束需要太长时间
adding constraints in C API of GUROBI takes too long
我的模型有 113000 个约束和 127940 个变量(28400 个连续变量和 99540 个整数)。问题是只写约束花费的时间太长(大约 108 秒)。解决它需要 32 秒。我打算解决比这更大的问题,而且所需的时间增长得非常快。我认为这不应该花这么长时间。有没有办法在 C API 中更快地添加约束?我正在使用 GRBaddconstr,它会一个一个地添加约束。在 GUROBI 中还有另一个函数,它由 bathces 执行此操作,即 GRBaddconstrs,但在文档中它说实际上没有太大的性能差异。另外,使用 GRBaddconstrs 是相当困难的。
我正在为 GUROBI 8.1.0 使用 C API。例如,一个约束集的写入时间超过 50 秒。当我仅删除使用 GRBaddconstr 添加约束的行时,运行 相同的循环(99400 次迭代)只需不到一秒的时间。我的模型太大,无法附加在这里,而且很复杂,但我添加了我提到的循环,以防它可能有帮助。
//LOGICAL
time ( &rawtime );
timeinfo = localtime ( &rawtime );
printf("LOGICAL %d:%d:%d\n", timeinfo->tm_hour,timeinfo->tm_min,timeinfo->tm_sec);
int c=0;
int tmp,tmp2,tmp3;
int tmp_sabit;
int week_start=wk*T;
int week_end=(wk+1)*T;
for(int i=0;i<orders_length;i++){
for(int t = week_start; t < week_end; t++){
tmp_sabit=i*(T*no_scenarios)+(t-week_start)*no_scenarios+B_threshold;
tmp2=(t-week_start)*no_of_terminals+booking[orders[i]].o;
tmp3=(t-week_start+1)*no_of_terminals+booking[orders[i]].d;
for(int w=0;w<no_scenarios;w++){
tmp=tmp_sabit+w;
val[tmp]=1;
if(booking[orders[i]].o>0){
val[tmp2]=-1;
}
else if(t+1<week_end)
{
val[tmp3]=-1;
}
error = GRBaddconstr(model, no_variable, ind, val, GRB_LESS_EQUAL, 0, "LOGICAL");
c++;
//Initialise val to 0
val[tmp]=0;
if(booking[orders[i]].o>0){
val[tmp2]=0;
}
else if(t+1<week_end)
{
val[tmp3]=0;
}
//for(int j = 0; j < no_variable; j++){val[j] = 0;}
}
}
}
error = GRBupdatemodel(model);
我认为问题在于您为 Gurobi 提供了约束的密集版本,而不是稀疏表示。考虑以下约束:
error = GRBaddconstr(model, no_variable, ind, val, GRB_LESS_EQUAL, 0, "LOGICAL");
此处,no_variable
等于 127940。因此,每次调用 GRBaddconstr
时,您都会将 所有 127940 个变量 的系数值传递给 Gurobi,尽管事实上,此约束中最多两个变量将具有非零系数值。如果你对每个约束都这样做,这会消耗很多时间。为了提高效率,您应该只传递具有非零系数的变量的 Gurobi index/value 信息。查看 documentation for GRBaddconstr 了解更多详情。
这可以通过一些小的代码更改来解决。在 for
循环之外,定义两个长度为 2 的数组,用于存储特定 "logical" 约束中具有非零系数的变量的索引和值:
lInd = (int *) malloc(sizeof(int) * 2);
lVal = (double *) malloc(sizeof(double) * 2);
然后,在最里面的 for
循环中,在添加每个约束之前适当地设置这些索引和值:
lInd[0] = tmp_sabit + w;
lVal[0] = 1;
if (booking[orders[i]].o > 0) {
lInd[1] = tmp2;
lVal[1] = -1;
}
else if (t+1 < week_end) {
lInd[1] = tmp3;
lVal[1] = -1;
}
else {
lVal[1] = 0;
}
error = GRBaddconstr(model, 2, lInd, lVal, GRB_LESS_EQUAL, 0, NULL);
最后,请注意约束应具有唯一的名称。如果约束有重名,您可能会 运行 在尝试按名称、read/write 模型文件等访问约束时出现意外行为。
我的模型有 113000 个约束和 127940 个变量(28400 个连续变量和 99540 个整数)。问题是只写约束花费的时间太长(大约 108 秒)。解决它需要 32 秒。我打算解决比这更大的问题,而且所需的时间增长得非常快。我认为这不应该花这么长时间。有没有办法在 C API 中更快地添加约束?我正在使用 GRBaddconstr,它会一个一个地添加约束。在 GUROBI 中还有另一个函数,它由 bathces 执行此操作,即 GRBaddconstrs,但在文档中它说实际上没有太大的性能差异。另外,使用 GRBaddconstrs 是相当困难的。
我正在为 GUROBI 8.1.0 使用 C API。例如,一个约束集的写入时间超过 50 秒。当我仅删除使用 GRBaddconstr 添加约束的行时,运行 相同的循环(99400 次迭代)只需不到一秒的时间。我的模型太大,无法附加在这里,而且很复杂,但我添加了我提到的循环,以防它可能有帮助。
//LOGICAL
time ( &rawtime );
timeinfo = localtime ( &rawtime );
printf("LOGICAL %d:%d:%d\n", timeinfo->tm_hour,timeinfo->tm_min,timeinfo->tm_sec);
int c=0;
int tmp,tmp2,tmp3;
int tmp_sabit;
int week_start=wk*T;
int week_end=(wk+1)*T;
for(int i=0;i<orders_length;i++){
for(int t = week_start; t < week_end; t++){
tmp_sabit=i*(T*no_scenarios)+(t-week_start)*no_scenarios+B_threshold;
tmp2=(t-week_start)*no_of_terminals+booking[orders[i]].o;
tmp3=(t-week_start+1)*no_of_terminals+booking[orders[i]].d;
for(int w=0;w<no_scenarios;w++){
tmp=tmp_sabit+w;
val[tmp]=1;
if(booking[orders[i]].o>0){
val[tmp2]=-1;
}
else if(t+1<week_end)
{
val[tmp3]=-1;
}
error = GRBaddconstr(model, no_variable, ind, val, GRB_LESS_EQUAL, 0, "LOGICAL");
c++;
//Initialise val to 0
val[tmp]=0;
if(booking[orders[i]].o>0){
val[tmp2]=0;
}
else if(t+1<week_end)
{
val[tmp3]=0;
}
//for(int j = 0; j < no_variable; j++){val[j] = 0;}
}
}
}
error = GRBupdatemodel(model);
我认为问题在于您为 Gurobi 提供了约束的密集版本,而不是稀疏表示。考虑以下约束:
error = GRBaddconstr(model, no_variable, ind, val, GRB_LESS_EQUAL, 0, "LOGICAL");
此处,no_variable
等于 127940。因此,每次调用 GRBaddconstr
时,您都会将 所有 127940 个变量 的系数值传递给 Gurobi,尽管事实上,此约束中最多两个变量将具有非零系数值。如果你对每个约束都这样做,这会消耗很多时间。为了提高效率,您应该只传递具有非零系数的变量的 Gurobi index/value 信息。查看 documentation for GRBaddconstr 了解更多详情。
这可以通过一些小的代码更改来解决。在 for
循环之外,定义两个长度为 2 的数组,用于存储特定 "logical" 约束中具有非零系数的变量的索引和值:
lInd = (int *) malloc(sizeof(int) * 2);
lVal = (double *) malloc(sizeof(double) * 2);
然后,在最里面的 for
循环中,在添加每个约束之前适当地设置这些索引和值:
lInd[0] = tmp_sabit + w;
lVal[0] = 1;
if (booking[orders[i]].o > 0) {
lInd[1] = tmp2;
lVal[1] = -1;
}
else if (t+1 < week_end) {
lInd[1] = tmp3;
lVal[1] = -1;
}
else {
lVal[1] = 0;
}
error = GRBaddconstr(model, 2, lInd, lVal, GRB_LESS_EQUAL, 0, NULL);
最后,请注意约束应具有唯一的名称。如果约束有重名,您可能会 运行 在尝试按名称、read/write 模型文件等访问约束时出现意外行为。