BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
Santha
Pyrite | Level 9

Hi Rob

I am trying to use the Vehicle Route Optimization code in SAS found in this link 

The problem I am trying to solve is similar for the most part.  The thing that is different in my problem from the link above is that

(a) I don't want the vehicle to return to the depot and (b) A given vehicle can travel only a max of 600 miles per day for which I am trying to use sparse modeling.  I am not sure if I have written the constraints right. Here is my code below. It takes forever to solve more than 12 hours and not yet complete.  I am not sure if I am missing anything in the code and/or can I speed by the modeling process.  Any leads would be helpful. Appreciate your help.

cas;
caslib _all_ assign;
run;

/* number of vehicles available */
%let num_vehicles = 10;
/* capacity of each vehicle */ /*How to add Distance Contraint*/
%let capacity = 40000;

data vrpdata;
   input node nodename $ city $ x y demand;
   datalines;
1 HAR HARRISBURG 40.2732 -76.8867 0
2 CLE C-44266 41.1677 -81.1638 120
3 CLE C-44707 40.7628 -81.3463 116
4 CMH C-26150 39.1584 -81.543 303
5 CMH C-43078 40.1186 -83.7536 551
6 CMH C-45601 39.3093 -82.9736 7680
7 DTW C-48170 42.3637 -83.5369 2.84
8 DTW C-48210 42.3377 -83.1278 438
9 IND C-46052 40.0438 -86.4828 315
10 IND C-46075 40.0285 -86.3318 1830.29750000002
11 IND C-46733 40.8299 -84.9372 7.348
12 IND C-47265 39.0156 -85.6402 41
13 IND C-47346 39.9367 -85.1654 0.0099225
15 MCI C-51301 43.1371 -95.1512 460.6
16 MKE C-53190 42.8056 -88.7269 5
17 MKE C-53578 43.3146 -89.7764 59.04
18 MKE C-53589 42.9323 -89.2011 104.12
19 MKE C-53707 43.0866 -89.3597 651.6
20 MKE C-54153 44.8879 -87.9014 78.42
21 MKE C-54902 43.9651 -88.4933 6.4
22 MSP C-55337 44.7795 -93.2786 252.75
23 MSP C-56057 44.3949 -93.7072 406.2
24 ORD C-46307 41.3969 -87.3182 6.31
25 ORD C-46514 41.7177 -85.9746 268.1
26 ORD C-46516 41.6682 -85.9454 558.05
27 ORD C-46545 41.6928 -86.1468 3739
28 ORD C-49107 41.8435 -86.3977 1542
29 ORD C-50436 43.2621 -93.6949 7.5
30 ORD C-60185 41.8959 -88.204 4488.9
31 ORD C-60191 41.9665 -87.9745 216.4
32 ORD C-60447 41.4827 -88.3287 525.2
33 ORD C-60525 41.7773 -87.8706 6170.880188
34 ORD C-61615 40.7737 -89.673 884.2
35 ORD C-61630 40.6921 -89.5912 7.32
36 ORD C-61639 40.6899 -89.594 620.4
37 SDF C-40403 37.5637 -84.2348 21.04
38 SDF C-40583 38.0464 -84.497 2
39 STL C-62626 39.2757 -89.903 702
40 STL C-63026 38.4984 -90.4561 1429
41 STL C-63132 38.6776 -90.3834 100
42 STL C-63147 38.6906 -90.2113 5482.729588
43 STL C-65050 38.5393 -92.6913 52
;

