IBM ILOG Dispatcher User's Manual > Transportation Industry Solutions > Docking Bays: Modeling External Resources > Complete Program

The complete program follows. You can also view it online in the file YourDispatcherHome/examples/src/bays.cpp.

// -------------------------------------------------------------- -*- C++ -*-
// File: examples/src/bays.cpp
//---------------------------------------------------------------------------


#include <ildispat/ilodispatcher.h>

#include <ilsched/iloscheduler.h>

ILOSTLBEGIN

///////////////////////////////////////////////////////////////////////////////
// Modeling
class RoutingModel {
  IloEnv              _env;
  IloModel            _mdl;
  IloDiscreteResource _bays;
  IloInt              _schedGranularity;

  void createDepotDockingBays(char* baysFileName);
  void createDimensions();
  void createNodes(char* nodeFileName);
  void createVehicles(char* vehicleFileName);
  void createVisits(char* visitsFileName);
public:
  RoutingModel(IloEnv env,
               int argc,
               char* argv[],
               IloInt schedGranularity = 100);
  ~RoutingModel() {}
  IloDiscreteResource getBays() const { return _bays; }
  IloEnv getEnv() const { return _env; }
  IloModel getModel() const { return _mdl; }
};

RoutingModel::RoutingModel(IloEnv env,
                           int argc,
                           char * argv[],
                           IloInt schedGranularity)
  : _env(env), _mdl(env), _schedGranularity(schedGranularity) {

  createDimensions();

  char* nodeFileName;
  if(argc < 4)
    nodeFileName = (char *) "../../../examples/data/pdp/nodes.csv";
  else
    nodeFileName = argv[3];
  createNodes(nodeFileName);

  char* baysFileName;
  if (argc < 5)
    baysFileName = (char*) "../../../examples/data/bays.csv";
  else
    baysFileName = argv[4];
  createDepotDockingBays(baysFileName);

  char* vehiclesFileName;
  if (argc < 2)
    vehiclesFileName = (char *) "../../../examples/data/pdp/vehicles.csv";
  else
    vehiclesFileName = argv[1];
  createVehicles(vehiclesFileName);

  char* visitsFileName;
  if (argc < 3)
    visitsFileName =
      (char*) "../../../examples/data/pdp/visitsWithPickupInDepot.csv";
  else
    visitsFileName = argv[2];
  createVisits(visitsFileName);
}

// Create docking bays
void RoutingModel::createDepotDockingBays(char* baysFileName) {
  // Scheduler environment settings
  IloCsvReader csvBaysReader(_env, baysFileName);
  IloCsvReader::LineIterator it(csvBaysReader);
  while (it.ok()) {
    IloCsvLine line = *it;
    IloSchedulerEnv schedEnv(_env);
    IloInt horizon = line.getIntByHeader("horizon");
    IloInt nbOfBays = line.getIntByHeader("bays");
    IloInt breakStart = line.getIntByHeader("breakStart");
    IloInt breakEnd = line.getIntByHeader("breakEnd");

    schedEnv.setHorizon(horizon * _schedGranularity);
    _bays = IloDiscreteResource(_env, nbOfBays);
    _bays.addBreak(breakStart * _schedGranularity,
                   breakEnd * _schedGranularity);
    ++it;
  }
  csvBaysReader.end();
}

// Create dimensions
void RoutingModel::createDimensions() {
  IloDimension1 weight(_env, "Weight");
  weight.setKey("Weight");
  _mdl.add(weight);
  IloDimension2 time(_env, IloEuclidean, "Time");
  time.setKey("Time");
  _mdl.add(time);
  IloDimension2 distance(_env, IloEuclidean, IloFalse, "Distance");
  distance.setKey("Distance");
  _mdl.add(distance);
}

// Create nodes
void RoutingModel::createNodes(char* nodeFileName) {
  IloCsvReader csvNodeReader(_env, nodeFileName);
  IloCsvReader::LineIterator  it(csvNodeReader);
  while(it.ok()) {
    IloCsvLine line = *it;
    char* name = line.getStringByHeader("name");
    IloNode node(_env,
                 line.getFloatByHeader("x"),
                 line.getFloatByHeader("y"),
                 0,
                 name);
    node.setKey(name);
    ++it;
  }
  csvNodeReader.end();
}

