ILOG Solver & Scheduler Technical Articles

The intention of this page is to build a set of example code over time.

All code and other information was provided by Mike Pegman of Vine Solutions Ltd unless otherwise noted.

Home

 

Contents

 


I want to ‘scan’ a list of solutions to a goal. How do I do it?

The mechanism built into Solver (IlcActive & IlcNextSolution) works only at the top level, i.e. not inside the scope of an IlcSolve, code like this:

IlcActive( myGoal() );

while (IlcNextSolution()) procToPrintSolution();

More interesting is the general form of non-deterministic loop which can be used anywhere you need this facility, thus:

ILCGOAL0(printSolution) {&ldots;}

IlcSolve( IlcAnd( myGoal(), printSolution(), IlcGoalFail() ) );


How do I model set-up times? (1)

The simplest case is the delay between two adjacent jobs, allocated to a machine. In Scheduler, to model the set up time between any pair of jobs (activities) on a machine (resource), you derive a sub-class of the resource implementation class, overloading the getTransitionTime member function:

class myMachineI : public IlcUnaryResourceI {

public:

myMachineI::myMachineI(IlcSchedule schedule)

:IlcUnaryResourceI( schedule.getImpl(), IlcRequiredResource )

{}

IlcInt getTransitionTime(

const IlcResourceConstraintI* rc1st, const IlcResourceConstraintI* rc2nd){

switch ( ((Job *)rc1st->getActivity()->getObject())->getJobType() ) {

case type1: return 5;

case type2: return 12;

case &ldots; }

}

‘Job’ is an application specific class which ‘owns’ the activity and which has knowledge about its characteristics, such as the type of the job to be scheduled on the resource, which here determines the set-up time.


Multiple Time Tables are Obsolescent