proc optmodel;
   /* read the node location and demand data */
   set NODES;
   str nodename {NODES};
   str city {NODES};
   num x {NODES};
   num y {NODES};
   num demand {NODES};
   num capacity = &capacity;
   num num_vehicles = &num_vehicles;
   read data vrpdata into NODES=[node] nodename city x y demand;
   set ARCS = {i in NODES, j in NODES: i <> j};
   set VEHICLES = 1..num_vehicles;

   num depot = 1;

   /* define the arc cost as the rounded Euclidean distance */
   num cost {<i,j> in ARCS} =  GEODIST(x[i],y[i],x[j],y[j],'M');   /*round(sqrt((x[i]-x[j])^2 + (y[i]-y[j])^2))*/

   set LOGICAL_ARCS = {i in NODES, j in NODES: cost[i,j] <= 600};

   /* Flow[i,j,k] is the amount of demand carried on arc (i,j) by vehicle k */
   var Flow {LOGICAL_ARCS, VEHICLES} >= 0 <= capacity;
   /* UseNode[i,k] = 1, if and only if node i is serviced by vehicle k */
   var UseNode {NODES, VEHICLES} binary;
   /* UseArc[i,j,k] = 1, if and only if arc (i,j) is traversed by vehicle k */
   var UseArc {LOGICAL_ARCS, VEHICLES} binary;

  
   /*-----------------------OBJECTIVE FUNCTION-------------------------------*/
   /* minimize the total distance traversed */
   min TotalCost = sum {<i,j> in LOGICAL_ARCS, k in VEHICLES} cost[i,j] * UseArc[i,j,k];

  /*----------------------------CONSTRAINTS-------------------------------------*/
  /* each non-depot node must be serviced by at least one vehicle */
           /*ASSIGNMENT CONSTRAINT*/ 
   con Assignment {i in NODES diff {depot}}:
      sum {k in VEHICLES} UseNode[i,k] >= 1;

  for{k in VEHICLES} fix UseNode[depot,k] = 1;

 /* each vehicle must start at the depot node */
		  /*DISTANCE CONSTRAINT*/
	CON Distance {k in VEHICLES}: 
	SUM {<i,j> in LOGICAL_ARCS: j not in {depot}} (Cost[i, j] * UseArc[i, j, k]) <= 600;


/*   some vehicle k traverses an arc that leaves node i if and only if UseNode[i,k] = 1 */
/* 			LEAVE NODE CONSTRAINT */
   con LeaveNode {i in NODES, k in VEHICLES}:
      sum {<(i),j> in LOGICAL_ARCS} UseArc[i,j,k] = UseNode[i,k];


/*   some vehicle k traverses an arc that enters node i if and only if UseNode[i,k] = 1 */
/* 			ENTER NODE CONSTRAINT   */
   con EnterNode {i in NODES, k in VEHICLES}:
      sum {<j,(i)> in LOGICAL_ARCS} UseArc[j,i,k] = UseNode[i,k];


  /* the amount of demand supplied by vehicle k to node i must equal demand if UseNode[i,k] = 1; otherwise, it must equal 0 */
			/*FLOW BALANCE CONSTRAINT*/ 
   con FlowBalance {i in NODES diff {depot}, k in VEHICLES}:
       sum {<j,(i)> in LOGICAL_ARCS} Flow[j,i,k] - sum {<(i),j> in LOGICAL_ARCS} Flow[i,j,k]
       = demand[i] * UseNode[i,k];


  /* if UseArc[i,j,k] = 1, then the flow on arc (i,j) must be at most capacity
      if UseArc[i,j,k] = 0, then no flow is allowed on arc (i,j) */
			/*VEHICLE CAPACITY CONSTRAINT*/ 
   con VehicleCapacity {<i,j> in LOGICAL_ARCS, k in VEHICLES}:
      Flow[i,j,k] <= Flow[i,j,k].ub * UseArc[i,j,k];

 /* decomp by vehicle *//*DECOMPOSITION*/
   for {i in NODES diff {depot}, k in VEHICLES} do;
      LeaveNode[i,k].block = k;
      EnterNode[i,k].block = k;
   end;
   for {i in NODES diff {depot}, k in VEHICLES} FlowBalance[i,k].block = k;
   for {<i,j> in LOGICAL_ARCS, k in VEHICLES} VehicleCapacity[i,j,k].block = k;


   /* solve using decomp (aggregate formulation) */
   solve with MILP / varsel=ryanfoster decomp=(logfreq=20);


   /* OUTPUT */
   /* create solution data set */
   create data solution_data (where=(x1<>x2 and y1<>y2)) from [i j k]=
      {<i,j> in LOGICAL_ARCS, k in VEHICLES: UseArc[i,j,k].sol > 0.5}
      StartNode=nodename[i] StartCity=city[i] x1=x[i] y1=y[i] 
      EndNode=nodename[j] EndCity=city[j] x2=x[j] y2=y[j]
	  flow=Flow[i,j,k] cost=cost[i,j] 
      function='line' drawspace='datavalue';
