Lost Baggage Distribution


PROC OPTMODEL Statements and Output

Although you could find the connected components of the support graph of each intermediate solution by using OPTMODEL’s programming language capabilities as in Chapter 23, the following SAS macro instead uses the SOLVE WITH NETWORK statement together with the CONCOMP option:

%macro findConnectedComponents;
   if card(ARCS_SOL) > 0 then do;
      solve with NETWORK /
         links    = (include=ARCS_SOL)
         subgraph = (nodes=NODES_SOL)
         concomp
         out      = (concomp=component_id);
      COMPONENT_IDS = setof {i in NODES_SOL} component_id[i];
      for {c in COMPONENT_IDS} COMPONENT[c] = {};
      for {i in NODES_SOL} do;
         ci = component_id[i];
         COMPONENT[ci] = COMPONENT[ci] union {i};
      end;
   end;
   else COMPONENT_IDS = {};
%mend findConnectedComponents;

The following SAS macro contains a DO UNTIL loop that implements dynamic generation of subtour elimination constraints (“row generation”), as in Chapter 23:

%macro subtourEliminationLoop;
   /* loop until each vehicle's support graph is connected */
   do until (and {v in VEHICLES} num_components[v] <= 1);
      solve;
      /* find connected components for each vehicle */
      for {v in VEHICLES} do;
         NODES_SOL = {i in NODES: UseNode[i,v].sol > 0.5};
         ARCS_SOL = {<i,j> in ARCS: UseArc[i,j,v].sol > 0.5};
         %findConnectedComponents;
         num_components[v] = card(COMPONENT_IDS);
         /* create subtour from each component not containing depot node */
         for {k in COMPONENT_IDS: depot not in COMPONENT[k]} do;
            num_subtours = num_subtours + 1;
            SUBTOUR[num_subtours] = COMPONENT[k];
            put SUBTOUR[num_subtours]=;
         end;
      end;
      print UseVehicle TimeUsed num_components;
   end;
%mend subtourEliminationLoop;

The previous two macros are used within the main PROC OPTMODEL call. The first several PROC OPTMODEL statements declare index sets and parameters and then read the input data:

proc optmodel;
   num num_vehicles init &num_vehicles;
   set VEHICLES = 1..num_vehicles;
   str depot = "&depot";
   set <str> NODES;
   read data time_data into NODES=[location];
   set ARCS init NODES cross NODES;
   num travel_time {ARCS};
   read data time_data into [i=location]
      {j in NODES} <travel_time[i,j]=col(j)>;

The following statements make the travel times symmetric, except that travel time back to the depot is set to 0:

   for {<i,j> in ARCS: travel_time[i,j] = .}
      travel_time[i,j] = travel_time[j,i];
   /* ignore travel time back to depot */
   for {i in NODES}
      travel_time[i,depot] = 0;
   /* remove self-loops */
   ARCS = ARCS diff setof {i in NODES} <i,i>;
   print travel_time;

The PRINT statement results in the first section of output, shown in Figure 27.1.

Figure 27.1: travel_time Parameter

The OPTMODEL Procedure

travel_time
  Barking Battersea Bromley Dartford Ealing Greenwich Hammersmith Harrow Heathrow Holborn Islington Kingston Richmond Sutton Woolwich
Barking   61 25 25 65 25 65 90 0 47 45 70 72 75 13
Battersea 61   27 43 24 20 7 37 0 12 30 12 14 15 40
Bromley 25 27   15 55 17 41 57 0 53 45 25 33 15 30
Dartford 25 43 15   70 15 45 55 0 60 63 65 53 46 70
Ealing 65 24 55 70   50 10 15 0 30 20 25 15 50 90
Greenwich 25 20 17 15 50   40 85 0 55 30 34 32 45 10
Hammersmith 65 7 41 45 10 40   25 0 12 15 20 8 25 25
Harrow 90 37 57 55 15 85 25   0 35 20 35 30 60 40
Heathrow 86 44 85 90 25 80 25 20   35 35 35 20 65 82
Holborn 47 12 53 60 30 55 12 35 0   10 22 20 45 21
Islington 45 30 45 63 20 30 15 20 0 10   45 34 25 27
Kingston 70 12 25 65 25 34 20 35 0 22 45   5 11 65
Richmond 72 14 33 53 15 32 8 30 0 20 34 5   19 56
Sutton 75 15 15 46 50 45 25 60 0 45 25 11 19   25
Woolwich 13 40 30 70 90 10 25 40 0 21 27 65 56 25  



