IBM ILOG Dispatcher User's Manual > Transportation Industry Solutions > Modeling Complex Costs > Complete cost2 Program

The complete program and output for cost2.cpp follows. You can also view it online in the file YourDispatcherHome/examples/src/cost2.cpp.

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

#include <ildispat/ilodispatcher.h>

ILOSTLBEGIN
///////////////////////////////////////////////////////////////////////////////
// Modeling
class RoutingModel {
  IloEnv              _env;
  IloModel            _mdl;

  void createDimensions();
  void createIloNodes(char* nodeFileName);
  void createVehicles(char* vehicleFileName);
  void createVisits(char* visitsFileName);
public:
  RoutingModel(IloEnv env,
               int argc,
               char* argv[]);
  ~RoutingModel() {}
  IloEnv getEnv() const { return _env; }
  IloModel getModel() const { return _mdl; }
};

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

  createDimensions();

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

  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/visits.csv";
  else
    visitsFileName = argv[2];
  createVisits(visitsFileName);
}

// 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 IloNodes
void RoutingModel::createIloNodes(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);
    IloNumToNumSegmentFunction costFunction(_env);
    costFunction.setSlope(75, 100, 37.5, 0.5);
    costFunction.setSlope(100, IloInfinity, 100, 1);
    vehicle.setCost(distance, costFunction);

    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));
    ++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
                << IloVerbose(dispatcher) << endl;
}

// Solving
void RoutingSolver::solve() {
  IloDispatcher dispatcher(_solver);
  IloEnv env = _solver.getEnv();
  // Subgoal
  IloGoal instantiateCost = IloDichotomize(env,
                                           dispatcher.getCostVar(),
                                           IloFalse);
  IloGoal restoreSolution = IloRestoreSolution(env, _solution);
  IloGoal goal = IloSavingsGenerate(env) && instantiateCost;

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

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