quit;

 

1 ACCEPTED SOLUTION

Accepted Solutions
RobPratt
SAS Super FREQ

First, I have a few corrections and suggestions for your code.

In PROC OPTMODEL, <> is the MAX operator, but here you want to express "not equal":

/*   set ARCS = {i in NODES, j in NODES: i <> j};*/
   set ARCS = {i in NODES, j in NODES: i ne j};

You want to avoid counting the (dummy) trip back to the depot, and you accounted for that in the Distance constraint, but you also need to do so in the objective:

/*   min TotalCost = sum {<i,j> in LOGICAL_ARCS, k in VEHICLES} cost[i,j] * UseArc[i,j,k];*/
   min TotalCost = sum {<i,j> in LOGICAL_ARCS: j ne depot} sum {k in VEHICLES} cost[i,j] * UseArc[i,j,k];

An alternative approach is to modify the cost definition:

*   num cost {<i,j> in ARCS} =  GEODIST(x[i],y[i],x[j],y[j],'M');   /*round(sqrt((x[i]-x[j])^2 + (y[i]-y[j])^2))*/
   num cost {<i,j> in ARCS} = (if j ne depot then GEODIST(x[i],y[i],x[j],y[j],'M') else 0);

And then you can just omit the logical conditions for the sums in both the objective and the Distance constraint.  This idea is also illustrated in the Lost Baggage Distribution example from the mathematical programming examples book.

The Distance constraint can also be associated with the block for vehicle k, and you should not exclude the depot in the blocks for LeaveNode and EnterNode:

   for {k in VEHICLES} Distance[k].block = k;
   for {i in NODES /*diff {depot}*/, k in VEHICLES} do;
      LeaveNode[i,k].block = k;
      EnterNode[i,k].block = k;
   end;
   for {i in NODES diff {depot}, k in VEHICLES} FlowBalance[i,k].block = k;
   for {<i,j> in LOGICAL_ARCS, k in VEHICLES} VehicleCapacity[i,j,k].block = k;

A best practice is to introduce a macro variable instead of hardcoding 600 in multiple places:

%let cost_limit = 600;
...
/*   set LOGICAL_ARCS = {i in NODES, j in NODES: cost[i,j] <= 600};*/
   set LOGICAL_ARCS = {<i,j> in ARCS: cost[i,j] <= &cost_limit};
...
   con Distance {k in VEHICLES}: 
/*   sum {<i,j> in LOGICAL_ARCS: j not in {depot}} (Cost[i, j] * UseArc[i, j, k]) <= 600;*/
      sum {<i,j> in LOGICAL_ARCS} cost[i,j] * UseArc[i,j,k] <= &cost_limit;

But even after all these changes, you will not be able to find a solution because the problem is infeasible.  Notice that the distance from the depot to node 15 is 962.99269, so a cost_limit of 600 cannot be satisfied.  After a few minutes, the decomposition algorithm reports this infeasibility:

7704     solve with MILP / varsel=ryanfoster decomp presolver=moderate;
NOTE: Problem generation will use 4 threads.
NOTE: The problem has 33300 variables (0 free, 10 fixed).
NOTE: The problem has 16860 binary and 0 integer variables.
NOTE: The problem has 17741 linear constraints (16450 LE, 1250 EQ, 41 GE, 0 range).
NOTE: The problem has 115710 linear constraint coefficients.
NOTE: The problem has 0 nonlinear constraints (0 LE, 0 EQ, 0 GE, 0 range).
NOTE: The initial MILP heuristics are applied.
NOTE: The MILP presolver value MODERATE is applied.
NOTE: The MILP presolver removed 7450 variables and 3720 constraints.
NOTE: The MILP presolver removed 26060 constraint coefficients.
NOTE: The MILP presolver modified 0 constraint coefficients.
NOTE: The presolved problem has 25850 variables, 14021 constraints, and 89650 constraint
      coefficients.
NOTE: The MILP solver is called.
NOTE: The Decomposition algorithm is used.
NOTE: The Decomposition algorithm is executing in single-machine mode.
NOTE: The DECOMP method value USER is applied.
NOTE: All blocks are identical and the master model is set covering.
WARNING: The master model is not a set partitioning and VARSEL=RYANFOSTER. The objective function
         must ensure that there exists at least one optimal solution that fulfills all of the
         master constraints at equality.
