Hi,
we are trying to implement edge-finder propagator. The algorithm is
implemented according to the book Baptiste,Le Pape, Nuijten (2001)
Constraint-Based Scheduling.
We also took some Gecode coding inspiration from the cumulatives
propagator.
The problem we have, is that for FT6 job-shop benchmark (we did not try
more benchmarks yet), the edge-finder combined with BAB branching founds
a solution that has Cmax>OptimalCmax, but is marked as an optimum, since
BAB founds no better solution.
However, when a constraint Cmax < 57 is added (optimum for FT6 is 55),
see line 96 of jobshop.cpp, then a solution with correct Cmax is found.
We do not know if the bug is in the edge-finder algoritm (not much
likely), in the Gecode stuff of the propagator code, or in the Gecode
itself.
Maybe someone who already implemented some edge-finder variation could
help us.
The code of the whole program is attached (about 300 lines of code). The
problem object Jobshop is inherited from the object Example, ie. it is
necessary to include "examples/support.hh" and to link it with
"example.o" and "options.o" from examples/support directory of the
Gecode distribution.
We use Gecode 2.1.1 binary distribution, Visual C++ 2008 and Windows XP.
There are some TODOs we know about, but we think that are not the source
of the problem:
* some methods of the propagator should be inlined
* vector used for sorting tasks should not be created each time
propagate() method is called - instead it should be created in
propagator constructor
* improve input function to be able to compute jobshops with different
number of jobs and tasks (n != m)
I also obtained the same memory allocation problem, that was already
reported by Nick Hindle on July 21st
http://www.gecode.org/gecode-users/2008-July/002344.html
The problematic code is in file jobshop.cpp line 115:
os << S[i] << ", "; //raises memory allocation exception
os << S[i].val() << ", "; //this works
Thanks, Jan
#include "edgeFinder.h"
#include "examples/support.hh"
#include "gecode/minimodel.hh"
#include <cstdio>
// random jobshop 4x4
const int processingTime04[] = {
2, 3, 5, 1,
7, 2, 3, 4,
3, 8, 2, 6,
1, 2, 8, 5
};
int resource04[] = {
1, 3, 2, 4,
2, 4, 3, 1,
4, 2, 1, 3,
1, 2, 4, 3
};
// jobshop FT6
const int processingTime06[] = {
1, 3, 6, 7, 3, 6,
8, 5, 10, 10, 10, 4,
5, 4, 8, 9, 1, 7,
5, 5, 5, 3, 8, 9,
9, 3, 5, 4, 3, 1,
3, 3, 9, 10, 4, 1
};
int resource06[] = {
3, 1, 2, 4, 6, 5,
2, 3, 5, 6, 1, 4,
3, 4, 6, 1, 2, 5,
2, 1, 3, 4, 5, 6,
3, 2, 5, 6, 1, 4,
2, 4, 6, 1, 5, 3
};
class Jobshop : public Example
{
protected:
IntVarArray S; // starting time variables
const int n; // size (jobshop n x n)
public:
// The actual problem
Jobshop(const SizeOptions& opt)
: n(opt.size()), S(this,opt.size()*opt.size()+1,0,100)
{
const int* processingTime = processingTime04;
int* resource = resource04;
switch(opt.size())
{
case 4:
resource = resource04;
processingTime = processingTime04;
break;
case 6:
resource = resource06;
processingTime = processingTime06;
break;
}
// count resources from 0
for (int i = 0; i < n*n; ++i)
resource[i]--;
// precedence constraints
for (int i = 0; i<n; ++i){
for (int j = 0; j < n-1; ++j){
post(this,S[i*n+j] + processingTime[i*n+j] <= S[i*n+j+1]);
}
}
// every job ends before makespan
for (int i = 0; i<n; ++i){
post(this,S[i*n+n-1] + processingTime[i*n+n-1] <= S[n*n]);
}
// disjunctive resource constraints
for (int k = 0; k < n*n; ++k) {
for (int l = k+1; l <n*n; ++l){
if(resource[k] == resource[l]) {
post(this,tt(~(S[k] + processingTime[k] <= S[l])||~(S[l] +
processingTime[l] <= S[k])));
}
}
}
EdgeFinder::post(this,S,resource,processingTime,n);
// ATTENTION: when uncommented, a solution with correct optimal Cmax=55 for
FT06 is found
//post(this,S[n*n] < 57);
branch(this, S, INT_VAR_SIZE_MIN, INT_VAL_MIN);
};
// Constructor for cloning \a s
Jobshop(bool share, Jobshop& s) : Example(share,s), n(s.n) {
S.update(this, share, s.S);
}
// Perform copying during cloning
virtual Space*
copy(bool share) {
return new Jobshop(share,*this);
}
// Print solution
virtual void print(std::ostream& os) {
for (int i = 0; i < S.size(); i++) {
//os << S[i] << ", "; //raises memory allocation exception
os << S[i].val() << ", "; //this works
if ((i+1) % n == 0){
os << std::endl;
}
}
os << std::endl;
}
// add requirement for better next result - for BAB
void constrain(Space * s){
rel(this, S[n*n], IRT_LE, static_cast<Jobshop*>(s)->S[n*n].min());
}
};
int main(int argc, char* argv[])
{
SizeOptions opt("Jobshop");
opt.solutions(0);
opt.size(6);
opt.parse(argc,argv);
opt.icl(ICL_BND);
Example::run<Jobshop,BAB,SizeOptions>(opt);
return 0;
};
#include "edgeFinder.h"
EdgeFinder::EdgeFinder(Space* home, ViewArray<Gecode::Int::IntView>& _start,
const IntArgs& _processing_time)
: processingTime(_processing_time.size()), NaryPropagator
<Gecode::Int::IntView, Gecode::Int::PC_INT_BND> (home,_start)
{
n = x.size();
for(int i = _processing_time.size(); i--; )
processingTime[i] = _processing_time[i];
};
EdgeFinder::EdgeFinder(Space* home, bool share, EdgeFinder& p)
: processingTime(p.processingTime.size()), n(p.n), NaryPropagator
<Gecode::Int::IntView, Gecode::Int::PC_INT_BND> (home, share, p)
{
processingTime.update(home, share, p.processingTime);
};
size_t EdgeFinder::dispose(Space* home)
{
processingTime.~SharedArray();
return NaryPropagator::dispose(home);
};
PropCost EdgeFinder::cost() { return Gecode::PC_QUADRATIC_LO ; };
ExecStatus EdgeFinder::propagate(Space * home, ModEventDelta med)
{
bool subsumed = true;
for (int i = n; i--; ) {
if (!x[i].assigned()) {
subsumed = false;
break;
}
};
// subsume propagator if all tasks have assigned starting time (x)
if (subsumed) return ES_SUBSUMED(this,home);
// Algorithm 1: Edge-Finding according the book [Baptiste,P., Le Pape, C. and
Nuijten, W. : Constraint-Based Scheduling. Applying..., 2001]
bool modified = false;
std::vector<TaskInfo> tasks;
for(int i=0; i < n; i++){
tasks.push_back(TaskInfo(x[i], processingTime[i]));
}
std::sort(tasks.begin(), tasks.end(),nondecreasingEstSort);
for (int k=0; k < n ; k++){
int P = 0;
int C = std::numeric_limits<int>::min();
int H = C;
for (int i = n; i--; ){
if((tasks[i].start.max()+tasks[i].processingTime) <=
(tasks[k].start.max()+tasks[k].processingTime) ){ //line 7
P += tasks[i].processingTime;
C = std::max(C, tasks[i].start.min() + P);
if (C > (tasks[k].start.max()+tasks[k].processingTime)) //line 10
return ES_FAILED;
}
tasks[i].Ci=C; //line 14
}
for (int i=0; i < n; i++){
if ((tasks[i].start.max()+tasks[i].processingTime) <=
(tasks[k].start.max()+tasks[k].processingTime)){ //line 17
H = std::max(H, tasks[i].start.min()+P);
P -= tasks[i].processingTime;
}
else{
if((( tasks[i].start.min() + P + tasks[i].processingTime )) >
tasks[k].start.max() ){ //line 21
tasks[i].releaseTime = std::max( tasks[i].releaseTime, tasks[i].Ci);
modified = true;
}
if ((H + tasks[i].processingTime) > (tasks[k].start.max() +
tasks[k].processingTime)){ //line 24
tasks[i].releaseTime = std::max(tasks[i].releaseTime,C);
modified = true;
}
}
}
}
if (!modified)
return ES_FIX;
for (int i=0; i < n; i++){
if (me_failed(tasks[i].start.gq(home, tasks[i].releaseTime))) //line 31
return ES_FAILED;
}
return ES_NOFIX;
};
Actor* EdgeFinder::copy(Space * home, bool share)
{
return new (home) EdgeFinder(home, share, *this);
}
ExecStatus EdgeFinder::post(Space* home, const IntVarArgs& _starts, const int*
_machine, const int* _processing_time, int size)
{
ViewArray<Int::IntView> starts(home,_starts);
for (int i=0; i < size ; i++){ // for each machine
ViewArray<Int::IntView> start(home, size);
IntArgs processingTime(size);
int j = 0;
int count =0;
while (count < size){
if(_machine[j]==i){ // task j is processed by machine i
processingTime[count] = _processing_time[j];
start[count]=starts[j];//copy view
count++;
}
j++;
}
new (home) EdgeFinder(home,start,processingTime);
}
return ES_OK;
};
#ifndef __EDGEFINDER_H__
#define __EDGEFINDER_H__
#include "gecode/int.hh"
#include <vector>
#include <limits>
#include <algorithm>
using namespace Gecode;
class TaskInfo
{
public:
Int::IntView start;
int processingTime;
int releaseTime;
int Ci;
TaskInfo(Int::IntView _start, int _proctime) : start(_start),
processingTime(_proctime), releaseTime(_start.min()), Ci(0) {};
};
inline bool nondecreasingEstSort(const TaskInfo& t1, const TaskInfo& t2)
{
return ( t1.start.min() < t2.start.min() );
};
class EdgeFinder: NaryPropagator<Gecode::Int::IntView, Gecode::Int::PC_INT_BND>
//Gecode::PC_GEN_ASSIGNED Gecode::Int::PC_INT_BND
{
protected:
// Number of tasks
int n;
// List of processingTimes
SharedArray<int> processingTime;
// Constructor for propagator creation
EdgeFinder(Space * home, ViewArray<Gecode::Int::IntView> &_start, const
IntArgs& _processing_time);
// Constructor for cloning \a p
EdgeFinder(Space * home, bool share, EdgeFinder& p);
public:
virtual PropCost cost();
virtual size_t dispose(Space* home);
virtual ExecStatus propagate(Space * home, ModEventDelta med);
virtual Actor* copy(Space * home, bool share);
static ExecStatus post(Space* home, const IntVarArgs& _starts, const int*
_machine, const int* _processing_time, int size);
};
#endif // __EDGEFINDER_H__
_______________________________________________
Gecode users mailing list
[EMAIL PROTECTED]
https://www.gecode.org/mailman/listinfo/gecode-users