 I note that the versions of the makeTimeTableConstraint member functions of resources which take a time range are now 'undocumented' (Schedule 2.1).

The Schedule development team have confirmed that they are considering moving away from multiple time tables. If you have views on this, especially if you use them, why not contact solver-support@ilog.fr ? (and please copy mp@vinesolutions.co.uk ).


How do I print the amounts of a resource which are used or which remain?

This code will step through a time table and dump the variables to the C++ output stream cout.

IlcIntVarTable tab = money.getTimeTable();

IlcIntVarTableIterator iter(tab);

IlcIntVar iv;

IlcInt oldMin = 0;

while(iter.next(iv)) {

  cout << iter.getIndexMin() << "\t" << iter.getIndexMax() << "\t";

  cout << iv.getMin() << "\t" << iv.getMin()-oldMin <<

  "\t" << iv.getMax() << endl;

 oldMin = iv.getMin();

}

 


The Kiln Example from the Scheduler White Paper

See the Scheduler White paper available from ILOG or email mp@vinesolutions.co.uk

// M Pegman (c) Vine Solutions Ltd 1996

// The Brick Kiln Example from the Schedule White Paper

#include <ilsched/intact.h>

ILCGOAL1( ScheduleActivity, IlcIntervalActivity, x ) {

  return( IlcOr( x.getStartVariable() == x.getStartMin(),

  And( x.getStartVariable() != x.getStartMin(), this) ) );

}

// Select unscheduled activities in aplha order

IlcActivitySelector1(ChooseActivity, !activity.getStartVariable().isBound(), activity.getName()[0], IlcIntervalActivity)

ILCGOAL1( ScheduleAll, IlcSchedule, x ) {

  IlcIntervalActivity ChosenActivity;

  if( ChooseActivity ( ChosenActivity, x ))

  return IlcAnd( ScheduleActivity(ChosenActivity), this );

  else

  return 0;

}

IlcIntervalActivity MakeActivity(IlcSchedule schedule,IlcDiscreteResource resource, IlcInt duration, IlcInt requires, char* name) {

  IlcIntervalActivity activity(schedule, duration);

  activity.setName(name);

  IlcPost(activity.requires(resource, requires));

  return activity;

}

 

IlcSchedule DefineProblem(void)

{

  IlcInt horizon = 11; // problem specification

  IlcSchedule schedule(0, horizon);

 

  IlcDiscreteResource kiln( schedule, IlcRequiredResource , 3);

  IlcPost( kiln.makeTimeTableConstraint());

  // IlcPost( kiln.makeDisjunctiveConstraint());

  // kiln.setEdgeFinder(IlcTrue);

 

  kiln.setCapacityMax( 0, 1, 2); kiln.setCapacityMax( 1, 2, 1); kiln.setCapacityMax( 2, 3, 0);

 kiln.setCapacityMax( 3, 5, 1); kiln.setCapacityMax( 10,11, 1);

  IlcIntervalActivity A = MakeActivity(schedule, kiln, 1, 2, "A ");

  IlcIntervalActivity B = MakeActivity(schedule, kiln, 4, 1, "B ");

  IlcIntervalActivity C = MakeActivity(schedule, kiln, 4, 1, "C ");

  IlcIntervalActivity D = MakeActivity(schedule, kiln, 2, 1, "D ");

  IlcIntervalActivity E = MakeActivity(schedule, kiln, 4, 2, "E ");

 

// Manual Method

// A.setStartTime(0);

// B.setStartTime(B.getStartMin());

// IlcPost( B.getStartVariable() != B.getStartMin() );

// IlcPost( A.getStartVariable() != 0 );

// A.setStartTime(5);

// B.setStartTime(3);

  return schedule;

}

 

void PrintSolution(IlcSchedule schedule) {

  IlcActivityIterator iterator(schedule);

  IlcActivity activity;

  while (iterator.next(activity))

  IlcOut << activity << endl;

}

int main(void) {

  IlcInit();

  IlcSchedule schedule = DefineProblem();

  if (IlcSolve(ScheduleAll(schedule))) {

  PrintSolution(schedule);

  }

  else

  cout << "Failed to schedule";

 

  IlcPrintInformation();

  IlcEnd();

  return 0;

}

 


Tutorial Example - Frequency Allocation - Search and Minimisation

// (c) 1996 Vine Solutions Ltd

// Mike Pegman's extended frequency allocation example

// shows:

// more appropriate use of OO & locus of instantiation - see Node::generate

// minimisation by goal in STEP 1

//STEP 2 and by constraint

// custom constraint - cost increment

// this code is developed from the 'node' example in the Solver material in

// stages to teach the concepts of incremental back propagating cost functions and

// then to ilustrate a binary constraint.

// STEP 3 should be to make the cost csts to be generic

#include <ilsolver/ilcint.h>

class Node {

const IlcInt _id;

IlcIntVar_channels;

public:

Node(IlcInt id, IlcInt numberOfChannels);

~Node() {};

IlcIntVar getChannel() {return _channels;}

IlcBool isBound() const {return _channels.isBound();}

void display(ostream& str=cout) ;

IlcGoal generate(IlcIntVar costVar);

};

ILCGOAL3(Generate, IlcInt, size, Node**, node, IlcIntVar, costVar) {

if (size==0) return 0;

return IlcAnd( node[0]->generate(costVar),

  Generate(size-1, &node[1], costVar)); }

// step 1

// 'bump' cost with a goal, called at the right time

ILCGOAL2(IncrCost, IlcIntVar, costVar, IlcIntVar, chan) {

 costVar.setMin(chan.getValue());

return 0; }

ILCGOAL1(InstCost, IlcIntVar, cost) {

cost.setValue(cost.getMin());

cout << " cost set to " << cost << endl;

return 0;}

Node::Node(IlcInt id, IlcInt numberOfChannels)

:_channels(0, numberOfChannels), _id(id)

{};

void Node::display(ostream& str) {

str << getChannel() << endl; };

IlcGoal Node::generate(IlcIntVar costVar) {

// step 1 use of goal

//return IlcAnd(IlcInstantiate(_channels), IncrCost(costVar,_channels));

// step 2 - back to the base case!

return IlcInstantiate(_channels);}

 

ostream & operator<< (ostream & str, Node &node) { node.display(str); return str; }

void offset(Node &nodea, Node &nodeb, IlcInt off) {

IlcPost(IlcAbs(nodea.getChannel() - nodeb.getChannel()) >= off); }

 

// step 2 binary incremental cost constraint

class costCstI : public IlcConstraintI{

IlcIntVar _cost;

Node* _node;

public:

costCstI(IlcIntVar var, Node* n): _cost(var), _node(n) {}

~costCstI() {}

void post();

void propagate();

void varValue(); };

ILCDEMON1(CostCstIValueDemon, costCstI*, ct) { ct->varValue(); }

void costCstI::post() { _node->getChannel().whenValue(CostCstIValueDemon(this)); }

 

void costCstI::propagate() {

if (_node->getChannel().isBound())

  varValue(); }

 

void costCstI::varValue() {

_cost.setMin(_node->getChannel().getValue());

cout << " in value " << _cost << " channel " << _node->getChannel().getValue()<< endl; }

 

IlcConstraint updateCost(IlcIntExp cost, Node* n) {

IlcConstraintI* ct = new (IlcHeap())costCstI(cost,n);

return ct;}

main () {

IlcInit();

Node node0(0,10); Node node1(0,10); Node node2(0,10); Node node3(0,10); Node node4(0,10);

IlcIntVar costVar(0,10);

offset(node0,node1,2); offset(node0,node2,2); offset(node1,node4,1); offset(node2,node3,3); offset(node2,node4,1);

Node *arrayOfNode[5];// = {&node0, &node1, &node2, &node3, &node4};

arrayOfNode[0]=&node0; arrayOfNode[1]=&node1; arrayOfNode[2]=&node2; arrayOfNode[3]=&node3; arrayOfNode[4]=&node4;

for (IlcInt i = 0; i<5; i++) IlcPost(updateCost(costVar,arrayOfNode[i]));

 

// showing alternative high level goal management

//IlcActive(IlcAnd( Generate(5, arrayOfNode, costVar), intCost(costVar) ) );

//while (IlcNextSolution())

 

if (IlcMinimize( IlcAnd( Generate(5,arrayOfNode,costVar), InstCost(costVar) ) , costVar)) {

  IlcOut << node0 << node1 << node2 << node3 << node4;

}

IlcEnd();

return 0; }

 


Top of page Home


© Copyright Vine Solutions Ltd 1997 Email to: mp@vinesolutions.co.uk