Make distance metric configurable; fix building period bug

feature/driving
Alinson S. Xavier 3 years ago
parent 48bd3c403f
commit 841fbf16fb
Signed by: isoron
GPG Key ID: 0DA8E4B9E1109DCA

@ -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 [pkjjl]: https://pkgdocs.julialang.org/v1/compatibility/#compat-pre-1.0
# [0.6.0] -- 2022-12-15 # [0.6.0] -- 2022-12-15
### Changed ### Added
- Switch from Euclidean distance to approximate driving distance - 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 ## [0.5.2] -- 2022-08-26
### Changed ### Changed

@ -14,6 +14,7 @@ The **parameters** section describes details about the simulation itself.
|:--------------------------|:---------------| |:--------------------------|:---------------|
|`time horizon (years)` | Number of years in the simulation. |`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. | |`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 #### Example
@ -21,7 +22,8 @@ The **parameters** section describes details about the simulation itself.
{ {
"parameters": { "parameters": {
"time horizon (years)": 2, "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. * 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. * 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. * 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) ## Output Data Format (JSON)

@ -1,6 +1,7 @@
{ {
"parameters": { "parameters": {
"time horizon (years)": 2 "time horizon (years)": 2,
"distance metric": "driving"
}, },
"products": { "products": {
"P1": { "P1": {

@ -40,7 +40,6 @@ function build_graph(instance::Instance)::Graph
end end
# Build arcs from collection centers to plants, and from one plant to another # 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 source in [collection_shipping_nodes; plant_shipping_nodes]
for dest in process_nodes_by_input_product[source.product] for dest in process_nodes_by_input_product[source.product]
distance = _calculate_distance( distance = _calculate_distance(
@ -48,7 +47,7 @@ function build_graph(instance::Instance)::Graph
source.location.longitude, source.location.longitude,
dest.location.latitude, dest.location.latitude,
dest.location.longitude, dest.location.longitude,
metric, instance.distance_metric,
) )
values = Dict("distance" => distance) values = Dict("distance" => distance)
arc = Arc(source, dest, values) arc = Arc(source, dest, values)

@ -6,19 +6,12 @@ using Geodesy
using NearestNeighbors using NearestNeighbors
using DataFrames using DataFrames
Base.@kwdef mutable struct _KnnDrivingDistance
tree = nothing
ratios = nothing
end
mutable struct _EuclideanDistance end
function _calculate_distance( function _calculate_distance(
source_lat, source_lat,
source_lon, source_lon,
dest_lat, dest_lat,
dest_lon, dest_lon,
::_EuclideanDistance, ::EuclideanDistance,
)::Float64 )::Float64
x = LLA(source_lat, source_lon, 0.0) x = LLA(source_lat, source_lon, 0.0)
y = LLA(dest_lat, dest_lon, 0.0) y = LLA(dest_lat, dest_lon, 0.0)
@ -30,7 +23,7 @@ function _calculate_distance(
source_lon, source_lon,
dest_lat, dest_lat,
dest_lon, dest_lon,
metric::_KnnDrivingDistance, metric::KnnDrivingDistance,
)::Float64 )::Float64
if metric.tree === nothing if metric.tree === nothing
basedir = joinpath(dirname(@__FILE__), "..", "..", "data") basedir = joinpath(dirname(@__FILE__), "..", "..", "data")
@ -54,13 +47,8 @@ function _calculate_distance(
end end
# Compute Euclidean distance # Compute Euclidean distance
dist_euclidean = _calculate_distance( dist_euclidean =
source_lat, _calculate_distance(source_lat, source_lon, dest_lat, dest_lon, EuclideanDistance())
source_lon,
dest_lat,
dest_lon,
_EuclideanDistance(),
)
# Predict ratio # Predict ratio
idxs, _ = knn(metric.tree, [source_lat, source_lon, dest_lat, dest_lon], 5) idxs, _ = knn(metric.tree, [source_lat, source_lon, dest_lat, dest_lon], 5)

@ -23,10 +23,23 @@ function parse(json)::Instance
validate(json, Schema(json_schema)) validate(json, Schema(json_schema))
building_period = [1] building_period = [1]
if "building period (years)" in keys(json) if "building period (years)" in keys(json["parameters"])
building_period = json["building period (years)"] building_period = json["parameters"]["building period (years)"]
end 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[] plants = Plant[]
products = Product[] products = Product[]
collection_centers = CollectionCenter[] collection_centers = CollectionCenter[]
@ -176,5 +189,12 @@ function parse(json)::Instance
@info @sprintf("%12d collection centers", length(collection_centers)) @info @sprintf("%12d collection centers", length(collection_centers))
@info @sprintf("%12d candidate plant locations", length(plants)) @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 end

@ -48,10 +48,21 @@ mutable struct Plant
storage_cost::Vector{Float64} storage_cost::Vector{Float64}
end end
abstract type DistanceMetric end
Base.@kwdef mutable struct KnnDrivingDistance <: DistanceMetric
tree = nothing
ratios = nothing
end
mutable struct EuclideanDistance <: DistanceMetric end
mutable struct Instance mutable struct Instance
time::Int64 time::Int64
products::Vector{Product} products::Vector{Product}
collection_centers::Vector{CollectionCenter} collection_centers::Vector{CollectionCenter}
plants::Vector{Plant} plants::Vector{Plant}
building_period::Vector{Int64} building_period::Vector{Int64}
distance_metric::DistanceMetric
end end

@ -14,6 +14,9 @@
"properties": { "properties": {
"time horizon (years)": { "time horizon (years)": {
"type": "number" "type": "number"
},
"distance metric": {
"type": "string"
} }
}, },
"required": [ "required": [

@ -11,7 +11,7 @@ using RELOG
-87.656, -87.656,
39.764, 39.764,
-86.148, -86.148,
RELOG._EuclideanDistance(), RELOG.EuclideanDistance(),
) == 265.818 ) == 265.818
# Approximate driving distance between Chicago and Indianapolis # Approximate driving distance between Chicago and Indianapolis
@ -20,6 +20,6 @@ using RELOG
-87.656, -87.656,
39.764, 39.764,
-86.148, -86.148,
RELOG._KnnDrivingDistance(), RELOG.KnnDrivingDistance(),
) == 316.43 ) == 316.43
end end

Loading…
Cancel
Save