NOTE: The Decomposition algorithm is using an aggregate formulation and Ryan-Foster branching.
NOTE: The number of block threads has been reduced to 1 threads.
NOTE: The problem has a decomposable structure with 10 blocks. The largest block covers 9.971% of
      the constraints in the problem.
NOTE: The decomposition subproblems cover 25850 (100%) variables and 13980 (99.71%) constraints.
NOTE: The deterministic parallel mode is enabled.
NOTE: The Decomposition algorithm is using up to 4 threads.
      Iter         Best       Master         Best       LP       IP  CPU Real
                  Bound    Objective      Integer      Gap      Gap Time Time
NOTE: Starting phase 1.
         1       0.0000      40.0000            . 4.00e+01        .   35   25
         8      20.0000      20.0000            .    0.00%        .  575  205
         9      20.0000      20.0000            .    0.00%        .  815  277
NOTE: Phase 1 continuous lower bound has positive value. The original problem is Infeasible.
         Node  Active   Sols         Best         Best      Gap    CPU   Real
                                  Integer        Bound            Time   Time
            0       1      0            .    2328.4549        .    815    277
NOTE: The Decomposition algorithm used 4 threads.
NOTE: The Decomposition algorithm time is 277.68 seconds.
NOTE: Infeasible.

If you change cost_limit to 963, the problem turns out to be feasible.  But the decomposition algorithm documentation example is not the best way to solve a vehicle routing problem, and a note at the top of that example recommends instead using a specialized VRP solver that is available in SAS Optimization in SAS Viya.  Support was recently added for time windows, and imposing a time window upper bound is a simple way to model the cost_limit.  The following code solves your problem instantly:

%let cost_limit = 963;

proc optmodel sessref=mysess;
   /* read the node location and demand data */
   set NODES;
   str nodename {NODES};
   str city {NODES};
   num x {NODES};
   num y {NODES};
   num demand {NODES};
   read data vrpdata into NODES=[node] nodename city x y demand;
   set ARCS = {i in NODES, j in NODES: i ne j};
   set DEPOT = {1};
   num cost {<i,j> in ARCS} = (if j not in DEPOT then GEODIST(x[i],y[i],x[j],y[j],'M') else 0);

   set <num,num,num,num> VRPLINKS; /* route, order, from, to */
   num twl{NODES} = 0;
   num twu{NODES} = &cost_limit;
   num route{NODES};
   num route_order{NODES};
   num delivery_time{NODES};

   /* call network solver for vehicle routing with time windows */
   solve with network /
      direction = directed
      links     = (weight=cost)
      vrp       = (depot=DEPOT
                   capacity=&capacity
                   demand=demand
                   maxRoutes=&num_vehicles
                   timeWindowLower=twl
                   timeWindowUpper=twu
                   milp=FALSE)
      out       = (route=route
                   order=route_order
                   arrival=delivery_time
                   vrplinks=VRPLINKS);

   set ROUTES init {};
   num costUsed {ROUTES} init 0;
   for {<r,o,i,j> in VRPLINKS} do;
      ROUTES = ROUTES union {r};
      costUsed[r] = costUsed[r] + cost[i,j];
   end;
   print costUsed;

   /* create solution data sets */
   create data RoutesNodes
      from [node] demand twl twu route route_order delivery_time;
   create data RoutesLinks(where=(cost ne 0))
      from [r order i j]=VRPLINKS cost[i,j]
      StartNode=nodename[i] StartCity=city[i] x1=x[i] y1=y[i] 
      EndNode=nodename[j] EndCity=city[j] x2=x[j] y2=y[j]
      function='line' drawspace='datavalue';
quit;

proc sgplot data=RoutesLinks;
   scatter x=x2 y=y2 / datalabel=j;
   vector x=x2 y=y2 / xorigin=x1 yorigin=y1 group=r noarrowheads;
   xaxis display=none;
   yaxis display=none;
run;

sascommunities060223.png

View solution in original post

8 REPLIES 8
RobPratt
SAS Super FREQ

First, I have a few corrections and suggestions for your code.

In PROC OPTMODEL, <> is the MAX operator, but here you want to express "not equal":

