Dial-A-Ride Problem (DARP)
Problem
In the Dial-A-Ride Problem (DARP), a fleet of vehicles must transport clients from one site to another. The vehicles start and end their routes at a common depot and have a maximum capacity. Clients must be picked up and dropped off by exactly one vehicle. They have a loading time at their pickup and dropoff sites, and their pickup and dropoff times must be within given time windows. The delay between each client’s pickup and dropoff times must not exceed a certain limit. The goal is to assign a sequence of clients to each truck while minimizing the total lateness and total traveled distance.
Principles learned
- Add list decision variables to model the trucks’ sequences of clients
- Define an array using a recursive lambda function to compute the clients’ pickup and dropoff times
- Add multiple objectives and model the lateness as a soft constraint
Data
We provide instances from the Chassaing et al. Dial-A-Ride Problem (DARP) dataset. Each data file is a JSON file containing:
- the number of vehicles
- the number of nodes
- the maximum travel time (common to all clients)
- the vehicle capacity
- a scaling factor for computing the travel time between two different nodes
- the speed of the vehicles
- the number of clients
- the matrix of distances between each pair of nodes
- information about the depot:
- its identifier (node index)
- its loading time
- the number of clients at the depot
- the starting time of the routes
- the maximum ending time of the routes
- the maximum travel time of the clients picked up at the depot (if any)
- information about each customer: for each node, the file indicates
- the number of clients who requested a vehicle
- information about their pickup (index of the pickup node, minimum pickup time requested, maximum pickup time requested, loading time in the vehicle, maximum time of travel)
- information about their dropoff (index of the dropoff node, minimum dropoff time requested, maximum dropoff time requested, loading time to exit the vehicle, maximum time of travel).
Program
The Hexaly model for the Dial-A-Ride Problem (DARP) uses list decision variables to model the sequence of nodes (customer pickups and dropoffs) visited by each truck, and float decision variables to represent the waiting time at the depot and at each node. We use a ‘partition‘ constraint on the lists to ensure that each node is visited exactly once.
The load each vehicle carries varies along the tour: it increases upon pickups and then decreases upon dropoffs. We compute the vehicles’ load over time using a recursive array: the load after visiting a node is equal to the load after visiting the previous node plus the current node’s quantity. We then use a variadic ‘and’ operator to ensure the capacity is respected at any point in the tour.
Since time windows can be difficult to respect, they are handled as a first-priority objective rather than hard constraints. In case of early arrival, the vehicle has to wait for the node’s opening hour. In case of late arrival, we measure and penalize the lateness. We compute the end times of each vehicle’s visits using a recursive array. For each vehicle and each node, the end time is the sum of:
- the node waiting time
- the node loading time
- the maximum of:
- the end time of the previous visit plus the travel time (for the first visit, it is the travel time from the depot)
- the earliest allowed arrival time for this node.
Using the ‘find‘ and ‘indexOf’ operators, we ensure that each client is picked up and then dropped off by the same vehicle.
Finally, we minimize in lexicographic order:
- the total route lateness (including the lateness to go back to the depot),
- the total client lateness
- the total distance traveled.
- Execution
-
hexaly mdvrp.hxm inFileName=instances/a5-40.json [hxTimeLimit=] [outputFileName=]
/********** darp.hxm **********/
use io;
use json;
function input() {
usage = "\nUsage: hexaly darp.hxm inFileName=inputFile"
+ "[solFileName=outputFile] [hxTimeLimit=timeLimit]\n";
if (inFileName == nil) throw usage;
instance = json.parse(inFileName);
for [k in 0...instance.nbClients] {
quantities[k] = instance.clients[k].nbClients;
quantities[k+instance.nbClients] = -instance.clients[k].nbClients;
}
distances = instance.distanceMatrix;
for [k in 0...instance.nbClients] {
starts[k] = instance.clients[k].pickup.start;
ends[k] = instance.clients[k].pickup.end;
starts[k+instance.nbClients] = instance.clients[k].delivery.start;
ends[k+instance.nbClients] = instance.clients[k].delivery.end;
}
for [k in 0...instance.nbClients] {
loadingTimes[k] = instance.clients[k].pickup.loadingTime;
loadingTimes[k+instance.nbClients] = instance.clients[k].delivery.loadingTime;
}
for [k in 0...instance.nbClients] {
maxTravelTimes[k] = instance.clients[k].pickup.maxTravelTime;
maxTravelTimes[k+instance.nbClients] = instance.clients[k].delivery.maxTravelTime;
}
factor = 1 / (instance.scale * instance.speed);
for [k in 0...instance.nbNodes] {
distanceWarehouse[k] = distances[0][k+1];
timeWarehouse[k] = distanceWarehouse[k] * factor;
}
for [i in 0...instance.nbNodes][j in 0...instance.nbNodes] {
distanceMatrix[i][j] = distances[i+1][j+1];
timeMatrix[i][j] = distanceMatrix[i][j] * factor;
}
}
function model() {
// routes[k] represents the nodes visited by vehicle k
routes[0...instance.nbVehicles] <- list(instance.nbNodes);
depotStarts[0...instance.nbVehicles] <- float(0, instance.depot.twEnd);
// waiting[k] is the waiting time at node k
waiting[0...instance.nbNodes] <- float(0, instance.depot.twEnd);
// Each node is taken by one vehicle
constraint partition[k in 0...instance.nbVehicles](routes[k]);
for [k in 0...instance.nbVehicles] {
route <- routes[k];
c <- count(route);
// routeQuantities[k][i] indicates the number of clients in vehicle k
// at its i-th taken node
routeQuantities[k] <- array(0...c, (i, prev) => prev + quantities[route[i]]);
// Vehicles have a maximum capacity
constraint and(0...c, i => routeQuantities[k][i] <= instance.capacity);
// times[k][i] is the time at which vehicle k leaves the i-th node
// (after waiting and loading time at node i)
times[k] <- array(0...c, (i, prev) => max(starts[route[i]],
i == 0 ?
(depotStarts[k] + timeWarehouse[route[0]]) :
(prev + timeMatrix[route[i-1]][route[i]]))
+ waiting[route[i]] + loadingTimes[route[i]]);
// Total lateness of the k-th route
lateness[k] <- sum(
0...c,
i => max(0, times[k][i] - loadingTimes[route[i]] - ends[route[i]])
);
homeLateness[k] <- (c > 0) ?
max(0, times[k][c-1] + timeWarehouse[route[c-1]] - instance.depot.twEnd) : 0;
routeDistances[k] <- sum(1...c, i => distanceMatrix[route[i-1]][route[i]])
+ (c > 0 ? distanceWarehouse[route[0]] + distanceWarehouse[route[c-1]] : 0);
}
for [k in 0...instance.nbClients] {
// For each pickup node k, its associated delivery node is k + instance.nbClients
pickupListIndex <- find(routes, k);
deliveryListIndex <- find(routes, k + instance.nbClients);
// A client picked up in route i is delivered in route i
constraint pickupListIndex == deliveryListIndex;
clientList <- routes[pickupListIndex];
pickupIndex <- indexOf(clientList, k);
deliveryIndex <- indexOf(clientList, k + instance.nbClients);
// Pickup before delivery
constraint pickupIndex < deliveryIndex;
pickupTime <- times[pickupListIndex][pickupIndex];
deliveryTime <- times[deliveryListIndex][deliveryIndex] - loadingTimes[k + instance.nbClients];
travelTime <- deliveryTime - pickupTime;
clientLateness[k] <- max(travelTime - maxTravelTimes[k], 0);
}
totalLateness <- sum[k in 0...instance.nbVehicles](lateness[k] + homeLateness[k]);
totalClientLateness <- sum[k in 0...instance.nbClients](clientLateness[k]);
totalDistance <- sum[k in 0...instance.nbVehicles](routeDistances[k]);
minimize totalLateness;
minimize totalClientLateness;
minimize totalDistance / instance.scale;
}
function param() {
if (hxTimeLimit == nil) hxTimeLimit = 20;
}
function output() {
if (solFileName == nil) return;
local outfile = io.openWrite(solFileName);
/* Write the solution in a file with the following format:
* - total lateness on the routes, total client lateness, total distance
* - for each vehicle, the depot start time, the nodes visited (omitting the start/end at the
* depot), and the waiting time at each node */
outfile.println(totalLateness.value, " ", totalClientLateness.value, " ", totalDistance.value);
for [k in 0...instance.nbVehicles] {
outfile.print("Vehicle ", k + 1, " (", depotStarts[k].value, "): ");
for [node in routes[k].value]
outfile.print(node, " (", waiting[node].value, "), ");
outfile.println();
}
}
- Execution (Windows)
-
set PYTHONPATH=%HX_HOME%\bin\pythonpython mdvrp.py instances/a5-40.json
- Execution (Linux)
-
export PYTHONPATH=/opt/hexaly_13_0/bin/pythonpython mdvrp.py instances/a5-40.json
import hexaly.optimizer
import sys
import json
def read_data(filename):
with open(filename) as f:
return json.load(f)
def read_input_darp(instance_file):
instance = read_data(instance_file)
nb_clients = instance["nbClients"]
nb_nodes = instance["nbNodes"]
nb_vehicles = instance["nbVehicles"]
depot_tw_end = instance["depot"]["twEnd"]
capacity = instance["capacity"]
scale = instance["scale"]
quantities = [-1 for i in range(2 * nb_clients)]
distances = instance["distanceMatrix"]
starts = [-1.0 for i in range(2 * nb_clients)]
ends = [-1.0 for i in range(2 * nb_clients)]
loading_times = [-1.0 for i in range(2 * nb_clients)]
max_travel_times = [-1.0 for i in range(2 * nb_clients)]
for k in range(nb_clients):
quantities[k] = instance["clients"][k]["nbClients"]
quantities[k+nb_clients] = -instance["clients"][k]["nbClients"]
starts[k] = instance["clients"][k]["pickup"]["start"]
ends[k] = instance["clients"][k]["pickup"]["end"]
starts[k+nb_clients] = instance["clients"][k]["delivery"]["start"]
ends[k+nb_clients] = instance["clients"][k]["delivery"]["end"]
loading_times[k] = instance["clients"][k]["pickup"]["loadingTime"]
loading_times[k+nb_clients] = instance["clients"][k]["delivery"]["loadingTime"]
max_travel_times[k] = instance["clients"][k]["pickup"]["maxTravelTime"]
max_travel_times[k+nb_clients] = instance["clients"][k]["delivery"]["maxTravelTime"]
factor = 1.0 / (scale * instance["speed"])
distance_warehouse = [-1.0 for i in range(nb_nodes)]
time_warehouse = [-1.0 for i in range(nb_nodes)]
distance_matrix = [[-1.0 for i in range(nb_nodes)] for j in range(nb_nodes)]
time_matrix = [[-1.0 for i in range(nb_nodes)] for j in range(nb_nodes)]
for i in range(nb_nodes):
distance_warehouse[i] = distances[0][i+1]
time_warehouse[i] = distance_warehouse[i] * factor
for j in range(nb_nodes):
distance_matrix[i][j] = distances[i+1][j+1]
time_matrix[i][j] = distance_matrix[i][j] * factor
return nb_clients, nb_nodes, nb_vehicles, depot_tw_end, capacity, scale, quantities, \
starts, ends, loading_times, max_travel_times, distance_warehouse, time_warehouse, \
distance_matrix, time_matrix
def main(instance_file, str_time_limit, sol_file):
nb_clients, nb_nodes, nb_vehicles, depot_tw_end, capacity, scale, quantities_data, \
starts_data, ends_data, loading_times_data, max_travel_times, distance_warehouse_data, \
time_warehouse_data, distance_matrix_data, time_matrix_data = read_input_darp(instance_file)
with hexaly.optimizer.HexalyOptimizer() as optimizer:
model = optimizer.model
# routes[k] represents the nodes visited by vehicle k
routes = [model.list(nb_nodes) for k in range(nb_vehicles)]
depot_starts = [model.float(0, depot_tw_end) for k in range(nb_vehicles)]
# Each node is taken by one vehicle
model.constraint(model.partition(routes))
quantities = model.array(quantities_data)
time_warehouse = model.array(time_warehouse_data)
time_matrix = model.array(time_matrix_data)
loading_times = model.array(loading_times_data)
starts = model.array(starts_data)
ends = model.array(ends_data)
# waiting[k] is the waiting time at node k
waiting = [model.float(0, depot_tw_end) for k in range(nb_nodes)]
waiting_array = model.array(waiting)
distance_matrix = model.array(distance_matrix_data)
distance_warehouse = model.array(distance_warehouse_data)
times = [None] * nb_vehicles
lateness = [None] * nb_vehicles
home_lateness = [None] * nb_vehicles
route_distances = [None] * nb_vehicles
for k in range(nb_vehicles):
route = routes[k]
c = model.count(route)
demand_lambda = model.lambda_function(lambda i, prev: prev + quantities[route[i]])
# route_quantities[k][i] indicates the number of clients in vehicle k
# at its i-th taken node
route_quantities = model.array(model.range(0, c), demand_lambda)
quantity_lambda = model.lambda_function(lambda i: route_quantities[i] <= capacity)
# Vehicles have a maximum capacity
model.constraint(model.and_(model.range(0, c), quantity_lambda))
times_lambda = model.lambda_function(
lambda i, prev: model.max(
starts[route[i]],
model.iif(
i == 0,
depot_starts[k] + time_warehouse[route[0]],
prev + time_matrix[route[i-1]][route[i]]
)
) + waiting_array[route[i]] + loading_times[route[i]]
)
# times[k][i] is the time at which vehicle k leaves the i-th node
# (after waiting and loading time at node i)
times[k] = model.array(model.range(0, c), times_lambda)
lateness_lambda = model.lambda_function(
lambda i: model.max(
0,
times[k][i] - loading_times[route[i]] - ends[route[i]]
)
)
# Total lateness of the k-th route
lateness[k] = model.sum(model.range(0, c), lateness_lambda)
home_lateness[k] = model.iif(
c > 0,
model.max(0, times[k][c-1] + time_warehouse[route[c-1]] - depot_tw_end),
0
)
route_dist_lambda = model.lambda_function(
lambda i: distance_matrix[route[i-1]][route[i]]
)
route_distances[k] = model.sum(
model.range(1, c),
route_dist_lambda
) + model.iif(
c > 0,
distance_warehouse[route[0]] + distance_warehouse[route[c-1]],
0
)
routes_array = model.array(routes)
times_array = model.array(times)
client_lateness = [None] * nb_clients
for k in range(nb_clients):
# For each pickup node k, its associated delivery node is k + nb_clients
pickup_list_index = model.find(routes_array, k)
delivery_list_index = model.find(routes_array, k + nb_clients)
# A client picked up in route i is delivered in route i
model.constraint(pickup_list_index == delivery_list_index)
client_list = routes_array[pickup_list_index]
pickup_index = model.index(client_list, k)
delivery_index = model.index(client_list, k + nb_clients)
# Pickup before delivery
model.constraint(pickup_index < delivery_index)
pickup_time = times_array[pickup_list_index][pickup_index]
delivery_time = times_array[delivery_list_index][delivery_index] \
- loading_times[k + nb_clients]
travel_time = delivery_time - pickup_time
client_lateness[k] = model.max(travel_time - max_travel_times[k], 0)
total_lateness = model.sum(lateness + home_lateness)
total_client_lateness = model.sum(client_lateness)
total_distance = model.sum(route_distances)
model.minimize(total_lateness)
model.minimize(total_client_lateness)
model.minimize(total_distance / scale)
model.close()
optimizer.param.time_limit = int(str_time_limit)
optimizer.solve()
#
# Write the solution in a file with the following format:
# - total lateness on the routes, total client lateness, and total distance
# - for each vehicle, the depot start time, the nodes visited (omitting the start/end at the
# depot), and the waiting time at each node
#
if sol_file is not None:
with open(sol_file, 'w') as f:
f.write("%d %d %.2f\n" % (
total_lateness.value,
total_client_lateness.value,
total_distance.value
))
for k in range(nb_vehicles):
f.write("Vehicle %d (%.2f): " %(k + 1, depot_starts[k].value))
for node in routes[k].value:
f.write("%d (%.2f), " % (node, waiting[node].value))
f.write("\n")
return 0
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python darp.py input_file [output_file] [time_limit]")
sys.exit(1)
instance_file = sys.argv[1]
sol_file = sys.argv[2] if len(sys.argv) > 2 else None
str_time_limit = sys.argv[3] if len(sys.argv) > 3 else "20"
main(instance_file, str_time_limit, sol_file)
- Compilation / Execution (Windows)
-
cl /EHsc mdvrp.cpp -I%HX_HOME%\include /link %HX_HOME%\bin\hexaly130.libmdvrp instances/a5-40.json
- Compilation / Execution (Linux)
-
g++ mdvrp.cpp -I/opt/hexaly_13_0/include -lhexaly130 -lpthread -o mdvrp./mdvrp instances/a5-40.json
#include "optimizer/hexalyoptimizer.h"
#include <fstream>
#include <iostream>
#include <json.hpp>
#include <sstream>
#include <vector>
using namespace hexaly;
using namespace std;
using json = nlohmann::json;
class Darp {
public:
HexalyOptimizer optimizer;
int nbClients;
int nbNodes;
int nbVehicles;
double factor;
double depotTwEnd;
int capacity;
double scale;
vector<int> quantitiesData;
vector<vector<double>> distances;
vector<double> startsData;
vector<double> endsData;
vector<double> loadingTimesData;
vector<double> maxTravelTimes;
vector<double> distanceWarehouseData;
vector<double> timeWarehouseData;
vector<vector<double>> distanceMatrixData;
vector<vector<double>> timeMatrixData;
vector<HxExpression> routes;
vector<HxExpression> depotStarts;
vector<HxExpression> waiting;
HxExpression totalLateness;
HxExpression totalClientLateness;
HxExpression totalDistance;
void readInstance(const string& fileName) {
ifstream infile;
infile.exceptions(ifstream::failbit | ifstream::badbit);
infile.open(fileName.c_str());
json instance;
instance << infile;
infile.close();
nbClients = instance["nbClients"];
nbNodes = instance["nbNodes"];
nbVehicles = instance["nbVehicles"];
depotTwEnd = instance["depot"]["twEnd"];
capacity = instance["capacity"];
scale = instance["scale"];
quantitiesData.resize(2 * nbClients);
startsData.resize(2 * nbClients);
endsData.resize(2 * nbClients);
loadingTimesData.resize(2 * nbClients);
maxTravelTimes.resize(2 * nbClients);
for (int k = 0; k < nbClients; ++k) {
quantitiesData[k] = instance["clients"][k]["nbClients"].get<int>();
quantitiesData[k + nbClients] = -instance["clients"][k]["nbClients"].get<int>();
startsData[k] = instance["clients"][k]["pickup"]["start"].get<double>();
startsData[k + nbClients] = instance["clients"][k]["delivery"]["start"].get<double>();
endsData[k] = instance["clients"][k]["pickup"]["end"].get<double>();
endsData[k + nbClients] = instance["clients"][k]["delivery"]["end"].get<double>();
loadingTimesData[k] = instance["clients"][k]["pickup"]["loadingTime"].get<double>();
loadingTimesData[k + nbClients] = instance["clients"][k]["delivery"]["loadingTime"].get<double>();
maxTravelTimes[k] = instance["clients"][k]["pickup"]["maxTravelTime"].get<double>();
maxTravelTimes[k + nbClients] = instance["clients"][k]["delivery"]["maxTravelTime"].get<double>();
}
distances.resize(nbNodes + 1, vector<double>(nbNodes + 1));
for (int i = 0; i < nbNodes + 1; ++i) {
for (int j = 0; j < nbNodes + 1; ++j) {
distances[i][j] = instance["distanceMatrix"][i][j].get<double>();
}
}
factor = 1.0 / (instance["scale"].get<double>() * instance["speed"].get<double>());
distanceWarehouseData.resize(nbNodes);
timeWarehouseData.resize(nbNodes);
for (int k = 0; k < nbNodes; ++k) {
distanceWarehouseData[k] = distances[0][k+1];
timeWarehouseData[k] = distanceWarehouseData[k] * factor;
}
distanceMatrixData.resize(nbNodes, vector<double>(nbNodes));
timeMatrixData.resize(nbNodes, vector<double>(nbNodes));
for (int i = 0; i < nbNodes; ++i) {
for (int j = 0; j < nbNodes; ++j){
distanceMatrixData[i][j] = distances[i+1][j+1];
timeMatrixData[i][j] = distanceMatrixData[i][j] * factor;
}
}
}
void solve(int limit) {
HxModel model = optimizer.getModel();
// routes[k] represents the nodes visited by vehicle k
routes.resize(nbVehicles);
depotStarts.resize(nbVehicles);
for (int k = 0; k < nbVehicles; ++k) {
routes[k] = model.listVar(nbNodes);
depotStarts[k] = model.floatVar(0.0, depotTwEnd);
}
// waiting[k] is the waiting time at node k
waiting.resize(nbNodes);
for (int k = 0; k < nbNodes; ++k) {
waiting[k] = model.floatVar(0.0, depotTwEnd);
}
// Each node is taken by one vehicle
model.constraint(model.partition(routes.begin(), routes.end()));
HxExpression quantities = model.array(quantitiesData.begin(), quantitiesData.end());
HxExpression timeWarehouse = model.array(timeWarehouseData.begin(), timeWarehouseData.end());
HxExpression timeMatrix = model.array();
for (int i = 0; i < nbNodes; ++i) {
timeMatrix.addOperand(model.array(timeMatrixData[i].begin(), timeMatrixData[i].end()));
}
HxExpression loadingTimes = model.array(loadingTimesData.begin(), loadingTimesData.end());
HxExpression starts = model.array(startsData.begin(), startsData.end());
HxExpression ends = model.array(endsData.begin(), endsData.end());
HxExpression waitingArray = model.array(waiting.begin(), waiting.end());
HxExpression distanceMatrix = model.array();
for (int i = 0; i < nbNodes; ++i) {
distanceMatrix.addOperand(model.array(distanceMatrixData[i].begin(), distanceMatrixData[i].end()));
}
HxExpression distanceWarehouse = model.array(distanceWarehouseData.begin(), distanceWarehouseData.end());
vector<HxExpression> times(nbVehicles);
vector<HxExpression> lateness(nbVehicles);
vector<HxExpression> homeLateness(nbVehicles);
vector<HxExpression> routeDistances(nbVehicles);
for (int k = 0; k < nbVehicles; ++k) {
HxExpression route = routes[k];
HxExpression c = model.count(route);
HxExpression demandLambda = model.createLambdaFunction(
[&](HxExpression i, HxExpression prev) { return prev + quantities[route[i]]; });
// routeQuantities[k][i] indicates the number of clients in vehicle k
// at its i-th taken node
HxExpression routeQuantities = model.array(model.range(0, c), demandLambda);
HxExpression quantityLambda = model.createLambdaFunction(
[&](HxExpression i) { return (routeQuantities[i] <= capacity); });
// Vehicles have a maximum capacity
model.constraint(model.and_(model.range(0, c), quantityLambda));
HxExpression timesLambda = model.createLambdaFunction(
[&](HxExpression i, HxExpression prev) {
return model.max(starts[route[i]], model.iif(
i == 0,
depotStarts[k] + timeWarehouse[route[0]],
prev + timeMatrix[route[i-1]][route[i]]
)) + waitingArray[route[i]] + loadingTimes[route[i]];
}
);
// times[k][i] is the time at which vehicle k leaves the i-th node
// (after waiting and loading time at node i)
times[k] = model.array(model.range(0, c), timesLambda);
HxExpression latenessLambda = model.createLambdaFunction(
[&](HxExpression i) {
return model.max(
0,
times[k][i] - loadingTimes[route[i]] - ends[route[i]]
);
}
);
// Total lateness of the k-th route
lateness[k] = model.sum(model.range(0, c), latenessLambda);
homeLateness[k] = model.iif(
c > 0,
model.max(0, times[k][c-1] + timeWarehouse[route[c-1]] - depotTwEnd),
0
);
HxExpression routeDistLambda = model.createLambdaFunction(
[&](HxExpression i) { return distanceMatrix[route[i-1]][route[i]]; });
routeDistances[k] = model.sum(model.range(1, c), routeDistLambda)
+ model.iif(
c > 0,
distanceWarehouse[route[0]] + distanceWarehouse[route[c-1]],
0
);
}
HxExpression routesArray = model.array(routes.begin(), routes.end());
HxExpression timesArray = model.array(times.begin(), times.end());
vector<HxExpression> clientLateness(nbClients);
for (int k = 0; k < nbClients; ++k) {
// For each pickup node k, its associated delivery node is k + instance.nbClients
HxExpression pickupListIndex = model.find(routesArray, k);
HxExpression deliveryListIndex = model.find(routesArray, k + nbClients);
// A client picked up in route i is delivered in route i
model.constraint(pickupListIndex == deliveryListIndex);
HxExpression clientList = routesArray[pickupListIndex];
HxExpression pickupIndex = model.indexOf(clientList, k);
HxExpression deliveryIndex = model.indexOf(clientList, k + nbClients);
// Pickup before delivery
model.constraint(pickupIndex < deliveryIndex);
HxExpression pickupTime = timesArray[pickupListIndex][pickupIndex];
HxExpression deliveryTime =
timesArray[deliveryListIndex][deliveryIndex] - loadingTimes[k + nbClients];
HxExpression travelTime = deliveryTime - pickupTime;
clientLateness[k] = model.max(travelTime - maxTravelTimes[k], 0);
}
vector<HxExpression> latenessPlusHomeLateness(nbVehicles);
for (int k = 0; k < nbVehicles; ++k) {
latenessPlusHomeLateness[k] = lateness[k] + homeLateness[k];
}
totalLateness = model.sum(latenessPlusHomeLateness.begin(), latenessPlusHomeLateness.end());
totalClientLateness = model.sum(clientLateness.begin(), clientLateness.end());
totalDistance = model.sum(routeDistances.begin(), routeDistances.end());
model.minimize(totalLateness);
model.minimize(totalClientLateness);
model.minimize(totalDistance / scale);
model.close();
optimizer.getParam().setTimeLimit(limit);
optimizer.getParam().setSeed(5);
optimizer.solve();
}
/* Write the solution in a file with the following format:
* - total lateness on the routes, total client lateness, total distance
* - for each vehicle, the depot start time, the nodes visited (omitting the start/end at the
* depot), and the waiting time at each node */
void writeSolution(const string& fileName) {
ofstream outfile;
outfile.exceptions(ofstream::failbit | ofstream::badbit);
outfile.open(fileName.c_str());
outfile << totalLateness.getDoubleValue() << " " << totalClientLateness.getDoubleValue()
<< " " << totalDistance.getDoubleValue() << endl;
for (int k = 0; k < nbVehicles; ++k) {
HxCollection route = routes[k].getCollectionValue();
outfile << "Vehicle " << k << " (" << depotStarts[k].getDoubleValue() << "): ";
for (int i = 0; i < route.count(); ++i) {
outfile << route[i] << " (" << waiting[route[i]].getDoubleValue() << "), ";
}
outfile << endl;
}
}
};
int main(int argc, char** argv) {
if (argc < 2) {
cerr << "Usage: darp inputFile [outputFile] [timeLimit] " << endl;
return 1;
}
const char* instanceFile = argv[1];
const char* solFile = argc > 2 ? argv[2] : NULL;
const char* strTimeLimit = argc > 3 ? argv[3] : "20";
try {
Darp model;
model.readInstance(instanceFile);
model.solve(atoi(strTimeLimit));
if (solFile != NULL){
model.writeSolution(solFile);
}
return 0;
} catch (const exception& e) {
cerr << "An error occurred: " << e.what() << endl;
return 1;
}
}
- Compilation / Execution (Windows)
-
copy %HX_HOME%\bin\Hexaly.NET.dll .csc Mdvrp.cs /reference:Hexaly.NET.dllMdvrp instances\a5-40.json
using System;
using System.IO;
using System.Collections.Generic;
using Hexaly.Optimizer;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
public class Darp : IDisposable
{
HexalyOptimizer optimizer;
int nbClients;
int nbNodes;
int nbVehicles;
double depotTwEnd;
int capacity;
double scale;
double factor;
int[] quantitiesData;
double[] startsData;
double[] endsData;
double[] loadingTimesData;
double[] maxTravelTimes;
double[][] distances;
double[] distanceWarehouseData;
double[] timeWarehouseData;
double[][] distanceMatrixData;
double[][] timeMatrixData;
HxExpression[] routes;
HxExpression[] depotStarts;
HxExpression[] waiting;
HxExpression[] clientLateness;
HxExpression[] latenessPlusHomeLateness;
HxExpression totalLateness;
HxExpression totalClientLateness;
HxExpression totalDistance;
public Darp()
{
optimizer = new HexalyOptimizer();
}
/* Read instance data */
void ReadInstance(string fileName)
{
JObject instance = JObject.Parse(File.ReadAllText(fileName));
nbClients = (int)instance["nbClients"];
nbNodes = (int)instance["nbNodes"];
nbVehicles = (int)instance["nbVehicles"];
depotTwEnd = (double)instance["depot"]["twEnd"];
capacity = (int)instance["capacity"];
scale = (double)instance["scale"];
quantitiesData = new int[2 * nbClients];
startsData = new double[2 * nbClients];
endsData = new double[2 * nbClients];
loadingTimesData = new double[2 * nbClients];
maxTravelTimes = new double[2 * nbClients];
for (int k = 0; k < nbClients; ++k)
{
quantitiesData[k] = (int)instance["clients"][k]["nbClients"];
quantitiesData[k + nbClients] = -(int)instance["clients"][k]["nbClients"];
startsData[k] = (double)instance["clients"][k]["pickup"]["start"];
startsData[k + nbClients] = (double)instance["clients"][k]["delivery"]["start"];
endsData[k] = (double)instance["clients"][k]["pickup"]["end"];
endsData[k + nbClients] = (double)instance["clients"][k]["delivery"]["end"];
loadingTimesData[k] = (double)instance["clients"][k]["pickup"]["loadingTime"];
loadingTimesData[k + nbClients] = (double)instance["clients"][k]["delivery"]["loadingTime"];
maxTravelTimes[k] = (double)instance["clients"][k]["pickup"]["maxTravelTime"];
maxTravelTimes[k + nbClients] =
(double)instance["clients"][k]["delivery"]["maxTravelTime"];
}
distances = new double[nbNodes + 1][];
for (int i = 0; i < nbNodes + 1; ++i)
{
distances[i] = new double[nbNodes + 1];
for (int j = 0; j < nbNodes + 1; ++j)
{
distances[i][j] = (double)instance["distanceMatrix"][i][j];
}
}
factor = 1 / ((double)instance["scale"] * (double)instance["speed"]);
distanceWarehouseData = new double[nbNodes];
timeWarehouseData = new double[nbNodes];
for (int k = 0; k < nbNodes; ++k)
{
distanceWarehouseData[k] = distances[0][k+1];
timeWarehouseData[k] = distanceWarehouseData[k] * factor;
}
distanceMatrixData = new double[nbNodes][];
timeMatrixData = new double[nbNodes][];
for (int i = 0; i < nbNodes; ++i)
{
distanceMatrixData[i] = new double[nbNodes];
timeMatrixData[i] = new double[nbNodes];
for (int j = 0; j < nbNodes; ++j)
{
distanceMatrixData[i][j] = distances[i+1][j+1];
timeMatrixData[i][j] = distanceMatrixData[i][j] * factor;
}
}
}
public void Dispose()
{
if (optimizer != null)
optimizer.Dispose();
}
void Solve(int limit)
{
HxModel model = optimizer.GetModel();
// routes[k] represents the nodes visited by vehicle k
routes = new HxExpression[nbVehicles];
depotStarts = new HxExpression[nbVehicles];
for (int k = 0; k < nbVehicles; ++k)
{
routes[k] = model.List(nbNodes);
depotStarts[k] = model.Float(0.0, depotTwEnd);
}
// waiting[k] is the waiting time at node k
waiting = new HxExpression[nbNodes];
for (int k = 0; k < nbNodes; ++k)
{
waiting[k] = model.Float(0.0, depotTwEnd);
}
// Each node is taken by one vehicle
model.Constraint(model.Partition(routes));
HxExpression quantities = model.Array(quantitiesData);
HxExpression timeWarehouse = model.Array(timeWarehouseData);
HxExpression loadingTimes = model.Array(loadingTimesData);
HxExpression starts = model.Array(startsData);
HxExpression ends = model.Array(endsData);
HxExpression waitingArray = model.Array(waiting);
HxExpression timeMatrix = model.Array();
HxExpression distanceMatrix = model.Array();
for (int i = 0; i < nbNodes; ++i)
{
timeMatrix.AddOperand(model.Array(timeMatrixData[i]));
distanceMatrix.AddOperand(model.Array(distanceMatrixData[i]));
}
HxExpression distanceWarehouse = model.Array(distanceWarehouseData);
HxExpression[] times = new HxExpression[nbVehicles];
HxExpression[] lateness = new HxExpression[nbVehicles];
HxExpression[] homeLateness = new HxExpression[nbVehicles];
HxExpression[] routeDistances = new HxExpression[nbVehicles];
for (int k = 0; k < nbVehicles; ++k)
{
HxExpression route = routes[k];
HxExpression c = model.Count(route);
HxExpression demandLambda = model.LambdaFunction(
(i, prev) => prev + quantities[route[i]]
);
// routeQuantities[k][i] indicates the number of clients in vehicle k
// at its i-th taken node
HxExpression routeQuantities = model.Array(model.Range(0, c), demandLambda);
HxExpression quantityLambda = model.LambdaFunction(i => routeQuantities[i] <= capacity);
// Vehicles have a maximum capacity
model.Constraint(model.And(model.Range(0, c), quantityLambda));
HxExpression timesLambda = model.LambdaFunction(
(i, prev) =>
model.Max(
starts[route[i]],
model.If(
i == 0,
depotStarts[k] + timeWarehouse[route[0]],
prev + timeMatrix[route[i-1]][route[i]]
)
) + waitingArray[route[i]] + loadingTimes[route[i]]);
// times[k][i] is the time at which vehicle k leaves the i-th node
// (after waiting and loading time at node i)
times[k] = model.Array(model.Range(0, c), timesLambda);
HxExpression latenessLambda = model.LambdaFunction(
i =>
model.Max(
0,
times[k][i] - loadingTimes[route[i]] - ends[route[i]]
)
);
// Total lateness of the k-th route
lateness[k] = model.Sum(model.Range(0, c), latenessLambda);
homeLateness[k] = model.If(
c > 0,
model.Max(0, times[k][c-1] + timeWarehouse[route[c-1]] - depotTwEnd),
0
);
HxExpression routeDistLambda = model.LambdaFunction(
i => distanceMatrix[route[i-1]][route[i]]
);
routeDistances[k] = model.Sum(
model.Range(1, c),
routeDistLambda
) + model.If(
c > 0,
distanceWarehouse[route[0]] + distanceWarehouse[route[c-1]],
0
);
}
HxExpression routesArray = model.Array(routes);
HxExpression timesArray = model.Array(times);
clientLateness = new HxExpression[nbClients];
for (int k = 0; k < nbClients; ++k)
{
// For each pickup node k, its associated delivery node is k + nbClients
HxExpression pickupListIndex = model.Find(routesArray, k);
HxExpression deliveryListIndex = model.Find(routesArray, k + nbClients);
// A client picked up in route i is delivered in route i
model.Constraint(pickupListIndex == deliveryListIndex);
HxExpression clientList = routesArray[pickupListIndex];
HxExpression pickupIndex = model.IndexOf(clientList, k);
HxExpression deliveryIndex = model.IndexOf(clientList, k + nbClients);
// Pickup before delivery
model.Constraint(pickupIndex < deliveryIndex);
HxExpression pickupTime = timesArray[pickupListIndex][pickupIndex];
HxExpression deliveryTime =
timesArray[deliveryListIndex][deliveryIndex] - loadingTimes[k + nbClients];
HxExpression travelTime = deliveryTime - pickupTime;
clientLateness[k] = model.Max(travelTime - maxTravelTimes[k], 0);
}
latenessPlusHomeLateness = new HxExpression[nbVehicles];
for (int k = 0; k < nbVehicles; ++k)
{
latenessPlusHomeLateness[k] = lateness[k] + homeLateness[k];
}
totalLateness = model.Sum(latenessPlusHomeLateness);
totalClientLateness = model.Sum(clientLateness);
totalDistance = model.Sum(routeDistances);
model.Minimize(totalLateness);
model.Minimize(totalClientLateness);
model.Minimize(totalDistance / scale);
model.Close();
optimizer.GetParam().SetTimeLimit(limit);
optimizer.GetParam().SetSeed(5);
optimizer.Solve();
}
/* Write the solution in a file with the following format:
* - total lateness on the routes, total client lateness, total distance
* - for each vehicle, the depot start time, the nodes visited (omitting the start/end at the
* depot), and the waiting time at each node */
void WriteSolution(string fileName)
{
using (StreamWriter output = new StreamWriter(fileName))
{
output.WriteLine(
totalLateness.GetDoubleValue()
+ " "
+ totalClientLateness.GetDoubleValue()
+ " "
+ totalDistance.GetDoubleValue()
);
for (int k = 0; k < nbVehicles; ++k)
{
HxCollection route = routes[k].GetCollectionValue();
output.Write("Vehicle " + (k + 1) + " (" + depotStarts[k].GetDoubleValue() + "): ");
for (int i = 0; i < route.Count(); ++i)
output.Write(route[i] + " (" + (waiting[route[i]].GetDoubleValue() + "), "));
output.WriteLine();
}
}
}
public static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Usage: Darp inputFile [solFile] [timeLimit]");
Environment.Exit(1);
}
string instanceFile = args[0];
string outputFile = args.Length > 1 ? args[1] : null;
string strTimeLimit = args.Length > 2 ? args[2] : "20";
using (Darp model = new Darp())
{
model.ReadInstance(instanceFile);
model.Solve(int.Parse(strTimeLimit));
if (outputFile != null)
model.WriteSolution(outputFile);
}
}
}
- Compilation / Execution (Windows)
-
javac Mdvrp.java -cp %HX_HOME%\bin\hexaly.jarjava -cp %HX_HOME%\bin\hexaly.jar;. Mdvrp instances\a5-40.json
- Compilation / Execution (Linux)
-
javac Mdvrp.java -cp /opt/hexaly_13_0/bin/hexaly.jarjava -cp /opt/hexaly_13_0/bin/hexaly.jar:. Mdvrp instances/a5-40.json
import com.hexaly.optimizer.*;
import java.io.*;
import java.util.*;
import com.google.gson.JsonObject;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
public class Darp {
private final HexalyOptimizer optimizer;
int nbClients;
int nbNodes;
int nbVehicles;
double depotTwEnd;
int capacity;
double scale;
double factor;
int[] quantitiesData;
double[] startsData;
double[] endsData;
double[] loadingTimesData;
double[] maxTravelTimes;
double[][] distances;
double[] distanceWarehouseData;
double[] timeWarehouseData;
double[][] distanceMatrixData;
double[][] timeMatrixData;
HxExpression[] routes;
HxExpression[] depotStarts;
HxExpression[] waiting;
HxExpression totalLateness;
HxExpression totalClientLateness;
HxExpression totalDistance;
private Darp() {
optimizer = new HexalyOptimizer();
}
// Read instance data
private void readInstance(String fileName) throws IOException {
JsonReader reader = new JsonReader(new InputStreamReader(new FileInputStream(fileName)));
JsonObject instance = JsonParser.parseReader(reader).getAsJsonObject();
nbClients = instance.get("nbClients").getAsInt();
nbNodes = instance.get("nbNodes").getAsInt();
nbVehicles = instance.get("nbVehicles").getAsInt();
JsonObject depot = (JsonObject) instance.get("depot");
depotTwEnd = depot.get("twEnd").getAsDouble();
capacity = instance.get("capacity").getAsInt();
scale = instance.get("scale").getAsDouble();
JsonArray clients = (JsonArray) instance.get("clients");
quantitiesData = new int[2 * nbClients];
startsData = new double[2 * nbClients];
endsData = new double[2 * nbClients];
loadingTimesData = new double[2 * nbClients];
maxTravelTimes = new double[2 * nbClients];
for (int k = 0; k < nbClients; ++k){
JsonObject clientk = (JsonObject) clients.get(k);
quantitiesData[k] = clientk.get("nbClients").getAsInt();
quantitiesData[k + nbClients] = -clientk.get("nbClients").getAsInt();
JsonObject clientkPickup = (JsonObject) clientk.get("pickup");
JsonObject clientkDelivery = (JsonObject) clientk.get("delivery");
startsData[k] = clientkPickup.get("start").getAsDouble();
startsData[k + nbClients] = clientkDelivery.get("start").getAsDouble();
endsData[k] = clientkPickup.get("end").getAsDouble();
endsData[k + nbClients] = clientkDelivery.get("end").getAsDouble();
loadingTimesData[k] = clientkPickup.get("loadingTime").getAsDouble();
loadingTimesData[k + nbClients] = clientkDelivery.get("loadingTime").getAsDouble();
maxTravelTimes[k] = clientkPickup.get("maxTravelTime").getAsDouble();
maxTravelTimes[k + nbClients] = clientkDelivery.get("maxTravelTime").getAsDouble();
}
distances = new double[nbNodes + 1][nbNodes + 1];
JsonArray distanceMatrixJson = (JsonArray) instance.get("distanceMatrix");
for (int i = 0; i < nbNodes + 1; ++i){
JsonArray distanceMatrixJsoni = (JsonArray) distanceMatrixJson.get(i);
for (int j = 0; j < nbNodes + 1; ++j){
distances[i][j] = distanceMatrixJsoni.get(j).getAsDouble();
}
}
factor = 1.0 / (instance.get("scale").getAsDouble() * instance.get("speed").getAsDouble());
distanceWarehouseData = new double[nbNodes];
timeWarehouseData = new double[nbNodes];
for (int k = 0; k < nbNodes; ++k){
distanceWarehouseData[k] = distances[0][k+1];
timeWarehouseData[k] = distanceWarehouseData[k] * factor;
}
distanceMatrixData = new double[nbNodes][nbNodes];
timeMatrixData = new double[nbNodes][nbNodes];
for (int i = 0; i < nbNodes; ++i){
for (int j = 0; j < nbNodes; ++j){
distanceMatrixData[i][j] = distances[i+1][j+1];
timeMatrixData[i][j] = distanceMatrixData[i][j] * factor;
}
}
}
private void solve(int limit) {
HxModel model = optimizer.getModel();
// routes[k] represents the nodes visited by vehicle k
routes = new HxExpression[nbVehicles];
depotStarts = new HxExpression[nbVehicles];
for (int k = 0; k < nbVehicles; ++k){
routes[k] = model.listVar(nbNodes);
depotStarts[k] = model.floatVar(0.0, depotTwEnd);
}
// waiting[k] is the waiting time at node k
waiting = new HxExpression[nbNodes];
for (int k = 0; k < nbNodes; ++k){
waiting[k] = model.floatVar(0.0, depotTwEnd);
}
// Each node is taken by one vehicle
model.constraint(model.partition(routes));
HxExpression quantities = model.array(quantitiesData);
HxExpression timeWarehouse = model.array(timeWarehouseData);
HxExpression timeMatrix = model.array(timeMatrixData);
for (int i = 0; i < nbNodes; ++i){
timeMatrix.addOperand(model.array(timeMatrixData[i]));
}
HxExpression loadingTimes = model.array(loadingTimesData);
HxExpression starts = model.array(startsData);
HxExpression ends = model.array(endsData);
HxExpression waitingArray = model.array(waiting);
HxExpression distanceMatrix = model.array();
for (int i = 0; i < nbNodes; ++i){
distanceMatrix.addOperand(model.array(distanceMatrixData[i]));
}
HxExpression distanceWarehouse = model.array(distanceWarehouseData);
HxExpression[] times = new HxExpression[nbVehicles];
HxExpression[] lateness = new HxExpression[nbVehicles];
HxExpression[] homeLateness = new HxExpression[nbVehicles];
HxExpression[] routeDistances = new HxExpression[nbVehicles];
for (int k = 0; k < nbVehicles; ++k){
HxExpression route = routes[k];
HxExpression c = model.count(route);
HxExpression demandLambda = model.lambdaFunction(
(i, prev) -> model.sum(prev, model.at(quantities, model.at(route, i))));
// routeQuantities[k][i] indicates the number of clients in vehicle k
// at its i-th taken node
HxExpression routeQuantities = model.array(model.range(0, c), demandLambda);
HxExpression quantityLambda = model.createLambdaFunction(
i -> model.leq(model.at(routeQuantities, i), capacity));
// Vehicles have a maximum capacity
model.constraint(model.and(model.range(0, c), quantityLambda));
HxExpression depotStartsk = depotStarts[k];
HxExpression timesLambda = model.lambdaFunction(
(i, prev) -> model.sum(
model.max(
model.at(starts, model.at(route, i)),
model.iif(
model.eq(i, 0),
model.sum(
depotStartsk,
model.at(timeWarehouse, model.at(route, 0))
),
model.sum(
prev,
model.at(
timeMatrix,
model.at(route, model.sub(i, 1)),
model.at(route, i)
)
)
)
),
model.sum(
model.at(waitingArray, model.at(route, i)),
model.at(loadingTimes, model.at(route, i))
)
)
);
HxExpression timesk = model.array(model.range(0, c), timesLambda);
// times[k][i] is the time at which vehicle k leaves the i-th node
// (after waiting and loading time at node i)
times[k] = timesk;
HxExpression latenessLambda = model.lambdaFunction(
i -> model.max(
0,
model.sub(
model.sub(
model.at(timesk, i),
model.at(loadingTimes, model.at(route, i))
),
model.at(ends, model.at(route, i))
)
)
);
// Total lateness of the k-th route
lateness[k] = model.sum(model.range(0, c), latenessLambda);
homeLateness[k] = model.iif(
model.gt(c, 0),
model.max(
0,
model.sum(
model.at(timesk, model.sub(c, 1)),
model.sub(
model.at(timeWarehouse, model.at(route, model.sub(c, 1))),
depotTwEnd
)
)
),
0
);
HxExpression routeDistLambda = model.lambdaFunction(
i -> model.at(
distanceMatrix,
model.at(route, model.sub(i, 1)),
model.at(route, i)
)
);
routeDistances[k] = model.sum(
model.sum(model.range(1, c), routeDistLambda),
model.iif(
model.gt(c, 0),
model.sum(
model.at(distanceWarehouse, model.at(route, 0)),
model.at(distanceWarehouse, model.at(route, model.sub(c, 1)))
),
0
)
);
}
HxExpression routesArray = model.array(routes);
HxExpression timesArray = model.array(times);
HxExpression[] clientLateness = new HxExpression[nbClients];
for (int k = 0; k < nbClients; ++k) {
// For each pickup node k, its associated delivery node is k + nbClients
HxExpression pickupListIndex = model.find(routesArray, k);
HxExpression deliveryListIndex = model.find(routesArray, k + nbClients);
// A client picked up in route i is delivered in route i
model.constraint(model.eq(pickupListIndex, deliveryListIndex));
HxExpression clientList = model.at(routesArray, pickupListIndex);
HxExpression pickupIndex = model.indexOf(clientList, k);
HxExpression deliveryIndex = model.indexOf(clientList, k + nbClients);
// Pickup before delivery
model.constraint(model.lt(pickupIndex, deliveryIndex));
HxExpression pickupTime = model.at(timesArray, pickupListIndex, pickupIndex);
HxExpression deliveryTime = model.sub(
model.at(timesArray, deliveryListIndex, deliveryIndex),
model.at(loadingTimes, k + nbClients)
);
HxExpression travelTime = model.sub(deliveryTime, pickupTime);
clientLateness[k] = model.max(model.sub(travelTime, maxTravelTimes[k]), 0);
}
HxExpression[] latenessPlusHomeLateness = new HxExpression[nbVehicles];
for (int k = 0; k < nbVehicles; ++k) {
latenessPlusHomeLateness[k] = model.sum(lateness[k], homeLateness[k]);
}
totalLateness = model.sum(latenessPlusHomeLateness);
totalClientLateness = model.sum(clientLateness);
totalDistance = model.sum(routeDistances);
model.minimize(totalLateness);
model.minimize(totalClientLateness);
model.minimize(model.div(totalDistance, scale));
model.close();
optimizer.getParam().setTimeLimit(limit);
optimizer.solve();
}
/* Write the solution in a file with the following format:
* - total lateness on the routes, total client lateness, total distance
* - for each vehicle, the depot start time, the nodes visited (omitting the start/end at the
* depot), and the waiting time at each node */
private void writeSolution(String fileName) throws IOException {
try (PrintWriter output = new PrintWriter(fileName)) {
output.println(
totalLateness.getDoubleValue()
+ " "
+ totalClientLateness.getDoubleValue()
+ " "
+ totalDistance.getDoubleValue()
);
for (int k = 0; k < nbVehicles; ++k) {
HxCollection route = routes[k].getCollectionValue();
output.print("Vehicle " + (k + 1) + " (" + depotStarts[k].getDoubleValue() + "): ");
for (int i = 0; i < route.count(); ++i) {
int routei = (int) route.get(i); // route.get(i) casted to long
output.print(routei + " (" + (waiting[routei].getDoubleValue() + "), "));
}
output.println();
}
}
}
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("Usage: java Darp inputFile [outputFile] [timeLimit]");
System.exit(1);
}
try {
String instanceFile = args[0];
String outputFile = args.length > 1 ? args[1] : null;
String strTimeLimit = args.length > 2 ? args[2] : "20";
Darp model = new Darp();
model.readInstance(instanceFile);
model.solve(Integer.parseInt(strTimeLimit));
if (outputFile != null) {
model.writeSolution(outputFile);
}
} catch (Exception ex) {
System.err.println(ex);
ex.printStackTrace();
System.exit(1);
}
}
}