Skip to content
This repository has been archived by the owner on Oct 8, 2021. It is now read-only.

Commit

Permalink
Sbromberger/fix dir inefficiency (#713)
Browse files Browse the repository at this point in the history
* remove generic bfs

modify tests for coverage

* comments

* fixes dir= inefficiency

* fixes dir= dispatch inefficiencies

* consistent treatment of dir as a kwarg

* more deprecations
  • Loading branch information
sbromberger authored Aug 14, 2017
1 parent f3dd544 commit 296381b
Show file tree
Hide file tree
Showing 14 changed files with 268 additions and 405 deletions.
5 changes: 4 additions & 1 deletion src/LightGraphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@ discover_vertex!, open_vertex!, close_vertex!,
examine_neighbor!, visited_vertices, traverse_graph!, traverse_graph_withlog,

# bfs
BreadthFirst, gdistances, gdistances!, bfs_tree, is_bipartite, bipartite_map,
BreadthFirst, gdistances, gdistances!, bfs_tree, bfs_parents,
has_path,

# bipartition
is_bipartite, bipartite_map,
# dfs
DepthFirst, is_cyclic, topological_sort_by_dfs, dfs_tree,

Expand Down Expand Up @@ -162,6 +164,7 @@ include("core.jl")
include("digraph/cycles/hadwick-james.jl")
include("traversals/graphvisit.jl")
include("traversals/bfs.jl")
include("traversals/bipartition.jl")
include("traversals/parallel_bfs.jl")
include("traversals/dfs.jl")
include("traversals/maxadjvisit.jl")
Expand Down
2 changes: 1 addition & 1 deletion src/centrality/katz.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function katz_centrality(g::AbstractGraph, α::Real=0.3)
nvg = nv(g)
v = ones(Float64, nvg)
spI = speye(Float64, nvg)
A = adjacency_matrix(g, :in, Bool)
A = adjacency_matrix(g, Bool; dir=:in)
v = (spI - α * A) \ v
v /= norm(v)
return v
Expand Down
2 changes: 1 addition & 1 deletion src/centrality/pagerank.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ is not reached within `n` iterations.
function pagerank end

@traitfn function pagerank(g::::IsDirected, α=0.85, n=100::Integer, ϵ=1.0e-6)
A = adjacency_matrix(g, :in, Float64)
A = adjacency_matrix(g, Float64; dir=:in)
S = vec(sum(A, 1))
S = 1 ./ S
S[find(S .== Inf)] = 0.0
Expand Down
82 changes: 44 additions & 38 deletions src/connectivity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,34 @@
Fill `label` with the `id` of the connected component in the undirected graph
`g` to which it belongs. Return a vector representing the component assigned
to each vertex. The component value is the smallest vertex ID in the component.
### Performance
This algorithm is linear in the number of edges of the graph.
"""
function connected_components! end
@traitfn function connected_components!(label::AbstractVector, g::::(!IsDirected))
# this version of connected components uses Breadth First Traversal
# with custom visitor type in order to improve performance.
# one BFS is performed for each component.
# This algorithm is linear in the number of edges of the graph
# each edge is touched once. memory performance is a single allocation.
# the return type is a vector of labels which can be used directly or
# passed to components(a)
T = eltype(g)
nvg = nv(g)

visitor = LightGraphs.ComponentVisitorVector(label, zero(T))
colormap = fill(0, nvg)
queue = Vector{T}()
sizehint!(queue, nvg)
for v in vertices(g)
if label[v] == zero(T)
visitor.labels[v] = v
visitor.seed = v
traverse_graph!(g, BreadthFirst(), v, visitor; vertexcolormap=colormap, queue=queue)
for u in vertices(g)
label[u] != zero(T) && continue
label[u] = u
Q = Vector{T}()
push!(Q, u)
while !isempty(Q)
src = shift!(Q)
for vertex in out_neighbors(g, src)
if label[vertex] == zero(T)
push!(Q, vertex)
label[vertex] = u
end
end
end
end
return label
end


"""
components_dict(labels)
Expand Down Expand Up @@ -274,22 +275,6 @@ function attracting_components end
return scc[attracting]
end

mutable struct NeighborhoodVisitor{T<:Integer} <: AbstractGraphVisitor
d::T
neigs::Vector{T}
end

NeighborhoodVisitor(d::T) where T<:Integer = NeighborhoodVisitor(d, Vector{T}())

function examine_neighbor!(visitor::NeighborhoodVisitor, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int)
-ucolor > visitor.d && return false # color is negative for not-closed vertices
if vcolor == 0
push!(visitor.neigs, v)
end
return true
end


"""
neighborhood(g, v, d)
Expand All @@ -300,14 +285,35 @@ from `v`.
- `dir=:out`: If `g` is directed, this argument specifies the edge direction
with respect to `v` of the edges to be considered. Possible values: `:in` or `:out`.
"""
function neighborhood(g::AbstractGraph, v::Integer, d::Integer; dir=:out)
neighborhood(g::AbstractGraph, v::Integer, d::Integer; dir=:out) = (dir == :out) ?
_neighborhood(g, v, d, out_neighbors) : _neighborhood(g, v, d, in_neighbors)

function _neighborhood(g::AbstractGraph, v::Integer, d::Integer, neighborfn::Function)
@assert d >= 0 "Distance has to be greater then zero."
T = eltype(g)
visitor = NeighborhoodVisitor(d)
push!(visitor.neigs, T(v))
traverse_graph!(g, BreadthFirst(), v, visitor,
vertexcolormap=Dict{T,Int}(), dir=dir)
return visitor.neigs
neighs = Vector{T}()
push!(neighs, v)

Q = Vector{T}()
seen = falses(nv(g))
dists = zeros(T, nv(g))
push!(Q, v)
dists[v] = d
while !isempty(Q)
src = shift!(Q)
seen[src] && continue
seen[src] = true
currdist = dists[src]
vertexneighbors = neighborfn(g, src)
for vertex in vertexneighbors
if !seen[vertex] && currdist > 0
push!(Q, vertex)
push!(neighs, vertex)
dists[vertex] = currdist - 1
end
end
end
return neighs
end

"""
Expand Down
6 changes: 6 additions & 0 deletions src/deprecations.jl
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
@deprecate in_edges in_neighbors
@deprecate out_edges out_neighbors
@deprecate adjacency_matrix(g::AbstractGraph, dir::Symbol, T::DataType) adjacency_matrix(g, T; dir=dir)
@deprecate adjacency_matrix(g::AbstractGraph, dir::Symbol) adjacency_matrix(g; dir=dir)
@deprecate laplacian_matrix(g::AbstractGraph, dir::Symbol, T::DataType) laplacian_matrix(g, T; dir=dir)
@deprecate laplacian_matrix(g::AbstractGraph, dir::Symbol) laplacian_matrix(g; dir=dir)
@deprecate laplacian_spectrum(g::AbstractGraph, dir::Symbol) laplacian_spectrum(g; dir=dir)

83 changes: 48 additions & 35 deletions src/linalg/spectral.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,45 @@ coo_sparse,
spectral_distance

"""
adjacency_matrix(g, dir=:out, T=Int)
adjacency_matrix(g[, T=Int; dir=:out])
Return a sparse adjacency matrix for a graph, indexed by `[u, v]`
vertices. Non-zero values indicate an edge between `u` and `v`. Users may
specify a direction (`:in`, `:out`, or `:both` are currently supported; `:out`
is default for both directed and undirected graphs) and a data type for the
matrix (defaults to `Int`).
override the default data type (`Int`) and specify an optional direction.
### Optional Arguments
`dir=:out`: `:in`, `:out`, or `:both` are currently supported.
### Implementation Notes
This function is optimized for speed and directly manipulates CSC sparse matrix fields.
"""
function adjacency_matrix(g::AbstractGraph, dir::Symbol=:out, T::DataType=Int)
n_v = nv(g)
nz = ne(g) * (is_directed(g) ? 1 : 2)
colpt = ones(Int, n_v + 1)

function adjacency_matrix(g::AbstractGraph, T::DataType=Int; dir::Symbol=:out)
nzmult = 1
# see below - we iterate over columns. That's why we take the
# "opposite" neighbor function. It's faster than taking the transpose
# at the end.
if dir == :out
neighborfn = in_neighbors
elseif dir == :in
neighborfn = out_neighbors
elseif dir == :both

if (dir == :out)
_adjacency_matrix(g, T, in_neighbors, 1)
elseif (dir == :in)
_adjacency_matrix(g, T, out_neighbors, 1)
elseif (dir == :both)
_adjacency_matrix(g, T, all_neighbors, 1)
if is_directed(g)
neighborfn = all_neighbors
nz *= 2
_adjacency_matrix(g, T, all_neighbors, 2)
else
neighborfn = out_neighbors
_adjacency_matrix(g, T, out_neighbors, 1)
end
else
error("Not implemented")
end
end

function _adjacency_matrix(g::AbstractGraph, T::DataType, neighborfn::Function, nzmult::Int=1)
n_v = nv(g)
nz = ne(g) * (is_directed(g) ? 1 : 2) * nzmult
colpt = ones(Int, n_v + 1)

rowval = sizehint!(Vector{Int}(), nz)
selfloops = Vector{Int}()
for j in 1:n_v # this is by column, not by row.
Expand All @@ -65,62 +71,69 @@ function adjacency_matrix(g::AbstractGraph, dir::Symbol=:out, T::DataType=Int)
return spmx
end

adjacency_matrix(g::AbstractGraph, T::DataType) = adjacency_matrix(g, :out, T)

"""
laplacian_matrix(g, dir=:unspec, T=Int)
laplacian_matrix(g[, T=Int; dir=:unspec])
Return a sparse [Laplacian matrix](https://en.wikipedia.org/wiki/Laplacian_matrix)
for a graph `g`, indexed by `[u, v]` vertices. For undirected graphs, `dir`
defaults to `:out`; for directed graphs, `dir` defaults to `:both`. `T`
defaults to `Int` for both graph types.
for a graph `g`, indexed by `[u, v]` vertices. `T` defaults to `Int` for both graph types.
### Optional Arguments
`dir=:unspec`: `:unspec`, `:both`, :in`, and `:out` are currently supported.
For undirected graphs, `dir` defaults to `:out`; for directed graphs,
`dir` defaults to `:both`.
"""
function laplacian_matrix(g::AbstractGraph, dir::Symbol=:unspec, T::DataType=Int)
function laplacian_matrix(g::AbstractGraph, T::DataType=Int; dir::Symbol=:unspec)
if dir == :unspec
dir = is_directed(g) ? :both : :out
end
A = adjacency_matrix(g, dir, T)
A = adjacency_matrix(g, T; dir=dir)
D = spdiagm(sum(A, 2)[:])
return D - A
end

@doc_str """
laplacian_spectrum(g, dir=:unspec, T=Int)
laplacian_spectrum(g[, T=Int; dir=:unspec])
Return the eigenvalues of the Laplacian matrix for a graph `g`, indexed
by vertex. Default values for `dir` and `T` are the same as those in
by vertex. Default values for `T` are the same as those in
[`laplacian_matrix`](@ref).
### Optional Arguments
`dir=:unspec`: Options for `dir` are the same as those in [`laplacian_matrix`](@ref).
### Performance
Converts the matrix to dense with ``nv^2`` memory usage.
### Implementation Notes
Use `eigs(laplacian_matrix(g); kwargs...)` to compute some of the
eigenvalues/eigenvectors.
"""
laplacian_spectrum(g::AbstractGraph, dir::Symbol=:unspec, T::DataType=Int) = eigvals(full(laplacian_matrix(g, dir, T)))
laplacian_spectrum(g::AbstractGraph, T::DataType=Int; dir::Symbol=:unspec) = eigvals(full(laplacian_matrix(g, T; dir=dir)))

@doc_str """
Return the eigenvalues of the adjacency matrix for a graph `g`, indexed
by vertex. Default values for `dir` and `T` are the same as those in
by vertex. Default values for `T` are the same as those in
[`adjacency_matrix`](@ref).
### Optional Arguments
`dir=:unspec`: Options for `dir` are the same as those in [`laplacian_matrix`](@ref).
### Performance
Converts the matrix to dense with ``nv^2`` memory usage.
### Implementation Notes
Use `eigs(adjacency_matrix(g); kwargs...)` to compute some of the
eigenvalues/eigenvectors.
"""
function adjacency_spectrum(g::AbstractGraph, dir::Symbol=:unspec, T::DataType=Int)
function adjacency_spectrum(g::AbstractGraph, T::DataType=Int; dir::Symbol=:unspec)
if dir == :unspec
dir = is_directed(g) ? :both : :out
end
return eigvals(full(adjacency_matrix(g, dir, T)))
return eigvals(full(adjacency_matrix(g, T; dir=dir)))
end

"""
incidence_matrix(g, T=Int)
incidence_matrix(g[, T=Int; oriented=false])
Return a sparse node-arc incidence matrix for a graph, indexed by
`[v, i]`, where `i` is in `1:ne(g)`, indexing an edge `e`. For
Expand All @@ -129,9 +142,9 @@ value of `1` indicates that `dst(e) == v`. Otherwise, the value is
`0`. For undirected graphs, both entries are `1` by default (this behavior
can be overridden by the `oriented` optional argument).
### Optional Arguments
- `oriented=false`: If true, for an undirected graph `g`, the matrix will
contain arbitrary non-zero values representing connectivity between `v` and `i`.
If `oriented` (default false) is true, for an undirected graph `g`, the
matrix will contain arbitrary non-zero values representing connectivity
between `v` and `i`.
"""
function incidence_matrix(g::AbstractGraph, T::DataType=Int; oriented=false)
isdir = is_directed(g)
Expand Down
Loading

0 comments on commit 296381b

Please sign in to comment.