Skip to content

Commit 000a2a3

Browse files
authored
Merge pull request #28 from BatyLeo/convex-meet-option
Add an option for using convex meet in bounding
2 parents 886b739 + 131c666 commit 000a2a3

File tree

7 files changed

+103
-20
lines changed

7 files changed

+103
-20
lines changed

CITATION.bib

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ @misc{ConstrainedShortestPaths.jl
22
author = {Léo Baty and contributors},
33
title = {ConstrainedShortestPaths.jl},
44
url = {https://github.com/BatyLeo/ConstrainedShortestPaths.jl},
5-
version = {v0.5.1},
5+
version = {v0.6.0},
66
year = {2024},
7-
month = {10}
7+
month = {12}
88
}

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ConstrainedShortestPaths"
22
uuid = "b3798467-87dc-4d99-943d-35a1bd39e395"
33
authors = ["Léo Baty and contributors"]
4-
version = "0.5.1"
4+
version = "0.6.0"
55

66
[deps]
77
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
@@ -15,7 +15,7 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1515
DataStructures = "0.18"
1616
DocStringExtensions = "0.9"
1717
Graphs = "1"
18-
PiecewiseLinearFunctions = "0.4"
18+
PiecewiseLinearFunctions = "0.4.2"
1919
SparseArrays = "<0.0.1,1"
2020
Statistics = "<0.0.1,1"
2121
julia = "1.10"

src/ConstrainedShortestPaths.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ using Graphs:
1313
edges,
1414
outneighbors,
1515
induced_subgraph
16-
using PiecewiseLinearFunctions: PiecewiseLinearFunction
16+
using PiecewiseLinearFunctions:
17+
PiecewiseLinearFunction, convex_meet, remove_redundant_breakpoints
1718
using SparseArrays: sparse
1819
using Statistics: mean
1920

src/algorithms.jl

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,56 @@ end
199199
"""
200200
$TYPEDSIGNATURES
201201
202+
Compute all paths below threshold.
203+
"""
204+
function generalized_a_star_with_threshold(
205+
instance::ForwardCSPInstance, threshold::Float64; kwargs...
206+
)
207+
(; graph, origin_vertex, destination_vertex, is_useful) = instance
208+
209+
empty_path = [origin_vertex]
210+
211+
forward_resources = Dict(empty_path => instance.origin_forward_resource)
212+
L = PriorityQueue{Vector{Int},Float64}(
213+
empty_path => instance.cost_function(forward_resources[empty_path])
214+
)
215+
216+
c_star = Float64[]
217+
p_star = Vector{Int}[]
218+
219+
while !isempty(L)
220+
p = dequeue!(L)
221+
v = p[end]
222+
for w in outneighbors(graph, v)
223+
if !is_useful[w]
224+
continue
225+
end
226+
q = copy(p)
227+
push!(q, w)
228+
rp = forward_resources[p]
229+
rq, is_feasible = instance.forward_functions[v, w](rp; kwargs...)
230+
if !is_feasible
231+
continue
232+
end
233+
forward_resources[q] = rq
234+
c = instance.cost_function(rq)
235+
if w == destination_vertex # if destination is reached
236+
if c < threshold
237+
push!(p_star, copy(q))
238+
push!(c_star, c)
239+
else # else add path to queue
240+
enqueue!(L, q => c)
241+
end
242+
end
243+
# else, discard path (i.e. do nothing)
244+
end
245+
end
246+
return (; p_star, c_star)
247+
end
248+
249+
"""
250+
$TYPEDSIGNATURES
251+
202252
Compute the shortest path of `instance`.
203253
"""
204254
function generalized_constrained_shortest_path(instance::CSPInstance; kwargs...)
@@ -216,8 +266,14 @@ $TYPEDSIGNATURES
216266
Compute shortest path between first and last nodes of `instance`
217267
"""
218268
function generalized_constrained_shortest_path_with_threshold(
219-
instance::CSPInstance{T,G}, threshold::Float64; kwargs...
220-
) where {T,G<:AbstractGraph}
269+
instance::CSPInstance, threshold::Float64; kwargs...
270+
)
221271
bounds = compute_bounds(instance; kwargs...)
222272
return generalized_a_star_with_threshold(instance, bounds, threshold; kwargs...)
223273
end
274+
275+
function generalized_constrained_shortest_path_with_threshold(
276+
instance::ForwardCSPInstance, threshold::Float64; kwargs...
277+
)
278+
return generalized_a_star_with_threshold(instance, threshold; kwargs...)
279+
end

src/examples/stochastic_routing.jl

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ $TYPEDEF
2121
# Fields
2222
$TYPEDFIELDS
2323
"""
24-
struct StochasticBackwardResource
24+
struct StochasticBackwardResource{is_convex}
2525
"piecewise linear function for each scenario, take as input propagated delay and outputs total delay of partail path"
2626
g::Vector{PiecewiseLinearFunction{Float64}}
2727
end
@@ -70,9 +70,19 @@ function Base.:<=(r1::StochasticForwardResource, r2::StochasticForwardResource)
7070
return true
7171
end
7272

73-
function Base.min(r1::StochasticBackwardResource, r2::StochasticBackwardResource)
73+
function Base.min(
74+
r1::StochasticBackwardResource{true}, r2::StochasticBackwardResource{true}
75+
)
76+
new_g = convex_meet.(r1.g, r2.g)
77+
# new_g = remove_redundant_breakpoints.(convex_meet.(r1.g, r2.g); atol=1e-8)
78+
return StochasticBackwardResource{true}(new_g)
79+
end
80+
81+
function Base.min(
82+
r1::StochasticBackwardResource{false}, r2::StochasticBackwardResource{false}
83+
)
7484
new_g = min.(r1.g, r2.g)
75-
return StochasticBackwardResource(new_g)
85+
return StochasticBackwardResource{false}(new_g)
7686
end
7787

7888
function (f::StochasticForwardFunction)(q::StochasticForwardResource)
@@ -90,8 +100,10 @@ function _backward_scenario(g::PiecewiseLinearFunction, delay::Float64, slack::F
90100
return f + g f - λᵥ
91101
end
92102

93-
function (f::StochasticBackwardFunction)(q::StochasticBackwardResource)
94-
return StochasticBackwardResource([
103+
function (f::StochasticBackwardFunction)(
104+
q::StochasticBackwardResource{is_convex}
105+
) where {is_convex}
106+
return StochasticBackwardResource{is_convex}([
95107
_backward_scenario(g, delay, slack, f.λ_value) for
96108
(g, delay, slack) in zip(q.g, f.intrinsic_delays, f.slacks)
97109
])
@@ -116,6 +128,7 @@ function create_instance(
116128
origin_vertex::T=one(T),
117129
destination_vertex::T=nv(graph),
118130
bounding=true,
131+
use_convex_resources=true,
119132
) where {T}
120133
@assert λ_values[origin_vertex] == 0.0 && λ_values[destination_vertex] == 0.0
121134
@assert all(intrinsic_delays[origin_vertex] .== 0.0)
@@ -139,7 +152,7 @@ function create_instance(
139152
StochasticBackwardFunction(slacks[u, v], intrinsic_delays[v, :], λ_values[v])
140153
for (u, v) in zip(I, J)
141154
]
142-
destination_backward_resource = StochasticBackwardResource([
155+
destination_backward_resource = StochasticBackwardResource{use_convex_resources}([
143156
piecewise_linear() for _ in 1:nb_scenarios
144157
])
145158

@@ -189,6 +202,7 @@ function stochastic_routing_shortest_path(
189202
origin_vertex::T=one(T),
190203
destination_vertex::T=nv(graph),
191204
bounding=true,
205+
use_convex_resources=true,
192206
) where {T}
193207
instance = create_instance(
194208
graph,
@@ -198,6 +212,7 @@ function stochastic_routing_shortest_path(
198212
origin_vertex=origin_vertex,
199213
destination_vertex=destination_vertex,
200214
bounding=bounding,
215+
use_convex_resources=use_convex_resources,
201216
)
202217
return generalized_constrained_shortest_path(instance)
203218
end
@@ -215,6 +230,7 @@ function stochastic_routing_shortest_path_with_threshold(
215230
origin_vertex::T=one(T),
216231
destination_vertex::T=nv(graph),
217232
bounding=true,
233+
use_convex_resources=true,
218234
threshold,
219235
) where {T}
220236
instance = create_instance(
@@ -225,6 +241,7 @@ function stochastic_routing_shortest_path_with_threshold(
225241
bounding=bounding,
226242
origin_vertex=origin_vertex,
227243
destination_vertex=destination_vertex,
244+
use_convex_resources=use_convex_resources,
228245
)
229246
return generalized_constrained_shortest_path_with_threshold(instance, threshold)
230247
end

test/examples/stochastic_routing.jl

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ end
124124
@test c_star == 5
125125
@test p_star == [1, 3, 4, 5]
126126
@test path_cost(p_star, slack_matrix, delays) == c_star
127+
(; c_star, p_star) = stochastic_routing_shortest_path(
128+
graph, slack_matrix, delays; use_convex_resources=true
129+
)
130+
@test c_star == 5
131+
@test p_star == [1, 3, 4, 5]
132+
@test path_cost(p_star, slack_matrix, delays) == c_star
127133
end
128134

129135
# @testset "Detour with slack" begin
@@ -140,7 +146,7 @@ end
140146
@testset "Random graphs" begin
141147
n = 5
142148
for nb_scenarios in 1:5
143-
for nb_vertices in 10:1:15
149+
for nb_vertices in 10:1:20
144150
for seed in 1:n
145151
rng = StableRNG(seed)
146152
graph = random_acyclic_digraph(
@@ -169,12 +175,9 @@ end
169175
graph, slack_matrix, delays, initial_paths
170176
)
171177
_value, _obj2, _paths, _dual, _dual_new = stochastic_PLNE(
172-
graph, slack_matrix, delays, initial_paths; bounding=false
178+
graph, slack_matrix, delays, initial_paths; bounding=true
173179
)
174180
@test obj2 _obj2
175-
# if !(obj2 ≈ _obj2)
176-
# jldsave("debug.jld2"; graph, slack_matrix, delays, initial_paths)
177-
# end
178181

179182
# Exact resolution
180183
obj, sol = solve_scenarios(graph, slack_matrix, delays)
@@ -198,6 +201,10 @@ end
198201
additional_paths, costs = stochastic_routing_shortest_path_with_threshold(
199202
graph, slack_matrix, delays, λ_val; threshold
200203
)
204+
_additional_paths, _costs = stochastic_routing_shortest_path_with_threshold(
205+
graph, slack_matrix, delays, λ_val; threshold, bounding=true
206+
)
207+
@test costs _costs
201208
full_paths = unique(vcat(initial_paths, paths, additional_paths))
202209
obj4, y4 = column_generation(graph, slack_matrix, delays, full_paths)
203210
@test obj4 cupp || obj4 < cupp

test/utils.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ function path_cost(path, slacks, delays, λ=zeros(size(delays, 1)))
8080
return C + vehicle_cost
8181
end
8282

83-
function stochastic_PLNE(g, slacks, delays, initial_paths; bounding=true)
83+
function stochastic_PLNE(
84+
g, slacks, delays, initial_paths; bounding=true, use_convex_resources=true
85+
)
8486
nb_nodes = nv(g)
8587
job_indices = 2:(nb_nodes - 1)
8688

@@ -107,7 +109,7 @@ function stochastic_PLNE(g, slacks, delays, initial_paths; bounding=true)
107109
optimize!(model)
108110
λ_val = value.(λ)
109111
(; c_star, p_star) = stochastic_routing_shortest_path(
110-
g, slacks, delays, λ_val; bounding
112+
g, slacks, delays, λ_val; bounding, use_convex_resources
111113
)
112114
# if c == 2
113115
# jldsave("debug2.jld2"; graph, slacks, delays, λ_val, c_star, p_star)

0 commit comments

Comments
 (0)