The following model declaration statements correspond directly to the mathematical programming formulation that is described earlier:

   var UseNode {NODES, VEHICLES} binary;
   var UseArc {ARCS, VEHICLES} binary;
   var UseVehicle {VEHICLES} binary;
   var TimeUsed {v in VEHICLES} >= 0 <= &time_limit;

   min NumVehiclesUsed =
      sum {v in VEHICLES} UseVehicle[v];

   con NodeCover {i in NODES diff {depot}}:
      sum {v in VEHICLES} UseNode[i,v] = 1;

   con Outflow {i in NODES, v in VEHICLES}:
      sum {<(i),j> in ARCS} UseArc[i,j,v] = UseNode[i,v];

   con Inflow {j in NODES, v in VEHICLES}:
      sum {<i,(j)> in ARCS} UseArc[i,j,v] = UseNode[j,v];

   con UseVehicle_con1 {i in NODES, v in VEHICLES}:
      UseNode[i,v] <= UseVehicle[v];

   con UseVehicle_con2 {v in VEHICLES}:
      UseVehicle[v] <= UseNode[depot,v];

   con TimeUsed_con {v in VEHICLES}:
      TimeUsed[v] = sum {<i,j> in ARCS} travel_time[i,j] * UseArc[i,j,v];

The following statements declare optional symmetry-breaking constraints to reduce the number of essentially identical branch-and-bound nodes that are explored by the mixed integer linear programming solver:

   /* several alternatives for symmetry-breaking constraints */
   con Symmetry {v in VEHICLES diff {1}}:
      sum {i in NODES} UseNode[i,v] <= sum {i in NODES} UseNode[i,v-1];
*  con Symmetry {v in VEHICLES diff {1}}:
      UseVehicle[v] <= UseVehicle[v-1];
*  con Symmetry {v in VEHICLES diff {1}}:
      TimeUsed[v] <= TimeUsed[v-1];

Williams (2013) breaks symmetry by using the first alternative, but you could use one of the other alternatives instead.

In SAS/OR 13.1, the mixed integer linear programming solver automatically detects and exploits symmetry without you having to explicitly declare such symmetry-breaking constraints. You can control the aggressiveness of symmetry detection by using the SYMMETRY= option in the SOLVE WITH MILP statement.

The following statements declare the subtour elimination constraints:

   num num_subtours init 0;

   /* subset of nodes not containing depot node */
   set <str> SUBTOUR {1..num_subtours};

   /* if node k in SUBTOUR[s] is used by vehicle v, then
      must use at least two arcs across partition induced by SUBTOUR[s] */
   con Subtour_elimination
      {s in 1..num_subtours, k in SUBTOUR[s], v in VEHICLES}:
      sum {i in NODES diff SUBTOUR[s], j in SUBTOUR[s]: <i,j> in ARCS}
         UseArc[i,j,v]
    + sum {i in SUBTOUR[s], j in NODES diff SUBTOUR[s]: <i,j> in ARCS}
         UseArc[i,j,v]
   >= 2 * UseNode[k,v];

The following statements declare the index sets and parameters that are needed to detect violated subtour elimination constraints:

   num num_components {VEHICLES};
   set <str> NODES_SOL;
   set <str,str> ARCS_SOL;
   num component_id {NODES_SOL};
   set COMPONENT_IDS;
   set <str> COMPONENT {COMPONENT_IDS};
   num ci;

The following statements call the %subtourEliminationLoop macro to minimize the number of vehicles used and then use the .sol objective suffix to update the num_vehicles parameter to the resulting minimum value:

   %subtourEliminationLoop;
   num_vehicles = round(NumVehiclesUsed.sol);

Changing the value of num_vehicles automatically updates the VEHICLES index set and consequently all the model declarations that depend on VEHICLES.

The following statements declare the additional variables, objective, and constraints that are needed to minimize the makespan, given the minimum number of vehicles already found:

   var MaxTimeUsed >= 0 <= &time_limit;

   min Makespan = MaxTimeUsed;

   con MaxTimeUsed_con {v in VEHICLES}:
      MaxTimeUsed >= TimeUsed[v];

