Last active
December 15, 2015 11:59
-
-
Save c3c/5256957 to your computer and use it in GitHub Desktop.
C+VRP+TW working + asserts added
Updated data
English comments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********************************************* | |
* OPL 12.5 Data | |
* Author: crash | |
* Creation Date: Mar 6, 2013 at 3:38:56 PM | |
*********************************************/ | |
// 17 nodes, first node is depot. | |
// <Name, NodeType {collab|client}, WGS84-Lat, WGS84-Lon, Lam93-X, Lam93-Y, TimeWin-Min, TimeWin-Max> | |
AllNodes = { | |
<"Poitiers" 1,46.589069,0.340576,496421,6613320,360,1200>, // 0 | |
<"La Rochelle" 2,46.176027,-1.145325,380354,6572413,360,660>, // 1 | |
<"Angouleme" 2,45.660127,0.140076,477343,6510763,660,990>, // 2 | |
<"Perigueux" 2,45.18978,0.722351,521155,6457075,840,1000>, // 3 | |
<"Limoges" 1,45.830713,1.263428,565198,6527156,360,1200>, // 4 | |
<"Clermont-Ferrand" 2,45.784764,3.109131,708479,6520577,780,940>, // 5 | |
<"Chateauroux" 2,46.80758,1.672668,598786,6635010,360,1200>, // 6 | |
<"Bourges" 2,47.073863,2.384033,653259,6663917,420,630>, // 7 | |
<"Blois" 2,47.585789,1.340332,575264,6721912,500,750>, // 8 | |
<"Angers" 2,47.476376,-0.563049,431735,6714499,360,700>, // 9 | |
<"Nantes" 1,47.215837,-1.554565,355488,6689443,360,1200>, // 10 | |
<"Cholet" 2,47.062638,-0.884399,405299,6669738,900,1200>, // 11 | |
<"La Roche-sur-Yon" 2,46.675826,-1.422729,362092,6628992,800,1000>, // 12 | |
<"Saintes" 1,45.73686,-0.6427,416833,6521784,360,1200>, // 13 | |
<"Tours" 2,47.394631,0.686646,525525,6701921,400,900>, // 14 | |
<"Orleans" 1,47.908978,1.895142,617461,6757086,360,1200>, // 15 | |
<"Le Mans" 2,48.004625,0.192261,490662,6770859,360,1200> // 16 | |
}; | |
Dist = [ // distance pairs, in meter, /1000 als kostbepaling? | |
123,104,158,110,231,104,164,134,120,160,107,135,121,93,187,157, | |
114,182,190,332,227,287,245,151,119,100,59,62,194,300,227,69,89, | |
231,173,233,232,208,216,174,165,61,197,283,260,82,197,194,245,270, | |
272,285,242,234,122,244,315,315,143,112,162,195,230,265,214,227, | |
148,179,235,254,158,153,241,337,391,337,362,291,257,253,331,61, | |
90,184,249,196,236,214,99,123,173,97,227,298,248,293,275,133,99, | |
194,143,222,177,232,255,53,54,97,80,51,110,193,94,190,81,53,60,178, | |
170,270,157,59,148,124,229,132,120,178,285,191,210,309,259,107,77,127 | |
]; | |
// <Destination, From, NumPalettes> | |
Deliveries = { | |
<1,0,5>, | |
<2,0,15>, | |
<3,0,14>, | |
<4,0,30>, | |
<5,0,13>, | |
<6,0,16>, | |
<7,0,19>, | |
<8,0,11>, | |
<9,0,18>, | |
//<1,10,10>, | |
//<2,13,19>, | |
//<5,10,6>, | |
//<14,15,16>, | |
//<9,10,13>, | |
//<2,4,17>, | |
//<8,10,15>, | |
//<0,10,20>, | |
//<0,4,20> | |
}; | |
// <ID, MaxPalettes, MaxTimeOnRoute> | |
Camions = { | |
<1,33,600>, | |
<2,16,600>, | |
<3,33,600>, | |
<4,22,600>, | |
<5,33,600>, | |
<6,16,600>, | |
<7,33,600>, | |
<8,33,600>, | |
<9,33,600>, | |
<10,22,600>, | |
<11,33,600>, | |
<12,33,600>, | |
<13,16,600>, | |
<14,33,600>, | |
<16,33,600>, | |
<17,33,600>, | |
<18,16,600>, | |
<19,33,600>, | |
<15,33,600> | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********************************************* | |
* OPL 12.5 Model | |
* Author: crash | |
* Creation Date: Mar 6, 2013 at 3:33:35 PM | |
*********************************************/ | |
using CP; | |
// Nodes | |
tuple node { string name; int type; float lat; float lon; int x; int y; int tmin; int tmax; } | |
{node} AllNodes = ...; | |
int N = card(AllNodes); | |
range nodes = 0..N-1; | |
// Edges | |
tuple edge {int i; int j;} | |
setof(edge) AllEdges = { <i,j> | i,j in nodes : i != j }; // not filtered! | |
setof(edge) OrderedEdges = { <i,j> | ordered i,j in nodes}; | |
int Dist[OrderedEdges] = ...; | |
// Deliveries | |
tuple delivery {int destination; int from; int numpalettes;} | |
{delivery} Deliveries = ...; | |
// Only keep concerned nodes | |
sorted {int} clients = { d.from | d in Deliveries : d.numpalettes > 0 } union { d.destination | d in Deliveries : d.numpalettes > 0 } diff {0}; | |
sorted {int} cities = {0} union clients; | |
int numCities = card(cities); | |
int demand[t in cities] = sum (d in Deliveries : d.destination == t) d.numpalettes; | |
int tmin[t in cities] = item(AllNodes, t).tmin; | |
int tmax[t in cities] = item(AllNodes, t).tmax; | |
setof(edge) Edges = { <i,j> | i,j in cities : i != j }; // filtered! :) | |
// Camions | |
tuple camion {key int id; int capacity; int maxtime;} | |
{camion} Camions = ...; | |
// Travel time | |
int vitesse = 65; // km/h | |
int tempstrajet[<i,j> in OrderedEdges] = ftoi(round(Dist[<i,j>]/vitesse*60)); | |
int tempsservice = 30; | |
// Model consistency | |
{int} CollabNodes = { c | c in cities : item(AllNodes, c).type == 1}; // We can only collect from collaborative nodes | |
assert forall(d in Deliveries) CollectFromCollaborativeNode:(sum(c in CollabNodes : c == d.from) 1) > 0; | |
{int} ClientNodes = { c | c in cities : item(AllNodes, c).type == 2}; // Client nodes can only receive | |
assert forall(c in ClientNodes) ClientCanOnlyReceive:(sum (d in Deliveries : d.from == c) 1) == 0; | |
// Decision variables | |
dvar boolean x[Edges,Camions]; // if truck visits the edge | |
dvar boolean y[Camions,cities]; // if truck visits the node (for convenience) | |
dvar int+ z[Deliveries][Camions]; // how many palettes of a certain order will the truck take | |
dvar boolean takenby[Deliveries][Camions]; // if a certain truck takes a certain order | |
dvar int+ arrival[Camions][cities] in 1..numCities; // expresses continuity | |
dvar int+ tarrival[Camions][cities] in 0..1440; // provides actual timeframe | |
dexpr int charge[w in cities][k in Camions] = (sum (d in Deliveries : d.from == 0) z[d][k]) + y[k][w] * sum (i in cities, d in Deliveries) ((y[k][i]+(arrival[k][i]<=(arrival[k][w]+1)) == 2) * (z[d][k]*((d.from==i && i != 0)-(d.destination==i)))); // how many palettes does the truck have at a certain arc <v,w> | |
dexpr int treated[k in Camions][i in clients] = sum(d in Deliveries : d.destination == i || d.from == i) z[d][k]; // number of palettes treated at node | |
execute { | |
cp.param.TimeMode = "ElapsedTime"; | |
cp.param.TimeLimit = 1800; | |
}; | |
minimize sum (<i,j> in Edges, m in Camions) Dist[<minl(i,j), maxl(i,j)>]*x[<i,j>,m]; | |
subject to { | |
// fill up dvar "takenby" | |
// whether a truck takes a delivery or not | |
forall (k in Camions, d in Deliveries) | |
(z[d][k]>=1) == takenby[d][k]; | |
// fill up dvar "y" | |
// whether or not a truck passes by a certain node | |
forall (k in Camions, j in cities) | |
(sum(i in cities : j!=i) x[<i,j>][k]) == y[k][j]; | |
forall (k in Camions, j in cities) | |
(sum(i in cities : j!=i) x[<j,i>][k]) == y[k][j]; | |
// order must be taken by at least one truck | |
forall (d in Deliveries) | |
sum (k in Camions) takenby[d][k] >= 1; | |
// if the order is taken by the truck, that truck must visit both the pickup node & delivery node | |
forall (d in Deliveries, k in Camions) | |
(takenby[d][k] == 1) => (y[k][d.from] == 1 && y[k][d.destination] == 1); | |
// continuity | |
forall (p in cities, k in Camions) | |
((sum(i in cities : i!=p) x[<i,p>][k]) - (sum(j in cities : j!=p) x[<p,j>][k])) == 0; | |
// continuity, forces truck to pick a number in the "arrival" interval | |
forall (k in Camions, <i,j> in Edges : i!=0) | |
(x[<i,j>][k] == 1) => (arrival[k][i] + 1 == arrival[k][j]); | |
// the number of palettes that all trucks will take for a certain delivery must match the size of the delivery | |
forall (d in Deliveries) | |
sum(k in Camions) z[d][k] == d.numpalettes; | |
// the sum of palettes carried by all trucks to a certain node, must match the demand of that node | |
forall (i in cities) | |
sum(k in Camions, d in Deliveries : d.destination == i) z[d][k] == demand[i]; | |
// the amount of palettes the truck is carrying must be inferior|equal to his capacity at all times | |
forall (k in Camions, i in cities) | |
(y[k][i] == 1) => (charge[i][k] <= k.capacity); | |
// respect timewindows (special case of depot) | |
forall(j in clients, k in Camions) | |
(x[<0,j>][k] == 1) => (tarrival[k][j] >= (tempstrajet[<0,j>] + tmin[0])); | |
// respect timewindows | |
forall (<i,j> in Edges : i!=0, k in Camions) | |
(x[<i,j>][k] == 1) => ((tarrival[k][i] + tempstrajet[<minl(i,j), maxl(i,j)>] + 15+(3 * treated[k][i])) <= tarrival[k][j]); | |
// min max timeframe for entry at client | |
forall(i in cities, k in Camions) | |
(y[k][i] ==1) => ((tmin[i] <= tarrival[k][i]) && (tarrival[k][i] <= tmax[i])); | |
// truck can't be longer than a certain time on route | |
forall (m in Camions) | |
m.maxtime >= ((sum (<i,j> in Edges) x[<i,j>,m]*(tempstrajet[<minl(i,j),maxl(i,j)>]+tempsservice))-tempsservice); | |
// pickup is before delivery | |
forall (d in Deliveries : d.from != 0, m in Camions) | |
(takenby[d][m] == 1) => (arrival[m][d.from] <= arrival[m][d.destination]); | |
}; | |
// POST-processing | |
edge E = <0,1>; // intermediary object | |
execute { | |
writeln(charge); | |
var unused = "We didn't use the following trucks: "; | |
var nUnused = 0; | |
for (var k in arrival) { | |
var num = 0; | |
var last = 0; | |
var timetotal = 0; | |
var str = "- Camion "+k.id+": 0 -> "; | |
for (var f = 1; f <= numCities-1; f++) { | |
for (var i in y[k]) { | |
if (y[k][i] == 1 && arrival[k][i] == f) { | |
if (!(last == 0 && i == 0)) { | |
E.i = Math.min(last, i); | |
E.j = Math.max(i, last); | |
timetotal += tempstrajet[E] + tempsservice; | |
last = i; | |
if (i!=0) { | |
str += i+" -> "; | |
} | |
} | |
num++; | |
} | |
} | |
} | |
timetotal -= tempsservice; | |
if (num > 0) { | |
writeln(str + "0 (total time: "+timetotal+")"); | |
} else { | |
nUnused++; | |
unused += "c"+k.id + " "; | |
} | |
} | |
if (nUnused > 0) { | |
writeln(unused); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment