diff --git a/doc/pathing.md b/doc/pathing.md index e7f032a22..e5361143f 100644 --- a/doc/pathing.md +++ b/doc/pathing.md @@ -66,7 +66,7 @@ Returns connected components of the undirected graph of `g`. ``` has_self_loop(g::Union{LightGraphs.DiGraph,LightGraphs.Graph}) ``` -Returns true if `g` is has any self loops. +Returns true if `g` has any self loops. ### attracting_components ``` diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 4c2cd812d..b25855b53 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -34,7 +34,7 @@ in_edges, out_edges, has_vertex, has_edge, is_directed, nv, ne, add_edge!, rem_edge!, add_vertex!, add_vertices!, indegree, outdegree, degree, degree_histogram, density, Δ, δ, Δout, Δin, δout, δin, neighbors, in_neighbors, out_neighbors, -common_neighbors, all_neighbors, has_self_loop, +common_neighbors, all_neighbors, has_self_loop, ordered, # distance eccentricity, diameter, periphery, radius, center, @@ -95,7 +95,10 @@ a_star, readgraph, readgraphml, readgml, writegraphml, writegexf, # randgraphs -erdos_renyi, watts_strogatz, random_regular_graph, random_regular_digraph, random_configuration_model +erdos_renyi, watts_strogatz, random_regular_graph, random_regular_digraph, random_configuration_model, + +# edgeit - TODO EdgeItState could probably not be exported +edge_it, in_edge_it, out_edge_it, EdgeIt, EdgeItState """An optimized graphs package. @@ -117,6 +120,7 @@ LightGraphs include("core.jl") include("digraph.jl") include("graph.jl") + include("edgeit.jl") include("traversals/graphvisit.jl") include("traversals/bfs.jl") include("traversals/dfs.jl") diff --git a/src/core.jl b/src/core.jl index 830e1336f..e9b28d57b 100644 --- a/src/core.jl +++ b/src/core.jl @@ -20,6 +20,8 @@ src(e::Edge) = e.first """Return destination of an edge.""" dst(e::Edge) = e.second +ordered(e::Edge) = src(e) <= dst(e) + @deprecate rev(e::Edge) reverse(e) ==(e1::Edge, e2::Edge) = (e1.first == e2.first && e1.second == e2.second) @@ -30,16 +32,18 @@ end """A type representing an undirected graph.""" type Graph + ne # number of edges vertices::UnitRange{Int} - edges::Set{Edge} + # edges::Set{Edge} fadjlist::Vector{Vector{Int}} # [src]: (dst, dst, dst) badjlist::Vector{Vector{Int}} # [dst]: (src, src, src) end """A type representing a directed graph.""" type DiGraph + ne # number of edges vertices::UnitRange{Int} - edges::Set{Edge} + # edges::Set{Edge} fadjlist::Vector{Vector{Int}} # [src]: (dst, dst, dst) badjlist::Vector{Vector{Int}} # [dst]: (src, src, src) end @@ -50,8 +54,8 @@ typealias SimpleGraph @compat(Union{Graph, DiGraph}) """Return the vertices of a graph.""" vertices(g::SimpleGraph) = g.vertices -"""Return the edges of a graph.""" -edges(g::SimpleGraph) = g.edges +"""Return an iterator to the edges of a graph.""" +edges(g::SimpleGraph) = edge_it(g) """Returns the forward adjacency list of a graph. @@ -118,7 +122,7 @@ has_vertex(g::SimpleGraph, v::Int) = v in vertices(g) """The number of vertices in `g`.""" nv(g::SimpleGraph) = length(vertices(g)) """The number of edges in `g`.""" -ne(g::SimpleGraph) = length(edges(g)) +ne(g::SimpleGraph) = g.ne """Add a new edge to `g` from `src` to `dst`. @@ -194,8 +198,8 @@ neighbors(g::SimpleGraph, v::Int) = out_neighbors(g, v) common_neighbors(g::SimpleGraph, u::Int, v::Int) = intersect(neighbors(g,u), neighbors(g,v)) function copy{T<:SimpleGraph}(g::T) - return T(g.vertices,copy(g.edges),deepcopy(g.fadjlist),deepcopy(g.badjlist)) + return T(g.ne, g.vertices,deepcopy(g.fadjlist),deepcopy(g.badjlist)) end -"Returns true if `g` is has any self loops." +"Returns true if `g` has any self loops." has_self_loop(g::SimpleGraph) = any(v->has_edge(g, v, v), vertices(g)) diff --git a/src/digraph.jl b/src/digraph.jl index 99acf1a5b..964d2c6cc 100644 --- a/src/digraph.jl +++ b/src/digraph.jl @@ -13,7 +13,7 @@ function DiGraph(n::Int) push!(badjlist, @compat(Vector{Int}())) push!(fadjlist, @compat(Vector{Int}())) end - return DiGraph(1:n, Set{Edge}(), badjlist, fadjlist) + return DiGraph(0, 1:n, badjlist, fadjlist) end DiGraph() = DiGraph(0) @@ -50,17 +50,14 @@ end function DiGraph(g::Graph) h = DiGraph(nv(g)) - for e in edges(g) - push!(h.edges,e) - push!(h.edges,reverse(e)) - end h.fadjlist = copy(fadj(g)) h.badjlist = copy(badj(g)) return h end +#warning: could be slow, function ==(g::DiGraph, h::DiGraph) - return (vertices(g) == vertices(h)) && (edges(g) == edges(h)) + return (vertices(g) == vertices(h)) && (Set(edges(g)) == Set(edges(h))) end is_directed(g::DiGraph) = true @@ -68,7 +65,7 @@ is_directed(g::DiGraph) = true function unsafe_add_edge!(g::DiGraph, e::Edge) push!(g.fadjlist[src(e)], dst(e)) push!(g.badjlist[dst(e)], src(e)) - push!(g.edges, e) + g.ne+=1 return e end @@ -80,7 +77,8 @@ function rem_edge!(g::DiGraph, e::Edge) deleteat!(g.fadjlist[src(e)], i) i = findfirst(g.badjlist[dst(e)], src(e)) deleteat!(g.badjlist[dst(e)], i) - return pop!(g.edges, e) + g.ne -= 1 + return e end has_edge(g::DiGraph, e::Edge) = e in edges(g) diff --git a/src/edgeit.jl b/src/edgeit.jl new file mode 100644 index 000000000..49333ae26 --- /dev/null +++ b/src/edgeit.jl @@ -0,0 +1,66 @@ +import Base: start, next, done, length, show, + in, == + +type EdgeItState + it::Int + v::Int + k::Int +end + +type EdgeIt + m::Int + adj::Vector{Vector{Int}} + start::EdgeItState # =[it, v, k] + directed::Bool +end + +edge_it(g::Graph) = EdgeIt(ne(g), g.fadjlist, EdgeItState(1,1,1), false) +edge_it(g::DiGraph) = EdgeIt(ne(g), g.fadjlist, EdgeItState(1,1,1), true) + +start(eit::EdgeIt) = eit.start +done(eit::EdgeIt, state::EdgeItState) = state.it > eit.m +function next(eit::EdgeIt, state::EdgeItState) + # println("$eit, $state") + assert(state.v <= length(eit.adj)) + u = 0 + while state.k <= length(eit.adj[state.v]) + u = eit.adj[state.v][state.k] + eit.directed && break + u >= state.v && break + state.k += 1 + end + while state.k > length(eit.adj[state.v]) + state.k = 1 + state.v += 1 + while state.k <= length(eit.adj[state.v]) + u = eit.adj[state.v][state.k] + eit.directed && break + u >= state.v && break + state.k += 1 + end + end + e = !eit.directed && u < state.v ? Edge(u, state.v) : Edge(state.v, u) + state.k += 1 + state.it += 1 + return (e, state) +end +length(eit::EdgeIt) = eit.m + +function in(e::Edge, eit::EdgeIt) + s = src(e) + t = dst(e) + n = length(eit.adj) + !(1 <= s <= n) && return false + !(1 <= t <= n) && return false + return t in eit.adj[s] +end + +#TODO implement using a loop and ∈, so that no memory is allocated +==(e1::EdgeIt, e2::Set{Edge}) = Set{Edge}(e1) == e2 +==(e1::Set{Edge}, e2::EdgeIt) = e1 == Set{Edge}(e2) +==(e1::EdgeIt, e2::EdgeIt) = Set{Edge}(e1) == Set{Edge}(e2) + + + +show(io::IO, eit::EdgeIt) = write(io, "edgeit $(eit.m)") +show(io::IO, s::EdgeItState) = write(io, "edgeitstate [ $(s.it),$(s.v),$(s.k) ]") diff --git a/src/graph.jl b/src/graph.jl index 70062e3c3..413d420e5 100644 --- a/src/graph.jl +++ b/src/graph.jl @@ -17,7 +17,7 @@ function Graph(n::Int) push!(badjlist, @compat(Vector{Int}())) push!(fadjlist, @compat(Vector{Int}())) end - return Graph(1:n, Set{Edge}(), badjlist, fadjlist) + return Graph(0, 1:n, badjlist, fadjlist) end Graph() = Graph(0) @@ -48,6 +48,7 @@ function Graph(g::DiGraph) return h end +#warning: could be slow function ==(g::Graph, h::Graph) gdigraph = DiGraph(g) hdigraph = DiGraph(h) @@ -65,16 +66,12 @@ function unsafe_add_edge!(g::Graph, e::Edge) push!(g.fadjlist[dst(e)], src(e)) push!(g.badjlist[src(e)], dst(e)) end - push!(g.edges, e) + g.ne += 1 return e end function rem_edge!(g::Graph, e::Edge) - if !(e in edges(g)) - reve = reverse(e) - (reve in edges(g)) || error("Edge $e is not in graph") - e = reve - end + (e in edges(g)) || error("Edge $e is not in graph") i = findfirst(g.fadjlist[src(e)], dst(e)) deleteat!(g.fadjlist[src(e)], i) @@ -84,7 +81,8 @@ function rem_edge!(g::Graph, e::Edge) deleteat!(g.fadjlist[dst(e)], i) i = findfirst(g.badjlist[src(e)], dst(e)) deleteat!(g.badjlist[src(e)], i) - return pop!(g.edges, e) + g.ne -= 1 + return ordered(e) ? e : reverse(e) end diff --git a/src/operators.jl b/src/operators.jl index 2c6729237..2aff26881 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -44,10 +44,6 @@ end function reverse!(g::DiGraph) gne = ne(g) reve = Set{Edge}() - for e in edges(g) - push!(reve, reverse(e)) - end - g.edges = reve g.fadjlist, g.badjlist = g.badjlist, g.fadjlist return g end diff --git a/test/core.jl b/test/core.jl index cc86d474a..6974a1137 100644 --- a/test/core.jl +++ b/test/core.jl @@ -22,7 +22,14 @@ add_edge!(h, 3, 5) @test sprint(show, e1) == "edge 1 - 2" @test vertices(g) == 1:5 +i = 0 +for e in edges(g) + i+=1 +end +@test i == 5 +@test has_edge(g,3,5) @test edges(g) == Set([e1, e2, e3, e4, e5]) +@test Set{Edge}(edges(g)) == Set([e1, e2, e3, e4, e5]) @test degree(g) == [3, 2, 2, 1, 2] @test indegree(g) == [3, 2, 2, 1, 2] @@ -65,13 +72,33 @@ add_edge!(h, 3, 5) @test common_neighbors(g, 2, 3) == [1, 5] @test common_neighbors(h, 2, 3) == [5] +@test ne(g) == 5 @test rem_edge!(g, 1, 2) == e1 +@test ne(g) == 4 +eit = edges(g) +@test eit.m == 4 +state = start(eit) +@test state.it == 1 +@test state.v == 1 +@test state.k == 1 +# @test edges(g) == EdgeIt(4, g.fadjlist, EdgeItState(1,1,1), false) + @test_throws ErrorException rem_edge!(g, 2, 1) add_edge!(g, 1, 2) +@test ne(g) == 5 +eit = edges(g) +@test eit.m == 5 +state = start(eit) +@test state.it == 1 +@test state.v == 1 +@test state.k == 1 + +@test Edge(2, 1) in edges(g) +@test Edge(1, 2) in edges(g) @test rem_edge!(g, 2, 1) == e1 @test rem_edge!(h, 1, 2) == e1 @test_throws ErrorException rem_edge!(h, 1, 2) @test g == copy(g) -@test !(g === copy(g)) \ No newline at end of file +@test !(g === copy(g)) diff --git a/test/runtests.jl b/test/runtests.jl index ed9ea3e5b..315b4185f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -43,10 +43,9 @@ a1 = Graph(adjmx1) a2 = DiGraph(adjmx2) tests = [ + "core", "randgraphs", "graphdigraph", - "persistence", - "core", "smallgraphs", "shortestpaths/astar", "shortestpaths/bellman-ford", @@ -65,7 +64,8 @@ tests = [ "centrality/degree", "centrality/katz", "centrality/pagerank", - "subgraphs" + "subgraphs", + "persistence" ]