Hi Rob
I have a question related to this thread.
The VRO model solves just fine. I am trying to run the model in a loop automatically. Basically, I want to run for two Depots (A and B) and for 26 weeks for each Depot. Weeknumbers are 1..26. For each Depot and for each week number, there are two modes (mode 1 and mode2). Here is the code: How do I set this up in a loop? or can i set this up in a loop so that if i run the run button once, all the combinations get run. Right now I am manually running it for all combinations.
/*Variables*/
%let capacity = 5000;
%let num_vehicles=20;
%let dist=3000;
%let max_stops=400;
%let Depot='A'; (I want to loop this one, one for Depot A and one for Depot B)
%let DepotID=99999; /* 88888 for A and 99999 for B*/
%Let WeekNumber='2'; (I want to loop this for 26 weeks for each Depot)
%Let Mode='Mode1'; /* Mode1 or Mode2 */ (I want to loop this for 2 modes, Mode1 and Mode2)
%let Scenario='xxx';
proc fedsql sessref=mysess;
create table CASUSER.vrpdata {options replace='TRUE'} AS
select ModelWeekNumber_2WK as ModelWeekNumber
,ModelNode
,ModelCity
,ModelLat
,ModelLong
,ModelZip
,ModelState
,Case_Count
,Unit_Count
,round(Volume)as Volume
,Weight
,Declared_Value
,ModelDepotState
,ModelDepotCity
,ModelDepotZip
,NodeID
,Mode
,WeekNumbers
,WeekNumber_EarliestStartDate
,WeekNumber_LatestEndDate
from DataSource
where ModelDepotCity=&Depot
and ModelWeekNumber_2WK=&WeekNumber
and Mode=&Mode;
;
QUIT;
proc optmodel;
/* read the node location and demand data */
set NODES;
str ModelNode {NODES};
num ModelLat {NODES};
num ModelLong {NODES};
num Volume {NODES};
num capacity = &capacity;
num num_vehicles = &num_vehicles;
str Mode {NODES};
read data CASUSER.vrpdata into NODES =[NodeID] ModelNode ModelLat ModelLong Volume Mode;
set ARCS = {i in NODES, j in NODES: i ne j};
set VEHICLES = 1..num_vehicles;
num depot = &DepotID;
/* define the arc dist as the rounded Euclidean distance */
num dist {<i,j> in ARCS} = (if i ne depot then GEODIST(ModelLat[i],ModelLong[i],ModelLat[j],ModelLong[j],'M') else 0);
/* Creating an Sparse Matrix to remove any unwanted arcs*/
set LOGICAL_ARCS = {<i,j> in ARCS: dist[i,j] <= &dist};
/*print {<i,j> in LOGICAL_ARCS} dist; */
/* Flow[k] is the amount of demand carried 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} dist[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; */
con VisitDepot {i in NODES diff {depot}, k in VEHICLES}:
UseNode[i,k] <= UseNode[depot,k]
suffixes=(block=k);
/*DISTANCE CONSTRAINT*/
CON Distance {k in VEHICLES}:
/* SUM {<i,j> in LOGICAL_ARCS: j not in {depot}} (dist[i, j] * UseArc[i, j, k]) <= &dist_limit; */
sum {<i,j> in LOGICAL_ARCS} dist[i,j] * UseArc[i,j,k] <= &dist;
/*STOPS CONSTRAINT*/
CON Stops {k in VEHICLES}:
sum {i in NODES diff {depot}} UseNode[i,k] <= &max_stops;
/* 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 Volume supplied by vehicle k to node i must equal Volume if UseNode[i,k] = 1; otherwise, it must equal 0 */
/*FLOW BALANCE CONSTRAINT*/
con FlowBalance {i in NODES diff {depot}, k in VEHICLES}:
sum {<(i),j> in LOGICAL_ARCS} Flow[i,j,k] - sum {<j,(i)> in LOGICAL_ARCS} Flow[j,i,k]
= Volume[i] * UseNode[i,k];
/* each vehicle is empty when it leaves the depot */
for {<(depot),j> in LOGICAL_ARCS, k in VEHICLES} fix Flow[depot,j,k] = 0;
/* 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];
/*Constraint VehicleCapacity[3,4,1]: Flow[3,4,1] - 3650*UseArc[3,4,1] <= 0 /*
/* decomp by vehicle *//*DECOMPOSITION*/
for {i in NODES, k in VEHICLES} do;
LeaveNode[i,k].block = k;
EnterNode[i,k].block = k;
end;
for {k in VEHICLES} Distance[k].block = k;
for {k in VEHICLES} Stops[k].block = k;
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 / decomp varsel=ryanfoster presolver=basic maxtime=300;
/* OUTPUT */
/* create solution data set */
create data solution_data from [i j k]=
{<i,j> in LOGICAL_ARCS, k in VEHICLES: UseArc[i,j,k].sol > 0.5 and i ne depot}
StartNode=ModelNode[i] StartCity=ModelNode[i] x1=ModelLat[i] y1=ModelLong[i]
EndNode=ModelNode[j] EndCity=ModelNode[j] x2=ModelLat[j] y2=ModelLong[j]
flow=Flow[i,j,k] dist=dist[i,j]
function='line' drawspace='datavalue'
;
quit;
proc sgplot data=solution_data noautolegend;
scatter x=x1 y=y1 / datalabel=i;
vector x=x2 y=y2 / xorigin=x1 yorigin=y1 group=k noarrowheads;
xaxis display=none;
yaxis display=none;
run;
DATA WORK.VRO_OUTPUT;
set WORK.solution_data;
Depot=&Depot;
WeekNumber=&WeekNumber;
VehicleByScenarioByWeekNumber= cat(Depot,'-',k,'-',WeekNumber);
Mode=&Mode;
Scenario=&Scenario;
;
run;
The simplest way is to use the runOptmodel action with the groupBy parameter. The idea is to write the OPTMODEL code as if you are solving for only one BY group and then make the following modifications.
Call PROC CAS instead of PROC OPTMODEL:
/*proc optmodel;*/
proc cas noqueue;
source pgm;
put _BY_LINE_;
Use NodeID instead of _N_ as the key column (always a good idea to avoid _N_ with CAS because the data order is unpredictable):
read data vrpdata into NODES =[NodeID] ModelNode ModelLat ModelLong Volume Mode;
After the end of your OPTMODEL code, call the runOptmodel action:
/*quit;*/
endsource;
action optimization.runOptmodel / code=pgm groupBy='ModelWeekNumber';
quit;
The resulting solution_data CAS table will automatically include any BY variables, so you can use a BY statement in the PROC SGPLOT call to plot one BY group at a time:
proc sgplot data=casuser.solution_data noautolegend;
by ModelWeekNumber;
scatter x=x1 y=y1 / datalabel=i;
vector x=x2 y=y2 / xorigin=x1 yorigin=y1 group=k noarrowheads;
xaxis display=none;
yaxis display=none;
run;
It is worth noting that you can optionally get a summary table with one row per BY group as follows:
action optimization.runOptmodel / code=pgm groupBy='ModelWeekNumber' statusOut='statusOut';
The resulting statusOut table contains information about the solver call for each BY group.
The following examples illustrate how to do what you want:
42332 - BY group processing with PROC OPTMODEL and the runOptmodel action (sas.com)
Awesome. Thanks. I will explore this and get back .
Rob. I went through these two examples that you had mentioned in addition to this one as well where you had helped. I really get the idea theoretically and able to understand. But when i tried to implement , I found difficult. Here is my code. I have relaxed my earlier ask of nested loop. There is only one loop that I want the model to do and that is ModelWeekNumber. I want the VRO model to loop through just that. Can you help me with that? I tried till creating Logical_ARCS but was not successful at all . I have attached the dataset . only 2 weeknumbers (3 and 4). just 81 observations in total.
/*Variables*/
%let capacity = 5000;
%let num_vehicles=20;
%let dist=3000;
%let max_stops=400;
%let Depot='A';
%let DepotID=99999; /* 88888 for B and 99999 for A*/
%Let Mode='Mode1'; /* Mode1 or Mode2 */
%let Scenario='Routing Alternates';
%let byvar=ModelWeekNumber;
proc fedsql sessref=mysess;
create table CASUSER.vrpdata {options replace='TRUE'} AS
select ModelWeekNumber_2WK as ModelWeekNumber
,ModelNode
,ModelCity
,ModelLat
,ModelLong
,ModelZip
,ModelState
,Case_Count
,Unit_Count
,round(Volume)as Volume
,Weight
,Declared_Value
,ModelDepotState
,ModelDepotCity
,ModelDepotZip
,NodeID
,Mode
,WeekNumbers
,WeekNumber_EarliestStartDate
,WeekNumber_LatestEndDate
from DATA
where ModelWeekNumber_2WK in ('3','4')
and ModelDepotCity=&Depot
and Mode=&Mode;
quit;
proc optmodel;
/* read the node location and demand data */
set NODES;
str ModelNode {NODES};
num NodeID {NODES};
num ModelLat {NODES};
num ModelLong {NODES};
num Volume {NODES};
num capacity = &capacity;
num num_vehicles = &num_vehicles;
str Mode {NODES};
num ModelWeekNumber {NODES};
read data CASUSER.vrpdata into NODES =[_N_] NodeID ModelNode ModelLat ModelLong Volume Mode ModelWeekNumber;
set ARCS = {i in NODES, j in NODES: i ne j};
set VEHICLES = 1..num_vehicles;
num depot = &DepotID;
/* Collect values of BY variable into an index set */
set BYSET = setof {i in NODES} &byvar.[i];
put BYSET=;
num by;
/* Declare the variable */
set OBS_BY = {i in NODES, j in NODES:&byvar.[i] = by};
/* define the arc dist as the rounded Euclidean distance */
num dist {<i,j> in OBS_BY} = (if i ne depot then GEODIST(ModelLat[i],ModelLong[i],ModelLat[j],ModelLong[j],'M') else 0);
print dist;
/* Creating an Sparse Matrix to remove any unwanted arcs*/
set LOGICAL_ARCS {o in OBS_BY} = {<i,j> in ARCS: dist[i,j,o] <= &dist};
print {<i,j> in LOGICAL_ARCS} dist;
/* Flow[k] is the amount of demand carried 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} dist[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; */
con VisitDepot {i in NODES diff {depot}, k in VEHICLES}:
UseNode[i,k] <= UseNode[depot,k]
suffixes=(block=k);
/*DISTANCE CONSTRAINT*/
CON Distance {k in VEHICLES}:
/* SUM {<i,j> in LOGICAL_ARCS: j not in {depot}} (dist[i, j] * UseArc[i, j, k]) <= &dist_limit; */
sum {<i,j> in LOGICAL_ARCS} dist[i,j] * UseArc[i,j,k] <= &dist;
/*STOPS CONSTRAINT*/
CON Stops {k in VEHICLES}:
sum {i in NODES diff {depot}} UseNode[i,k] <= &max_stops;
/* 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 Volume supplied by vehicle k to node i must equal Volume if UseNode[i,k] = 1; otherwise, it must equal 0 */
/*FLOW BALANCE CONSTRAINT*/
con FlowBalance {i in NODES diff {depot}, k in VEHICLES}:
sum {<(i),j> in LOGICAL_ARCS} Flow[i,j,k] - sum {<j,(i)> in LOGICAL_ARCS} Flow[j,i,k]
= Volume[i] * UseNode[i,k];
/* each vehicle is empty when it leaves the depot */
for {<(depot),j> in LOGICAL_ARCS, k in VEHICLES} fix Flow[depot,j,k] = 0;
/* 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, k in VEHICLES} do;
LeaveNode[i,k].block = k;
EnterNode[i,k].block = k;
end;
for {k in VEHICLES} Distance[k].block = k;
for {k in VEHICLES} Stops[k].block = k;
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 / decomp varsel=ryanfoster presolver=basic maxtime=1200;
/* OUTPUT */
/* create solution data set */
create data solution_data from [i j k]=
{<i,j> in LOGICAL_ARCS, k in VEHICLES: UseArc[i,j,k].sol > 0.5 and i ne depot}
StartNode=ModelNode[i] StartCity=ModelNode[i] x1=ModelLat[i] y1=ModelLong[i]
EndNode=ModelNode[j] EndCity=ModelNode[j] x2=ModelLat[j] y2=ModelLong[j]
flow=Flow[i,j,k] dist=dist[i,j]
;
quit;
proc sgplot data=solution_data noautolegend;
scatter x=x1 y=y1 / datalabel=i;
vector x=x2 y=y2 / xorigin=x1 yorigin=y1 group=k noarrowheads;
xaxis display=none;
yaxis display=none;
run;
DATA WORK.VRO_OUTPUT;
set WORK.solution_data;
Depot=&Depot;
WeekNumber=&WeekNumber;
VehicleByScenarioByWeekNumber= cat(Depot,'-',k,'-',WeekNumber);
Mode=&Mode;
Scenario=&Scenario;
;
run;
Data WORK.VRO_OUTPUT_Adjusted;
length StartNode $ 60;
length StartCity $ 60;
length EndNode $ 60;
length EndCity $ 60;
length Depot $ 60;
length WeekNumber $60;
length VehicleByScenarioByWeekNumber $ 200;
length Mode $ 60;
length Scenario $60;
set WORK.VRO_OUTPUT;
run;
The simplest way is to use the runOptmodel action with the groupBy parameter. The idea is to write the OPTMODEL code as if you are solving for only one BY group and then make the following modifications.
Call PROC CAS instead of PROC OPTMODEL:
/*proc optmodel;*/
proc cas noqueue;
source pgm;
put _BY_LINE_;
Use NodeID instead of _N_ as the key column (always a good idea to avoid _N_ with CAS because the data order is unpredictable):
read data vrpdata into NODES =[NodeID] ModelNode ModelLat ModelLong Volume Mode;
After the end of your OPTMODEL code, call the runOptmodel action:
/*quit;*/
endsource;
action optimization.runOptmodel / code=pgm groupBy='ModelWeekNumber';
quit;
The resulting solution_data CAS table will automatically include any BY variables, so you can use a BY statement in the PROC SGPLOT call to plot one BY group at a time:
proc sgplot data=casuser.solution_data noautolegend;
by ModelWeekNumber;
scatter x=x1 y=y1 / datalabel=i;
vector x=x2 y=y2 / xorigin=x1 yorigin=y1 group=k noarrowheads;
xaxis display=none;
yaxis display=none;
run;
It is worth noting that you can optionally get a summary table with one row per BY group as follows:
action optimization.runOptmodel / code=pgm groupBy='ModelWeekNumber' statusOut='statusOut';
The resulting statusOut table contains information about the solver call for each BY group.
Rob. I want exactly this. I tried the approach. It is giving me errors. See below. I have the code as well after this one. I think i am not setting the model to read it correctly.
256 endsource;
257 action optimization.runOptmodel / code=pgm groupBy='ModelWeekNumber' statusOut='statusOut';
258 quit;
NOTE: Active Session now MYSESS.
ModelWeekNumber=3
NOTE: There were 1 rows read from table 'VRPDATA' in caslib 'CASUSER(SRHY-Exp)'.
NOTE: Problem generation will use 16 threads.
ERROR: The array subscript 'UseNode[99999,1]' is invalid at line 51 column 23.
ERROR: The array subscript 'UseNode[99999,2]' is invalid at line 51 column 23.
ERROR: The array subscript 'UseNode[99999,3]' is invalid at line 51 column 23.
ERROR: The array subscript 'UseNode[99999,4]' is invalid at line 51 column 23.
ERROR: The array subscript 'UseNode[99999,5]' is invalid at line 51 column 23.
ERROR: The array subscript 'UseNode[99999,6]' is invalid at line 51 column 23.
ERROR: The array subscript 'UseNode[99999,7]' is invalid at line 51 column 23.
ERROR: The array subscript 'UseNode[99999,8]' is invalid at line 51 column 23.
ERROR: The array subscript 'UseNode[99999,9]' is invalid at line 51 column 23.
ERROR: The array subscript 'UseNode[99999,10]' is invalid at line 51 column 23.
NOTE: The maximum error limit was reached during OPTMODEL problem generation.
/*Variables*/
%let capacity = 3400;
%let num_vehicles=20;
%let dist=3000;
%let max_stops=5;
%let Depot='A';
%let DepotID=99999; /* Bfor Lebanon and Afor Lebec*/
/*%Let WeekNumber='2'; */
%Let Mode='Mode1'; /* Mode1 or Mode2*/
%let Scenario='Routing Alternates';
proc fedsql sessref=mysess;
create table CASUSER.vrpdata {options replace='TRUE'} AS
select ModelWeekNumber_2WK as ModelWeekNumber
,ModelNode
,ModelCity
,ModelLat
,ModelLong
,ModelZip
,ModelState
,Case_Count
,Unit_Count
,round(Volume)as Volume
,Weight
,Declared_Value
,ModelDepotState
,ModelDepotCity
,ModelDepotZip
,NodeID
,Mode
,WeekNumbers
,WeekNumber_EarliestStartDate
,WeekNumber_LatestEndDate
from Data
where ModelDepotCity=&Depot
and ModelWeekNumber_2WK in ('3','4')
and Mode=&Mode;
;
QUIT;
proc cas;
source pgm;
put _BY_LINE_;
/* read the node location and demand data */
set NODES;
str ModelNode {NODES};
num ModelLat {NODES};
num ModelLong {NODES};
num Volume {NODES};
num capacity = &capacity;
num num_vehicles = &num_vehicles;
str Mode {NODES};
read data CASUSER.vrpdata into NODES =[NodeID] ModelNode ModelLat ModelLong Volume Mode;
set ARCS = {i in NODES, j in NODES: i ne j};
set VEHICLES = 1..num_vehicles;
num depot = &DepotID;
/* define the arc dist as the rounded Euclidean distance */
num dist {<i,j> in ARCS} = (if i ne depot then GEODIST(ModelLat[i],ModelLong[i],ModelLat[j],ModelLong[j],'M') else 0);
/*print dist;*/
/* Creating an Sparse Matrix to remove any unwanted arcs*/
set LOGICAL_ARCS = {<i,j> in ARCS: dist[i,j] <= &dist};
/*print {<i,j> in LOGICAL_ARCS} dist; */
/* Flow[k] is the amount of demand carried 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} dist[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; */
con VisitDepot {i in NODES diff {depot}, k in VEHICLES}:
UseNode[i,k] <= UseNode[depot,k]
suffixes=(block=k);
/*DISTANCE CONSTRAINT*/
CON Distance {k in VEHICLES}:
/* SUM {<i,j> in LOGICAL_ARCS: j not in {depot}} (dist[i, j] * UseArc[i, j, k]) <= &dist_limit; */
sum {<i,j> in LOGICAL_ARCS} dist[i,j] * UseArc[i,j,k] <= &dist;
/*STOPS CONSTRAINT*/
CON Stops {k in VEHICLES}:
sum {i in NODES diff {depot}} UseNode[i,k] <= &max_stops;
/* 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 Volume supplied by vehicle k to node i must equal Volume if UseNode[i,k] = 1; otherwise, it must equal 0 */
/*FLOW BALANCE CONSTRAINT*/
con FlowBalance {i in NODES diff {depot}, k in VEHICLES}:
sum {<(i),j> in LOGICAL_ARCS} Flow[i,j,k] - sum {<j,(i)> in LOGICAL_ARCS} Flow[j,i,k]
= Volume[i] * UseNode[i,k];
/* each vehicle is empty when it leaves the depot */
for {<(depot),j> in LOGICAL_ARCS, k in VEHICLES} fix Flow[depot,j,k] = 0;
/* 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];
/*Constraint VehicleCapacity[3,4,1]: Flow[3,4,1] - 3650*UseArc[3,4,1] <= 0 /*
/* expand; */
/* decomp by vehicle *//*DECOMPOSITION*/
for {i in NODES, k in VEHICLES} do;
LeaveNode[i,k].block = k;
EnterNode[i,k].block = k;
end;
for {k in VEHICLES} Distance[k].block = k;
for {k in VEHICLES} Stops[k].block = k;
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 / decomp varsel=ryanfoster presolver=basic maxtime=1200;
/* OUTPUT */
/* create solution data set */
create data solution_data from [i j k]=
{<i,j> in LOGICAL_ARCS, k in VEHICLES: UseArc[i,j,k].sol > 0.5 and i ne depot}
StartNode=ModelNode[i] StartCity=ModelNode[i] x1=ModelLat[i] y1=ModelLong[i]
EndNode=ModelNode[j] EndCity=ModelNode[j] x2=ModelLat[j] y2=ModelLong[j]
flow=Flow[i,j,k] dist=dist[i,j]
;
endsource;
action optimization.runOptmodel / code=pgm groupBy='ModelWeekNumber' statusOut='statusOut';
quit;
proc sgplot data=solution_data noautolegend;
scatter x=x1 y=y1 / datalabel=i;
vector x=x2 y=y2 / xorigin=x1 yorigin=y1 group=k noarrowheads;
xaxis display=none;
yaxis display=none;
run;
DATA WORK.VRO_OUTPUT;
set WORK.solution_data;
Depot=&Depot;
WeekNumber=&WeekNumber;
VehicleByScenarioByWeekNumber= cat(Depot,'-',k,'-',WeekNumber);
Mode=&Mode;
Scenario=&Scenario;
run;
Rob I just ran the model again. I checked the VRP data. It has 29 observations. I get the same error. I have attached the excel file as well in the previous mail from me. So I am not sure why it says only observation for WeekNumber 3.
What does the following yield?
proc print data=casuser.vrpdata(where=(ModelWeekNumber=3));
run;
Rob
I ran the proc print data=casuser.vrpdata(where=(ModelWeekNumber=3));
run;
It gives me that exact 29 observations for Model week number=3. So that is correct. I do not know why inside the model it says only 1 and after the UseNode error that I showed earlier. It is beyond me to know why this would happen, as the actual optmodel solved just fine if i were to manually run weeknumber by weeknumber.
Here is the data attached (82 obs) just in case.
I am not able to replicate the behavior you are seeing. Please attach the full log from a fresh session.
Rob Okay. I will replicate it and let you know soon. Thank you
Hi Rob
Figured it out. It was a datatype issue. Fixed it and now it took all the 29 observations. It solved exactly. Thanks a lot for your support as always. You are the best
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 16. Read more here about why you should contribute and what is in it for you!
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.