IBM ILOG Dispatcher User's Manual > The Basics > Multiple Tours per Vehicle > Complete Program

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

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

#include <ildispat/ilodispatcher.h>

ILOSTLBEGIN
///////////////////////////////////////////////////////////////////////////////
// Modeling
class RoutingModel {
  IloEnv              _env;
  IloModel            _mdl;
  IloDispatcherGraph  _graph;
  IloDimension2       _distance;
  IloDimension1       _weight;
  IloVisitArray       _unorderedVisitArray;

  void addDimensions();
  void loadGraphInformation (const char* arcFileName);
  void lastMinuteGraphChanges ();
  void createIloNodes(const char* nodeFileName, const char* coordFileName);
  void createVehicles(const char* vehicleFileName);
  void createVisits(const char* visitsFileName);

public:
  RoutingModel(IloEnv env, int argc, char* argv[]);
  ~RoutingModel() {}
  IloEnv    getEnv() const { return _env; }
  IloModel  getModel() const { return _mdl; }
  IloDispatcherGraph getGraph() const {return _graph;}
  IloVisitArray getUnorderedVisitArray () const {return _unorderedVisitArray;}
};

RoutingModel::RoutingModel( IloEnv env,
                            int argc,
                            char* argv[]):
  _env(env),
  _mdl(env),
  _graph(env),
  _unorderedVisitArray(env){
  addDimensions();

  // Load dispatcher graph information from file .
  // Add instance-specific features to network
  char * arcFileName;
  if(argc < 2)  arcFileName =
       (char*) "../../../examples/data/dispatcherGraphData/gridNetwork.csv";
  else          arcFileName = argv[1];
  char * turnFileName;
  if(argc < 3)  turnFileName =
       (char*) "../../../examples/data/dispatcherGraphData/turnData.csv";
  else          turnFileName = argv[2];
  loadGraphInformation (arcFileName);

  //create IloNodes and associate them to graph nodes
  char * nodeFileName;
  if(argc < 4)  nodeFileName =
       (char*) "../../../examples/data/vrp100/vrp100nodes.csv";
  else          nodeFileName = argv[3];
  char * nodeCoordsFile;
  if(argc < 5)  nodeCoordsFile =
       (char*) "../../../examples/data/dispatcherGraphData/coordTable.csv";
  else          nodeCoordsFile = argv[4];
  createIloNodes(nodeFileName, nodeCoordsFile);

  //create vehicles
  char * vehiclesFileName;
  if(argc < 6)  vehiclesFileName =
       (char*) "../../../examples/data/vrp100/vrp100vehicles.csv";
  else          vehiclesFileName = argv[5];
  createVehicles(vehiclesFileName);

  //create visits
  char * visitsFileName;
  if(argc < 7)  visitsFileName =
       (char*) "../../../examples/data/vrp100/vrp100visits.csv";
  else          visitsFileName = argv[6];
  createVisits(visitsFileName);
}

// create distance functions for dimensions, add dimensions to model
void RoutingModel::addDimensions() {
  _weight =IloDimension1 (_env, "weight");
  _mdl.add(_weight);

  IloDistance SP_distance = IloGraphDistance (_graph);
  _distance =IloDimension2 (_env, SP_distance, "distance");
  _mdl.add(_distance);

}

// load network topology and travel costs from files.
// note that by default, all turns are allowed with no penalty.
void RoutingModel::loadGraphInformation (const char* arcFileName)    {
  _graph.createArcsFromFile (arcFileName);
  _graph.loadArcDimensionDataFromFile (arcFileName, _distance);
  lastMinuteGraphChanges();
}

// Make modifications to network conditions based on latest information.
void RoutingModel::lastMinuteGraphChanges () {
  _graph.forbidArcUse(_graph.getArcByEnds(2785-1, 2785));
  _graph.forbidArcUse(_graph.getArcByEnds(2785+1, 2785));
  _graph.forbidArcUse(_graph.getArcByEnds(2785-56, 2785));
}