/*   set ARCS = {i in NODES, j in NODES: i <> j};*/
   set ARCS = {i in NODES, j in NODES: i ne j};

You want to avoid counting the (dummy) trip back to the depot, and you accounted for that in the Distance constraint, but you also need to do so in the objective:

/*   min TotalCost = sum {<i,j> in LOGICAL_ARCS, k in VEHICLES} cost[i,j] * UseArc[i,j,k];*/
   min TotalCost = sum {<i,j> in LOGICAL_ARCS: j ne depot} sum {k in VEHICLES} cost[i,j] * UseArc[i,j,k];

An alternative approach is to modify the cost definition:

*   num cost {<i,j> in ARCS} =  GEODIST(x[i],y[i],x[j],y[j],'M');   /*round(sqrt((x[i]-x[j])^2 + (y[i]-y[j])^2))*/
   num cost {<i,j> in ARCS} = (if j ne depot then GEODIST(x[i],y[i],x[j],y[j],'M') else 0);

And then you can just omit the logical conditions for the sums in both the objective and the Distance constraint.  This idea is also illustrated in the Lost Baggage Distribution example from the mathematical programming examples book.

The Distance constraint can also be associated with the block for vehicle k, and you should not exclude the depot in the blocks for LeaveNode and EnterNode:

   for {k in VEHICLES} Distance[k].block = k;
   for {i in NODES /*diff {depot}*/, k in VEHICLES} do;
      LeaveNode[i,k].block = k;
      EnterNode[i,k].block = k;
   end;
   for {i in NODES diff {depot}, k in VEHICLES} FlowBalance[i,k].block = k;
   for {<i,j> in LOGICAL_ARCS, k in VEHICLES} VehicleCapacity[i,j,k].block = k;

A best practice is to introduce a macro variable instead of hardcoding 600 in multiple places:

%let cost_limit = 600;
...
/*   set LOGICAL_ARCS = {i in NODES, j in NODES: cost[i,j] <= 600};*/
   set LOGICAL_ARCS = {<i,j> in ARCS: cost[i,j] <= &cost_limit};
...
   con Distance {k in VEHICLES}: 
/*   sum {<i,j> in LOGICAL_ARCS: j not in {depot}} (Cost[i, j] * UseArc[i, j, k]) <= 600;*/
      sum {<i,j> in LOGICAL_ARCS} cost[i,j] * UseArc[i,j,k] <= &cost_limit;

But even after all these changes, you will not be able to find a solution because the problem is infeasible.  Notice that the distance from the depot to node 15 is 962.99269, so a cost_limit of 600 cannot be satisfied.  After a few minutes, the decomposition algorithm reports this infeasibility:

7704     solve with MILP / varsel=ryanfoster decomp presolver=moderate;
NOTE: Problem generation will use 4 threads.
NOTE: The problem has 33300 variables (0 free, 10 fixed).
NOTE: The problem has 16860 binary and 0 integer variables.
NOTE: The problem has 17741 linear constraints (16450 LE, 1250 EQ, 41 GE, 0 range).
NOTE: The problem has 115710 linear constraint coefficients.
NOTE: The problem has 0 nonlinear constraints (0 LE, 0 EQ, 0 GE, 0 range).
NOTE: The initial MILP heuristics are applied.
NOTE: The MILP presolver value MODERATE is applied.
NOTE: The MILP presolver removed 7450 variables and 3720 constraints.
NOTE: The MILP presolver removed 26060 constraint coefficients.
NOTE: The MILP presolver modified 0 constraint coefficients.
NOTE: The presolved problem has 25850 variables, 14021 constraints, and 89650 constraint
      coefficients.
NOTE: The MILP solver is called.
NOTE: The Decomposition algorithm is used.
NOTE: The Decomposition algorithm is executing in single-machine mode.
NOTE: The DECOMP method value USER is applied.
NOTE: All blocks are identical and the master model is set covering.
WARNING: The master model is not a set partitioning and VARSEL=RYANFOSTER. The objective function
         must ensure that there exists at least one optimal solution that fulfills all of the
         master constraints at equality.
NOTE: The Decomposition algorithm is using an aggregate formulation and Ryan-Foster branching.
NOTE: The number of block threads has been reduced to 1 threads.
NOTE: The problem has a decomposable structure with 10 blocks. The largest block covers 9.971% of
      the constraints in the problem.
