Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OR-Tools like dictionary input #7

Merged
merged 3 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,26 @@ result = solve_cvrp(dist_mtx, service_time, demand, vehicle_capacity, n_vehicles
- Using the distance matrix, with optional x, y coordinate information. The objective function is calculated based on the distance matrix, but the x, y coordinates just provide some helpful information. The distance matrix may not be consistent with the coordinates.
```julia
ap = AlgorithmParameters(timeLimit=3.2) # seconds
result = solve_cvrp(dist_mtx, service_time, demand, vehicle_capacity, n_vehicles, ap; x_coords=x, y_coords=y, verbose=true)
result = solve_cvrp(dist_mtx, service_time, demand, vehicle_capacity, n_vehicles, ap; x_coordinates=x, y_coordinates=y, verbose=true)
```

This package also supports a dictionary input as in Google OR-Tools:
```julia
data = Dict()
data["distance_matrix"] = ... # Matrix
data["demands"] = ... # Vector
data["service_times"] ... # Vector
data["vehicle_capacity"] = ... # Integer
data["num_vehicles"] = ... # Integer
data["x_coordinaes"] = ... # Vector
data["y_coordinaes"] = ... # Vector

ap = AlgorithmParameters(timeLimit=3.2) # seconds
result = solve_cvrp(data, ap; verbose=true)
```
Again, you can omit either the distance matrix or the coordinates.


## TSP interfaces

As TSP is a special case of CVRP, the same solver can be used for solving TSP.
Expand All @@ -126,9 +143,24 @@ result = solve_tsp(tsp, ap; use_dist_mtx=true)
```julia
result1 = solve_tsp(x, y, ap)
result2 = solve_tsp(dist_mtx, ap)
result3 = solve_tsp(dist_mtx, ap; x_coords=x, y_coords=y)
result3 = solve_tsp(dist_mtx, ap; x_coordinates=x, y_coordinates=y)
```

- The dictionary input:
```julia
This package also supports a dictionary input as in Google OR-Tools:
```julia
data = Dict()
data["distance_matrix"] = ... # Matrix
data["x_coordinaes"] = ... # Vector
data["y_coordinaes"] = ... # Vector

ap = AlgorithmParameters(timeLimit=3.2) # seconds
result = solve_tsp(data, ap; verbose=true)
```
You can omit either the distance matrix or the coordinates.


## AlgorithmParamters

The paramters for the HGS algorithm with default values are:
Expand Down
65 changes: 62 additions & 3 deletions src/cvrp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,69 @@ end

function solve_cvrp(
dist_mtx::Matrix, service_time::Vector, demand::Vector, vehicle_capacity::Integer, n_vehicles::Integer, parameters=AlgorithmParameters();
verbose=true, x_coords=zeros(length(demand)), y_coords=zeros(length(demand))
verbose=true, x_coordinates=zeros(length(demand)), y_coordinates=zeros(length(demand))
)
@assert length(x_coords) == length(y_coords) == length(service_time) == length(demand)
return c_api_solve_cvrp_dist_mtx(length(x_coords), x_coords, y_coords, Matrix(dist_mtx'), service_time, demand, vehicle_capacity, n_vehicles, parameters, verbose)
@assert length(x_coordinates) == length(y_coordinates) == length(service_time) == length(demand)
return c_api_solve_cvrp_dist_mtx(length(x_coordinates), x_coordinates, y_coordinates, Matrix(dist_mtx'), service_time, demand, vehicle_capacity, n_vehicles, parameters, verbose)
end


# const CVRP_KEYS = Dict(
# "distance_matrix" => Matrix{Real},
# "num_vehicles" => Integer,
# "demands" => Vector{Real},
# "vehicle_capacity" => Integer,
# "service_times" => Vector{Real},
# "x_coordinates" => Vector{Real},
# "y_coordinates" => Vector{Real}
# )

function solve_cvrp(data::Dict, parameters=AlgorithmParameters(); verbose=true)
use_dist_mtx = haskey(data, "distance_matrix")
has_coordinates = haskey(data, "x_coordinates") && haskey(data, "y_coordinates")

if !use_dist_mtx && !has_coordinates
error("Insufficient data input. Either coordinates or a distance matrix must be provided.")
end

n = use_dist_mtx ? size(data["distance_matrix"], 1) : length(data["x_coordinates"])
service_time = get(data, "service_times", zeros(n))

if !use_dist_mtx
return solve_cvrp(
data["x_coordinates"] :: Vector,
data["y_coordinates"] :: Vector,
service_time :: Vector,
data["demands"] :: Vector,
data["vehicle_capacity"] :: Integer,
data["num_vehicles"] :: Integer,
parameters;
verbose=verbose
)
else
if has_coordinates
return solve_cvrp(
data["distance_matrix"] :: Matrix,
service_time :: Vector,
data["demands"] :: Vector,
data["vehicle_capacity"] :: Integer,
data["num_vehicles"] :: Integer,
parameters;
verbose=verbose,
x_coordinates = data["x_coordinates"],
y_coordinates = data["y_coordinates"]
)

else
return solve_cvrp(
data["distance_matrix"] :: Matrix,
service_time :: Vector,
data["demands"] :: Vector,
data["vehicle_capacity"] :: Integer,
data["num_vehicles"] :: Integer,
parameters;
verbose=verbose
)
end
end
end
28 changes: 25 additions & 3 deletions src/tsp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function solve_tsp(tsp::TSP, parameters=AlgorithmParameters(); verbose=true, use
end


function solve_tsp(dist_mtx::Matrix, parameters=AlgorithmParameters(); verbose=true, x_coords=zeros(size(dist_mtx,1)), y_coords=zeros(size(dist_mtx,1)))
function solve_tsp(dist_mtx::Matrix, parameters=AlgorithmParameters(); verbose=true, x_coordinates=zeros(size(dist_mtx,1)), y_coordinates=zeros(size(dist_mtx,1)))
n = size(dist_mtx, 1)

serv_time = zeros(n)
Expand All @@ -45,7 +45,7 @@ function solve_tsp(dist_mtx::Matrix, parameters=AlgorithmParameters(); verbose=t
# need to input dist_mtx' instead of dist_mtx
# Julia: column-first indexing
# C: row-first indexing
return c_api_solve_cvrp_dist_mtx(n, x_coords, y_coords, c_dist_mtx, serv_time, dem, vehicleCapacity, maximum_number_of_vehicles, parameters, verbose)
return c_api_solve_cvrp_dist_mtx(n, x_coordinates, y_coordinates, c_dist_mtx, serv_time, dem, vehicleCapacity, maximum_number_of_vehicles, parameters, verbose)
end
function solve_tsp(x::Vector, y:: Vector, parameters=AlgorithmParameters(); verbose=true)
n = length(x)
Expand All @@ -55,4 +55,26 @@ function solve_tsp(x::Vector, y:: Vector, parameters=AlgorithmParameters(); verb
maximum_number_of_vehicles = 1

return c_api_solve_cvrp(n, x, y, serv_time, dem, vehicleCapacity, maximum_number_of_vehicles, parameters, verbose)
end
end



function solve_tsp(data::Dict, parameters=AlgorithmParameters(); verbose=true)

use_dist_mtx = haskey(data, "distance_matrix")
has_coordinates = haskey(data, "x_coordinates") && haskey(data, "y_coordinates")

if !use_dist_mtx && !has_coordinates
error("Insufficient data input. Either coordinates or a distance matrix must be provided.")
end

n = use_dist_mtx ? size(data["distance_matrix"], 1) : length(data["x_coordinates"])
demands = ones(n)
demands[1] = 0
data["demands"] = demands
data["num_vehicles"] = 1
data["vehicle_capacity"] = n
return solve_cvrp(data, parameters; verbose=verbose)
end


46 changes: 45 additions & 1 deletion test/cvrp_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,51 @@ end

result1 = solve_cvrp(x, y, service_time, demand, cvrp.capacity, n_vehicles, ap; verbose=true)
result2 = solve_cvrp(dist_mtx, service_time, demand, cvrp.capacity, n_vehicles, ap; verbose=true)
result3 = solve_cvrp(dist_mtx, service_time, demand, cvrp.capacity, n_vehicles, ap; x_coords=x, y_coords=y, verbose=true)
result3 = solve_cvrp(dist_mtx, service_time, demand, cvrp.capacity, n_vehicles, ap; x_coordinates=x, y_coordinates=y, verbose=true)

@test result1.cost == result2.cost == result3.cost
end


@testset "dictionary CVRP" begin
cvrp, _, _ = CVRPLIB.readCVRPLIB("A-n32-k5")
x = cvrp.coordinates[:, 1]
y = cvrp.coordinates[:, 2]
dist_mtx = cvrp.weights
service_time = zeros(cvrp.dimension)
demand = cvrp.demand
capacity = cvrp.capacity
n_vehicles = 5

data = Dict()
data["distance_matrix"] = dist_mtx
data["demands"] = demand
data["vehicle_capacity"] = capacity
data["num_vehicles"] = n_vehicles

ap = AlgorithmParameters(timeLimit=1.8)

result1 = solve_cvrp(data, ap; verbose=false)

data["service_times"] = service_time
result2 = solve_cvrp(data, ap; verbose=false)

data["x_coordinates"] = x
data["y_coordinates"] = y
result3 = solve_cvrp(data, ap; verbose=false)

delete!(data, "distance_matrix")
result4 = solve_cvrp(data, ap; verbose=false)

result5 = solve_cvrp(data; verbose=false)

@test result1.cost == result2.cost == result3.cost == result4.cost == result5.cost



delete!(data, "y_coordinates")
@test_broken solve_cvrp(data; verbose=false)

delete!(data, "x_coordinates")
@test_broken solve_cvrp(data; verbose=false)
end
37 changes: 36 additions & 1 deletion test/tsp_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,42 @@ end
ap = AlgorithmParameters(timeLimit=1)
result1 = solve_tsp(x, y, ap)
result2 = solve_tsp(dist_mtx, ap)
result3 = solve_tsp(dist_mtx, ap; x_coords=x, y_coords=y)
result3 = solve_tsp(dist_mtx, ap; x_coordinates=x, y_coordinates=y)

@test result1.cost == result2.cost == result3.cost
end



@testset "dictionary TSP" begin
tsp = TSPLIB.readTSPLIB(:pr76)
dist_mtx = tsp.weights
x = tsp.nodes[:, 1]
y = tsp.nodes[:, 2]

data = Dict()
data["distance_matrix"] = dist_mtx

ap = AlgorithmParameters(timeLimit=1.8)

result1 = solve_tsp(data, ap; verbose=false)

data["x_coordinates"] = x
data["y_coordinates"] = y
result2 = solve_tsp(data, ap; verbose=false)

delete!(data, "distance_matrix")
result3 = solve_tsp(data, ap; verbose=false)

result4 = solve_tsp(data, ap; verbose=false)

@test result1.cost == result2.cost == result3.cost == result4.cost



delete!(data, "y_coordinates")
@test_broken solve_tsp(data; verbose=false)

delete!(data, "x_coordinates")
@test_broken solve_tsp(data; verbose=false)
end