//create IloNodes
void RoutingModel::createIloNodes(const char* nodeFileName,
                                  const char* coordFileName) {
  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);
    _graph.associateByCoordsInFile (node, coordFileName);
    ++it;
  }
  csvNodeReader.end();
}

//create vehicles
void RoutingModel::createVehicles(const char* vehicleFileName) {
  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");
    IloNode node1 = IloNode::Find(_env, namefirst);
    IloNode node2 = IloNode::Find(_env, namelast);
    IloVisit first(node1, "depot");
    _mdl.add(first.getCumulVar(_weight) == 0);
    IloVisit last(node2, "depot");
    IloVehicle vehicle(first, last, name);

    vehicle.setCost(250.0);

    vehicle.setCost(_distance, 1.0);

    vehicle.setCapacity(_weight, capacity);
    last.getCumulVar(_distance).setUb(350);

    _mdl.add(vehicle);
    ++it;
  }
  csvVehicleReader.end();
}

//create visits
void RoutingModel::createVisits(const char* visitsFileName) {
  IloCsvReader csvVisitReader(_env, visitsFileName);
  IloCsvReader::LineIterator  it(csvVisitReader);
  while(it.ok()){
    IloCsvLine line = *it;
    //read visit data from files
    char * visitName =  line.getStringByHeader("name");
    char * nodeName = line.getStringByHeader("node");
    IloNum quantity = line.getFloatByHeader("quantity");
    IloNode node = IloNode::Find(_env, nodeName);
    IloVisit visit(node, visitName);

    visit.getTransitVar(_weight).setBounds(quantity, quantity);

    _unorderedVisitArray.add(visit);

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

///////////////////////////////////////////////////////////////////////////////
// Solving
class RoutingSolver {
  IloEnv              _env;
  IloModel            _mdl;
  IloSolver           _solver;
  IloDispatcher       _dispatcher;
  IloRoutingSolution  _solution;
  IloGoal             _instantiateCost;
  IloGoal             _restoreSolution;
  IloVisitArray       _orderedVisitArray;

public:
  RoutingSolver(RoutingModel mdl);
  ~RoutingSolver() {}
  void    insertAllReturnVisits ();
  void    orderVisits (IloVisitArray visitArray,
                      IloDispatcherGraph graph);
  bool    insertCustomerVisits ();
  void    improveWithNhood();
  void    printInformation(const char* =0) const;

};

RoutingSolver::RoutingSolver(RoutingModel mdl):
  _env(mdl.getEnv()),
  _mdl(mdl.getModel()),
  _solver(mdl.getModel()),
  _dispatcher(_solver),
  _solution(mdl.getModel()){
    _instantiateCost =
        IloDichotomize(_env, _dispatcher.getCostVar(), IloFalse);
    _restoreSolution = IloRestoreSolution (_env, _solution);
  }

void RoutingSolver::insertAllReturnVisits () {
  _solver.out() << "Inserting return visits" << endl;
  IloNode depot = IloNode::Find(_env, "depot");
  for (IloVehicleIterator vehIt(_mdl); vehIt.ok(); ++vehIt) {
    IloVehicle vehicle = *vehIt;
    for (IloInt i = 0; i < 2; i++) {
      IloVisit visit(depot, "depot");

      visit.setPenaltyCost(-0.1);

      _mdl.add    (visit);

      IloGoal insert =
          IloInsertVisit(_env, visit, vehicle, _solution, _instantiateCost);
      if (!_solver.solve(insert)) {
        _solver.out() << "Cannot insert new visit in solution" << endl;
        _mdl.remove(visit);
      }
      else {
        _solution.add(visit);
        _solution.store(_solver);
      }
    }
  }
}

void RoutingSolver::orderVisits(IloVisitArray unorderedVisitArray,
                                IloDispatcherGraph graph) {
  IloModel tspModel(_env);

  IloInt nbOfVisits = unorderedVisitArray.getSize();
  IloVisit first(unorderedVisitArray[0].getNode(), "first");
  IloVisit last(unorderedVisitArray[0].getNode(), "last");
  IloVehicle vehicle(first, last, "TSP");
  tspModel.add(vehicle);
  IloDistance dist =  IloGraphDistance (graph);
  IloDimension2 dim(_env, dist, IloFalse);
  vehicle.setCost(dim, 1.0);
  tspModel.add(dim);

  for (IloInt i = 1; i < nbOfVisits; i++)
    tspModel.add(unorderedVisitArray[i]);

  IloSolver solver(tspModel);
  IloDispatcher dispatcher(solver);
  IloGoal instCost = IloDichotomize(_env, dispatcher.getCostVar(), IloFalse);
  solver.out() << "Producing insertion order" << endl;

  solver.solve(IloNearestAdditionGenerate(_env) && instCost);
  IloRoutingSolution rsolution(tspModel);
  rsolution.store(solver);

  IloNHood nhood = IloTwoOpt(_env);
  IloMetaHeuristic improve = IloImprove(_env);
  IloGoal move = IloSingleMove(_env, rsolution, nhood, improve, instCost);
  while (solver.solve(move)) {
    }

  _orderedVisitArray = IloVisitArray (_env, nbOfVisits);
  IloRoutingSolution::RouteIterator rit(rsolution, vehicle);
  ++rit;
  _orderedVisitArray[0] = unorderedVisitArray[0];
  for (IloInt k = 1; k < nbOfVisits; ++k, ++rit)
    _orderedVisitArray[k] = *rit;

  nhood.end();
  solver.end();
  rsolution.end();
}

bool RoutingSolver::insertCustomerVisits () {
  _solver.out() << "Inserting customer visits" << endl;
  for (IloInt i = 0; i < _orderedVisitArray.getSize(); i++) {
    IloVisit visit = _orderedVisitArray[i];
    _mdl.add(visit);

    IloGoal insert = IloInsertVisit(_env, visit, _solution, _instantiateCost);
    if (!_solver.solve(insert)) {
      _solver.error() << "Could not generate an initial solution" << endl;
      return IloFalse;
    }
    else {
      _solution.add(visit);
      _solution.store(_solver);
    }
  }
  return IloTrue;
}

//Improve solution using nhood
void RoutingSolver::improveWithNhood() {
  IloNHood nhood = IloRelocate(_env)
                     + IloTwoOpt(_env)
                     + IloOrOpt(_env)
                     + IloCross(_env)
                     + IloExchange(_env)
                     + IloMakeUnperformed(_env)
                     + IloMakePerformed(_env);
  _solver.out() << "Improving solution" << endl;
  IloGoal improve = IloSingleMove(_env,
                                  _solution,
                                  nhood,
                                  IloImprove(_env),
                                  _instantiateCost);
  while (_solver.solve(improve)) {
  }
   _solver.solve(_restoreSolution);
}

// Display Dispatcher information
void RoutingSolver::printInformation(const char* heading) const {
  if(heading)
      _solver.out()<<heading<<endl;
  _solver.printInformation();
  _dispatcher.printInformation();
  _solver.out() << "===============" << endl
    << "Cost         : " << _dispatcher.getTotalCost() << endl
    << "Number of vehicles used : "
    << _dispatcher.getNumberOfVehiclesUsed() << endl
    << "Solution     : " << endl
    << _dispatcher << endl;
}

//////////////////////////////////////////////////////////////////////////////
int main(int argc, char * argv[]) {
  IloEnv env;
  try {
    RoutingModel mdl(env, argc, argv);
    RoutingSolver solver(mdl);
    solver.insertAllReturnVisits();
    solver.orderVisits(mdl.getUnorderedVisitArray(), mdl.getGraph());
    if (solver.insertCustomerVisits()) {
      solver.improveWithNhood();
      solver.printInformation("***Solution after improvements with nhood***");
    }
  } catch(IloException& ex) {
    cerr << "Error: " << ex << endl;
  }
  env.end();
  return 0;
}