NOTE: The decomposition subproblems cover 25850 (100%) variables and 13980 (99.71%) constraints.
NOTE: The deterministic parallel mode is enabled.
NOTE: The Decomposition algorithm is using up to 4 threads.
      Iter         Best       Master         Best       LP       IP  CPU Real
                  Bound    Objective      Integer      Gap      Gap Time Time
NOTE: Starting phase 1.
         1       0.0000      40.0000            . 4.00e+01        .   35   25
         8      20.0000      20.0000            .    0.00%        .  575  205
         9      20.0000      20.0000            .    0.00%        .  815  277
NOTE: Phase 1 continuous lower bound has positive value. The original problem is Infeasible.
         Node  Active   Sols         Best         Best      Gap    CPU   Real
                                  Integer        Bound            Time   Time
            0       1      0            .    2328.4549        .    815    277
NOTE: The Decomposition algorithm used 4 threads.
NOTE: The Decomposition algorithm time is 277.68 seconds.
NOTE: Infeasible.

If you change cost_limit to 963, the problem turns out to be feasible.  But the decomposition algorithm documentation example is not the best way to solve a vehicle routing problem, and a note at the top of that example recommends instead using a specialized VRP solver that is available in SAS Optimization in SAS Viya.  Support was recently added for time windows, and imposing a time window upper bound is a simple way to model the cost_limit.  The following code solves your problem instantly:

%let cost_limit = 963;

proc optmodel sessref=mysess;
   /* read the node location and demand data */
   set NODES;
   str nodename {NODES};
   str city {NODES};
   num x {NODES};
   num y {NODES};
   num demand {NODES};
   read data vrpdata into NODES=[node] nodename city x y demand;
   set ARCS = {i in NODES, j in NODES: i ne j};
   set DEPOT = {1};
   num cost {<i,j> in ARCS} = (if j not in DEPOT then GEODIST(x[i],y[i],x[j],y[j],'M') else 0);

   set <num,num,num,num> VRPLINKS; /* route, order, from, to */
   num twl{NODES} = 0;
   num twu{NODES} = &cost_limit;
   num route{NODES};
   num route_order{NODES};
   num delivery_time{NODES};

   /* call network solver for vehicle routing with time windows */
   solve with network /
      direction = directed
      links     = (weight=cost)
      vrp       = (depot=DEPOT
                   capacity=&capacity
                   demand=demand
                   maxRoutes=&num_vehicles
                   timeWindowLower=twl
                   timeWindowUpper=twu
                   milp=FALSE)
      out       = (route=route
                   order=route_order
                   arrival=delivery_time
                   vrplinks=VRPLINKS);

   set ROUTES init {};
   num costUsed {ROUTES} init 0;
   for {<r,o,i,j> in VRPLINKS} do;
      ROUTES = ROUTES union {r};
      costUsed[r] = costUsed[r] + cost[i,j];
   end;
   print costUsed;

   /* create solution data sets */
   create data RoutesNodes
      from [node] demand twl twu route route_order delivery_time;
   create data RoutesLinks(where=(cost ne 0))
      from [r order i j]=VRPLINKS cost[i,j]
      StartNode=nodename[i] StartCity=city[i] x1=x[i] y1=y[i] 
      EndNode=nodename[j] EndCity=city[j] x2=x[j] y2=y[j]
      function='line' drawspace='datavalue';
quit;

proc sgplot data=RoutesLinks;
   scatter x=x2 y=y2 / datalabel=j;
   vector x=x2 y=y2 / xorigin=x1 yorigin=y1 group=r noarrowheads;
   xaxis display=none;
   yaxis display=none;
run;

sascommunities060223.png

Santha
Pyrite | Level 9

wow that is terrific Rob. I shall read the code again with your inputs. You are the best.

thank you.

Santha
Pyrite | Level 9

Hi Rob

I wanted to try the solve with optwork ,the special vrp solver that you mentioned. However I get an error that it is not able to recognize the option VRP.  Here is the log file and the SAS Viya release detail below. Can you please help in terms of what I need to do about this issue Rob?

Thanks much

Santha_0-1685951711301.png

 