The following statements call the %subtourEliminationLoop macro again to minimize the makespan and then print the nodes and arcs that are used by each vehicle in the final solution:

   %subtourEliminationLoop;
   for {v in VEHICLES: UseVehicle[v].sol > 0.5} do;
      print v;
      print {<i,j> in ARCS: UseArc[i,j,v].sol > 0.5} travel_time[i,j];
   end;
quit;

Figure 27.2 through Figure 27.9 show the output from each iteration of subtour elimination.

Figure 27.2: Output from Subtour Elimination, Iteration 1

[1] UseVehicle TimeUsed num_components
1 1 110 5
2 1 110 2
3 0 0 0
4 0 0 0
5 0 0 0
6 0 0 0



Figure 27.3: Output from Subtour Elimination, Iteration 2

[1] UseVehicle TimeUsed num_components
1 1 102 3
2 1 120 2
3 0 0 0
4 0 0 0
5 0 0 0
6 0 0 0



Figure 27.4: Output from Subtour Elimination, Iteration 3

[1] UseVehicle TimeUsed num_components
1 1 118 2
2 1 100 2
3 0 0 0
4 0 0 0
5 0 0 0
6 0 0 0



Figure 27.5: Output from Subtour Elimination, Iteration 4

[1] UseVehicle TimeUsed num_components
1 1 106 2
2 1 113 3
3 0 0 0
4 0 0 0
5 0 0 0
6 0 0 0



Figure 27.6: Output from Subtour Elimination, Iteration 5

[1] UseVehicle TimeUsed num_components
1 1 102 2
2 1 116 1
3 0 0 0
4 0 0 0
5 0 0 0
6 0 0 0



Figure 27.7: Output from Subtour Elimination, Iteration 6

[1] UseVehicle TimeUsed num_components
1 1 1.0100E+02 2
2 1 1.1300E+02 2
3 0 -6.4347E-12 0
4 0 -5.0215E-13 0
5 -0 -4.2153E-12 0
6 0 1.2757E-12 0



Figure 27.8: Output from Subtour Elimination, Iteration 7

[1] UseVehicle TimeUsed num_components
1 1 120 1
2 1 102 1
3 0 0 0
4 0 0 0
5 0 0 0
6 0 0 0



Figure 27.9: Output from Subtour Elimination, Iteration 8

[1] UseVehicle TimeUsed num_components
1 1 99 1
2 1 100 1



Figure 27.10 shows the final problem and solution summaries from the mixed integer linear programming solver.

Figure 27.10: Final Problem and Solution Summaries from Mixed Integer Linear Programming Solver

Problem Summary
Objective Sense Minimization
Objective Function Makespan
Objective Type Linear
   
Number of Variables 455
Bounded Above 0
Bounded Below 0
Bounded Below and Above 455
Free 0
Fixed 0
Binary 452
Integer 0
   
Number of Constraints 197
Linear LE (<=) 33
Linear EQ (=) 76
Linear GE (>=) 88
Linear Range 0
   
Constraint Coefficients 7586

Performance Information
Execution Mode Single-Machine
Number of Threads 4

Solution Summary
Solver MILP
Algorithm Branch and Cut
Objective Function Makespan
Solution Status Optimal
Objective Value 100
   
Relative Gap 0
Absolute Gap 0
Primal Infeasibility 0
Bound Infeasibility 0
Integer Infeasibility 0
   
Best Bound 100
Nodes 185
Iterations 13859
Presolve Time 0.08
Solution Time 0.94



Figure 27.11 shows the travel times for the arcs that are used by each vehicle in the final solution.

Figure 27.11: Travel Times for Arcs Used by Each Vehicle in the Final Solution

v
1

[1] [2]  
Bromley Dartford 15
Dartford Heathrow 0
Ealing Hammersmith 10
Hammersmith Richmond 8
Harrow Ealing 15
Heathrow Harrow 20
Kingston Sutton 11
Richmond Kingston 5
Sutton Bromley 15

v
2

[1] [2]  
Barking Heathrow 0
Battersea Greenwich 20
Greenwich Woolwich 10
Heathrow Islington 35
Holborn Battersea 12
Islington Holborn 10
Woolwich Barking 13