Hi Odenwald,
I am very sorry for the slow response. I have been following up in the background. I am very familiar with LSO, not so much PROC GA/MILP/CVRP. I followed up with our MILP team and they are discussing the possibility of a dedicated VRP solver similar to our TSP. So crossing fingers that project gets a green light.
Note that LSO has been renamed "Blackbox" for future releases.
Disclaimer, not being an expert in either PROC GA or CVRP I took a stab at transforming the PROC GA TSP problem into a CVRP example. It seems to run correctly now, but I can't be certain. So it is a sketch of how one would do it. It gives results to the output like the below in cycle format.
Cycle for vehicle: 1 1 -> 5 (load = 1400 ) 5 -> 4 (load = 2200 ) 4 -> 8 (load = 3000 ) 8 -> 1 (load = 3000 ) Cycle for vehicle: 2
1 -> 12 (load = 1200 ) 12 -> 14 (load = 2500 ) 14 -> 15 (load = 2800 ) 15 -> 1 (load = 2800 ) Cycle for vehicle: 3 1 -> 22 (load = 700 ) 22 -> 18 (load = 1700 ) 18 -> 13 (load = 3000 ) 13 -> 1 (load = 3000 ) Cycle for vehicle: 4
data locations;
input node x y demand;
datalines;
1 145 215 0
2 151 264 1100
3 159 261 700
4 130 254 800
5 128 252 1400
6 163 247 2100
7 146 246 400
8 161 242 800
9 142 239 100
10 163 236 500
11 148 232 600
12 128 231 1200
13 156 217 1300
14 129 214 1300
15 146 208 300
16 164 208 900
17 141 206 2100
18 147 193 1000
19 164 193 900
20 129 189 2500
21 155 185 1800
22 139 182 700
;
run;
proc ga data1 = locations seed = 5554 lastgen=lastgen;
call SetEncoding('S22');
ncities = 22;
ncar = 8;
cap = 3000;
/* v1 = 1 -> 2 -> 3 -> 4 -> 1 (indices of S owned by vehicial 1)
v2 = 1 -> 5 -> 6 -> 7 -> 1
v3 = 1 -> 8 -> 9 -> 10 -> 1
v4 = 1 -> 11 -> 12 -> 13 -> 1
v5 = 1 -> 14 -> 15 -> 16 -> 1
v6 = 1 -> 17 -> 18 -> 1
v7 = 1 -> 19 -> 20 -> 1
v8 = 1 -> 21 -> 22 -> 1 */
/* Create distance matix once up front */
array distances[22,22] /nosym;
do i = 1 to 22;
do j = 1 to i;
distances[i,j] = sqrt((x[i] - x[j])**2 + (y[i] - y[j])**2);
distances[j,i] = distances[i,j];
end;
end;
/* Populate vehicle map start/stop */
array vehmap[8,2] / nosym;
cnt=2;
inc=2;
do i=1 to 8;
vehmap[i,1]=cnt;
vehmap[i,2]=cnt+inc;
cnt=cnt+inc+1;
if (i > 4) then inc=1;
end;
/* Objective function with local optimization */
function CVRPSwap(selected[*],ncities,ncar, cap, distances[*,*], demand[*], vehmap[*,*]);
array s[1] /nosym;
call dynamic_array(s,ncities);
call ReadMember(selected,1,s);
/* Permut city 1 back to front */
do i = 1 to ncities;
cityi=s[i];
if (cityi = 1) then do;
s[i]=s[1];
s[1]=cityi;
end;
end;
/* Permute first city
/* First try to improve solution by swapping adjacent cities */
do i = 2 to ncities;
city1 = s[i];
inext = 1 + mod(i,ncities);
if (inext=1) then inext=2;
city2 = s[inext];
if i=1 then
before = s[ncities];
else
before = s[i-1];
after = s[1 + mod(inext,ncities)];
if (distances[before,city1]+distances[city2,after]) >
(distances[before,city2]+distances[city1,after]) then do;
s[i] = city2;
s[inext] = city1;
end;
end;
call WriteMember(selected,1,s);
/* Calculate cost and max load */
cost=0;
loaderr=0; /* one norm of contraint violation */
do c = 1 to ncar;
load=0;
city_from=1;
do loc = vehmap[c,1] to vehmap[c,2];
city_to = s[loc];
load = load + demand[city_to]; /* demand on 1 is 0, so okay to add on return as well */
cost = cost + distances[city_from, city_to];
city_from=city_to;
end;
city_to=1;
cost = cost + distances[city_from, city_to];
loaderr = loaderr+max(0, load-cap); /* need load <= 3000 */
end;
merit = cost + 1e10*loaderr;
/* put merit; */
return(merit);
endsub;
/* Called after each iteration */
subroutine logReport(itercnt, ncities,ncar, cap, distances[*,*], demand[*], vehmap[*,*]);
outargs itercnt;
itercnt=itercnt + 1;
array s[1,22] / nosym;
call GetSolutions(s, 1, 1);
/* Calculate cost and max load */
cost=0;
loaderr=0; /* one norm of contraint violation */
do c = 1 to ncar;
load=0;
city_from=1;
do loc = vehmap[c,1] to vehmap[c,2];
city_to = s[1,loc];
load = load + demand[city_to]; /* demand on 1 is 0, so okay to add on return as well */
cost = cost + distances[city_from, city_to];
city_from=city_to;
end;
city_to=1;
cost = cost + distances[city_from, city_to];
loaderr = max(0, load-cap); /* need load <= 3000 */
end;
merit = cost + 1e10*loaderr;
put "iter " itercnt ": " s merit;
endsub;
/* Called when finished to see solution */
subroutine printSol(itercnt, ncities,ncar, cap, distances[*,*], demand[*], vehmap[*,*]);
outargs itercnt;
itercnt=itercnt + 1;
array s[1,22] / nosym;
call GetSolutions(s, 1, 1);
/* Calculate cost and max load */
cost=0;
loaderr=0; /* one norm of contraint violation */
do c = 1 to ncar;
load=0;
put "Cycle for vehicle: " c;
city_from=1;
do loc = vehmap[c,1] to vehmap[c,2];
city_to = s[1,loc];
load = load + demand[city_to]; /* demand on 1 is 0, so okay to add on return as well */
cost = cost + distances[city_from, city_to];
put city_from " -> " city_to " (load = " load ")";
city_from=city_to;
end;
city_to=1;
cost = cost + distances[city_from, city_to];
put city_from " -> " city_to " (load = " load ")";
loaderr = max(0, load-cap); /* need load <= 3000 */
end;
merit = cost + 1e10*loaderr;
put "iter " itercnt ": " s merit;
endsub;
itercnt=0;
call SetObjFunc('CVRPSwap',0);
call SetUpdateRoutine('logReport');
call SetFinalize('printSol');
call SetCross('Order');
call SetMut('Invert');
call SetMutProb(0.05);
call SetCrossProb(0.8);
call SetElite(1);
call Initialize('DEFAULT',200);
call ContinueFor(100);
run;
proc print data=lastgen; run;
Not sure if this is helpful as I realize it may not be easy to understand what is going on. I am borrowing time from other projects, so I probably cannot develop this code more. In general, we do need a dedicated solver for this problem type (which would have heuristics); and so I hope we get clearance to work on in the near future.
... View more