将 IloCP 用于过多工作人员的问题
Issues with using IloCP with too many workers
我建立了一个简单的约束,强制变量的值必须与模运算一致。有两个一致性级别可用:边界一致性和域一致性。在此示例中,使用边界一致性解决问题对于 1 或 2 个工作人员来说效果很好,但对于 3 个或更多工作人员,propagate()
函数会无限循环,因为它似乎无法删除域的最大值。我的计算机有 2 个物理内核,所以我的猜测是使用比物理内核更多的工人是问题所在。我正在使用最新版本的 Cplex Optimization Studio。这是代码:
文件main.cc
:
#include <ilcp/cp.h>
#include "IloModulo.h"
int main() {
IloEnv env;
IloModel model(env);
IloIntVar x(env, 1, 100000);
model.add(IloModulo(env, x, 3, BOUNDS_CONSISTENCY));
model.add(IloMinimize(env, x));
IloCP solver(model);
solver.setParameter(IloCP::Workers, 1); // Infinite loop with 3 or more workers
solver.solve();
return 0;
}
文件IloModulo.h
:
#ifndef ILOMODULO_H_
#define ILOMODULO_H_
#include <ilcp/cpext.h>
#define BOUNDS_CONSISTENCY 1
#define DOMAIN_CONSISTENCY 2
class IlcModuloI : public IlcConstraintI {
private:
IlcIntVar x_;
IlcInt modulo_;
IlcInt consistency_;
public:
IlcModuloI(IloCPEngine cp,
IlcIntVar x,
IlcInt modulo,
IlcInt consistency);
~IlcModuloI();
virtual void propagate();
virtual void post();
};
// The IloModulo constraint is used with the following function
IloConstraint IloModulo(IloEnv env,
IloIntVar x,
IloInt modulo,
IloInt consistency);
#endif // ILOMODULO_H_
文件IloModulo.cc
:
#include "IloModulo.h"
IlcModuloI::IlcModuloI(IloCPEngine solver,
IlcIntVar x,
IlcInt modulo,
IlcInt consistency) :
IlcConstraintI(solver),
modulo_(modulo),
x_(x),
consistency_(consistency) {
;
}
IlcModuloI::~IlcModuloI() {
;
}
void IlcModuloI::propagate() {
switch (consistency_) {
case BOUNDS_CONSISTENCY: {
while ((x_.getMin() % modulo_ != 0) ||
(x_.getMax() % modulo_ != 0)) {
if (x_.getMin() % modulo_ != 0) {
x_.setMin(x_.getMin()+1);
continue;
}
if (x_.getMax() % modulo_ != 0) {
std::cout << "Min/max values: " << x_.getMin() << "/" << x_.getMax() << std::endl;
std::cout << "Decreasing maximum value by 1." << std::endl;
x_.setMax(x_.getMax()-1);
std::cout << "Min/max values: " << x_.getMin() << "/" << x_.getMax() << std::endl;
std::cout << "------------------------------" << std::endl;
continue;
}
}
break;
}
case DOMAIN_CONSISTENCY: {
IlcInt threshold = x_.getMin();
while (threshold <= x_.getMax()) {
if (threshold % modulo_ != 0) {
x_.removeValue(threshold);
}
if (threshold == x_.getMax()) {
break;
}
threshold = x_.getNextHigher(threshold);
}
break;
}
}
}
void IlcModuloI::post() {
switch (consistency_) {
case BOUNDS_CONSISTENCY: {
x_.whenRange(this);
break;
}
case DOMAIN_CONSISTENCY: {
x_.whenDomain(this);
break;
}
}
}
ILOCPCONSTRAINTWRAPPER3(IloModuloWrapper, solver,
IloIntVar, x_,
IloInt, modulo_,
IloInt, consistency_) {
use(solver, x_);
return new (solver.getHeap()) IlcModuloI(solver,
solver.getIntVar(x_),
modulo_,
consistency_);
}
IloConstraint IloModulo(IloEnv env,
IloIntVar x,
IloInt modulo,
IloInt consistency) {
return IloModuloWrapper(env, x, modulo, consistency);
}
确实,在某些非常特定的条件下,用户定义的约束的传播似乎确实存在问题。我不确定工人的数量是否有直接的link。 CP Optimizer 的开发团队将调查此问题,我们会通知您。
其实CP Optimizer没有问题,问题出在传播中的while循环。在传播期间,当您更改变量的 min/max 时,更改不一定会立即应用,因此如果您执行 x.setMax(x.getMax()-1),则新的最大值不会立即应用并在同一个 propagate() 中,x.getMax() 仍然是 returns“旧”最大值。这就是循环的原因。所以如果你真的想像在你当前的算法中那样一个一个地删除值,你应该存储当前边界:
case BOUNDS_CONSISTENCY: {
IloInt min = x_.getMin();
IloInt max = x_.getMax();
while ((min % modulo_ != 0) || (max % modulo_ != 0)) {
if (min % modulo_ != 0) {
min += 1;
x_.setMin(min);
continue;
}
if (max % modulo_ != 0) {
max -=1;
x_.setMax(max);
continue;
}
}
当然,您不需要按增量工作,您可以使用整数除法直接设置正确的 min/max 范围:
IloInt min = x_.getMin();
IloInt max = x_.getMax();
min = ( (min+_modulo-1) / _modulo) * _modulo;
max = ( (max) / _modulo) * _modulo;
x_.setRange(min, max);
当然(但我不知道你的问题的上下文是什么),你不需要定义新的约束,你可以只使用模型中预定义的模表达式,即发布:
model.add( (x % _modulo)==0 );
演唱会级别。
我建立了一个简单的约束,强制变量的值必须与模运算一致。有两个一致性级别可用:边界一致性和域一致性。在此示例中,使用边界一致性解决问题对于 1 或 2 个工作人员来说效果很好,但对于 3 个或更多工作人员,propagate()
函数会无限循环,因为它似乎无法删除域的最大值。我的计算机有 2 个物理内核,所以我的猜测是使用比物理内核更多的工人是问题所在。我正在使用最新版本的 Cplex Optimization Studio。这是代码:
文件main.cc
:
#include <ilcp/cp.h>
#include "IloModulo.h"
int main() {
IloEnv env;
IloModel model(env);
IloIntVar x(env, 1, 100000);
model.add(IloModulo(env, x, 3, BOUNDS_CONSISTENCY));
model.add(IloMinimize(env, x));
IloCP solver(model);
solver.setParameter(IloCP::Workers, 1); // Infinite loop with 3 or more workers
solver.solve();
return 0;
}
文件IloModulo.h
:
#ifndef ILOMODULO_H_
#define ILOMODULO_H_
#include <ilcp/cpext.h>
#define BOUNDS_CONSISTENCY 1
#define DOMAIN_CONSISTENCY 2
class IlcModuloI : public IlcConstraintI {
private:
IlcIntVar x_;
IlcInt modulo_;
IlcInt consistency_;
public:
IlcModuloI(IloCPEngine cp,
IlcIntVar x,
IlcInt modulo,
IlcInt consistency);
~IlcModuloI();
virtual void propagate();
virtual void post();
};
// The IloModulo constraint is used with the following function
IloConstraint IloModulo(IloEnv env,
IloIntVar x,
IloInt modulo,
IloInt consistency);
#endif // ILOMODULO_H_
文件IloModulo.cc
:
#include "IloModulo.h"
IlcModuloI::IlcModuloI(IloCPEngine solver,
IlcIntVar x,
IlcInt modulo,
IlcInt consistency) :
IlcConstraintI(solver),
modulo_(modulo),
x_(x),
consistency_(consistency) {
;
}
IlcModuloI::~IlcModuloI() {
;
}
void IlcModuloI::propagate() {
switch (consistency_) {
case BOUNDS_CONSISTENCY: {
while ((x_.getMin() % modulo_ != 0) ||
(x_.getMax() % modulo_ != 0)) {
if (x_.getMin() % modulo_ != 0) {
x_.setMin(x_.getMin()+1);
continue;
}
if (x_.getMax() % modulo_ != 0) {
std::cout << "Min/max values: " << x_.getMin() << "/" << x_.getMax() << std::endl;
std::cout << "Decreasing maximum value by 1." << std::endl;
x_.setMax(x_.getMax()-1);
std::cout << "Min/max values: " << x_.getMin() << "/" << x_.getMax() << std::endl;
std::cout << "------------------------------" << std::endl;
continue;
}
}
break;
}
case DOMAIN_CONSISTENCY: {
IlcInt threshold = x_.getMin();
while (threshold <= x_.getMax()) {
if (threshold % modulo_ != 0) {
x_.removeValue(threshold);
}
if (threshold == x_.getMax()) {
break;
}
threshold = x_.getNextHigher(threshold);
}
break;
}
}
}
void IlcModuloI::post() {
switch (consistency_) {
case BOUNDS_CONSISTENCY: {
x_.whenRange(this);
break;
}
case DOMAIN_CONSISTENCY: {
x_.whenDomain(this);
break;
}
}
}
ILOCPCONSTRAINTWRAPPER3(IloModuloWrapper, solver,
IloIntVar, x_,
IloInt, modulo_,
IloInt, consistency_) {
use(solver, x_);
return new (solver.getHeap()) IlcModuloI(solver,
solver.getIntVar(x_),
modulo_,
consistency_);
}
IloConstraint IloModulo(IloEnv env,
IloIntVar x,
IloInt modulo,
IloInt consistency) {
return IloModuloWrapper(env, x, modulo, consistency);
}
确实,在某些非常特定的条件下,用户定义的约束的传播似乎确实存在问题。我不确定工人的数量是否有直接的link。 CP Optimizer 的开发团队将调查此问题,我们会通知您。
其实CP Optimizer没有问题,问题出在传播中的while循环。在传播期间,当您更改变量的 min/max 时,更改不一定会立即应用,因此如果您执行 x.setMax(x.getMax()-1),则新的最大值不会立即应用并在同一个 propagate() 中,x.getMax() 仍然是 returns“旧”最大值。这就是循环的原因。所以如果你真的想像在你当前的算法中那样一个一个地删除值,你应该存储当前边界:
case BOUNDS_CONSISTENCY: {
IloInt min = x_.getMin();
IloInt max = x_.getMax();
while ((min % modulo_ != 0) || (max % modulo_ != 0)) {
if (min % modulo_ != 0) {
min += 1;
x_.setMin(min);
continue;
}
if (max % modulo_ != 0) {
max -=1;
x_.setMax(max);
continue;
}
}
当然,您不需要按增量工作,您可以使用整数除法直接设置正确的 min/max 范围:
IloInt min = x_.getMin();
IloInt max = x_.getMax();
min = ( (min+_modulo-1) / _modulo) * _modulo;
max = ( (max) / _modulo) * _modulo;
x_.setRange(min, max);
当然(但我不知道你的问题的上下文是什么),你不需要定义新的约束,你可以只使用模型中预定义的模表达式,即发布:
model.add( (x % _modulo)==0 );
演唱会级别。