%let cost_limit = 963;
173  
174  proc optmodel sessref=mysess;
175     /* read the node location and demand data */
176     set NODES;
177     str nodename {NODES};
178     str city {NODES};
179     num x {NODES};
180     num y {NODES};
181     num demand {NODES};
182     read data vrpdata into NODES=[node] nodename city x y demand;
NOTE: There were 42 observations read from the data set WORK.VRPDATA.
183     set ARCS = {i in NODES, j in NODES: i ne j};
184     set DEPOT = {1};
185     num cost {<i,j> in ARCS} = (if j not in DEPOT then GEODIST(x[i],y[i],x[j],y[j],'M') else 0);
186  
187     set <num,num,num,num> VRPLINKS;
187!                                    /* route, order, from, to */
188     num twl{NODES} = 0;
189     num twu{NODES} = &cost_limit;
190     num route{NODES};
191     num route_order{NODES};
192     num delivery_time{NODES};
193  
194     /* call network solver for vehicle routing with time windows */
195     solve with network /
196        direction = directed
197        links     = (weight=cost)
198        vrp       = (depot=DEPOT
           ---
           690
           22
ERROR 690-782: The option VRP is unrecognized.
ERROR 22-322: Expecting one of the following: BICONCOMP, BICONNECTEDCOMPONENTS, CLIQUE, CONCOMP, CONNECTEDCOMPONENTS, CYCLE, 
              GRAPH_DIRECTION, INCLUDE_SELFLINK, LAP, LINEARASSIGNMENT, LINKS, LOGFREQ, LOGLEVEL, MAXTIME, MCF, MINCOSTFLOW, 
              MINCUT, MINSPANTREE, MST, NODES, NTHREADS, OUT, SHORTESTPATH, SHORTPATH, SUBGRAPH, TIMETYPE, TRANSCL, 
              TRANSITIVECLOSURE, TSP.
199                     capacity=&capacity
                        --------
                        22
                        76
ERROR 22-322: Syntax error, expecting one of the following: !, !!, &, (, ), *, **, +, -, .., /, <, <=, <>, =, >, ><, >=, AND, BY, 
              CROSS, DIFF, ELSE, IN, INTER, OR, SYMDIFF, TO, UNION, WITHIN, [, ^, ^=, |, ||, ~=.  
ERROR 76-322: Syntax error, statement will be ignored.
200                     demand=demand
201                     maxRoutes=&num_vehicles
202                     timeWindowLower=twl
203                     timeWindowUpper=twu
204                     milp=FALSE)
205        out       = (route=route
206                     order=route_order
207                     arrival=delivery_time
208                     vrplinks=VRPLINKS);
209  
210     set ROUTES init {};
211     num costUsed {ROUTES} init 0;
212     for {<r,o,i,j> in VRPLINKS} do;
213        ROUTES = ROUTES union {r};
214        costUsed[r] = costUsed[r] + cost[i,j];
215     end;
ERROR: The symbol 'VRPLINKS' has no value at line 212 column 22.
216     print costUsed;
217  
218     /* create solution data sets */
219     create data RoutesNodes
220        from [node] demand twl twu route route_order delivery_time;
NOTE: The data set WORK.ROUTESNODES has 42 observations and 7 variables.
221     create data RoutesLinks(where=(cost ne 0))
222        from [r order i j]=VRPLINKS cost[i,j]
223        StartNode=nodename[i] StartCity=city[i] x1=x[i] y1=y[i]
224        EndNode=nodename[j] EndCity=city[j] x2=x[j] y2=y[j]
225        function='line' drawspace='datavalue';
ERROR: The symbol 'VRPLINKS' has no value at line 222 column 26.
226  quit;
NOTE: The SAS System stopped processing this step because of errors.
NOTE: PROCEDURE OPTMODEL used (Total process time):
      real time           0.01 seconds
      cpu time            0.01 seconds
    

 

Santha
Pyrite | Level 9

Rob

I feel it may be a version issue but I am not 100% sure. 

should we be using this ? because it says 2023.05 in SAS Viya Platform Programming Documentation 2023.05?

 

Santha
Pyrite | Level 9

Rob

I tried this but still it gives the same error as seen below.

89   /* number of vehicles available */
90   %let num_vehicles = 10;
92   %let capacity = 40000;
94   %let cost_limit = 600;
 
96   %let depot = 1;
101  /* node, x coordinate, y coordinate, demand */
102  data CASUSER.NodeSetIn;
103     input node nodename $ city $ x y demand;
104     datalines;
NOTE: The data set CASUSER.NODESETIN has 10 observations and 6 variables.
NOTE: DATA statement used (Total process time):
      real time           0.02 seconds
      cpu time            0.01 seconds
      
115  ;
116  
117  
118  data CASUSER.NodeSetIn;
119     set CASUSER.NodeSetIn;
120  run;
NOTE: Running DATA step in Cloud Analytic Services.
NOTE: The DATA step will run in multiple threads.
NOTE: There were 10 observations read from the table NODESETIN in caslib CASUSER(PBBL-Exp).
NOTE: The table NodeSetIn in caslib CASUSER(PBBL-Exp) has 10 observations and 6 variables.
NOTE: DATA statement used (Total process time):
      real time           0.02 seconds
      cpu time            0.02 seconds
      
121  proc sql;
122     create table CASUSER.LinkSetIn as
123     select
124        a.node as node1, a.x as x1, a.y as y1,
125        b.node as node2, b.x as x2, b.y as y2,
126        round(sqrt((a.x-b.x)**2 + (a.y-b.y)**2)) as distance
127        from NodeSetIn as a, NodeSetIn as b
128        where a.node < b.node;
NOTE: The execution of this query involves performing one or more Cartesian product joins that can not be optimized.
NOTE: Table CASUSER.LINKSETIN created, with 45 rows and 7 columns.
129  quit;
NOTE: PROCEDURE SQL used (Total process time):
      real time           0.02 seconds
      cpu time            0.01 seconds
      
130  
131  proc optnetwork
132     logLevel     = moderate
133     links        = CASUSER.LinkSetIn
134     nodes        = CASUSER.NodeSetIn
135     outNodes     = CASUSER.RoutesNodes;
136     linksVar
137        from      = node1
138        to        = node2
139        weight    = distance;
140     nodesVar
141        lower     = demand
142        vars      = (x y);
143     vrp
        ---
        180
ERROR 180-322: Statement is not valid or it is used out of proper order.
144        depot     = &depot
145        capacity  = &capacity
146        minRoutes = &num_vehicles
147        maxRoutes = &num_vehicles
148        out       = RoutesLinks;
149  run;
NOTE: The SAS System stopped processing this step because of errors.
WARNING: The data set CASUSER.ROUTESNODES may be incomplete.  When this step was stopped there were 0 observations and 0 variables.
WARNING: Data set CASUSER.ROUTESNODES was not replaced because this step was stopped.
NOTE: PROCEDURE OPTNETWORK used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds
      
RobPratt
SAS Super FREQ

It looks like you are using SAS Viya 3.5, but the VRP solver is available only in SAS Viya 4.  And support for time windows (VRPTW) was added recently.

 

2023.01 release for PROC OPTNETWORK and the vrp action:

SAS Help Center: 2023.01 (January 2023)

 

2023.03 release for PROC OPTMODEL and the runOptmodel action: 

SAS Help Center: 2023.03 (March 2023)

Santha
Pyrite | Level 9

Rob

Ok. Right now, not sure when we will be upgraded to sas viya 4.0. 

in the interim, what is your recommended way?

RobPratt
SAS Super FREQ

I don't have an easy workaround that will be competitive with the specialized VRP solver.  Until you can upgrade, you might consider the approach illustrated in this SAS Global Forum paper:

https://support.sas.com/resources/papers/proceedings18/1758-2018.pdf

 

It uses dynamically generated subtour elimination constraints, together with a repair heuristic that uses the TSP solver.  The problem in the paper is not exactly the same as yours, but you can apply the same technique to your problem, with minimal changes to the code provided in the paper.  The d in DAYS in the paper corresponds to k in VEHICLES in your problem.  Replace the Cover and FirstVisit constraints in the paper with the Assignment constraints in your problem.

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

Multiple Linear Regression in SAS

Learn how to run multiple linear regression models with and without interactions, presented by SAS user Alex Chaplin.

Find more tutorials on the SAS Users YouTube channel.

Discussion stats
  • 8 replies
  • 8858 views
  • 3 likes
  • 2 in conversation