IBM ILOG Dispatcher User's Manual > Transportation Industry Solutions > Pickup and Delivery Problems > Model

Once you have written a description of your problem, you can use Dispatcher classes to model it.

Step 2   -  

Open the example file

Open the example file YourDispatcherHome/examples/src/tutorial/pdp_partial.cpp in your development environment.

As in the previous examples, you will use a RoutingModel class to call the functions that create the dimensions, nodes, vehicles, and visits. For this example, this does not vary from the form you have used before.

Step 3   -  

Declare the RoutingModel class

Add the following code after the comment //Declare the RoutingModel class.

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; }
};

The RoutingModel class is identified as belonging to the model _mdl and the environment _env. Next, you will create the functions that RoutingModel calls.

Step 4   -  

Create the dimensions

Add the following code after the comment //Create the 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);
}

These dimensions are used in determining the cost and feasibility of potential routes. For example, weight is used for keeping track of the capacity constraints on the truck, and time is used to ensure that the truck is available during necessary time windows. The dimension distance will not be constrained for any vehicle, so you use IloFalse to prevent its propagation (although it is used to determine the cost of the route). Preventing its propagation improves performance. Just as in the VRP, time and distance are computed as Euclidian distance, although other options are available.

The member function setKey is used on dimensions to make it easy to find them in other functions, such as createVisits and createVehicles.

The function createVehicles constrains vehicles to visit the depot first and last, and adds the capacity constraint weight to the vehicles. The constraint of time is added to ensure compliance with the time windows, and distance is added to compute the cost of using the vehicle.

Step 5   -  

Create the vehicles

Add the following code after the comment //Create the vehicles.

    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.setCapacity(weight, capacity);
    _mdl.add(vehicle);

This code is the second part of the createVehicles function. The first part of the iteration reads in the truck capacity and the opening and closing times of the depot, which are then assigned as the first and last visits of the vehicle. The Find function is also used to locate the dimensions (identified with setKey, previously) as shown in the code provided for you:

  IloDimension1 weight = IloDimension1::Find(_env, "Weight");
  IloDimension2 time = IloDimension2::Find(_env, "Time");
  IloDimension2 distance = IloDimension2::Find(_env, "Distance");

The next function called by the class RoutingModel is createIloNodes, and it is provided for you. createIloNodes reads in the (x, y) coordinates of each node, which are used to compute the Euclidian distances between visits.

Next, you must create the pickup and delivery visits. The function createVisits is similar in format to createVehicles; that is, the first part of the iteration reads in data associated with each visit, like the pickup location (pickupNode), the parcel to be picked up (quantity), the time window (pickupMinTime and pickupMaxTime), and the amount of time it takes to pickup the parcel (pickupTime). Corresponding data for the drop off is also processed. Then, the second section of the iteration actually creates and adds the pickup and delivery visits.

Step 6   -  

Create the pickup 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);

Add the following code after the comment //Create the pickup visits.

This section of the function CreateVisits ensures that the pickups and deliveries will occur within the time windows, including any delays associated with the pickup and drop off.

Step 7   -  

Create the delivery visits

Add the following code for the drop off visits after the comment
//Create the delivery visits.

    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);

You have now added the pickups and deliveries; but nothing yet guarantees that a delivery will occur after the pickup. The member function IloOrderedVisitPair is used to fix the order of two events.

Step 8   -  

Create the pickup and delivery order constraint

Add the following code after the comment
//Create the pickup and delivery order constraint.

    _mdl.add(IloOrderedVisitPair(_env, pickup, delivery));

The function IloOrderedVisitPair ensures that pickup occurs before delivery in the environment _env. This line of code is within the iterator of createVisits, so each parcel pickup is paired with the drop off.