diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f45517..6aa093c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,11 @@ All notable changes to this project will be documented in this file. [pkjjl]: https://pkgdocs.julialang.org/v1/compatibility/#compat-pre-1.0 # [0.6.0] -- 2022-12-15 -### Changed -- Switch from Euclidean distance to approximate driving distance +### Added +- Allow RELOG to calculate approximate driving distances, instead of just straight-line distances between points. + +### Fixed +- Fix bug that caused building period parameter to be ignored ## [0.5.2] -- 2022-08-26 ### Changed diff --git a/docs/src/format.md b/docs/src/format.md index 01fdc7d..4164370 100644 --- a/docs/src/format.md +++ b/docs/src/format.md @@ -14,6 +14,7 @@ The **parameters** section describes details about the simulation itself. |:--------------------------|:---------------| |`time horizon (years)` | Number of years in the simulation. |`building period (years)` | List of years in which we are allowed to open new plants. For example, if this parameter is set to `[1,2,3]`, we can only open plants during the first three years. By default, this equals `[1]`; that is, plants can only be opened during the first year. | +|`distance metric` | Metric used to compute distances between pairs of locations. Valid options are: `"Euclidean"`, for the straight-line distance between points; or `"driving"` for an approximated driving distance. If not specified, defaults to `"Euclidean"`. #### Example @@ -21,7 +22,8 @@ The **parameters** section describes details about the simulation itself. { "parameters": { "time horizon (years)": 2, - "building period (years)": [1] + "building period (years)": [1], + "distance metric": "driving", } } ``` @@ -220,6 +222,7 @@ Database | Description | Examples * Plants can be expanded at any time, even long after they are open. * All material available at the beginning of a time period must be entirely processed by the end of that time period. It is not possible to store unprocessed materials from one time period to the next. * Up to two plant sizes are currently supported. Variable operating costs must be the same for all plant sizes. +* Accurate driving distances are only available for the continental United States. ## Output Data Format (JSON) diff --git a/instances/s1.json b/instances/s1.json index e88e8c5..279f1ae 100644 --- a/instances/s1.json +++ b/instances/s1.json @@ -1,6 +1,7 @@ { "parameters": { - "time horizon (years)": 2 + "time horizon (years)": 2, + "distance metric": "driving" }, "products": { "P1": { diff --git a/src/graph/build.jl b/src/graph/build.jl index 12640d6..418ac5e 100644 --- a/src/graph/build.jl +++ b/src/graph/build.jl @@ -40,7 +40,6 @@ function build_graph(instance::Instance)::Graph end # Build arcs from collection centers to plants, and from one plant to another - metric = _KnnDrivingDistance() for source in [collection_shipping_nodes; plant_shipping_nodes] for dest in process_nodes_by_input_product[source.product] distance = _calculate_distance( @@ -48,7 +47,7 @@ function build_graph(instance::Instance)::Graph source.location.longitude, dest.location.latitude, dest.location.longitude, - metric, + instance.distance_metric, ) values = Dict("distance" => distance) arc = Arc(source, dest, values) diff --git a/src/graph/dist.jl b/src/graph/dist.jl index abaecc0..777ad87 100644 --- a/src/graph/dist.jl +++ b/src/graph/dist.jl @@ -6,19 +6,12 @@ using Geodesy using NearestNeighbors using DataFrames -Base.@kwdef mutable struct _KnnDrivingDistance - tree = nothing - ratios = nothing -end - -mutable struct _EuclideanDistance end - function _calculate_distance( source_lat, source_lon, dest_lat, dest_lon, - ::_EuclideanDistance, + ::EuclideanDistance, )::Float64 x = LLA(source_lat, source_lon, 0.0) y = LLA(dest_lat, dest_lon, 0.0) @@ -30,7 +23,7 @@ function _calculate_distance( source_lon, dest_lat, dest_lon, - metric::_KnnDrivingDistance, + metric::KnnDrivingDistance, )::Float64 if metric.tree === nothing basedir = joinpath(dirname(@__FILE__), "..", "..", "data") @@ -54,13 +47,8 @@ function _calculate_distance( end # Compute Euclidean distance - dist_euclidean = _calculate_distance( - source_lat, - source_lon, - dest_lat, - dest_lon, - _EuclideanDistance(), - ) + dist_euclidean = + _calculate_distance(source_lat, source_lon, dest_lat, dest_lon, EuclideanDistance()) # Predict ratio idxs, _ = knn(metric.tree, [source_lat, source_lon, dest_lat, dest_lon], 5) diff --git a/src/instance/parse.jl b/src/instance/parse.jl index 5720393..d105aee 100644 --- a/src/instance/parse.jl +++ b/src/instance/parse.jl @@ -23,10 +23,23 @@ function parse(json)::Instance validate(json, Schema(json_schema)) building_period = [1] - if "building period (years)" in keys(json) - building_period = json["building period (years)"] + if "building period (years)" in keys(json["parameters"]) + building_period = json["parameters"]["building period (years)"] end + distance_metric = EuclideanDistance() + if "distance metric" in keys(json["parameters"]) + metric_name = json["parameters"]["distance metric"] + if metric_name == "driving" + distance_metric = KnnDrivingDistance() + elseif metric_name == "Euclidean" + # nop + else + error("Unknown distance metric: $metric_name") + end + end + @show distance_metric + plants = Plant[] products = Product[] collection_centers = CollectionCenter[] @@ -176,5 +189,12 @@ function parse(json)::Instance @info @sprintf("%12d collection centers", length(collection_centers)) @info @sprintf("%12d candidate plant locations", length(plants)) - return Instance(T, products, collection_centers, plants, building_period) + return Instance( + T, + products, + collection_centers, + plants, + building_period, + distance_metric, + ) end diff --git a/src/instance/structs.jl b/src/instance/structs.jl index 190ae9b..692fbca 100644 --- a/src/instance/structs.jl +++ b/src/instance/structs.jl @@ -48,10 +48,21 @@ mutable struct Plant storage_cost::Vector{Float64} end + +abstract type DistanceMetric end + +Base.@kwdef mutable struct KnnDrivingDistance <: DistanceMetric + tree = nothing + ratios = nothing +end + +mutable struct EuclideanDistance <: DistanceMetric end + mutable struct Instance time::Int64 products::Vector{Product} collection_centers::Vector{CollectionCenter} plants::Vector{Plant} building_period::Vector{Int64} + distance_metric::DistanceMetric end diff --git a/src/schemas/input.json b/src/schemas/input.json index fd46ec0..1cafb71 100644 --- a/src/schemas/input.json +++ b/src/schemas/input.json @@ -14,6 +14,9 @@ "properties": { "time horizon (years)": { "type": "number" + }, + "distance metric": { + "type": "string" } }, "required": [ diff --git a/test/graph/dist_test.jl b/test/graph/dist_test.jl index cce8b6a..d643602 100644 --- a/test/graph/dist_test.jl +++ b/test/graph/dist_test.jl @@ -11,7 +11,7 @@ using RELOG -87.656, 39.764, -86.148, - RELOG._EuclideanDistance(), + RELOG.EuclideanDistance(), ) == 265.818 # Approximate driving distance between Chicago and Indianapolis @@ -20,6 +20,6 @@ using RELOG -87.656, 39.764, -86.148, - RELOG._KnnDrivingDistance(), + RELOG.KnnDrivingDistance(), ) == 316.43 end