// Create vehicles
void RoutingModel::createVehicles(char* vehicleFileName) {
  IloDimension1 weight = IloDimension1::Find(_env, "Weight");
  IloDimension2 time = IloDimension2::Find(_env, "Time");
  IloDimension2 distance = IloDimension2::Find(_env, "Distance");

  IloCsvReader csvVehicleReader(_env, vehicleFileName);
  IloCsvReader::LineIterator it(csvVehicleReader);
  while(it.ok()) {
    IloCsvLine line = *it;
    char * namefirst = line.getStringByHeader("first");
    char * namelast = line.getStringByHeader("last");
    char * name = line.getStringByHeader("name");
    IloNum capacity = line.getFloatByHeader("capacity");
    IloNum openTime = line.getFloatByHeader("open");
    IloNum closeTime = line.getFloatByHeader("close");
    IloNode node1 = IloNode::Find(_env, namefirst);
    IloNode node2 = IloNode::Find(_env, namelast);

    IloVisit first(node1, "depot");
    _mdl.add(first.getCumulVar(weight) == 0);
    _mdl.add(first.getCumulVar(time) >= openTime);

    IloVisit last(node2, "depot");
    _mdl.add(last.getCumulVar(time) <= closeTime);
    IloVehicle vehicle(first, last, name);
    vehicle.setCost(distance, 1.0);
    vehicle.setCost(time, 1.0);
    vehicle.setCapacity(weight, capacity);
    _mdl.add(vehicle);
    ++it;
  }
  csvVehicleReader.end();
}

// Create visits
void RoutingModel::createVisits(char* visitsFileName) {
  IloDimension1 weight = IloDimension1::Find(_env, "Weight");
  IloDimension2 time = IloDimension2::Find(_env, "Time");

  IloCsvReader csvVisitReader(_env, visitsFileName);
  IloCsvReader::LineIterator it(csvVisitReader);
  while(it.ok()){
    IloCsvLine line = *it;
    //read visit data from files
    char * pickupVisitName = line.getStringByHeader("pickup");
    char * pickupNodeName = line.getStringByHeader("pickupNode");
    char * deliveryVisitName = line.getStringByHeader("delivery");
    char * deliveryNodeName = line.getStringByHeader("deliveryNode");
    IloNum quantity = line.getFloatByHeader("quantity");
    IloNum pickupMinTime  = line.getFloatByHeader("pickupMinTime");
    IloNum pickupMaxTime  = line.getFloatByHeader("pickupMaxTime");
    IloNum deliveryMinTime  = line.getFloatByHeader("deliveryMinTime");
    IloNum deliveryMaxTime  = line.getFloatByHeader("deliveryMaxTime");
    IloNum dropTime = line.getFloatByHeader("dropTime");
    IloNum pickupTime = line.getFloatByHeader("pickupTime");

    // read data nodes from the file nodes.csv
    // and create pickup and delivery nodes

    IloNode pickupNode = IloNode::Find(_env, pickupNodeName);
    IloNode deliveryNode = IloNode::Find(_env, deliveryNodeName);
    //create and add pickup and delivery visits
    IloVisit pickup(pickupNode, pickupVisitName);
    _mdl.add(pickup.getDelayVar(time) >= pickupTime);
    _mdl.add(pickup.getTransitVar(weight) == quantity);
    _mdl.add(pickupMinTime <= pickup.getCumulVar(time) <= pickupMaxTime);
    _mdl.add(pickup);
    IloVisit delivery(deliveryNode, deliveryVisitName);
    _mdl.add(delivery.getDelayVar(time) == dropTime);
    _mdl.add(delivery.getTransitVar(weight) == -quantity);
    _mdl.add(deliveryMinTime <= delivery.getCumulVar(time) <= deliveryMaxTime);
    _mdl.add(delivery);
    //add pickup and delivery order constraint
    _mdl.add(IloOrderedVisitPair(_env, pickup, delivery));

    // Pickup requiring docking bay

    IloNumVar process(_env, 0, pickupMaxTime * _schedGranularity, ILOINT);
    IloActivity vehicleLoad(_env, process);
    IloNumVar start(_env, 0,  pickupMaxTime * _schedGranularity, ILOINT);
    IloNumVar end(_env, 0, pickupMaxTime * _schedGranularity, ILOINT);
    _mdl.add(start == vehicleLoad.getStartExpr());
    _mdl.add(end == vehicleLoad.getEndExpr());
    vehicleLoad.setName(pickupVisitName);
    vehicleLoad.setBreakable();
    _mdl.add(vehicleLoad.requires(_bays, 1));
    _mdl.add(start == pickup.getCumulVar(time) * _schedGranularity);
    _mdl.add(end <= (pickup.getCumulVar(time) + pickup.getDelayVar(time))
             * _schedGranularity);
    _mdl.add(process >= pickupTime * _schedGranularity);

    ++it;
  }
  csvVisitReader.end();
}

