Skip to content

Commit 14bb353

Browse files
committed
add non-allocating bellman-ford code
1 parent 6130332 commit 14bb353

File tree

2 files changed

+41
-9
lines changed

2 files changed

+41
-9
lines changed

src/shortestpaths/bellman-ford.jl

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ single destinations, the path is represented by a single vector of vertices,
107107
and will be length 0 if the path does not exist.
108108
109109
### Implementation Notes
110+
110111
For Floyd-Warshall path states, please note that the output is a bit different,
111112
since this algorithm calculates all shortest paths for all pairs of vertices:
112113
`enumerate_paths(state)` will return a vector (indexed by source vertex) of
@@ -116,13 +117,47 @@ to all other vertices. In addition, `enumerate_paths(state, v, d)` will return
116117
a vector representing the path from vertex `v` to vertex `d`.
117118
"""
118119
function enumerate_paths(state::AbstractPathState, vs::AbstractVector{<:Integer})
119-
parents = state.parents
120-
T = eltype(parents)
120+
T = eltype(state.parents)
121+
all_paths = Vector{T}[Vector{eltype(state.parents)}() for _ in 1:length(vs)]
122+
return enumerate_paths!(all_paths, state, vs)
123+
end
124+
enumerate_paths(state::AbstractPathState, v::Integer) = enumerate_paths(state, v:v)[1]
125+
function enumerate_paths(state::AbstractPathState)
126+
return enumerate_paths(state, 1:length(state.parents))
127+
end
128+
129+
"""
130+
enumerate_paths!(paths::AbstractVector{<:AbstractVector}, state, vs::AbstractVector{Int})
131+
132+
In-place version of [`enumerate_paths`](@ref).
133+
134+
`paths` must be a `Vector{Vectors{eltype(state.parents)}}`, `state` an `AbstractPathState`,
135+
and `vs`` an AbstractRange or other AbstractVector of `Int`.
121136
137+
See the `enumerate_paths` documentation for details.
138+
139+
`enumerate_paths!` should be more efficient when used in a loop,
140+
as the same memory can be used for each iteration.
141+
"""
142+
function enumerate_paths!(
143+
all_paths::AbstractVector{<:AbstractVector},
144+
state::AbstractPathState,
145+
vs::AbstractVector{<:Integer},
146+
)
147+
Base.require_one_based_indexing(all_paths)
148+
Base.require_one_based_indexing(vs)
149+
length(all_paths) == length(vs) || throw(
150+
ArgumentError(
151+
"length of destination paths $(length(vs)) deos not match length of vs $(length(all_paths))",
152+
),
153+
)
154+
155+
parents = state.parents
156+
T = eltype(state.parents)
122157
num_vs = length(vs)
123-
all_paths = Vector{Vector{T}}(undef, num_vs)
158+
124159
for i in 1:num_vs
125-
all_paths[i] = Vector{T}()
160+
empty!(all_paths[i])
126161
index = T(vs[i])
127162
if parents[index] != 0 || parents[index] == index
128163
while parents[index] != 0
@@ -135,8 +170,3 @@ function enumerate_paths(state::AbstractPathState, vs::AbstractVector{<:Integer}
135170
end
136171
return all_paths
137172
end
138-
139-
enumerate_paths(state::AbstractPathState, v::Integer) = enumerate_paths(state, [v])[1]
140-
function enumerate_paths(state::AbstractPathState)
141-
return enumerate_paths(state, [1:length(state.parents);])
142-
end

test/shortestpaths/bellman-ford.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262
@test getfield.(y.dists, :val) == getfield.(z.dists, :val) == [Inf, 0, 6, 17, 33]
6363
@test @inferred(enumerate_paths(z))[2] == []
6464
@test @inferred(enumerate_paths(z))[4] == enumerate_paths(z, 4) == [2, 3, 4]
65+
@test @inferred(enumerate_paths!([[0]], z, 4:4))[1] == [2, 3, 4]
66+
@test_throws ArgumentError enumerate_paths!([[0, 0], [0, 0]], z, 4:4)
6567
@test @inferred(!has_negative_edge_cycle(g))
6668
@test @inferred(!has_negative_edge_cycle(g, d3))
6769

0 commit comments

Comments
 (0)