From 84df17dd8aea3741f1728840d1709f450fa6c1f2 Mon Sep 17 00:00:00 2001 From: yeesian Date: Thu, 15 Jan 2015 02:09:48 -0500 Subject: [PATCH] fix enumerate_paths see discussion in #150 --- doc/source/algorithms.rst | 43 ++++++++++++++++++++++++++++++--------- src/Graphs.jl | 2 +- src/bellmanford.jl | 33 +++++------------------------- src/dijkstra_spath.jl | 40 ++++++++++++++++++------------------ test/bellman_test.jl | 12 +++++------ test/dijkstra.jl | 42 ++++++++++++++++++++++++++++++++++---- 6 files changed, 102 insertions(+), 70 deletions(-) diff --git a/doc/source/algorithms.rst b/doc/source/algorithms.rst index fdb909af..24df9d44 100644 --- a/doc/source/algorithms.rst +++ b/doc/source/algorithms.rst @@ -184,36 +184,59 @@ The user can (optionally) provide a visitor that perform operations along with t Invoked when a vertex is closed (all its neighbors have been examined). -.. py:function:: enumerate_paths(result[, dest]) +.. py:function:: enumerate_paths(vertices, parent_indices[, dest]) - Returns an array of vectors (containing vertices), whose ``i``-th element corresponds to the path from a source to vertex ``dest[i]``. Empty vectors indicate vertices that are unreachable from the source. ``dest`` can be a subset of vertices, or left unspecified (in which case, all the vertices in the graph will be considered). If ``dest`` is a single vertex, then the result is just an array of vertices, corresponding to the path from a source to ``dest``. + Returns an array of vectors (containing vertices), whose ``i``-th element corresponds to the path from a source to vertex ``dest[i]``. Empty vectors indicate vertices that are unreachable from the source. ``dest`` can be a subset of indices, or left unspecified (in which case, all the indices will be considered). If ``dest`` is a single index, then the result is just an array of vertices, corresponding to the path from a source to ``dest``. - **Remark**: ``enumerate_paths`` is applicable to both ``DijkstraStates`` and ``BellmanFordStates``. +.. py:function:: enumerate_indices(parent_indices[, dest]) + + Returns an array of indices corresponding to the vertices returned by `enumerate_paths(vertices, parent_indices[, dest])` The following is an example that shows how to use this function: .. code-block:: python - julia> using Graphs - julia> g3 = simple_graph(4) - julia> add_edge!(g3,1,2); add_edge!(g3,1,3); add_edge!(g3,2,3); add_edge!(g3,3,4); - julia> s3 = dijkstra_shortest_paths(g3,2) - julia> sps = enumerate_paths(s3) # dest: all vertices + julia> g4 = Graphs.inclist([4,5,6,7],is_directed=true) + julia> add_edge!(g4,4,5); add_edge!(g4,4,6); add_edge!(g4,5,6); add_edge!(g4,6,7) + julia> s4 = dijkstra_shortest_paths(g4,5) + julia> sps = enumerate_indices(s4.parent_indices) # dest: all indices 4-element Array{Array{Int64,1},1}: [] [2] [2,3] [2,3,4] - julia> sps = enumerate_paths(s3, [2,4]) # dest: subset of vertices + + julia> enumerate_indices(s4.parent_indices, [2,4]) # dest: subset of indices 2-element Array{Array{Int64,1},1}: [2] [2,3,4] - julia> sps = enumerate_paths(s3, 4) # dest: single vertex + + julia> enumerate_indices(s4.parent_indices, 4) # dest: single index 3-element Array{Int64,1}: 2 3 4 + julia> enumerate_paths(vertices(g4), s4.parent_indices) # dest: all vertices + 4-element Array{Array{Int64,1},1}: + [] + [5] + [5,6] + [5,6,7] + + julia> enumerate_paths(vertices(g4), s4.parent_indices, [2,4]) # dest: subset of vertices + 2-element Array{Array{Int64,1},1}: + [5] + [5,6,7] + + julia> enumerate_paths(vertices(g4), s4.parent_indices, 4) # dest: single vertex + 3-element Array{Int64,1}: + 5 + 6 + 7 + +**Remark**: ``enumerate_paths`` and ``enumerate_indices`` are applicable to the results from both ``dijkstra_shortest_paths`` and ``bellman_ford_shortest_paths``. + Bellman Ford Algorithm ~~~~~~~~~~~~~~~~~~~~ diff --git a/src/Graphs.jl b/src/Graphs.jl index 31d3718f..b6f75b8a 100644 --- a/src/Graphs.jl +++ b/src/Graphs.jl @@ -78,7 +78,7 @@ module Graphs DijkstraStates, create_dijkstra_states, AbstractDijkstraVisitor, dijkstra_shortest_paths!, dijkstra_shortest_paths, dijkstra_shortest_paths_withlog, - enumerate_paths, + enumerate_paths, enumerate_indices, # bellmanford BellmanFordStates, create_bellman_ford_states, NegativeCycleError, diff --git a/src/bellmanford.jl b/src/bellmanford.jl index 895fa4f9..ffe3d4db 100644 --- a/src/bellmanford.jl +++ b/src/bellmanford.jl @@ -10,7 +10,7 @@ type NegativeCycleError <: Exception end type BellmanFordStates{V,D<:Number} parents::Vector{V} - hasparent::Vector{Bool} + parent_indices::Vector{Int} dists::Vector{D} end @@ -19,10 +19,10 @@ end function create_bellman_ford_states{V,D<:Number}(g::AbstractGraph{V}, ::Type{D}) n = num_vertices(g) parents = Array(V, n) - hasparent = fill(false, n) + parent_indices = zeros(Int, n) dists = fill(typemax(D), n) - BellmanFordStates(parents,hasparent, dists) + BellmanFordStates(parents, parent_indices, dists) end function bellman_ford_shortest_paths!{V,D}( @@ -40,6 +40,7 @@ function bellman_ford_shortest_paths!{V,D}( i = vertex_index(v, graph) state.dists[i] = 0 state.parents[i] = v + state.parent_indices[i] = i push!(active, v) end no_changes = false @@ -55,7 +56,7 @@ function bellman_ford_shortest_paths!{V,D}( if state.dists[vind] > state.dists[uind] + edist state.dists[vind] = state.dists[uind] + edist state.parents[vind] = u - state.hasparent[vind] = true + state.parent_indices[vind] = vertex_index(u, graph) no_changes = false push!(new_active, v) end @@ -109,27 +110,3 @@ function has_negative_edge_cycle{V, D}( has_negative_edge_cycle(graph, edge_inspector) end -function enumerate_paths{V,D<:Number}(state::BellmanFordStates{V,D}, dest::Vector{V}) - parents = state.parents - hasparent = state.hasparent - - num_dest = length(dest) - all_paths = Array(Vector{V},num_dest) - for i=1:num_dest - all_paths[i] = V[] - index = dest[i] - if hasparent[index] || parents[index] == index - while hasparent[index] - push!(all_paths[i], index) - index = parents[index] - end - push!(all_paths[i], index) - reverse!(all_paths[i]) - end - end - all_paths -end - -enumerate_paths{V,D<:Number}(state::BellmanFordStates{V,D}, dest) = enumerate_paths(state, [dest])[1] -enumerate_paths{V,D<:Number}(state::BellmanFordStates{V,D}) = enumerate_paths(state, [1:length(state.parents)]) - diff --git a/src/dijkstra_spath.jl b/src/dijkstra_spath.jl index 8dcaf259..c9763a7f 100644 --- a/src/dijkstra_spath.jl +++ b/src/dijkstra_spath.jl @@ -8,7 +8,7 @@ type DijkstraStates{V,D<:Number,Heap,H} parents::Vector{V} - hasparent::Vector{Bool} + parent_indices::Vector{Int} dists::Vector{D} colormap::Vector{Int} heap::Heap @@ -27,13 +27,13 @@ end function create_dijkstra_states{V,D<:Number}(g::AbstractGraph{V}, ::Type{D}) n = num_vertices(g) parents = Array(V, n) - hasparent = fill(false, n) + parent_indices = zeros(Int, n) dists = fill(typemax(D), n) colormap = zeros(Int, n) heap = mutable_binary_minheap(DijkstraHEntry{V,D}) hmap = zeros(Int, n) - DijkstraStates(parents, hasparent, dists, colormap, heap, hmap) + DijkstraStates(parents, parent_indices, dists, colormap, heap, hmap) end ################################################################### @@ -98,7 +98,7 @@ end function set_source!{V,D}(state::DijkstraStates{V,D}, g::AbstractGraph{V}, s::V) i = vertex_index(s, g) state.parents[i] = s - state.hasparent[i] = false + state.parent_indices[i] = i state.dists[i] = 0 state.colormap[i] = 2 end @@ -111,7 +111,7 @@ function process_neighbors!{V,D,Heap,H}( dists::Vector{D} = state.dists parents::Vector{V} = state.parents - hasparent::Vector{Bool} = state.hasparent + parent_indices::Vector{Int} = state.parent_indices colormap::Vector{Int} = state.colormap heap::Heap = state.heap hmap::Vector{H} = state.hmap @@ -125,7 +125,7 @@ function process_neighbors!{V,D,Heap,H}( if v_color == 0 dists[iv] = dv = du + edge_property(edge_dists, e, graph) parents[iv] = u - hasparent[iv] = true + parent_indices[iv] = vertex_index(u, graph) colormap[iv] = 1 discover_vertex!(visitor, u, v, dv) @@ -137,7 +137,7 @@ function process_neighbors!{V,D,Heap,H}( if dv < dists[iv] dists[iv] = dv parents[iv] = u - hasparent[iv] = true + parent_indices[iv] = vertex_index(u, graph) # update the value on the heap update_vertex!(visitor, u, v, dv) @@ -254,19 +254,16 @@ dijkstra_shortest_paths{V}( graph::AbstractGraph{V}, s::V ) = dijkstra_shortest_paths(graph, ones(num_vertices(graph)), s) -function enumerate_paths{V,D,Heap,H}(state::DijkstraStates{V,D,Heap,H}, dest::Vector{V}) - parents = state.parents - hasparent = state.hasparent - - num_dest = length(dest) - all_paths = Array(Vector{V},num_dest) +function enumerate_indices(parent_indices::Vector{Int}, dest_indices::Vector{Int}) + num_dest = length(dest_indices) + all_paths = Array(Vector{Int},num_dest) for i=1:num_dest - all_paths[i] = V[] - index = dest[i] - if hasparent[index] || parents[index] == index - while hasparent[index] + all_paths[i] = Int[] + index = dest_indices[i] + if parent_indices[index] != 0 + while parent_indices[index] != index push!(all_paths[i], index) - index = parents[index] + index = parent_indices[index] end push!(all_paths[i], index) reverse!(all_paths[i]) @@ -275,5 +272,8 @@ function enumerate_paths{V,D,Heap,H}(state::DijkstraStates{V,D,Heap,H}, dest::Ve all_paths end -enumerate_paths{V,D,Heap,H}(state::DijkstraStates{V,D,Heap,H}, dest::V) = enumerate_paths(state, V[dest])[1] -enumerate_paths{V,D,Heap,H}(state::DijkstraStates{V,D,Heap,H}) = enumerate_paths(state, [1:length(state.parents)]) +enumerate_indices(parent_indices::Vector{Int}, dest_index::Int) = enumerate_indices(parent_indices, Int[dest_index])[1] +enumerate_indices(parent_indices::Vector{Int}) = enumerate_indices(parent_indices, [1:length(parent_indices)]) +enumerate_paths(vertices, parent_indices::Vector{Int}, dest_indices::Vector{Int}) = [vertices[i] for i in enumerate_indices(parent_indices, dest_indices)] +enumerate_paths(vertices, parent_indices::Vector{Int}, dest_index::Int) = enumerate_paths(vertices, parent_indices, [dest_index])[1] +enumerate_paths(vertices, parent_indices::Vector{Int}) = enumerate_paths(vertices, parent_indices, [1:length(parent_indices)]) diff --git a/test/bellman_test.jl b/test/bellman_test.jl index a6bd28d4..466015f1 100644 --- a/test/bellman_test.jl +++ b/test/bellman_test.jl @@ -35,10 +35,9 @@ s1 = bellman_ford_shortest_paths(g1, eweights1, [1]) @test s1.parents == [1, 3, 1, 2, 3] @test s1.dists == [0., 8., 5., 9., 7.] -@test s1.hasparent == [false, true, true, true, true] ## all destinations -sps = enumerate_paths(s1) +sps = enumerate_paths(vertices(g1), s1.parent_indices) @test length(sps) == 5 @test sps[1] == [1] @test sps[2] == [1,3,2] @@ -47,24 +46,23 @@ sps = enumerate_paths(s1) @test sps[5] == [1,3,5] ## multiple destinations -sps = enumerate_paths(s1, [2,4]) +sps = enumerate_paths(vertices(g1), s1.parent_indices, [2,4]) @test length(sps) == 2 @test sps[1] == [1,3,2] @test sps[2] == [1,3,2,4] ## single destination -sps = enumerate_paths(s1, 2) +sps = enumerate_paths(vertices(g1), s1.parent_indices, 2) @test sps == [1,3,2] -@test sps == enumerate_paths(s1, [2])[1] +@test sps == enumerate_paths(vertices(g1), s1.parent_indices, [2])[1] # Multiple Sources s1 = bellman_ford_shortest_paths(g1, eweights1, [1, 2]) @test s1.parents == [1, 2, 2, 2, 3] @test s1.dists == [0., 0., 2., 1., 4.] -@test s1.hasparent == [false, false, true, true, true] -sps = enumerate_paths(s1) +sps = enumerate_paths(vertices(g1), s1.parent_indices) @test sps[1] == [1] @test sps[2] == [2] @test sps[3] == [2,3] diff --git a/test/dijkstra.jl b/test/dijkstra.jl index ecec26ea..f6c48c71 100644 --- a/test/dijkstra.jl +++ b/test/dijkstra.jl @@ -115,17 +115,51 @@ g3 = simple_graph(4) add_edge!(g3,1,2); add_edge!(g3,1,3); add_edge!(g3,2,3); add_edge!(g3,3,4) s3 = dijkstra_shortest_paths(g3,2) -sps = enumerate_paths(s3) +sps = enumerate_paths(vertices(g3), s3.parent_indices) @test length(sps) == 4 @test sps[1] == [] @test sps[2] == [2] @test sps[3] == [2, 3] @test sps[4] == [2, 3, 4] -sps = enumerate_paths(s3, [2,4]) +sps = enumerate_paths(vertices(g3), s3.parent_indices, [2,4]) @test length(sps) == 2 @test sps[1] == [2] @test sps[2] == [2, 3, 4] -sps = enumerate_paths(s3, 4) -@test sps == [2, 3, 4] \ No newline at end of file +sps = enumerate_paths(vertices(g3), s3.parent_indices, 4) +@test sps == [2, 3, 4] + +g4 = Graphs.inclist([4,5,6,7],is_directed=true) +add_edge!(g4,4,5); add_edge!(g4,4,6); add_edge!(g4,5,6); add_edge!(g4,6,7) + +s4 = dijkstra_shortest_paths(g4,5) +sps = enumerate_indices(s4.parent_indices) +@test length(sps) == 4 +@test sps[1] == [] +@test sps[2] == [2] +@test sps[3] == [2, 3] +@test sps[4] == [2, 3, 4] + +sps = enumerate_indices(s4.parent_indices, [2,4]) +@test length(sps) == 2 +@test sps[1] == [2] +@test sps[2] == [2, 3, 4] + +sps = enumerate_indices(s4.parent_indices, 4) +@test sps == [2, 3, 4] + +sps = enumerate_paths(vertices(g4), s4.parent_indices) +@test length(sps) == 4 +@test sps[1] == [] +@test sps[2] == [5] +@test sps[3] == [5, 6] +@test sps[4] == [5, 6, 7] + +sps = enumerate_paths(vertices(g4), s4.parent_indices, [2,4]) +@test length(sps) == 2 +@test sps[1] == [5] +@test sps[2] == [5, 6, 7] + +sps = enumerate_paths(vertices(g4), s4.parent_indices, 4) +@test sps == [5, 6, 7] \ No newline at end of file