///////////////////////////////////////////////////////////////////////////////
// Solving
class RoutingSolver {
  IloModel            _mdl;
  IloSolver           _solver;
  IloRoutingSolution  _solution;

  IloBool findFirstSolution(IloGoal goal);
  void greedyImprove(IloNHood nhood, IloGoal subGoal);
  void improve(IloGoal subgoal);
public:
  RoutingSolver(RoutingModel mdl);
  ~RoutingSolver() {}
  IloRoutingSolution getSolution() const { return _solution; }
  void printInformation() const;
  void solve();
};

RoutingSolver::RoutingSolver(RoutingModel mdl)
  : _mdl(mdl.getModel()), _solver(mdl.getModel()), _solution(mdl.getModel()) {}

// Solving : find first solution
IloBool RoutingSolver::findFirstSolution(IloGoal goal) {
  if (!_solver.solve(goal)) {
    _solver.error() << "Infeasible Routing Plan" << endl;
    return IloFalse;
  }
  IloDispatcher dispatcher(_solver);
  _solver.out() << dispatcher.getTotalCost() << endl;
  _solution.store(_solver);
  return IloTrue;
}

// Improve solution using nhood
void RoutingSolver::greedyImprove(IloNHood nhood, IloGoal subGoal) {
  _solver.out() << "Improving solution" << endl;
  IloEnv env = _solver.getEnv();
  nhood.reset();
  IloGoal improve = IloSingleMove(env,
                                  _solution,
                                  nhood,
                                  IloImprove(env),
                                  subGoal);
  while (_solver.solve(improve)) {}
}

// Improve solution
void RoutingSolver::improve(IloGoal subGoal) {
  IloEnv env = _solver.getEnv();
  IloNHood nhood = IloTwoOpt(env)
                 + IloOrOpt(env)
                 + IloRelocate(env)
                 + IloCross(env)
                 + IloExchange(env);
  greedyImprove(nhood, subGoal);
}

// Display Dispatcher information
void RoutingSolver::printInformation() const {
  IloDispatcher dispatcher(_solver);
  _solver.printInformation();
  dispatcher.printInformation();
  _solver.out() << "===============" << endl
                << "Cost         : " << dispatcher.getTotalCost() << endl
                << "Number of vehicles used : "
                << dispatcher.getNumberOfVehiclesUsed() << endl
                << "Solution     : " << endl
                << dispatcher << endl;

  IlcScheduler sched(_solver);
  for (IlcActivityIterator iter(sched); iter.ok(); ++iter) {
    _solver.out() << *iter << endl;

  }
}

// Solving
void RoutingSolver::solve() {
  IloDispatcher dispatcher(_solver);
  IloEnv env = _solver.getEnv();

  // Subgoals
  IloGoal instantiateCost = IloDichotomize(env,
                                           dispatcher.getCostVar(),
                                           IloFalse);
  IloGoal schedGoal = IloSetTimesForward(env);
  IloGoal subGoal = schedGoal && instantiateCost;
  IloGoal restoreSolution = IloRestoreSolution(env, _solution) && subGoal;
  IloGoal goal = IloSavingsGenerate(env, schedGoal) && instantiateCost;

  // Solving
  if (findFirstSolution(goal)) {
    improve(subGoal);
    _solver.solve(restoreSolution);
  }
}

///////////////////////////////////////////////////////////////////////////////

int main(int argc, char * argv[]) {
  IloEnv env;
  try {
    IloInt schedGranularity = 100;
    RoutingModel mdl(env, argc, argv, schedGranularity);
    RoutingSolver solver(mdl);
    solver.solve();
    solver.printInformation();
  } catch(IloException& ex) {
    cerr << "Error: " << ex << endl;
  }
  env.end();
  return 0;
}