From e01c358c4221dcd3735e6b5ac9f2e3536fb755a7 Mon Sep 17 00:00:00 2001 From: Seth Bromberger <github@bromberger.com> Date: Sun, 26 Mar 2017 16:32:47 -0700 Subject: [PATCH 1/2] docs, plus some various fixes (see blocking_flow). --- src/biconnectivity/articulation.jl | 39 +-- src/biconnectivity/biconnect.jl | 54 ++-- src/centrality/betweenness.jl | 57 ++--- src/centrality/closeness.jl | 12 +- src/centrality/degree.jl | 18 +- src/centrality/katz.jl | 10 +- src/centrality/pagerank.jl | 14 +- src/community/cliques.jl | 7 +- src/community/clustering.jl | 73 +++--- src/community/core-periphery.jl | 9 +- src/community/label_propagation.jl | 33 ++- src/community/modularity.jl | 6 +- src/connectivity.jl | 126 +++++---- src/core.jl | 129 ++++++++-- src/digraph-transitivity.jl | 52 ++-- src/distance.jl | 68 +++-- src/edit_distance.jl | 58 +++-- src/flow/boykov_kolmogorov.jl | 25 +- src/flow/dinic.jl | 39 ++- src/flow/edmonds_karp.jl | 104 ++++---- src/flow/ext_multiroute_flow.jl | 91 ++++--- src/flow/kishimoto.jl | 18 +- src/flow/maximum_flow.jl | 100 ++++---- src/flow/multiroute_flow.jl | 67 ++--- src/flow/push_relabel.jl | 83 ++---- src/generators/euclideangraphs.jl | 44 ++-- src/generators/randgraphs.jl | 253 +++++++++++++------ src/generators/smallgraphs.jl | 4 +- src/generators/staticgraphs.jl | 131 +++++++--- src/graphtypes/simplegraphs/SimpleGraphs.jl | 23 +- src/graphtypes/simplegraphs/simpledigraph.jl | 6 +- src/graphtypes/simplegraphs/simplegraph.jl | 38 ++- src/linalg/graphmatrices.jl | 93 +++++-- src/linalg/nonbacktracking.jl | 45 ++-- src/linalg/spectral.jl | 71 ++++-- src/persistence/common.jl | 19 +- src/persistence/gexf.jl | 13 +- src/persistence/gml.jl | 13 +- src/persistence/graph6.jl | 19 +- src/persistence/jld.jl | 2 + src/persistence/lg.jl | 20 +- src/persistence/net.jl | 43 ++-- src/shortestpaths/astar.jl | 22 +- src/shortestpaths/bellman-ford.jl | 36 ++- src/shortestpaths/dijkstra.jl | 25 +- src/shortestpaths/floyd-warshall.jl | 21 +- src/spanningtrees/kruskal.jl | 23 +- src/spanningtrees/prim.jl | 20 +- src/traversals/bfs.jl | 69 +++-- src/traversals/dfs.jl | 23 +- src/traversals/maxadjvisit.jl | 13 +- src/traversals/randomwalks.jl | 21 +- test/flow/dinic.jl | 8 +- 53 files changed, 1436 insertions(+), 974 deletions(-) diff --git a/src/biconnectivity/articulation.jl b/src/biconnectivity/articulation.jl index 9bd0ee7cb..9701b1e9f 100644 --- a/src/biconnectivity/articulation.jl +++ b/src/biconnectivity/articulation.jl @@ -1,19 +1,7 @@ """ -Computes the articulation points(https://en.wikipedia.org/wiki/Biconnected_component) -of a connected graph `g` and returns an array containing all cut vertices. -""" -function articulation(g::AbstractGraph) - state = Articulations(g) - for u in vertices(g) - if state.depth[u] == 0 - visit!(state, g, u, u) - end - end - return find(state.articulation_points) -end + Articulations{T} -""" -Articulations: a state type for the Depth first search that finds the articulation points in a graph. +A state type for the depth-first search that finds the articulation points in a graph. """ mutable struct Articulations{T<:Integer} low::Vector{T} @@ -29,8 +17,11 @@ function Articulations(g::AbstractGraph) end """ -Does a depth first search storing the depth (in `depth`) and low-points (in `low`) of each vertex. -Call this function repeatedly to complete the DFS see `articulation` for usage. + visit!(state, g, u, v) + +Perform a depth first search storing the depth (in `depth`) and low-points +(in `low`) of each vertex. +Call this function repeatedly to complete the DFS (see `articulation` for usage). """ function visit!(state::Articulations, g::AbstractGraph, u::Integer, v::Integer) children = 0 @@ -56,3 +47,19 @@ function visit!(state::Articulations, g::AbstractGraph, u::Integer, v::Integer) state.articulation_points[v] = true end end + +""" + articulation(g) + +Compute the [articulation points](https://en.wikipedia.org/wiki/Biconnected_component) +of a connected graph `g` and return an array containing all cut vertices. +""" +function articulation(g::AbstractGraph) + state = Articulations(g) + for u in vertices(g) + if state.depth[u] == 0 + visit!(state, g, u, u) + end + end + return find(state.articulation_points) +end diff --git a/src/biconnectivity/biconnect.jl b/src/biconnectivity/biconnect.jl index 26fe054a0..d7c1ec037 100644 --- a/src/biconnectivity/biconnect.jl +++ b/src/biconnectivity/biconnect.jl @@ -1,5 +1,7 @@ """ -Biconnections: A state type for Depth First Search that finds the biconnected components + Biconnections + +A state type for depth-first search that finds the biconnected components. """ mutable struct Biconnections low::Vector{Int} @@ -14,29 +16,11 @@ end return Biconnections(zeros(Int, n), zeros(Int, n), Vector{Edge}(), Vector{Vector{Edge}}(), 0) end -""" -Computes the biconnected components of an undirected graph `g` -and returns a Vector of vectors containing each biconnected component. -(https://en.wikipedia.org/wiki/Biconnected_component).It's a DFS based linear time algorithm. -""" -function biconnected_components end -@traitfn function biconnected_components(g::::(!IsDirected)) - state = Biconnections(g) - for u in vertices(g) - if state.depth[u] == 0 - visit!(g, state, u, u) - end - - if !isempty(state.stack) - push!(state.biconnected_comps, reverse(state.stack)) - empty!(state.stack) - end - end - return state.biconnected_comps -end """ -Does a DFS visit and stores the depth and low-points of each vertex + visit!(g, state, u, v) + +Perform a DFS visit storing the depth and low-points of each vertex. """ function visit!(g::AbstractGraph, state::Biconnections, u::Integer, v::Integer) children = 0 @@ -68,3 +52,29 @@ function visit!(g::AbstractGraph, state::Biconnections, u::Integer, v::Integer) end end end + +@doc_str """ + biconnected_components(g) + +Compute the [biconnected components](https://en.wikipedia.org/wiki/Biconnected_component) +of an undirected graph `g`and return a vector of vectors containing each +biconnected component. + +Performance: +Time complexity is ``\\mathcal{O}(|V|)``. +""" +function biconnected_components end +@traitfn function biconnected_components(g::::(!IsDirected)) + state = Biconnections(g) + for u in vertices(g) + if state.depth[u] == 0 + visit!(g, state, u, u) + end + + if !isempty(state.stack) + push!(state.biconnected_comps, reverse(state.stack)) + empty!(state.stack) + end + end + return state.biconnected_comps +end diff --git a/src/centrality/betweenness.jl b/src/centrality/betweenness.jl index 4fb998d21..5c56fd717 100644 --- a/src/centrality/betweenness.jl +++ b/src/centrality/betweenness.jl @@ -2,47 +2,30 @@ # TODO - weighted, separate unweighted, edge betweenness -doc""" -betweenness_centrality(g, k=0; normalize=true, endpoints=false) +@doc_str """ + betweenness_centrality(g[, nodes]) + betweenness_centrality(g, k) +Calculate the [betweenness centrality](https://en.wikipedia.org/wiki/Centrality#Betweenness_centrality) +of a graph `g` across all nodes, a specified subset of nodes `nodes`, or a random subset of `k` +nodes. Return a vector representing the centrality calculated for each node in `g`. -Calculates the [betweenness centrality](https://en.wikipedia.org/wiki/Centrality#Betweenness_centrality) of -the graph `g`, or, optionally, of a random subset of `k` vertices. Can -optionally include endpoints in the calculations. Normalization is enabled by -default. +### Optional arguments +* `normalize=true`: If true, normalize the betweenness values by the + total number of possible distinct paths between all pairsin the graphs. + For an undirected graph, this number is ``\\frac{(|V|-1)(|V|-2)}{2}`` + and for a directed graph, ``\\frac{(|V|-1)(|V|-2)}``. +* `endpoints=false`: If true, include endpoints in the shortest path count. -Betweenness centrality is defined as: - -$bc(v) = \frac{1}{\mathcal{N}} \sum_{s \neq t \neq v} -\frac{\sigma_{st}(v)}{\sigma_{st}}$. - -**Parameters** - -g: AbstractGraph -A Graph, directed or undirected. - -k: Integer, optional -Use `k` nodes sample to estimate the betweenness centrality. If none, -betweenness centrality is computed using the `n` nodes in the graph. - -normalize: bool, optional -If true, the betweenness values are normalized by the total number -of possible distinct paths between all pairs in the graphs. For an undirected graph, -this number if `((n-1)*(n-2))/2` and for a directed graph, `(n-1)*(n-2)` -where `n` is the number of nodes in the graph. - -endpoints: bool, optional -If true, endpoints are included in the shortest path count. -**Returns** - -betweenness: Array{Float64} -Betweenness centrality value per node id. - - -**References** +Betweenness centrality is defined as: +`` +bc(v) = \\frac{1}{\\mathcal{N}} \sum_{s \\neq t \\neq v} +\\frac{\\sigma_{st}(v)}{\\sigma_{st}} +``. -[1] Brandes 2001 & Brandes 2008 +### References +* Brandes 2001 & Brandes 2008 """ function betweenness_centrality( g::AbstractGraph, @@ -109,8 +92,6 @@ function _accumulate_basic!( end end - - function _accumulate_endpoints!( betweenness::Vector{Float64}, state::DijkstraState, diff --git a/src/centrality/closeness.jl b/src/centrality/closeness.jl index 238925bcf..b15702796 100644 --- a/src/centrality/closeness.jl +++ b/src/centrality/closeness.jl @@ -1,5 +1,13 @@ -"""Calculates the [closeness centrality](https://en.wikipedia.org/wiki/Centrality#Closeness_centrality) -of the graph `g`. +@doc_str """ + closeness_centrality(g) + +Calculate the [closeness centrality](https://en.wikipedia.org/wiki/Centrality#Closeness_centrality) +of the graph `g`. Return a vector representing the centrality calculated for each node in `g`. + +### Optional arguments +* `normalize=true`: If true, normalize the centrality value of each +node `n` by ``\\frac{|δ_n|}{|V|-1}, where ``δ_n`` is the set of nodes reachable +from node `n`. """ function closeness_centrality( g::AbstractGraph; diff --git a/src/centrality/degree.jl b/src/centrality/degree.jl index 812106e93..23c9401a4 100644 --- a/src/centrality/degree.jl +++ b/src/centrality/degree.jl @@ -15,13 +15,17 @@ function _degree_centrality(g::AbstractGraph, gtype::Integer; normalize=true) return c end -# TODO avoid repetition of this docstring -"""Calculates the [degree centrality](https://en.wikipedia.org/wiki/Centrality#Degree_centrality) -of the graph `g`, with optional (default) normalization.""" +""" + degree_centrality(g) + indegree_centrality(g) + outdegree_centrality(g) + +Calculate the [degree centrality](https://en.wikipedia.org/wiki/Centrality#Degree_centrality) +of graph `g`. Return a vector representing the centrality calculated for each node in `g`. + +### Optional arguments: +* `normalize=true`: If true, normalize each centrality measure by ``\frac{1}{|V|-1}``. +""" degree_centrality(g::AbstractGraph; all...) = _degree_centrality(g, 0; all...) -"""Calculates the [degree centrality](https://en.wikipedia.org/wiki/Centrality#Degree_centrality) -of the graph `g`, with optional (default) normalization.""" indegree_centrality(g::AbstractGraph; all...) = _degree_centrality(g, 1; all...) -"""Calculates the [degree centrality](https://en.wikipedia.org/wiki/Centrality#Degree_centrality) -of the graph `g`, with optional (default) normalization.""" outdegree_centrality(g::AbstractGraph; all...) = _degree_centrality(g, 2; all...) diff --git a/src/centrality/katz.jl b/src/centrality/katz.jl index adac43fab..3497fd1ce 100644 --- a/src/centrality/katz.jl +++ b/src/centrality/katz.jl @@ -21,10 +21,14 @@ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -"""Calculates the [Katz centrality](https://en.wikipedia.org/wiki/Katz_centrality) -of the graph `g`. """ -function katz_centrality(g::AbstractGraph, α::Real = 0.3) + katz_centrality(g, α=0.3) + +Calculate the [Katz centrality](https://en.wikipedia.org/wiki/Katz_centrality) +of the graph `g` optionally parameterized by `α`. Return a vector representing +the centrality calculated for each node in `g`. +""" +function katz_centrality(g::AbstractGraph, α::Real=0.3) nvg = nv(g) v = ones(Float64, nvg) spI = speye(Float64, nvg) diff --git a/src/centrality/pagerank.jl b/src/centrality/pagerank.jl index 9abed102f..41c588633 100644 --- a/src/centrality/pagerank.jl +++ b/src/centrality/pagerank.jl @@ -1,14 +1,18 @@ # Parts of this code were taken / derived from NetworkX. See LICENSE for # licensing details. -"""Calculates the [PageRank](https://en.wikipedia.org/wiki/PageRank) of the graph -`g`. Can optionally specify a different damping factor (`α`), number of -iterations (`n`), and convergence threshold (`ϵ`). If convergence is not -reached within `n` iterations, an error will be returned. +""" + pagerank(g, α=0.85, n=100, ϵ=1.0e-6) + +Calculate the [PageRank](https://en.wikipedia.org/wiki/PageRank) of the +directed graph `g` parameterized by damping factor `α`, number of +iterations `n`, and convergence threshold `ϵ` (default 1.0e-6). Return a +vector representing the centrality calculated for each node in `g`, or an +error if convergence is not reached within `n` iterations. """ function pagerank end -@traitfn function pagerank(g::::IsDirected, α=0.85, n=100, ϵ = 1.0e-6) +@traitfn function pagerank(g::::IsDirected, α=0.85, n=100, ϵ=1.0e-6) A = adjacency_matrix(g,:in,Float64) S = vec(sum(A,1)) S = 1./S diff --git a/src/community/cliques.jl b/src/community/cliques.jl index d0def23af..c995df4bc 100644 --- a/src/community/cliques.jl +++ b/src/community/cliques.jl @@ -6,9 +6,12 @@ ################################################################## """ -Finds all maximal cliques of an undirected graph. + maximal_cliques(g) + +Return a vector of vectors representing the node indices in each of the maximal +cliques found in the undirected graph `g`. -``` +```jldoctest julia> using LightGraphs julia> g = Graph(3) julia> add_edge!(g, 1, 2) diff --git a/src/community/clustering.jl b/src/community/clustering.jl index 83b369d90..ba71ab10c 100644 --- a/src/community/clustering.jl +++ b/src/community/clustering.jl @@ -1,20 +1,30 @@ """ -local_clustering_coefficient(g, v) + local_clustering_coefficient(g, v) + local_clustering_coefficient(g, vs) -Computes the [local clustering coefficient](https://en.wikipedia.org/wiki/Clustering_coefficient) for node `v`. +Return the [local clustering coefficient](https://en.wikipedia.org/wiki/Clustering_coefficient) +for node `v` in graph `g`. If a list of nodes `vs` is specified, return a vector +of coefficients for each node in the list. """ function local_clustering_coefficient(g::AbstractGraph, v::Integer) ntriang, alltriang = local_clustering(g, v) - return alltriang == 0 ? 0. : ntriang / alltriang + return alltriang == 0 ? 0. : ntriang * 1.0 / alltriang end +local_clustering_coefficient(g::AbstractGraph, vs = vertices(g)) = + [local_clustering_coefficient(g, v) for v in vs] + """ -local_clustering(g, v) + local_clustering(g, v) + local_clustering(g, vs) + +Return a tuple `(a,b)`, where `a` is the number of triangles in the neighborhood +of `v` and `b` is the maximum number of possible triangles. If a list of nodes +`vs` is specified, return two vectors representing the number of triangles and +the maximum number of possible triangles, respectively, for each node in the list. -Returns a tuple `(a,b)`, where `a` is the number of triangles in the neighborhood of -`v` and `b` is the maximum number of possible triangles. -It is related to the local clustering coefficient by `r=a/b`. +This function is related to the local clustering coefficient `r` by ``r=\frac{a}{b}``. """ function local_clustering(g::AbstractGraph, v::Integer) k = degree(g, v) @@ -29,51 +39,34 @@ function local_clustering(g::AbstractGraph, v::Integer) end return is_directed(g) ? (c , k*(k-1)) : (div(c,2) , div(k*(k-1),2)) end - -""" -triangles(g, v) - -Returns the number of triangles in the neighborhood for node `v`. -""" -triangles(g::AbstractGraph, v::Integer) = local_clustering(g, v)[1] - - -""" -local_clustering_coefficient(g, vlist = vertices(g)) - -Returns a vector containing the [local clustering coefficients](https://en.wikipedia.org/wiki/Clustering_coefficient) for vertices `vlist`. -""" -local_clustering_coefficient(g::AbstractGraph, vlist = vertices(g)) = Float64[local_clustering_coefficient(g, v) for v in vlist] - -""" -local_clustering(g, vlist = vertices(g)) - -Returns two vectors, respectively containing the first and second result of `local_clustering_coefficients(g, v)` -for each `v` in `vlist`. -""" -function local_clustering(g::AbstractGraph, vlist = vertices(g)) - ntriang = zeros(Int, length(vlist)) - nalltriang = zeros(Int, length(vlist)) +function local_clustering(g::AbstractGraph, vs = vertices(g)) + ntriang = zeros(Int, length(vs)) + nalltriang = zeros(Int, length(vs)) i = 0 - for v in vlist - i+=1 + for (i, v) in enumerate(vs) ntriang[i], nalltriang[i] = local_clustering(g, v) end return ntriang, nalltriang end + """ -triangles(g, vlist = vertices(g)) + triangles(g[, v]) + triangles(g, vs) -Returns a vector containing the number of triangles for vertices `vlist`. +Return the number of triangles in the neighborhood of node `v` in graph `g`. +If a list of nodes `vs` is specified, return a vector of number of triangles +for each node in the list. If no vertices are specified, return the number +of triangles for each node in the graph. """ -triangles(g::AbstractGraph, vlist = vertices(g)) = local_clustering(g, vlist)[1] +triangles(g::AbstractGraph, v::Integer) = local_clustering(g, v)[1] +triangles(g::AbstractGraph, vs = vertices(g)) = local_clustering(g, vs)[1] """ -global_clustering_coefficient(g) - -Computes the [global clustering coefficient](https://en.wikipedia.org/wiki/Clustering_coefficient). + global_clustering_coefficient(g) +Return the [global clustering coefficient](https://en.wikipedia.org/wiki/Clustering_coefficient) +of graph `g`. """ function global_clustering_coefficient(g::AbstractGraph) c = 0 diff --git a/src/community/core-periphery.jl b/src/community/core-periphery.jl index 93831af77..dd77849ea 100644 --- a/src/community/core-periphery.jl +++ b/src/community/core-periphery.jl @@ -1,8 +1,11 @@ """ -core_periphery_deg(g) + core_periphery_deg(g) -A simple degree-based core-periphery detection algorithm (see [Lip](http://arxiv.org/abs/1102.5511)). -Returns the vertex assignments (1 for core and 2 for periphery). +Compute the degree-based core-periphery for graph `g`. Return the vertex +assignments (1 for core and 2 for periphery) for each node in `g`. + +References: + [Lip](http://arxiv.org/abs/1102.5511)) """ function core_periphery_deg end @traitfn function core_periphery_deg(g::::(!IsDirected)) diff --git a/src/community/label_propagation.jl b/src/community/label_propagation.jl index d97e2ed61..4d491d391 100644 --- a/src/community/label_propagation.jl +++ b/src/community/label_propagation.jl @@ -1,10 +1,15 @@ """ -Community detection using the label propagation algorithm (see [Raghavan et al.](http://arxiv.org/abs/0709.2938)). -`g`: input Graph -`maxiter`: maximum number of iterations -return : vertex assignments and the convergence history + label_propagation(g, maxiter=1000) + +Community detection using the label propagation algorithm. Return two vectors: +the first is the label number assigned to each node, and the second is the +convergence history for each node. Will return after `maxiter` iterations +if convergence has not completed. + +References: + [Raghavan et al.](http://arxiv.org/abs/0709.2938) """ -function label_propagation(g::AbstractGraph; maxiter=1000) +function label_propagation(g::AbstractGraph, maxiter=1000) T = eltype(g) n = nv(g) label = collect(one(T):n) @@ -41,14 +46,22 @@ function label_propagation(g::AbstractGraph; maxiter=1000) label, convergence_hist end -"""Type to record neighbor labels and their counts.""" +""" + NeighComm{T} + +Type to record neighbor labels and their counts. +""" mutable struct NeighComm{T<:Integer} neigh_pos::Vector{T} neigh_cnt::Vector{Int} neigh_last::T end -"""Fast shuffle Array `a` in UnitRange `r` inplace.""" +""" + range_shuffle!(r, a) + +Fast shuffle Array `a` in UnitRange `r`. +""" function range_shuffle!(r::UnitRange, a::AbstractVector) (r.start > 0 && r.stop <= length(a)) || error("out of bounds") @inbounds for i=length(r):-1:2 @@ -59,7 +72,11 @@ function range_shuffle!(r::UnitRange, a::AbstractVector) end end -"""Return the most frequency label.""" +""" + vote!(g, m, c, u) + +Return the label with greatest frequency. +""" function vote!(g::AbstractGraph, m::Vector, c::NeighComm, u::Integer) @inbounds for i=1:c.neigh_last-1 c.neigh_cnt[c.neigh_pos[i]] = -1 diff --git a/src/community/modularity.jl b/src/community/modularity.jl index 05ac2f0d5..b14d13046 100644 --- a/src/community/modularity.jl +++ b/src/community/modularity.jl @@ -1,8 +1,8 @@ """ -modularity(g, c) + modularity(g, c) -Computes Newman's modularity `Q` -for graph `g` given the partitioning `c`. +Return a value representing Newman's modularity `Q` for the undirected graph +`g` given the partitioning vector `c`. """ function modularity end @traitfn function modularity(g::::(!IsDirected), c::Vector) diff --git a/src/connectivity.jl b/src/connectivity.jl index 14d86a409..85d592892 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -1,16 +1,11 @@ # Parts of this code were taken / derived from Graphs.jl. See LICENSE for # licensing details. """ - connected_components!(label::Vector{Int}, g::AbstractGraph) - -Fills `label` with the `id` of the connected component to which it belongs. + connected_components!(label, g) -Arguments: - label: a place to store the output - g: the graph -Output: - c = labels[i] => vertex i belongs to component c. - c is the smallest vertex id in the component. +Fill `label` with the `id` of the connected component in `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. """ function connected_components!(label::Vector{T}, g::AbstractGraph) where T<:Integer # this version of connected components uses Breadth First Traversal @@ -35,12 +30,12 @@ function connected_components!(label::Vector{T}, g::AbstractGraph) where T<:Inte return label end -"""components_dict(labels) converts an array of labels to a Dict{Integer,Vector{Int}} of components +""" + components_dict(labels) -Arguments: - c = labels[i] => vertex i belongs to component c. -Output: - vs = d[c] => vertices in vs belong to component c. +Convert an array of labels to a map of component id to vertices, and return +a `Dict{Integer,Vector{Int}}` with each key corresponding to a given component id +and each value containing the vertices associated with that component. """ function components_dict(labels::Vector{T}) where T<:Integer d = Dict{T,Vector{T}}() @@ -53,15 +48,10 @@ function components_dict(labels::Vector{T}) where T<:Integer end """ - components(labels::Vector{Int}) - -Converts an array of labels to a Vector{Vector{Int}} of components + components(labels) -Arguments: - c = labels[i] => vertex i belongs to component c. -Output: - vs = c[i] => vertices in vs belong to component i. - a = d[i] => if labels[v]==i then v in c[a] end +Given a vector of component labels, return a vector of vectors representing the vertices associated +with a given component id. """ function components(labels::Vector{T}) where T<:Integer d = Dict{T, T}() @@ -82,9 +72,9 @@ end """ connected_components(g) -Returns the [connected components](https://en.wikipedia.org/wiki/Connectivity_(graph_theory)) -of `g` as a vector of components, each represented by a -vector of vertices belonging to the component. +Return the [connected components](https://en.wikipedia.org/wiki/Connectivity_(graph_theory)) +of `g` as a vector of components, with each element a vector of vertices +belonging to the component. """ function connected_components(g::AbstractGraph) T = eltype(g) @@ -97,18 +87,26 @@ end """ is_connected(g) -Returns `true` if `g` is connected. -For DiGraphs, this is equivalent to a test of weak connectivity. +Return `true` if `g` is connected. For directed graphs, this is equivalent to +a test of weak connectivity. """ function is_connected end @traitfn is_connected(g::::(!IsDirected)) = ne(g)+1 >= nv(g) && length(connected_components(g)) == 1 @traitfn is_connected(g::::IsDirected) = ne(g)+1 >= nv(g) && is_weakly_connected(g) -"""Returns connected components of the undirected graph of `g`.""" +""" + weakly_connected_components(g) +Return the weakly connected components of the directed graph `g`. This +is equivalent to the connected components of the undirected equivalent of `g`. +""" function weakly_connected_components end @traitfn weakly_connected_components(g::::IsDirected) = connected_components(Graph(g)) -"""Returns `true` if the undirected graph of `g` is connected.""" +""" + is_weakly_connected(g) + +Return `true` if the directed graph `g` is connected. +""" function is_weakly_connected end @traitfn is_weakly_connected(g::::IsDirected) = length(weakly_connected_components(g)) == 1 @@ -156,7 +154,11 @@ function close_vertex!(vis::TarjanVisitor, v) return true end -"""Computes the (strongly) connected components of a directed graph.""" +""" + strongly_connected_components(g) + +Computes the strongly connected components of a directed graph `g`. +""" function strongly_connected_components end @traitfn function strongly_connected_components(g::::IsDirected) T = eltype(g) @@ -176,11 +178,20 @@ function strongly_connected_components end return components end -"""Returns `true` if `g` is (strongly) connected.""" +""" + is_strongly_connected(g) + +Return `true` if directed graph `g` is strongly connected. +""" function is_strongly_connected end @traitfn is_strongly_connected(g::::IsDirected) = length(strongly_connected_components(g)) == 1 -"""Computes the (common) period for all nodes in a strongly connected graph.""" +""" + period(g) + +Return the (common) period for all nodes in a strongly connected directed graph. +Will throw an error if the graph is not strongly connected. +""" function period end @traitfn function period(g::::IsDirected) T = eltype(g) @@ -204,7 +215,11 @@ function period end return divisor end -"""Computes the condensation graph of the strongly connected components.""" +""" + condensation(g, scc) +Return the condensation graph of the strongly connected components `scc` +in graph `g`. +""" function condensation end @traitfn function condensation{T<:Integer}(g::::IsDirected, scc::Vector{Vector{T}}) h = DiGraph{T}(length(scc)) @@ -224,17 +239,28 @@ function condensation end return h end -"""Returns the condensation graph associated with `g`. The condensation `h` of -a graph `g` is the directed graph where every node in `h` represents a strongly -connected component in `g`, and the presence of an edge between between nodes -in `h` indicates that there is at least one edge between the associated -strongly connected components in `g`. The node numbering in `h` corresponds to -the ordering of the components output from `strongly_connected_components`.""" +""" + attracting_components(g) + +Return the condensation graph associated with `g`. + +The condensation `h` of a graph `g` is the directed graph where every node +in `h` represents a strongly connected component in `g`, and the presence +of an edge between between nodes in `h` indicates that there is at least one +edge between the associated strongly connected components in `g`. The node +numbering in `h` corresponds to the ordering of the components output from +`strongly_connected_components`. +""" condensation(g) = condensation(g,strongly_connected_components(g)) -"""Returns a vector of vectors of integers representing lists of attracting -components in `g`. The attracting components are a subset of the strongly -connected components in which the components do not have any leaving edges.""" +""" + attracting_components(g) +Return a vector of vectors of integers representing lists of attracting +components in `g`. + +The attracting components are a subset of the strongly +connected components in which the components do not have any leaving edges. +""" function attracting_components end @traitfn function attracting_components(g::::IsDirected) T = eltype(g) @@ -268,11 +294,14 @@ end """ - neighborhood(g, v::Integer, d::Integer; dir=:out) + neighborhood(g, v, d) + +Return a vector of the vertices in `g` at a geodesic distance less or equal to `d` +from `v`. -Returns a vector of the vertices in `g` at distance less or equal to `d` -from `v`. If `g` is a `DiGraph` the `dir` optional argument specifies the edge direction -the edge direction with respect to `v` (i.e. `:in` or `:out`) to be considered. +### Optional arguments +- `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) @assert d >= 0 "Distance has to be greater then zero." @@ -285,12 +314,13 @@ function neighborhood(g::AbstractGraph, v::Integer, d::Integer; dir=:out) end """ - isgraphical(degs::Vector{Int}) + isgraphical(degs) Check whether the degree sequence `degs` is graphical, according to [Erdös-Gallai condition](http://mathworld.wolfram.com/GraphicSequence.html). -Time complexity: O(length(degs)^2) +### Performance + Time complexity: ``\\mathcal{O}(|degs|^2)`` """ function isgraphical(degs::Vector{Int}) iseven(sum(degs)) || return false diff --git a/src/core.jl b/src/core.jl index c901e7a35..d12dc4595 100644 --- a/src/core.jl +++ b/src/core.jl @@ -3,26 +3,42 @@ abstract type AbstractPathState end # returns true if insert succeeded, false if it was a duplicate _insert_and_dedup!{T<:Integer}(v::Vector{T}, x::T) = isempty(splice!(v, searchsorted(v,x), x)) -"""Returns true if the edge is ordered (source vertex <= dest vertex)""" +""" + is_ordered(e) +Return true if the source vertex of edge `e` is less than or equal to +the destination vertex. +""" is_ordered(e::AbstractEdge) = src(e) <= dst(e) """ + add_vertices!(g, n) Add `n` new vertices to the graph `g`. -Returns true if all vertices -were added successfully, false otherwise. +Return `true` if all vertices were added successfully, `false` otherwise. """ add_vertices!(g::AbstractGraph, n::Integer) = all([add_vertex!(g) for i=1:n]) -"""Return the number of edges which end at vertex `v`.""" +""" + indegree(g[, v]) + +Return a vector corresponding to the number of edges which end at each vertex in +graph `g`. If `v` is specified, only return degrees for vertices in `v`. +""" indegree(g::AbstractGraph, v::Integer) = length(in_neighbors(g, v)) indegree(g::AbstractGraph, v::AbstractVector = vertices(g)) = [indegree(g,x) for x in v] -"""Return the number of edges which start at vertex `v`.""" +""" + outdegree(g[, v]) + +Return a vector corresponding to the number of edges which start at each vertex in +graph `g`. If `v` is specified, only return degrees for vertices in `v`. +""" outdegree(g::AbstractGraph, v::Integer) = length(out_neighbors(g, v)) outdegree(g::AbstractGraph, v::AbstractVector = vertices(g)) = [outdegree(g,x) for x in v] """ -Return the number of edges from the vertex `v`. + degree(g[, v]) +Return a vector corresponding to the number of edges which start or end at each +vertex in graph `g`. If `v` is specified, only return degrees for vertices in `v`. For directed graphs, this value equals the incoming plus outgoing edges. For undirected graphs, it equals the connected edges. """ @@ -32,20 +48,51 @@ function degree end degree(g::AbstractGraph, v::AbstractVector = vertices(g)) = [degree(g, x) for x in v] -"Return the maximum `outdegree` of vertices in `g`." +""" + Δout(g) + +Return the maximum `outdegree` of vertices in `g`. +""" Δout(g) = noallocextreme(outdegree,(>), typemin(Int), g) -"Return the minimum `outdegree` of vertices in `g`." +""" + δout(g) + +Return the minimum `outdegree` of vertices in `g`. +""" δout(g) = noallocextreme(outdegree,(<), typemax(Int), g) -"Return the maximum `indegree` of vertices in `g`." -Δin(g) = noallocextreme(indegree,(>), typemin(Int), g) -"Return the minimum `indegree` of vertices in `g`." -δin(g) = noallocextreme(indegree,(<), typemax(Int), g) -"Return the maximum `degree` of vertices in `g`." -Δ(g) = noallocextreme(degree,(>), typemin(Int), g) -"Return the minimum `degree` of vertices in `g`." -δ(g) = noallocextreme(degree,(<), typemax(Int), g) - -"Computes the extreme value of `[f(g,i) for i=i:nv(g)]` without gathering them all" + +""" + Δin(g) + +Return the maximum `indegree` of vertices in `g`. +""" +Δin(g) = noallocextreme(indegree,(>), typemin(Int), g) + +""" + δin(g) + +Return the minimum `indegree` of vertices in `g`. +""" +δin(g) = noallocextreme(indegree,(<), typemax(Int), g) + +""" + Δ(g) + +Return the maximum `degree` of vertices in `g`. +""" +Δ(g) = noallocextreme(degree,(>), typemin(Int), g) + +""" + δ(g) +Return the minimum `degree` of vertices in `g`. +""" +δ(g) = noallocextreme(degree,(<), typemax(Int), g) + + +""" + noallocextreme(f, comparison, initial, g) +Compute the extreme value of `[f(g,i) for i=i:nv(g)]` without gathering them all +""" function noallocextreme(f, comparison, initial, g) value = initial for i in vertices(g) @@ -58,24 +105,32 @@ function noallocextreme(f, comparison, initial, g) end """ -degree_histogram(g) + degree_histogram(g) -Returns a `StatsBase.Histogram` of the degrees of vertices in `g`. +Return a `StatsBase.Histogram` of the degrees of vertices in `g`. """ degree_histogram(g::AbstractGraph) = fit(Histogram, degree(g)) """ + neighbors(g, v) + Return a list of all neighbors reachable from vertex `v` in `g`. For DiGraphs, the default is equivalent to `out_neighbors(g, v)`; use `all_neighbors` to list inbound and outbound neighbors. -NOTE: returns a reference, not a copy. Do not modify result. + +### Implementation Notes +Returns a reference, not a copy. Do not modify result. """ neighbors(g::AbstractGraph, v::Integer) = out_neighbors(g, v) """ + all_neighbors(g, v) Return a list of all inbound and outbound neighbors of `v` in `g`. For undirected graphs, this is equivalent to `out_neighbors` and `in_neighbors`. + +### Implementation Notes +Returns a reference, not a copy. Do not modify result. """ function all_neighbors end @traitfn all_neighbors(g::::IsDirected, v::Integer) = @@ -84,21 +139,37 @@ function all_neighbors end neighbors(g, v) -"Returns the neighbors common to vertices `u` and `v` in `g`." +""" + common_neighbors(g, u, v) + +Return the neighbors common to vertices `u` and `v` in `g`. + +### Implementation Notes +Returns a reference, not a copy. Do not modify result. +""" common_neighbors(g::AbstractGraph, u::Integer, v::Integer) = intersect(neighbors(g, u), neighbors(g, v)) -"Returns true if `g` has any self loops." +""" + has_self_loops(g) + +Return true if `g` has any self loops. +""" has_self_loops(g::AbstractGraph) = nv(g) == 0? false : any(v->has_edge(g, v, v), vertices(g)) -"Returns the number of self loops in `g`." -num_self_loops(g::AbstractGraph) = nv(g) == 0 ? 0 : sum(v->has_edge(g, v, v), vertices(g)) +""" + num_self_loops(g) +Return the number of self loops in `g`. """ +num_self_loops(g::AbstractGraph) = nv(g) == 0 ? 0 : sum(v->has_edge(g, v, v), vertices(g)) + +@doc_str """ + density(g) Return the density of `g`. Density is defined as the ratio of the number of actual edges to the -number of possible edges ( |v| |v-1| for directed graphs and -(|v| |v-1|) / 2 for undirected graphs). +number of possible edges (``|V|×(|V|-1)`` for directed graphs and +``\\frac{|V|×(|V|-1)}{2}`` for undirected graphs). """ function density end @traitfn density(g::::IsDirected) = @@ -108,7 +179,9 @@ ne(g) / (nv(g) * (nv(g)-1)) """ -Returns a copy of a graph with the smallest practical type that + squash(g) + +Return a copy of a graph with the smallest practical type that can accommodate all vertices. """ function squash(g::AbstractGraph) diff --git a/src/digraph-transitivity.jl b/src/digraph-transitivity.jl index daf48d62c..96686dec3 100644 --- a/src/digraph-transitivity.jl +++ b/src/digraph-transitivity.jl @@ -1,51 +1,43 @@ -""" -```transitiveclosure!(dg::DiGraph, selflooped = false)``` +@doc_str """ + transitiveclosure!(g, selflooped=false) Compute the transitive closure of a directed graph, using the Floyd-Warshall -algorithm. - -Version of the function that modifies the original graph. +algorithm. If `selflooped` is true, add self loops to the graph. -Note: This is an O(V^3) algorithm. +This version of the function modifies the original graph. -# Arguments -* `dg`: the directed graph on which the transitive closure is computed. -* `selflooped`: whether self loop should be added to the directed graph, -default to `false`. +### Performance +Time complexity is \\mathcal{O}(|V|^3). """ -function transitiveclosure!(dg::DiGraph, selflooped = false) - for k in vertices(dg) - for i in vertices(dg) +function transitiveclosure! end +@traitfn function transitiveclosure!(g::::IsDirected, selflooped=false) + for k in vertices(g) + for i in vertices(g) i == k && continue - for j in vertices(dg) + for j in vertices(g) j == k && continue - if (has_edge(dg, i, k) && has_edge(dg, k, j)) + if (has_edge(g, i, k) && has_edge(g, k, j)) if ( i != j || selflooped ) - add_edge!(dg, i, j) + add_edge!(g, i, j) end end end end end - return dg + return g end """ -```transitiveclosure(dg::DiGraph, selflooped = false)``` + transitiveclosure(g, selflooped=false) Compute the transitive closure of a directed graph, using the Floyd-Warshall -algorithm. - -Version of the function that does not modify the original graph. - -Note: This is an O(V^3) algorithm. +algorithm. Return a graph represetning the transitive closure. If `selflooped` +is true, add self loops to the graph. -# Arguments -* `dg`: the directed graph on which the transitive closure is computed. -* `selflooped`: whether self loop should be added to the directed graph, -default to `false`. +### Performance +Time complexity is \\mathcal{O}(|V|^3). """ -function transitiveclosure(dg::DiGraph, selflooped = false) - copydg = copy(dg) - return transitiveclosure!(copydg, selflooped) +function transitiveclosure(g::DiGraph, selflooped = false) + copyg = copy(g) + return transitiveclosure!(copyg, selflooped) end diff --git a/src/distance.jl b/src/distance.jl index e3bed74a4..95c12ea46 100644 --- a/src/distance.jl +++ b/src/distance.jl @@ -12,20 +12,25 @@ transpose(d::DefaultDistance) = d ctranspose(d::DefaultDistance) = d """ -Calculates the eccentricity[ies] of a vertex `v`, vertex vector `vs`, or the -entire graph. An optional matrix of edge distances may be supplied. + eccentricity(g[, v][, distmx]) + +Return the eccentricity[ies] of a vertex / vertex list `v` or the +entire graph. An optional matrix of edge distances may be supplied; if missing, +edge distances default to `1`. The eccentricity of a vertex is the maximum shortest-path distance between it and all other vertices in the graph. -Because this function must calculate shortest paths for all vertices supplied -in the argument list, it may take a long time. - The output is either a single float (when a single vertex is provided) or a vector of floats corresponding to the vertex vector. If no vertex vector is provided, the vector returned corresponds to each vertex in the graph. -Note: the eccentricity vector returned by `eccentricity()` may be used as input +### Performance +Because this function must calculate shortest paths for all vertices supplied +in the argument list, it may take a long time. + +### Implementation Notes +The eccentricity vector returned by `eccentricity()` may be used as input for the rest of the distance measures below. If an eccentricity vector is provided, it will be used. Otherwise, an eccentricity vector will be calculated for each call to the function. It may therefore be more efficient to calculate, @@ -51,33 +56,56 @@ eccentricity( eccentricity(g::AbstractGraph, distmx::AbstractMatrix) = eccentricity(g, vertices(g), distmx) -"""Returns the maximum eccentricity of the graph.""" -diameter(all_e::Vector) = maximum(all_e) +""" + diameter(g, distmx=DefaultDistance()) + diameter(eccentricities) + +Given a graph and optional distance matrix, or a vector of precomputed +eccentricities, return the maximum eccentricity of the graph. +""" +diameter(eccentricities::Vector) = maximum(eccentricities) diameter(g::AbstractGraph, distmx::AbstractMatrix = DefaultDistance())= maximum(eccentricity(g, distmx)) -"""Returns the set of all vertices whose eccentricity is equal to the graph's -diameter (that is, the set of vertices with the largest eccentricity). """ -function periphery(all_e::Vector) - diam = maximum(all_e) - return filter((x)->all_e[x] == diam, 1:length(all_e)) + periphery(g, distmx=DefaultDistance()) + periphery(eccentricities) + +Given a graph and optional distance matrix, or a vector of precomputed +eccentricities, return the set of all vertices whose eccentricity is +equal to the graph's diameter (that is, the set of vertices with the +largest eccentricity). +""" +function periphery(eccentricities::Vector) + diam = maximum(eccentricities) + return filter((x)->eccentricities[x] == diam, 1:length(eccentricities)) end periphery(g::AbstractGraph, distmx::AbstractMatrix = DefaultDistance()) = periphery(eccentricity(g, distmx)) -"""Returns the minimum eccentricity of the graph.""" -radius(all_e::Vector) = minimum(all_e) +""" + radius(g, distmx=DefaultDistance()) + radius(eccentricities) + +Given a graph and optional distance matrix, or a vector of precomputed +eccentricities, return the minimum eccentricity of the graph. +""" +radius(eccentricities::Vector) = minimum(eccentricities) radius(g::AbstractGraph, distmx::AbstractMatrix = DefaultDistance()) = minimum(eccentricity(g, distmx)) -"""Returns the set of all vertices whose eccentricity is equal to the graph's -radius (that is, the set of vertices with the smallest eccentricity). """ -function center(all_e::Vector) - rad = radius(all_e) - return filter((x)->all_e[x] == rad, 1:length(all_e)) + center(g, distmx=DefaultDistance()) + center(eccentricities) + +Given a graph and optional distance matrix, or a vector of precomputed +eccentricities, return the set of all vertices whose eccentricity is equal +to the graph's radius (that is, the set of vertices with the smallest eccentricity). +""" +function center(eccentricities::Vector) + rad = radius(eccentricities) + return filter((x)->eccentricities[x] == rad, 1:length(eccentricities)) end center(g::AbstractGraph, distmx::AbstractMatrix = DefaultDistance()) = diff --git a/src/edit_distance.jl b/src/edit_distance.jl index 102abdffd..774cb1864 100644 --- a/src/edit_distance.jl +++ b/src/edit_distance.jl @@ -1,14 +1,20 @@ -""" -Computes the edit distance between graphs G₁ and G₂. +@doc_str """ +edit_distance(G₁::AbstractGraph, G₂::AbstractGraph) + +Compute the edit distance between graphs `G₁` and `G₂`. Return the minimum +edit cost and edit path to transform graph `G₁` into graph `G₂``. +An edit path consists of a sequence of pairs of vertices ``(u,v) ∈ [0,|G₁|] × [0,|G₂|]`` +representing vertex operations: + +- ``(0,v)``: insertion of vertex ``v ∈ G₂`` +- ``(u,0)``: deletion of vertex ``u ∈ G₁`` +- ``(u>0,v>0)``: substitution of vertex ``u ∈ G₁`` by vertex ``v ∈ G₂`` -Returns the minimum edit cost and edit path to transform graph -G₁ into graph G₂. An edit path consists of a sequence of pairs -of vertices (u,v) ∈ [0,|G₁|] × [0,|G₂|] representing vertex -operations: -- (0,v): insertion of vertex v ∈ G₂ -- (u,0): deletion of vertex u ∈ G₁ -- (u>0,v>0): substitution of vertex u ∈ G₁ by vertex v ∈ G₂ +### Optional arguments +- `insert_cost::Function=v->1.0` +- `delete_cost::Function=u->1.0` +- `subst_cost::Function=(u,v)->0.5` By default, the algorithm uses constant operation costs. The user can provide classical Minkowski costs computed from vertex @@ -19,23 +25,21 @@ search, for example: edit_distance(G₁, G₂, subst_cost=MinkowskiCost(μ₁, μ₂)) ``` -A custom heuristic can be provided to the A* search in case the -default heuristic is not satisfactory. +- `heuristic::Function=DefaultEditHeuristic`: a custom heuristic provided to the A* +search in case the default heuristic is not satisfactory. -Performance tips: ------------------ -- Given two graphs |G₁| < |G₂|, `edit_distance(G₁, G₂)` is faster to +### Performance +- Given two graphs ``|G₁| < |G₂|``, `edit_distance(G₁, G₂)` is faster to compute than `edit_distance(G₂, G₁)`. Consider swapping the arguments -if involved costs are ``symmetric''. +if involved costs are equivalent. - The use of simple Minkowski costs can improve performance considerably. - Exploit vertex attributes when designing operation costs. -For further details, please refer to: +### References +- RIESEN, K., 2015. Structural Pattern Recognition with Graph Edit Distance: Approximation Algorithms and Applications. (Chapter 2) -RIESEN, K., 2015. Structural Pattern Recognition with Graph Edit -Distance: Approximation Algorithms and Applications. (Chapter 2) - -Author: Júlio Hoffimann Mendes (juliohm@stanford.edu) +### Author +- Júlio Hoffimann Mendes (juliohm@stanford.edu) """ function edit_distance(G₁::AbstractGraph, G₂::AbstractGraph; insert_cost::Function=v->1.0, @@ -100,21 +104,33 @@ function DefaultEditHeuristic(λ, G₁::AbstractGraph, G₂::AbstractGraph) return nv(G₂) - length(vs) end + #------------------------- # Edit path cost functions #------------------------- """ + MinkowskiCost(μ₁, μ₂; p::Real=1) + For labels μ₁ on the vertices of graph G₁ and labels μ₂ on the vertices of graph G₂, compute the p-norm cost of substituting vertex u ∈ G₁ by vertex v ∈ G₂. + +### Optional arguments +`p=1`: the p value for p-norm calculation. """ function MinkowskiCost(μ₁::AbstractVector, μ₂::AbstractVector; p::Real=1) (u,v) -> norm(μ₁[u] - μ₂[v], p) end """ -Similar to MinkowskiCost, but ensures costs smaller than 2τ. + BoundedMinkowskiCost(μ₁, μ₂) + +Return value similar to `MinkowskiCost`, but ensure costs smaller than 2τ. + +### Optional arguments +`p=1`: the p value for p-norm calculation. +`τ=1`: value specifying half of the upper limit of the Minkowski cost. """ function BoundedMinkowskiCost(μ₁::AbstractVector, μ₂::AbstractVector; p::Real=1, τ::Real=1) (u,v) -> 1 / (1/(2τ) + exp(-norm(μ₁[u] - μ₂[v], p))) diff --git a/src/flow/boykov_kolmogorov.jl b/src/flow/boykov_kolmogorov.jl index 4ff134e68..c92e2e4c9 100644 --- a/src/flow/boykov_kolmogorov.jl +++ b/src/flow/boykov_kolmogorov.jl @@ -1,24 +1,15 @@ """ -Computes the max-flow/min-cut between source and target using -Boykov-Kolmogorov algorithm. + boykov_kolmogorov_impl(residual_graph, source, target, capacity_matrix) -Returns the maximum flow in the network, the flow matrix and -the partition {S,T} in the form of a vector of 1's and 2's. -The partition vector may also contain 0's. These can be -assigned any label (1 or 2), it is a user choice. +Compute the max-flow/min-cut between `source` and `target` for `residual_graph` +using the Boykov-Kolmogorov algorithm. -For further details, please refer to the paper: +Return the maximum flow in the network, the flow matrix and the partition +`{S,T}` in the form of a vector of 0's, 1's and 2's. -BOYKOV, Y.; KOLMOGOROV, V., 2004. An Experimental Comparison of -Min-Cut/Max-Flow Algorithms for Energy Minimization in Vision. - -Uses a default capacity of 1 when the capacity matrix isn\'t specified. - -Requires arguments: -residual_graph::DiGraph # the input graph -source::Integer # the source vertex -target::Integer # the target vertex -capacity_matrix::AbstractMatrix # edge flow capacities +References: + BOYKOV, Y.; KOLMOGOROV, V., 2004. An Experimental Comparison of + Min-Cut/Max-Flow Algorithms for Energy Minimization in Vision. Author: Júlio Hoffimann Mendes (juliohm@stanford.edu) """ diff --git a/src/flow/dinic.jl b/src/flow/dinic.jl index ac93dc259..c57d75e54 100644 --- a/src/flow/dinic.jl +++ b/src/flow/dinic.jl @@ -1,15 +1,10 @@ """ -Computes the maximum flow between the source and target vertexes in a flow -graph using [Dinic\'s Algorithm](https://en.wikipedia.org/wiki/Dinic%27s_algorithm) -Returns the value of the maximum flow as well as the final flow matrix. + function dinic_impl(residual_graph, source, target, capacity_matrix) -Use a default capacity of 1 when the capacity matrix isn\'t specified. - -Requires arguments: -residual_graph::DiGraph # the input graph -source::Integer # the source vertex -target::Integer # the target vertex -capacity_matrix::AbstractMatrix # edge flow capacities +Compute the maximum flow between the `source` and `target` for `residual_graph` +with edge flow capacities in `capacity_matrix` using +[Dinic\'s Algorithm](https://en.wikipedia.org/wiki/Dinic%27s_algorithm). +Return the value of the maximum flow as well as the final flow matrix. """ function dinic_impl end @traitfn function dinic_impl( @@ -35,18 +30,11 @@ end + """ -Uses BFS to identify a blocking flow in -the input graph and then backtracks from the target to the source, aumenting flow -along all possible paths. - -Requires arguments: -residual_graph::DiGraph # the input graph -source::Integer # the source vertex -target::Integer # the target vertex -capacity_matrix::AbstractMatrix # edge flow capacities -flow_matrix::AbstractMatrix # the current flow matrix -P::AbstractVector{Int} # Parent vector to store Level Graph + blocking_flow!(residual_graph, source, target, capacity_matrix, flow-matrix, P) + +Like `blocking_flow`, but requires a preallocated parent vector `P`. """ function blocking_flow! end @traitfn function blocking_flow!( @@ -110,7 +98,14 @@ function blocking_flow! end return total_flow end -blocking_flow!( +""" + blocking_flow(residual_graph, source, target, capacity_matrix, flow-matrix) + +Use BFS to identify a blocking flow in the `residual_graph` with current flow +matrix `flow_matrix`and then backtrack from `target` to `source`, +augmenting flow along all possible paths. +""" +blocking_flow( residual_graph::AbstractGraph, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex diff --git a/src/flow/edmonds_karp.jl b/src/flow/edmonds_karp.jl index dcbc30aa4..7160324b7 100644 --- a/src/flow/edmonds_karp.jl +++ b/src/flow/edmonds_karp.jl @@ -1,13 +1,10 @@ """ -Computes the maximum flow between the source and target vertexes in a flow -graph using the [Edmonds-Karp algorithm](https://en.wikipedia.org/wiki/Edmondss%E2%80%93Karp_algorithm). -Returns the value of the maximum flow as well as the final flow matrix. -Use a default capacity of 1 when the capacity matrix isn\'t specified. -Requires arguments: -- residual_graph::DiGraph # the input graph -- source::Integer # the source vertex -- target::Integer # the target vertex -- capacity_matrix::AbstracMatrix # edge flow capacities + edmonds_karp_impl(residual_graph, source, target, capacity_matrix) + +Compute the maximum flow in flow graph `residual_graph` between `source` and +`target` and capacities defined in `capacity_matrix` using the +[Edmonds-Karp algorithm](https://en.wikipedia.org/wiki/Edmondss%E2%80%93Karp_algorithm). +Return the value of the maximum flow as well as the final flow matrix. """ function edmonds_karp_impl end @traitfn function edmonds_karp_impl( @@ -54,14 +51,11 @@ function edmonds_karp_impl end end """ -Calculates the amount by which flow can be augmented in the given path. -Augments the flow and returns the augment value. -Requires arguments: -- path::Vector{Int} # input path -- flow_matrix::AbstractMatrix # the current flow matrix -- capacity_matrix::AbstractMatrix # edge flow capacities -""" + augment_path!(path, flow_matrix, capacity_matrix) +Calculate the amount by which flow can be augmented in the given path. +Augment the flow and returns the augment value. +""" function augment_path!( path::Vector{Int}, # input path flow_matrix::AbstractMatrix, # the current flow matrix @@ -86,51 +80,10 @@ function augment_path!( end """ -Uses Bidirectional BFS to look for augmentable-paths. Returns the vertex where -the two BFS searches intersect, the Parent table of the path, the -Successor table of the path found, and a flag indicating success -Flag Values: -0 => success -1 => No Path to target -2 => No Path to source -""" -function fetch_path end -@traitfn function fetch_path( - residual_graph::::IsDirected, # the input graph - source::Integer, # the source vertex - target::Integer, # the target vertex - flow_matrix::AbstractMatrix, # the current flow matrix - capacity_matrix::AbstractMatrix # edge flow capacities - ) - n = nv(residual_graph) - P = -1 * ones(Int, n) - S = -1 * ones(Int, n) - return fetch_path!(residual_graph, - source, - target, - flow_matrix, - capacity_matrix, - P, - S) -end + fetch_path!(residual_graph, source, target, flow_matrix, capacity_matrix, P, S) -""" -A preallocated version of fetch_paths. The parent and successor tables are pre-allocated. -Uses Bidirectional BFS to look for augmentable-paths. Returns the vertex where -the two BFS searches intersect, the Parent table of the path, the -Successor table of the path found, and a flag indicating success -Flag Values: -0 => success -1 => No Path to target -2 => No Path to source -Requires arguments: -residual_graph::DiGraph # the input graph -source::Int # the source vertex -target::Int # the target vertex -flow_matrix::AbstractMatrix # the current flow matrix -capacity_matrix::AbstractMatrix # edge flow capacities -P::Vector{Int} # parent table of path init to -1s -S::Vector{Int} # successor table of path init to -1s +Like `fetch_path`, but requires preallocated parent vector `P` and successor +vector `S`. """ function fetch_path! end @traitfn function fetch_path!( @@ -184,3 +137,34 @@ function fetch_path! end end end end + + +""" + fetch_path(residual_graph, source, target, flow_matrix, capacity_matrix) + + +Use bidirectional BFS to look for augmentable paths from `source` to `target` in +`residual_graph`. Return the vertex where the two BFS searches intersect, +the parent table of the path, the successor table of the path found, and a +flag indicating success (0 => success; 1 => no path to target, 2 => no path +to source). +""" +function fetch_path end +@traitfn function fetch_path( + residual_graph::::IsDirected, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex + flow_matrix::AbstractMatrix, # the current flow matrix + capacity_matrix::AbstractMatrix # edge flow capacities + ) + n = nv(residual_graph) + P = fill(-1, n) + S = fill(-1, n) + return fetch_path!(residual_graph, + source, + target, + flow_matrix, + capacity_matrix, + P, + S) +end diff --git a/src/flow/ext_multiroute_flow.jl b/src/flow/ext_multiroute_flow.jl index 98ab00052..d01b783ee 100644 --- a/src/flow/ext_multiroute_flow.jl +++ b/src/flow/ext_multiroute_flow.jl @@ -1,20 +1,17 @@ """ -Computes the maximum multiroute flow (for any real number of routes) -between the source and target vertexes in a flow graph using the -[Extended Multiroute Flow algorithm](http://dx.doi.org/10.1016/j.disopt.2016.05.002). -If a number of routes is given, returns the value of the multiroute flow as -well as the final flow matrix, along with a multiroute cut if + emrf(flow_graph, source, target, capacity_matrix, flow_algorithm, routes=0) + +Compute the maximum multiroute flow (for any number of `route`s) +between `source` and `target` in `flow_graph` via flow algorithm `flow_algorithm`. + +If a number of routes is given, return the value of the multiroute flow as +well as the final flow matrix, along with a multiroute cut if the Boykov-Kolmogorov max-flow algorithm is used as a subroutine. -Otherwise, it returns the vector of breaking points of the parametric +Otherwise, return the vector of breaking points of the parametric multiroute flow function. -Use a default capacity of 1 when the capacity matrix isn\'t specified. -Requires arguments: -- flow_graph::DiGraph # the input graph -- source::Int # the source vertex -- target::Int # the target vertex -- capacity_matrix::AbstractMatrix{T} # edge flow capacities -- flow_algorithm::AbstractFlowAlgorithm # keyword argument for algorithm -- routes::Int # keyword argument for routes + +### Reference: +* [Extended Multiroute Flow algorithm](http://dx.doi.org/10.1016/j.disopt.2016.05.002) """ # EMRF (Extended Multiroute Flow) algorithms function emrf( @@ -34,14 +31,14 @@ function emrf( return breakingpoints end -""" -Output a set of (point,slope) that compose the restricted max-flow function. -One point by possible slope is enough (hence O(λ×max_flow) complexity). -Requires arguments: -- flow_graph::DiGraph # the input graph -- source::Int # the source vertex -- target::Int # the target vertex -- capacity_matrix::AbstractMatrix{T} # edge flow capacities +@doc_str """ + auxiliaryPoints(flow_graph, source, target, capacity_matrix) + +Output a set of (point, slope) that compose the restricted max-flow function +of `flow_graph` from `source to `target` using capacities in `capacity_matrix`. + +### Performance +One point by possible slope is enough (hence \mathcal{O}(λ×max_flow) complexity). """ function auxiliaryPoints end @traitfn function auxiliaryPoints( @@ -101,12 +98,10 @@ function auxiliaryPoints end end """ -Calculates the breaking of the restricted max-flow from a set of auxiliary points. -Requires arguments: -- flow_graph::DiGraph # the input graph -- source::Int # the source vertex -- target::Int # the target vertex -- capacity_matrix::AbstractMatrix{T} # edge flow capacities + breakingPoints(flow_graph::::IsDirected, source, target, capacity_matrix) + +Calculates the breaking of the restricted max-flow from a set of auxiliary points +for `flow_graph` from `source to `target` using capacities in `capacity_matrix`. """ function breakingPoints end @traitfn function breakingPoints( @@ -138,13 +133,12 @@ function breakingPoints end end """ -Function to get the nonzero min and max function of a Matrix. + minmaxCapacity(capacity_matrix) -Note: this is more efficient than maximum() / minimum() / extrema() -since we have to ignore zero values.since we have to ignore zero values. +Return the nonzero min and max function of `capacity_matrix`. -Requires argument: -- capacity_matrix::AbstractMatrix{T} # edge flow capacities +Note: this is more efficient than maximum() / minimum() / extrema() +since we have to ignore zero values. """ # Function to get the nonzero min and max function of a Matrix @@ -165,13 +159,11 @@ function minmaxCapacity( end """ -Function to get the slope of the restricted flow. The slope is initialized at 0 -and is incremented for each non saturated edge in the restricted min-cut. -Requires argument: -flow_graph::DiGraph, # the input graph -capacity_matrix::AbstractMatrix{T}, # edge flow capacities -cut::Vector{Int}, # cut information for vertices -restriction::T # value of the restriction + slope(flow_graph, capacity_matrix, cut, restriction) + +Return the slope of `flow_graph` using capacities in `capacity_matrix` and +a cut vector `cut`. The slope is initialized at 0 and is incremented for +each edge whose capacity does not exceed `restriction`. """ function slope end # Function to get the slope of the restricted flow @@ -198,8 +190,9 @@ function slope end end """ -Computes the intersection between: -1) Two lines + intersection(x1, y1, a1, x2, y2, a2) + +Return the intersection of two lines defined by `x` and `y` with slopes `a`. 2) A set of segments and a linear function of slope k passing by the origin. Requires argument: 1) - x1, y1, a1, x2, y2, a2::T<:AbstractFloat # Coordinates/slopes @@ -225,6 +218,13 @@ function intersection( return x, y end # Compute the intersection between a set of segment and a line of slope k passing by the origin + +""" + intersection(points, k) + +Return the intersection of a set of line segments and a line of slope `k` +passing by the origin. Segments are defined as a triple (x, y, slope). +""" function intersection( points::Vector{Tuple{T, T, I}}, # vector of breaking points k::R # number of routes (slope of the line) @@ -245,10 +245,9 @@ function intersection( end """ -Redefinition of ≈ (isapprox) for a pair of points -Requires argument: -a::Tuple{T, T}, # Point A with floating coordinates -b::Tuple{T, T} # Point B with floating coordinates + ≈(a, b) + +Redefinition of ≈ (isapprox) for a pair of points `a` and `b`. """ ≈(a::Tuple{T, T}, b::Tuple{T, T}) where T <: AbstractFloat = a[1] ≈ b[1] && a[2] ≈ b[2] diff --git a/src/flow/kishimoto.jl b/src/flow/kishimoto.jl index 50d05d1a9..2edcd2ef4 100644 --- a/src/flow/kishimoto.jl +++ b/src/flow/kishimoto.jl @@ -31,19 +31,13 @@ end """ -Computes the maximum multiroute flow (for an integer number of routes) -between the source and target vertexes in a flow graph using the -[Kishimoto algorithm](http://dx.doi.org/10.1109/ICCS.1992.255031). -Returns the value of the multiroute flow as well as the final flow matrix, + kishimoto(flow_graph, source, target, capacity_matrix, flow_algorithm, routes) + +Compute the maximum multiroute flow (for an integer number of `route`s) +between `source` and `target` in `flow_graph` with capacities in `capacity_matrix` +using the [Kishimoto algorithm](http://dx.doi.org/10.1109/ICCS.1992.255031). +Return the value of the multiroute flow as well as the final flow matrix, along with a multiroute cut if Boykov-Kolmogorov is used as a subroutine. -Use a default capacity of 1 when the capacity matrix isn\'t specified. -Requires arguments: -- flow_graph::DiGraph # the input graph -- source::Integer # the source vertex -- target::Integer # the target vertex -- capacity_matrix::AbstractMatrix{T} # edge flow capacities -- flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm -- routes::Int # keyword argument for routes """ function kishimoto end @traitfn function kishimoto( diff --git a/src/flow/maximum_flow.jl b/src/flow/maximum_flow.jl index c5dc53ab2..3ca8cd932 100644 --- a/src/flow/maximum_flow.jl +++ b/src/flow/maximum_flow.jl @@ -1,34 +1,40 @@ """ -Abstract type that allows users to pass in their preferred Algorithm + AbstractFlowAlgorithm + +Abstract type that allows users to pass in their preferred algorithm """ abstract type AbstractFlowAlgorithm end """ + EdmondsKarpAlgorithm <: AbstractFlowAlgorithm + Forces the maximum_flow function to use the Edmonds–Karp algorithm. """ -struct EdmondsKarpAlgorithm <: AbstractFlowAlgorithm -end +struct EdmondsKarpAlgorithm <: AbstractFlowAlgorithm end """ + DinicAlgorithm <: AbstractFlowAlgorithm + Forces the maximum_flow function to use Dinic\'s algorithm. """ -struct DinicAlgorithm <: AbstractFlowAlgorithm -end +struct DinicAlgorithm <: AbstractFlowAlgorithm end """ + BoykovKolmogorovAlgorithm <: AbstractFlowAlgorithm + Forces the maximum_flow function to use the Boykov-Kolmogorov algorithm. """ -struct BoykovKolmogorovAlgorithm <: AbstractFlowAlgorithm -end +struct BoykovKolmogorovAlgorithm <: AbstractFlowAlgorithm end """ Forces the maximum_flow function to use the Push-Relabel algorithm. """ -struct PushRelabelAlgorithm <: AbstractFlowAlgorithm -end +struct PushRelabelAlgorithm <: AbstractFlowAlgorithm end """ -Type that returns 1 if a forward edge exists, and 0 otherwise + DefaultCapacity{T} + +Structure that returns 1 if a forward edge exists in `flow_graph`, and 0 otherwise. """ struct DefaultCapacity{T<:Integer} <: AbstractMatrix{T} flow_graph::DiGraph @@ -45,23 +51,18 @@ transpose(d::DefaultCapacity) = DefaultCapacity(reverse(d.flow_graph)) ctranspose(d::DefaultCapacity) = DefaultCapacity(reverse(d.flow_graph)) """ -Constructs a residual graph for the input flow graph. Creates a new graph instead -of modifying the input flow graph. - -The residual graph comprises of the same Vertex list, but ensures that for each -edge (u,v), (v,u) also exists in the graph. (to allow flow in the reverse direction). + residual(flow_graph) -If only the forward edge exists, a reverse edge is created with capacity 0. If both -forward and reverse edges exist, their capacities are left unchanged. Since the capacities -in DefaultDistance cannot be changed, an array of ones is created. Returns the -residual graph and the modified capacity_matrix (when DefaultDistance is used.) +Returns a directed residual graph for a directed `flow_graph`. -Requires arguments: +The residual graph comprises the same node list as the orginal flow graph, but +ensures that for each edge (u,v), (v,u) also exists in the graph. This allows +flow in the reverse direction. -- flow_graph::DiGraph, # the input graph -- source::Integer # the source vertex -- target::Integer # the target vertex -- capacity_matrix::AbstractMatrix # input capacity matrix +If only the forward edge exists, a reverse edge is created with capacity 0. +If both forward and reverse edges exist, their capacities are left unchanged. +Since the capacities in `DefaultDistance` cannot be changed, an array of ones +is created. """ function residual end @traitfn residual(flow_graph::::IsDirected) = DiGraph(Graph(flow_graph)) @@ -119,55 +120,46 @@ end end """ -Generic maximum_flow function. Requires arguments: +maximum_flow(flow_graph, source, target[, capacity_matrix][, algorithm][, restriction]) -- flow_graph::DiGraph # the input graph -- source::Integer # the source vertex -- target::Integer # the target vertex -- capacity_matrix::AbstractMatrix # edge flow capacities -- algorithm::AbstractFlowAlgorithm # keyword argument for algorithm -- restriction::Real # keyword argument for a restriction +Generic maximum_flow function for `flow_graph` from `source` to `target` with +capacities in `capacity_matrix`. Uses flow algorithm `algorithm` and cutoff restriction +`restriction`. -The function defaults to the Push-relabel algorithm. Alternatively, the algorithm -to be used can also be specified through a keyword argument. A default capacity of 1 -is assumed for each link if no capacity matrix is provided. -If the restriction is bigger than 0, it is applied to capacity_matrix. +- If `capacity_matrix` is not specified, `DefaultCapacity(flow_graph)` will be used. +- If `algorithm` is not specified, it will default to `PushRelabelAlgorithm`. +- If `restriction` is not specified, it will default to `0`. -All algorithms return a tuple with 1) the maximum flow and 2) the flow matrix. -For the Boykov-Kolmogorov algorithm, the associated mincut is returned as a third output. +Return a tuple of (maximum flow, flow matrix). For the Boykov-Kolmogorov +algorithm, the associated mincut is returned as a third output. ### Usage Example: -```julia - -# Create a flow-graph and a capacity matrix -flow_graph = DiGraph(8) -flow_edges = [ +```jldoctest +julia> flow_graph = DiGraph(8) # Create a flow-graph +julia> flow_edges = [ (1,2,10),(1,3,5),(1,4,15),(2,3,4),(2,5,9), (2,6,15),(3,4,4),(3,6,8),(4,7,16),(5,6,15), (5,8,10),(6,7,15),(6,8,10),(7,3,6),(7,8,10) ] -capacity_matrix = zeros(Int, 8, 8) -for e in flow_edges + +julia> capacity_matrix = zeros(Int, 8, 8) # Create a capacity matrix + +julia> for e in flow_edges u, v, f = e add_edge!(flow_graph, u, v) capacity_matrix[u,v] = f end -# Run default maximum_flow without the capacity_matrix -f, F = maximum_flow(flow_graph, 1, 8) +julia> f, F = maximum_flow(flow_graph, 1, 8) # Run default maximum_flow without the capacity_matrix -# Run default maximum_flow with the capacity_matrix -f, F = maximum_flow(flow_graph, 1, 8) +julia> f, F = maximum_flow(flow_graph, 1, 8) # Run default maximum_flow with the capacity_matrix -# Run Endmonds-Karp algorithm -f, F = maximum_flow(flow_graph,1,8,capacity_matrix,algorithm=EdmondsKarpAlgorithm()) +julia> f, F = maximum_flow(flow_graph,1,8,capacity_matrix,algorithm=EdmondsKarpAlgorithm()) # Run Edmonds-Karp algorithm -# Run Dinic's algorithm -f, F = maximum_flow(flow_graph,1,8,capacity_matrix,algorithm=DinicAlgorithm()) +julia> f, F = maximum_flow(flow_graph,1,8,capacity_matrix,algorithm=DinicAlgorithm()) # Run Dinic's algorithm -# Run Boykov-Kolmogorov algorithm -f, F, labels = maximum_flow(flow_graph,1,8,capacity_matrix,algorithm=BoykovKolmogorovAlgorithm()) +julia> f, F, labels = maximum_flow(flow_graph,1,8,capacity_matrix,algorithm=BoykovKolmogorovAlgorithm()) # Run Boykov-Kolmogorov algorithm ``` """ diff --git a/src/flow/multiroute_flow.jl b/src/flow/multiroute_flow.jl index 956ec5f7f..cdae9845e 100644 --- a/src/flow/multiroute_flow.jl +++ b/src/flow/multiroute_flow.jl @@ -1,19 +1,23 @@ """ -Abstract type that allows users to pass in their preferred Algorithm + AbstractMultirouteFlowAlgorithm + +Abstract type that allows users to pass in their preferred algorithm. """ abstract type AbstractMultirouteFlowAlgorithm end """ + KishimotoAlgorithm + Forces the multiroute_flow function to use the Kishimoto algorithm. """ -struct KishimotoAlgorithm <: AbstractMultirouteFlowAlgorithm -end +struct KishimotoAlgorithm <: AbstractMultirouteFlowAlgorithm end """ + ExtendedMultirouteFlowAlgorithm + Forces the multiroute_flow function to use the Extended Multiroute Flow algorithm. """ -struct ExtendedMultirouteFlowAlgorithm <: AbstractMultirouteFlowAlgorithm -end +struct ExtendedMultirouteFlowAlgorithm <: AbstractMultirouteFlowAlgorithm end # Methods when the number of routes is more than the connectivity # 1) When using Boykov-Kolmogorov as a flow subroutine @@ -91,13 +95,21 @@ function multiroute_flow( algorithm = flow_algorithm, restriction = x) end +### TODO: CLEAN UP THIS FUNCTION AND DOCUMENTATION. THERE SHOULD BE NO NEED TO +### HAVE A TYPE-UNSTABLE FUNCTION HERE. (sbromberger 2017-03-26) """ -The generic multiroute_flow function will output three kinds of results: +multiroute_flow(flow_graph, source, target[, DefaultCapacity][, flow_algorithm][, mrf_algorithm][, routes]) + +The generic multiroute_flow function. -- When the number of routes is 0 or non-specified, the set of breaking points of -the multiroute flow is returned. -- When the input is limited to a set of breaking points and a route value k, -only the value of the k-route flow is returned +The output will vary depending on the input: + +- When the number of `route`s is `0`, return the set of breaking points of +the multiroute flow. +- When the number of `route`s is `1`, return a flow with a set of 1-disjoint paths +(this is the classical max-flow implementation). +- When the input is limited to a set of breaking points and a route value `k`, +return only the k-route flow. - Otherwise, a tuple with 1) the maximum flow and 2) the flow matrix. When the max-flow subroutine is the Boykov-Kolmogorov algorithm, the associated mincut is returned as a third output. @@ -144,37 +156,34 @@ in the following cases: (please consult the max_flow section for options about flow_algorithm and capacity_matrix) -```julia +```jldoctest +julia> flow_graph = DiGraph(8) # Create a flow graph -# Create a flow-graph and a capacity matrix -flow_graph = DiGraph(8) -flow_edges = [ +julia> flow_edges = [ (1, 2, 10), (1, 3, 5), (1, 4, 15), (2, 3, 4), (2, 5, 9), (2, 6, 15), (3, 4, 4), (3, 6, 8), (4, 7, 16), (5, 6, 15), (5, 8, 10), (6, 7, 15), (6, 8, 10), (7, 3, 6), (7, 8, 10) ] -capacity_matrix = zeros(Int, 8, 8) -for e in flow_edges + +julia> capacity_matrix = zeros(Int, 8, 8) # Create a capacity matrix + +julia> for e in flow_edges u, v, f = e add_edge!(flow_graph, u, v) capacity_matrix[u, v] = f end -# Run default multiroute_flow with an integer number of routes = 2 -f, F = multiroute_flow(flow_graph, 1, 8, capacity_matrix, routes = 2) +julia> f, F = multiroute_flow(flow_graph, 1, 8, capacity_matrix, routes = 2) # Run default multiroute_flow with an integer number of routes = 2 + +julia> f, F = multiroute_flow(flow_graph, 1, 8, capacity_matrix, routes = 1.5) # Run default multiroute_flow with a noninteger number of routes = 1.5 + +julia> points = multiroute_flow(flow_graph, 1, 8, capacity_matrix) # Run default multiroute_flow for all the breaking points values -# Run default multiroute_flow with a noninteger number of routes = 1.5 -f, F = multiroute_flow(flow_graph, 1, 8, capacity_matrix, routes = 1.5) +julia> f, F = multiroute_flow(points, 1.5) # Then run multiroute flow algorithm for any positive number of routes -# Run default multiroute_flow for all the breaking points values -points = multiroute_flow(flow_graph, 1, 8, capacity_matrix) -# Then run multiroute flow algorithm for any positive number of routes -f, F = multiroute_flow(points, 1.5) -f = multiroute_flow(points, 1.5, valueonly = true) +julia> f = multiroute_flow(points, 1.5, valueonly = true) -# Run multiroute flow algorithm using Boykov-Kolmogorov algorithm as max_flow routine -f, F, labels = multiroute_flow(flow_graph, 1, 8, capacity_matrix, -algorithm = BoykovKolmogorovAlgorithm(), routes = 2) +julia> f, F, labels = multiroute_flow(flow_graph, 1, 8, capacity_matrix, algorithm = BoykovKolmogorovAlgorithm(), routes = 2) # Run multiroute flow algorithm using Boykov-Kolmogorov algorithm as max_flow routine ``` """ @@ -191,7 +200,7 @@ function multiroute_flow( routes::R = 0 # keyword argument for number of routes (0 = all values) ) where R <: Real - # a flow with a set of 1-disjoint pathes is a classical max-flow + # a flow with a set of 1-disjoint paths is a classical max-flow (routes == 1) && return maximum_flow(flow_graph, source, target, capacity_matrix, flow_algorithm) diff --git a/src/flow/push_relabel.jl b/src/flow/push_relabel.jl index 54572dd75..1e2ab297e 100644 --- a/src/flow/push_relabel.jl +++ b/src/flow/push_relabel.jl @@ -1,20 +1,11 @@ -""" -Implementation of the FIFO push relabel algorithm with gap heuristic. Takes -approximately O(V^3) time. - -Maintains the following auxillary arrays: -- height -> Stores the labels of all vertices -- count -> Stores the number of vertices at each height -- excess -> Stores the difference between incoming and outgoing flow for all vertices -- active -> Stores the status of all vertices. (e(v)>0 => active[v] = true) -- Q -> The FIFO queue that stores active vertices waiting to be discharged. +@doc_str """ + push_relabel(residual_graph, source, target, capacity_matrix) -Requires arguments: +Return the maximum flow of `residual_graph` from `source` to `target` using the +FIFO push relabel algorithm with gap heuristic. -- residual_graph::DiGraph # the input graph -- source::Integer # the source vertex -- target::Integer # the target vertex -- capacity_matrix::AbstractMatrix # edge flow capacities +### Performance +Takes approximately ``\\mathcal{O}(|V|^{3})`` time. """ function push_relabel end @traitfn function push_relabel( @@ -60,14 +51,10 @@ function push_relabel end end """ -Pushes inactive nodes into the queue and activates them. - -Requires arguments: + enqueue_vertex!(Q, v, active, excess) -- Q::AbstractVector -- v::Integer -- active::AbstractVector{Bool} -- excess::AbstractVector +Push inactive node `v` into queue `Q` and activates it. Requires preallocated +`active` and `excess` vectors. """ function enqueue_vertex!( @@ -84,19 +71,10 @@ function enqueue_vertex!( end """ -Pushes as much flow as possible through the given edge. + push_flow!(residual_graph, u, v, capacity_matrix, flow_matrix, excess, height, active, Q) -Requires arguements: - -- residual_graph::DiGraph # the input graph -- u::Integer # input from-vertex -- v::Integer # input to-vetex -- capacity_matrix::AbstractMatrix -- flow_matrix::AbstractMatrix -- excess::AbstractVector -- height::AbstractVector{Int} -- active::AbstractVector{Bool} -- Q::AbstractVector +Using `residual_graph` with capacities in `capacity_matrix`, push as much flow as possible through the given edge(`u`, `v`). +Requires preallocated `flow_matrix` matrix, and `excess`, `height, `active`, and `Q` vectors. """ function push_flow! end @traitfn function push_flow!( @@ -126,8 +104,10 @@ function push_flow! end end """ -Implements the gap heuristic. Relabels all vertices above a cutoff height. -Reduces the number of relabels required. + gap!(residual_graph, h, excess, height, active, count, Q) + +Implement the push-relabel gap heuristic. Relabel all nodes above a cutoff height. +Reduce the number of relabels required. Requires arguments: @@ -161,20 +141,9 @@ function gap! end end """ -Relabels a vertex with respect to its neighbors, to produce an admissable -edge. + relabel!(residual_graph, v, capacity_matrix, flow_matrix, excess, height, active, count, Q) -Requires arguments: - -- residual_graph::DiGraph # the input graph -- v::Integer # input vertex to be relabeled -- capacity_matrix::AbstractMatrix -- flow_matrix::AbstractMatrix -- excess::AbstractVector -- height::AbstractVector{Int} -- active::AbstractVector{Bool} -- count::AbstractVector{Int} -- Q::AbstractVector +Relabel a node `v` with respect to its neighbors to produce an admissable edge. """ function relabel! end @traitfn function relabel!( @@ -203,20 +172,10 @@ end """ -Drains the excess flow out of a vertex. Runs the gap heuristic or relabels the -vertex if the excess remains non-zero. + discharge!(residual_graph, v, capacity_matrix, flow_matrix, excess, height, active, count, Q) -Requires arguments: - -- residual_graph::DiGraph # the input graph -- v::Integer # vertex to be discharged -- capacity_matrix::AbstractMatrix -- flow_matrix::AbstractMatrix -- excess::AbstractVector -- height::AbstractVector{Int} -- active::AbstractVector{Bool} -- count::AbstractVector{Int} -- Q::AbstractVector +Drain the excess flow out of node `v`. Run the gap heuristic or relabel the +vertex if the excess remains non-zero. """ function discharge! end @traitfn function discharge!( diff --git a/src/generators/euclideangraphs.jl b/src/generators/euclideangraphs.jl index c8d916a25..d68702ef1 100644 --- a/src/generators/euclideangraphs.jl +++ b/src/generators/euclideangraphs.jl @@ -1,28 +1,12 @@ -""" -euclidean_graph(points::Matrix, L=1., p=2., cutoff=-1., bc=:open) - -Given the `d×N` matrix `points` builds an Euclidean graph of `N` vertices -according to the following procedure. - -Defining the `d`-dimensional vectors `x[i] = points[:,i]`, an edge between -vertices `i` and `j` is inserted if `norm(x[i]-x[j], p) < cutoff`. -In case of negative `cutoff` instead every edge is inserted. -For `p=2` we have the standard Euclidean distance. -Set `bc=:periodic` to impose periodic boundary conditions in the box ``[0,L]^d``. - -Returns a graph and Dict containing the distance on each edge. - - +@doc_str """ euclidean_graph(N, d; seed = -1, L=1., p=2., cutoff=-1., bc=:open) -Generates `N` uniformly distributed points in the box ``[0,L]^d`` -and builds and Euclidean graph. +Generates `N` uniformly distributed points in the box ``[0,L]^{d}`` +and builds a Euclidean graph. -Returns a graph, a Dict containing the distance on each edge and a matrix with +Return a graph, a Dict containing the distance on each edge and a matrix with the points' positions. """ -function euclidean_graph end - function euclidean_graph(N::Int, d::Int; L=1., seed = -1, kws...) rng = LightGraphs.getRNG(seed) @@ -30,6 +14,26 @@ function euclidean_graph(N::Int, d::Int; return (euclidean_graph(points; L=L, kws...)..., points) end +### TODO: finish this documentation. sbromberger 2017/03/26 +""" + euclidean_graph(points, ) + +Given the `d×N` matrix `points` build an Euclidean graph of `N` vertices +according to the following procedure. + +Defining the `d`-dimensional vectors `x[i] = points[:,i]`, an edge between +vertices `i` and `j` is inserted if `norm(x[i]-x[j], p) < cutoff`. +In case of negative `cutoff` instead every edge is inserted. +For `p=2` we have the standard Euclidean distance. +Set `bc=:periodic` to impose periodic boundary conditions in the box ``[0,L]^d``. + +### Optional arguments +- `L=1`: used to bound the `d` dimensional box from which points are selected. +- `p=2` +- `bc=:open` + +Return a graph and Dict containing the distance on each edge. +""" function euclidean_graph(points::Matrix; L=1., p=2., cutoff=-1., bc=:open) d, N = size(points) diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 244655bf9..7738bad87 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -32,17 +32,15 @@ function DiGraph(nv::Integer, ne::Integer; seed::Int = -1) end """ -erdos_renyi(n::Integer, p::Real; is_directed=false, seed=-1) -erdos_renyi(n::Integer, ne::Integer; is_directed=false, seed=-1) + erdos_renyi(n, p) Create an [Erdős–Rényi](http://en.wikipedia.org/wiki/Erdős–Rényi_model) -random graph with `n` vertices. Edges are added between pairs of vertices with -probability `p`. Undirected graphs are created by default; use -`is_directed=true` to override. +random graph with `n` nodes. Edges are added between pairs of nodes with +probability `p`. -Note also that Erdős–Rényi graphs may be generated quickly using `erdos_renyi(n, ne)` -or the `Graph(nv, ne)` constructor, which randomly select `ne` edges among all the potential -edges. +### Optional arguments +- `is_directed=false`: if true, return a directed graph. +- `seed=-1`: set the RNG seed. """ function erdos_renyi(n::Integer, p::Real; is_directed=false, seed::Integer=-1) m = is_directed ? n*(n-1) : div(n*(n-1),2) @@ -54,16 +52,31 @@ function erdos_renyi(n::Integer, p::Real; is_directed=false, seed::Integer=-1) return is_directed ? DiGraph(n, ne, seed=seed) : Graph(n, ne, seed=seed) end +""" + erdos_renyi(n, ne) + +Create an [Erdős–Rényi](http://en.wikipedia.org/wiki/Erdős–Rényi_model) random +graph with `n` nodes and `ne` edges. + +### Optional arguments +- `is_directed=false`: if true, return a directed graph. +- `seed=-1`: set the RNG seed. +""" function erdos_renyi(n::Integer, ne::Integer; is_directed=false, seed::Integer=-1) return is_directed ? DiGraph(n, ne, seed=seed) : Graph(n, ne, seed=seed) end """ -Create a [Watts-Strogatz](https://en.wikipedia.org/wiki/Watts_and_Strogatz_model) + watts_strogatz(n, k, β) + +Return a [Watts-Strogatz](https://en.wikipedia.org/wiki/Watts_and_Strogatz_model) small model random graph with `n` vertices, each with degree `k`. Edges are -randomized per the model based on probability `β`. Undirected graphs are -created by default; use `is_directed=true` to override. +randomized per the model based on probability `β`. + +### Optional arguments +- `is_directed=false`: if true, return a directed graph. +- `seed=-1`: set the RNG seed. """ function watts_strogatz(n::Integer, k::Integer, β::Real; is_directed=false, seed::Int = -1) @assert k < n/2 @@ -151,27 +164,35 @@ function _try_creation(n::T, k::Vector{T}, rng::AbstractRNG) where T<:Integer end """ -barabasi_albert(n::Integer, k::Integer; is_directed::Bool = false, complete::Bool = false, seed::Int = -1) + barabasi_albert(n, k) Create a [Barabási–Albert model](https://en.wikipedia.org/wiki/Barab%C3%A1si%E2%80%93Albert_model) random graph with `n` vertices. It is grown by adding new vertices to an initial graph with `k` vertices. Each new vertex is attached with `k` edges to `k` different vertices already present in the system by preferential attachment. -Initial graphs are undirected and consist of isolated vertices by default; -use `is_directed=true` and `complete=true` for directed and complete initial graphs. +Initial graphs are undirected and consist of isolated vertices by default. + +### Optional arguments +- `is_directed=false`: if true, return a directed graph. +- `complete=false`: if true, use a complete graph for the initial graph. +- `seed=-1`: set the RNG seed. """ barabasi_albert(n::Integer, k::Integer; keyargs...) = barabasi_albert(n, k, k; keyargs...) """ -barabasi_albert(n::Integer, n0::Integer, k::Integer; is_directed::Bool = false, complete::Bool = false, seed::Int = -1) + barabasi_albert(n::Integer, n0::Integer, k::Integer) -Creates a [Barabási–Albert model](https://en.wikipedia.org/wiki/Barab%C3%A1si%E2%80%93Albert_model) +Create a [Barabási–Albert model](https://en.wikipedia.org/wiki/Barab%C3%A1si%E2%80%93Albert_model) random graph with `n` vertices. It is grown by adding new vertices to an initial graph with `n0` vertices. Each new vertex is attached with `k` edges to `k` different vertices already present in the system by preferential attachment. -Initial graphs are undirected and consist of isolated vertices by default; -use `is_directed=true` and `complete=true` for directed and complete initial graphs. +Initial graphs are undirected and consist of isolated vertices by default. + +### Optional arguments +- `is_directed=false`: if true, return a directed graph. +- `complete=false`: if true, use a complete graph for the initial graph. +- `seed=-1`: set the RNG seed. """ function barabasi_albert(n::Integer, n0::Integer, k::Integer; is_directed::Bool = false, complete::Bool = false, seed::Int = -1) if complete @@ -185,12 +206,15 @@ function barabasi_albert(n::Integer, n0::Integer, k::Integer; is_directed::Bool end """ -barabasi_albert!(g::AbstractGraph, n::Integer, k::Integer; seed::Int = -1) + barabasi_albert!(g::AbstractGraph, n::Integer, k::Integer) Create a [Barabási–Albert model](https://en.wikipedia.org/wiki/Barab%C3%A1si%E2%80%93Albert_model) random graph with `n` vertices. It is grown by adding new vertices to an initial graph `g`. Each new vertex is attached with `k` edges to `k` different vertices already present in the system by preferential attachment. + +### Optional arguments +- `seed=-1`: set the RNG seed. """ function barabasi_albert!(g::AbstractGraph, n::Integer, k::Integer; seed::Int=-1) n0 = nv(g) @@ -260,17 +284,21 @@ function barabasi_albert!(g::AbstractGraph, n::Integer, k::Integer; seed::Int=-1 end -""" -static_fitness_model{T<:Real}(m::Integer, fitness::Vector{T}; seed::Int=-1) +@doc_str """ +static_fitness_model(m, fitness) -Generate a random graph with `length(fitness)` nodes and `m` edges, +Generate a random graph with ``|fitness|`` nodes and `m` edges, in which the probability of the existence of edge `(i, j)` is proportional -to `fitness[i]*fitness[j]`. Time complexity is O(|V| + |E| log |E|). +to `fitness[i]*fitness[j]`. + +### Optional arguments +- `seed=-1`: set the RNG seed. -Reference: +### Performance +Time complexity is ``\\mathcal{O}(|V| + |E| log |E|)``. -* Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution -in scale-free networks. Phys Rev Lett 87(27):278701, 2001. +### References +- Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution in scale-free networks. Phys Rev Lett 87(27):278701, 2001. """ function static_fitness_model(m::Integer, fitness::Vector{T}; seed::Int=-1) where T<:Real @assert(m >= 0, "invalid number of edges") @@ -292,6 +320,22 @@ function static_fitness_model(m::Integer, fitness::Vector{T}; seed::Int=-1) wher return g end +@doc_str """ +static_fitness_model(m, fitness_out, fitness_in) + +Generate a random graph with ``|fitness_out + fitness_in|`` nodes and `m` edges, +in which the probability of the existence of edge `(i, j)` is proportional with +respect to `i ∝ fitness_out` and `j ∝ fitness_in`. + +### Optional arguments +- `seed=-1`: set the RNG seed. + +### Performance +Time complexity is ``\\mathcal{O}(|V| + |E| log |E|)``. + +### References +- Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution in scale-free networks. Phys Rev Lett 87(27):278701, 2001. +""" function static_fitness_model(m::Integer, fitness_out::Vector{T}, fitness_in::Vector{S}; seed::Int=-1) where T<:Real where S<:Real @assert(m >= 0, "invalid number of edges") n = length(fitness_out) @@ -332,21 +376,23 @@ function _create_static_fitness_graph!(g::AbstractGraph, m::Integer, cum_fitness end end -""" -function static_scale_free(n::Integer, m::Integer, α::Real; seed::Int=-1, finite_size_correction::Bool=true) +@doc_str """ + static_scale_free(n, m, α) Generate a random graph with `n` vertices, `m` edges and expected power-law -degree distribution with exponent `α`. `finite_size_correction` determines -whether to use the finite size correction proposed by Cho et al. -This generator calls internally the `static_fitness_model function`. -Time complexity is O(|V| + |E| log |E|). +degree distribution with exponent `α`. -References: +### Optional arguments +- `seed=-1`: set the RNG seed. +- `finite_size_correction=true`: determines whether to use the finite size correction +proposed by Cho et al. -* Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution in scale-free networks. Phys Rev Lett 87(27):278701, 2001. +### Performance +Time complexity is ``\\mathcal{O}(|V| + |E| log |E|)``. +### References +* Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution in scale-free networks. Phys Rev Lett 87(27):278701, 2001. * Chung F and Lu L: Connected components in a random graph with given degree sequences. Annals of Combinatorics 6, 125-145, 2002. - * Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in scale-free networks under the Achlioptas process. Phys Rev Lett 103:135702, 2009. """ function static_scale_free(n::Integer, m::Integer, α::Real; seed::Int=-1, finite_size_correction::Bool=true) @@ -356,6 +402,26 @@ function static_scale_free(n::Integer, m::Integer, α::Real; seed::Int=-1, finit static_fitness_model(m, fitness, seed=seed) end +@doc_str """ + static_scale_free(n, m, α_out, α_in) + +Generate a random graph with `n` vertices, `m` edges and expected power-law +degree distribution with exponent `α_out` for outbound edges and `α_in` for +inbound edges. + +### Optional arguments +- `seed=-1`: set the RNG seed. +- `finite_size_correction=true`: determines whether to use the finite size correction +proposed by Cho et al. + +### Performance +Time complexity is ``\\mathcal{O}(|V| + |E| log |E|)``. + +### References +* Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution in scale-free networks. Phys Rev Lett 87(27):278701, 2001. +* Chung F and Lu L: Connected components in a random graph with given degree sequences. Annals of Combinatorics 6, 125-145, 2002. +* Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in scale-free networks under the Achlioptas process. Phys Rev Lett 103:135702, 2009. +""" function static_scale_free(n::Integer, m::Integer, α_out::Real, α_in::Float64; seed::Int=-1, finite_size_correction::Bool=true) @assert(n >= 0, "Invalid number of nodes") @assert(α_out >= 2, "out-degree exponent must be >= 2") @@ -384,16 +450,22 @@ function _construct_fitness(n::Integer, α::Real, finite_size_correction::Bool) return fitness end -doc""" -random_regular_graph(n::Int, k::Int; seed=-1) +@doc_str """ + random_regular_graph(n, k) Create a random undirected [regular graph](https://en.wikipedia.org/wiki/Regular_graph) with `n` vertices, each with degree `k`. -For undirected graphs, allocates an array of `nk` `Int`s, and takes -approximately $nk^2$ time. For $k > n/2$, generates a graph of degree -`n-k-1` and returns its complement. +### Optional arguments +- `seed=-1`: set the RNG seed. + +### Performance +Time complexity is approximately ``nk^2``. + +### Implementation Notes +Allocates an array of `nk` `Int`s, and . For ``k > \\frac{n}{2}``, generates a graph of degree +``n-k-1`` and returns its complement. """ function random_regular_graph(n::Integer, k::Integer; seed::Int=-1) @assert(iseven(n*k), "n * k must be even") @@ -420,18 +492,22 @@ function random_regular_graph(n::Integer, k::Integer; seed::Int=-1) return g end -doc""" -random_configuration_model(n::Integer, k::Array{Integer}; seed=-1, check_graphical=false) +@doc_str """ + random_configuration_model(n, ks; seed=-1, check_graphical=false) Create a random undirected graph according to the [configuration model] -(http://tuvalu.santafe.edu/~aaronc/courses/5352/fall2013/csci5352_2013_L11.pdf). -It contains `n` vertices, the vertex `i` having degree `k[i]`. +(http://tuvalu.santafe.edu/~aaronc/courses/5352/fall2013/csci5352_2013_L11.pdf) +containing `n` vertices, with each node `i` having degree `k[i]`. -Defining `c = mean(k)`, it allocates an array of `nc` `Int`s, and takes -approximately $nc^2$ time. +### Optional arguments +- `seed=-1`: set the RNG seed. +- `check_graphical=false`: if true, ensure that `k` is a graphical sequence (see `isgraphical`). -If `check_graphical=true` makes sure that `k` is a graphical sequence (see `isgraphical`). +### Performance +Time complexity is approximately ``n \\bar{k}^2``. +### Implementation Notes +Allocates an array of ``n \\bar{k}`` `Int`s. """ function random_configuration_model(n::Integer, k::Array{T}; seed::Int=-1, check_graphical::Bool=false) where T<:Integer @assert(n == length(k), "a degree sequence of length n has to be provided") @@ -456,16 +532,19 @@ function random_configuration_model(n::Integer, k::Array{T}; seed::Int=-1, check return g end -doc""" -random_regular_digraph(n::Integer, k::Integer; dir::Symbol=:out, seed=-1) +@doc_str """ + random_regular_digraph(n, k) -Create a random directed -[regular graph](https://en.wikipedia.org/wiki/Regular_graph) with `n` vertices, -each with degree `k`. The degree (in or out) can be -specified using `dir=:in` or `dir=:out`. The default is `dir=:out`. +Create a random directed [regular graph](https://en.wikipedia.org/wiki/Regular_graph) +with `n` vertices, each with degree `k`. + +### Optional arguments +- `dir=:out`: the direction of the edges for degree parameter. +- `seed=-1`: set the RNG seed. -For directed graphs, allocates an $n \times n$ sparse matrix of boolean as an -adjacency matrix and uses that to generate the directed graph. +### Implementation Notes +Allocates an ``n \times n`` sparse matrix of boolean as an adjacency matrix and +uses that to generate the directed graph. """ function random_regular_digraph(n::Integer, k::Integer; dir::Symbol=:out, seed::Int=-1) #TODO remove the function sample from StatsBase for one allowing the use @@ -497,18 +576,19 @@ function random_regular_digraph(n::Integer, k::Integer; dir::Symbol=:out, seed:: end end -doc""" -stochastic_block_model(c::Matrix{Real}, n::Vector{Integer}; seed::Int = -1) +@doc_str """ + stochastic_block_model(c, n) stochastic_block_model(cin::Real, coff::Float64, n::Vector{Integer}; seed::Int = -1) Return a Graph generated according to the Stochastic Block Model (SBM). `c[a,b]` : Mean number of neighbors of a vertex in block `a` belonging to block `b`. Only the upper triangular part is considered, since the lower traingular is - determined by $c[b,a] = c[a,b] * n[a]/n[b]$. + determined by ``c[b,a] = c[a,b] * \\frac{n[a]}{n[b]}``. `n[a]` : Number of vertices in block `a` -The second form samples from a SBM with `c[a,a]=cin`, and `c[a,b]=coff`. +### Optional arguments +- `seed=-1`: set the RNG seed. For a dynamic version of the SBM see the `StochasticBlockModel` type and related functions. @@ -549,6 +629,16 @@ function stochastic_block_model(c::Matrix{T}, n::Vector{U}; seed::Int = -1) wher return g end +@doc_str """ + stochastic_block_model(cint, cext, n) + +Return a Graph generated according to the Stochastic Block Model (SBM). + +Samples from a SBM with `c[a,a]=cint`, and `c[a,b]=cext`. + +### Optional arguments +- `seed=-1`: set the RNG seed. +""" function stochastic_block_model(cint::T, cext::T, n::Vector{U}; seed::Int=-1) where T<:Real where U<:Integer K = length(n) c = [ifelse(a==b, cint, cext) for a=1:K,b=1:K] @@ -556,12 +646,7 @@ function stochastic_block_model(cint::T, cext::T, n::Vector{U}; seed::Int=-1) wh end """ - type StochasticBlockModel{T<:Integer,P<:Real} - n::T - nodemap::Array{T} - affinities::Matrix{P} - rng::MersenneTwister - end + StochasticBlockModel{T,P} A type capturing the parameters of the SBM. Each vertex is assigned to a block and the probability of edge `(i,j)` @@ -573,7 +658,8 @@ matrix is stored in affinities. `affinities[k,l]` is the probability of an edge between any vertex in block k and any vertex in block `l`. -We are generating the graphs by taking random `i,j in vertices(g)` and +### Implementation Notes +Graphs are generated by taking random ``i,j ∈ V`` and flipping a coin with probability `affinities[nodemap[i],nodemap[j]]`. """ mutable struct StochasticBlockModel{T<:Integer,P<:Real} @@ -586,10 +672,10 @@ end ==(sbm::StochasticBlockModel, other::StochasticBlockModel) = (sbm.n == other.n) && (sbm.nodemap == other.nodemap) && (sbm.affinities == other.affinities) -"""A constructor for StochasticBlockModel that uses the sizes of the blocks -and the affinity matrix. This construction implies that consecutive -vertices will be in the same blocks, except for the block boundaries. -""" + +# A constructor for StochasticBlockModel that uses the sizes of the blocks +# and the affinity matrix. This construction implies that consecutive +# vertices will be in the same blocks, except for the block boundaries. function StochasticBlockModel(sizes::AbstractVector, affinities::AbstractMatrix; seed::Int = -1) csum = cumsum(sizes) j = 1 @@ -604,9 +690,12 @@ function StochasticBlockModel(sizes::AbstractVector, affinities::AbstractMatrix; end +### TODO: This documentation needs work. sbromberger 20170326 """ -Produce the sbm affinity matrix where the external probabilities are the same -the internal probabilities and sizes differ by blocks. + sbmaffinity(internalp, externalp, sizes) + +Produce the sbm affinity matrix with internal probabilities `internalp` +and external probabilities `externalp`. """ function sbmaffinity(internalp::Vector{T}, externalp::Real, sizes::Vector{U}) where T<:Real where U<:Integer numblocks = length(sizes) @@ -634,10 +723,13 @@ end const biclique = ones(2,2) - eye(2) +#TODO: this documentation needs work. sbromberger 20170326 """ + nearbipartiteaffinity(sizes, between, intra) + Construct the affinity matrix for a near bipartite SBM. -between is the affinity between the two parts of each bipartite community -intra is the probability of an edge within the parts of the partitions. +`between` is the affinity between the two parts of each bipartite community. +`intra` is the probability of an edge within the parts of the partitions. This is a specific type of SBM with k/2 blocks each with two halves. Each half is connected as a random bipartite graph with probability `intra` @@ -672,10 +764,10 @@ end """ -make_edgestream(sbm::StochasticBlockModel) + make_edgestream(sbm) -Take an infinite sample from the sbm. -Pass to `Graph(nvg, neg, edgestream)` to get a Graph object. +Take an infinite sample from the Stochastic Block Model `sbm`. +Pass to `Graph(nvg, neg, edgestream)` to get a Graph object based on `sbm`. """ function make_edgestream(sbm::StochasticBlockModel) pairs = Channel(random_pair(sbm.rng, sbm.n), ctype=Edge, csize=32) @@ -709,7 +801,12 @@ end Graph(nvg::Integer, neg::Integer, sbm::StochasticBlockModel) = Graph(nvg, neg, make_edgestream(sbm)) -"""count the number of edges that go between each block""" +#TODO: this documentation needs work. sbromberger 20170326 +""" + blockcounts(sbm, A) + +Count the number of edges that go between each block +""" function blockcounts(sbm::StochasticBlockModel, A::AbstractMatrix) # info("making Q") I = collect(1:sbm.n) diff --git a/src/generators/smallgraphs.jl b/src/generators/smallgraphs.jl index 5bb431fde..a3c061895 100644 --- a/src/generators/smallgraphs.jl +++ b/src/generators/smallgraphs.jl @@ -19,8 +19,8 @@ function _make_simple_directed_graph(n::T, edgelist::Vector{Tuple{T,T}}) where T end doc""" -smallgraph(s::Symbol) -smallgraph(s::AbstractString) + smallgraph(s) + smallgraph(s) Creates a small graph of type `s`. Admissible values for `s` are: diff --git a/src/generators/staticgraphs.jl b/src/generators/staticgraphs.jl index 951683eb8..bf5f4a9cb 100644 --- a/src/generators/staticgraphs.jl +++ b/src/generators/staticgraphs.jl @@ -2,8 +2,10 @@ # licensing details. """ -Creates a complete graph with `n` vertices. A complete graph has edges -connecting each pair of vertices. + CompleteGraph(n) + +Create an undirected [complete graph](https://en.wikipedia.org/wiki/Complete_graph) +with `n` vertices. """ function CompleteGraph(n::Integer) g = Graph(n) @@ -16,8 +18,11 @@ function CompleteGraph(n::Integer) end -"""Creates a complete bipartite graph with `n1+n2` vertices. It has edges -connecting each pair of vertices in the two sets. +@doc_str """ + CompleteBipartiteGraph(n1, n2) + +Create an undirected [complete bipartite graph](https://en.wikipedia.org/wiki/Complete_bipartite_graph) +with ``n1+n2`` vertices. """ function CompleteBipartiteGraph(n1::Integer, n2::Integer) g = Graph(n1+n2) @@ -27,8 +32,11 @@ function CompleteBipartiteGraph(n1::Integer, n2::Integer) return g end -"""Creates a complete digraph with `n` vertices. A complete digraph has edges -connecting each pair of vertices (both an ingoing and outgoing edge). +""" + CompleteDiGraph(n) + +Create a directed [complete graph](https://en.wikipedia.org/wiki/Complete_graph) +with `n` vertices. """ function CompleteDiGraph(n::Integer) g = DiGraph(n) @@ -40,8 +48,11 @@ function CompleteDiGraph(n::Integer) return g end -"""Creates a star graph with `n` vertices. A star graph has a central vertex -with edges to each other vertex. +""" + StarGraph(n) + +Create an undirected [star graph](https://en.wikipedia.org/wiki/Star_(graph_theory)) +with `n` vertices. """ function StarGraph(n::Integer) g = Graph(n) @@ -51,8 +62,11 @@ function StarGraph(n::Integer) return g end -"""Creates a star digraph with `n` vertices. A star digraph has a central -vertex with directed edges to every other vertex. +""" + StarDiGraph(n) + +Create a directed [star graph](https://en.wikipedia.org/wiki/Star_(graph_theory)) +with `n` vertices. """ function StarDiGraph(n::Integer) g = DiGraph(n) @@ -62,8 +76,12 @@ function StarDiGraph(n::Integer) return g end -"""Creates a path graph with `n` vertices. A path graph connects each -successive vertex by a single edge.""" +""" + PathGraph(n) + +Create an undirected [path graph](https://en.wikipedia.org/wiki/Path_graph) +with `n` vertices. +""" function PathGraph(n::Integer) g = Graph(n) for i = 2:n @@ -72,8 +90,12 @@ function PathGraph(n::Integer) return g end -"""Creates a path digraph with `n` vertices. A path graph connects each -successive vertex by a single directed edge.""" +""" + PathDiGraph(n) + +Creates a directed [path graph](https://en.wikipedia.org/wiki/Path_graph) +with `n` vertices. +""" function PathDiGraph(n::Integer) g = DiGraph(n) for i = 2:n @@ -82,7 +104,11 @@ function PathDiGraph(n::Integer) return g end -"""Creates a cycle graph with `n` vertices. A cycle graph is a closed path graph. +""" + CycleGraph(n) + +Create an undirected [cycle graph](https://en.wikipedia.org/wiki/Cycle_graph) +with `n` vertices. """ function CycleGraph(n::Integer) g = Graph(n) @@ -93,7 +119,11 @@ function CycleGraph(n::Integer) return g end -"""Creates a cycle digraph with `n` vertices. A cycle digraph is a closed path digraph. +""" + CycleDiGraph(n) + +Create a directed [cycle graph](https://en.wikipedia.org/wiki/Cycle_graph) +with `n` vertices. """ function CycleDiGraph(n::Integer) g = DiGraph(n) @@ -105,8 +135,11 @@ function CycleDiGraph(n::Integer) end -"""Creates a wheel graph with `n` vertices. A wheel graph is a star graph with -the outer vertices connected via a closed path graph. +""" + WheelGraph(n) + +Create an undirected [wheel graph](https://en.wikipedia.org/wiki/Wheel_graph) +with `n` vertices. """ function WheelGraph(n::Integer) g = StarGraph(n) @@ -119,8 +152,11 @@ function WheelGraph(n::Integer) return g end -"""Creates a wheel digraph with `n` vertices. A wheel graph is a star digraph -with the outer vertices connected via a closed path graph. +""" + WheelDiGraph(n) + +Create a directed [wheel graph](https://en.wikipedia.org/wiki/Wheel_graph) +with `n` vertices. """ function WheelDiGraph(n::Integer) g = StarDiGraph(n) @@ -133,11 +169,15 @@ function WheelDiGraph(n::Integer) return g end -""" -Grid(dims::AbstractVector; periodic=false) +@doc_str """ + Grid(dims; periodic=false) + +Create a ``|dims|``-dimensional cubic lattice, with length `dims[i]` +in dimension `i`. -Creates a `d`-dimensional cubic lattice, with `d=length(dims)` and length `dims[i]` in dimension `i`. -If `periodic=true` the resulting lattice will have periodic boundary condition in each dimension. +### Optional arguments +- `periodic=false`: If true, the resulting lattice will have periodic boundary +condition in each dimension. """ function Grid(dims::AbstractVector; periodic=false) if periodic @@ -154,10 +194,16 @@ function Grid(dims::AbstractVector; periodic=false) return g end -"""create a binary tree with k-levels vertices are numbered 1:2^levels-1""" -function BinaryTree(levels::Integer) - g = Graph(Int(2^levels-1)) - for i in 0:levels-2 +""" + BinaryTree(k::Integer) + +Create a [binary tree](https://en.wikipedia.org/wiki/Binary_tree) +of depth `k`. +""" + +function BinaryTree(k::Integer) + g = Graph(Int(2^k-1)) + for i in 0:k-2 for j in 2^i:2^(i+1)-1 add_edge!(g, j, 2j) add_edge!(g, j, 2j+1) @@ -167,19 +213,30 @@ function BinaryTree(levels::Integer) end """ -Create a double complete binary tree with k-levels -used as an example for spectral clustering by Guattery and Miller 1998. + BinaryTree(k::Integer) + +Create a double complete binary tree with `k` levels. + +### References +- Used as an example for spectral clustering by Guattery and Miller 1998. """ -function DoubleBinaryTree(levels::Integer) - gl = BinaryTree(levels) - gr = BinaryTree(levels) +function DoubleBinaryTree(k::Integer) + gl = BinaryTree(k) + gr = BinaryTree(k) g = blkdiag(gl, gr) add_edge!(g,1, nv(gl)+1) return g end -"""The Roach Graph from Guattery and Miller 1998""" +""" + RoachGraph(k) + +Create a Roach Graph of size `k`. + +### References +- Guattery and Miller 1998 +""" function RoachGraph(k::Integer) dipole = CompleteGraph(2) nopole = Graph(2) @@ -192,7 +249,11 @@ function RoachGraph(k::Integer) end -"""This function generates `n` connected k-cliques """ +""" + CliqueGraph(k, n) + +Create a graph consisting of `n` connected `k`-cliques. +""" function CliqueGraph(k::Integer, n::Integer) g = Graph(k*n) for c=1:n diff --git a/src/graphtypes/simplegraphs/SimpleGraphs.jl b/src/graphtypes/simplegraphs/SimpleGraphs.jl index f4ffc0e51..3cc25d862 100644 --- a/src/graphtypes/simplegraphs/SimpleGraphs.jl +++ b/src/graphtypes/simplegraphs/SimpleGraphs.jl @@ -17,6 +17,9 @@ export AbstractSimpleGraph, AbstractSimpleDiGraph, AbstractSimpleEdge, """ + AbstractSimpleGraph + +An abstract type representing a simple graph structure. AbstractSimpleGraphs must have the following elements: - vertices::UnitRange{Integer} - fadjlist::Vector{Vector{Integer}} @@ -75,14 +78,22 @@ function rem_edge!(g::AbstractSimpleGraph, u::Integer, v::Integer) rem_edge!(g, edgetype(g)(T(u), T(v))) end -""" -Remove the vertex `v` from graph `g`. +@doc_str """ + rem_vertex!(g, v) + +Remove the vertex `v` from graph `g`. Return false if removal fails +(e.g., if vertex is not in the graph); true otherwise. + +### Performance +Time complexity is ``\\mathcal{O}(k^2)``, where ``k`` is the max of the degrees +of vertex ``v`` and vertex ``|V|``. + +### Implementation Notes This operation has to be performed carefully if one keeps external data structures indexed by edges or vertices in the graph, since -internally the removal is performed swapping the vertices `v` and `n=nv(g)`, -and removing the vertex `n` from the graph. After removal the vertices in the ` g` will be indexed by 1:n-1. -This is an O(k^2) operation, where `k` is the max of the degrees of vertices `v` and `n`. -Returns false if removal fails (e.g., if vertex is not in the graph); true otherwise. +internally the removal is performed swapping the vertices `v` and ``|V|``, +and removing the last vertex ``|V|`` from the graph. After removal the +vertices in `g` will be indexed by ``1:|V|-1``. """ function rem_vertex!(g::AbstractSimpleGraph, v::Integer) v in vertices(g) || return false diff --git a/src/graphtypes/simplegraphs/simpledigraph.jl b/src/graphtypes/simplegraphs/simpledigraph.jl index 9721c871d..f86de9443 100644 --- a/src/graphtypes/simplegraphs/simpledigraph.jl +++ b/src/graphtypes/simplegraphs/simpledigraph.jl @@ -1,6 +1,10 @@ const SimpleDiGraphEdge = SimpleEdge -"""A type representing a directed graph.""" +""" + SimpleDiGraph{T} + +A type representing a directed graph. +""" mutable struct SimpleDiGraph{T<:Integer} <: AbstractSimpleGraph ne::Int fadjlist::Vector{Vector{T}} # [src]: (dst, dst, dst) diff --git a/src/graphtypes/simplegraphs/simplegraph.jl b/src/graphtypes/simplegraphs/simplegraph.jl index 831ff5454..d1357611b 100644 --- a/src/graphtypes/simplegraphs/simplegraph.jl +++ b/src/graphtypes/simplegraphs/simplegraph.jl @@ -1,6 +1,10 @@ const SimpleGraphEdge = SimpleEdge -"""A type representing an undirected graph.""" +""" + SimpleGraph{T} + +A type representing an undirected graph. +""" mutable struct SimpleGraph{T<:Integer} <: AbstractSimpleGraph ne::Int fadjlist::Vector{Vector{T}} # [src]: (dst, dst, dst) @@ -76,19 +80,27 @@ end edgetype(::SimpleGraph{T}) where T<:Integer = SimpleGraphEdge{T} """ -Returns the backwards adjacency list of a graph. -For each vertex the Array of `dst` for each edge eminating from that vertex. + badj(g::SimpleGraph[, v::Integer]) + +Return the backwards adjacency list of a graph. If `v` is specified, +return only the adjacency list for that vertex. -NOTE: returns a reference, not a copy. Do not modify result. +###Implementation Notes +Returns a reference, not a copy. Do not modify result. """ badj(g::SimpleGraph) = fadj(g) badj(g::SimpleGraph, v::Integer) = fadj(g, v) -"""Returns the adjacency list of a graph. -For each vertex the Array of `dst` for each edge eminating from that vertex. +""" + adj(g[, v]) + +Return the adjacency list of a graph. If `v` is specified, return only the +adjacency list for that vertex. + -NOTE: returns a reference, not a copy. Do not modify result. +### Implementation Notes +Returns a reference, not a copy. Do not modify result. """ adj(g::SimpleGraph) = fadj(g) adj(g::SimpleGraph, v::Integer) = fadj(g, v) @@ -101,7 +113,11 @@ ne(g) == ne(h) && fadj(g) == fadj(h) -"""Return `true` if `g` is a directed graph.""" +""" + is_directed(g) + +Return `true` if `g` is a directed graph. +""" is_directed(::Type{SimpleGraph}) = false is_directed(::Type{SimpleGraph{T}}) where T = false is_directed(g::SimpleGraph) = false @@ -143,7 +159,11 @@ function rem_edge!(g::SimpleGraph, e::SimpleGraphEdge) end -"""Add a new vertex to the graph `g`.""" +""" + add_vertex!(g) + +Add a new vertex to the graph `g`. Return true if addition was successful. +""" function add_vertex!(g::SimpleGraph) T = eltype(g) (nv(g) + one(T) <= nv(g)) && return false # test for overflow diff --git a/src/linalg/graphmatrices.jl b/src/linalg/graphmatrices.jl index 6f3b78a99..64a0c9e2b 100644 --- a/src/linalg/graphmatrices.jl +++ b/src/linalg/graphmatrices.jl @@ -28,16 +28,22 @@ export convert, const SparseMatrix{T} = SparseMatrixCSC{T,Int64} -"""An abstract type to allow opertions on any type of graph matrix""" +""" + GraphMatrix{T} + +An abstract type to allow opertions on any type of graph matrix +""" abstract type GraphMatrix{T} end """ + Adjacency{T} + The core Adjacency matrix structure. Keeps the vertex degrees around. Subtypes are used to represent the different normalizations of the adjacency matrix. Laplacian and its subtypes are used for the different Laplacian matrices. -Adjacency(lapl::Laplacian) provide a generic function for getting the +Adjacency(lapl::Laplacian) provides a generic function for getting the adjacency matrix of a Laplacian matrix. If your subtype of Laplacian does not provide an field A for the Adjacency instance, then attach another method to this function to provide an Adjacency{T} representation of the Laplacian. The Adjacency matrix here @@ -46,7 +52,11 @@ is the final subtype that corresponds to this type of Laplacian abstract type Adjacency{T} <: GraphMatrix{T} end abstract type Laplacian{T} <: GraphMatrix{T} end -"""Combinatorial Adjacency matrix is the standard adjacency matrix from math""" +""" + CombinatorialAdjacency{T,S,V} + +The standard adjacency matrix. +""" struct CombinatorialAdjacency{T,S,V} <: Adjacency{T} A::S D::V @@ -58,8 +68,10 @@ function CombinatorialAdjacency(A::SparseMatrix{T}) where T end -""" -Normalized Adjacency matrix is \$\\hat{A} = D^{-1/2} A D^{-1/2}\$. +@doc_str """ + NormalizedAdjacency{T} + +The normalized adjacency matrix is ``\\hat{A} = D^{-1/2} A D^{-1/2}``. If A is symmetric, then the normalized adjacency is also symmetric with real eigenvalues bounded by [-1, 1]. """ @@ -72,7 +84,11 @@ function NormalizedAdjacency(adjmat::CombinatorialAdjacency) return NormalizedAdjacency(adjmat, sf) end -"""Transition matrix for the random walk.""" +""" + StochasticAdjacency{T} + +A transition matrix for the random walk. +""" struct StochasticAdjacency{T} <: Adjacency{T} A::CombinatorialAdjacency{T} scalefactor::Vector{T} @@ -83,7 +99,11 @@ function StochasticAdjacency(adjmat::CombinatorialAdjacency) return StochasticAdjacency(adjmat, sf) end -"""The matrix whos action is to average over each neighborhood.""" +""" + AveragingAdjacency{T} + +The matrix whose action is to average over each neighborhood. +""" struct AveragingAdjacency{T} <: Adjacency{T} A::CombinatorialAdjacency{T} scalefactor::Vector{T} @@ -107,8 +127,13 @@ end perron(m::PunchedAdjacency) = m.perron """ -Noop: a type to represent don't do anything. -The purpose is to help write more general code for the different scaled GraphMatrix types. + Noop + +A type that represents no action. + +### Implementation Notes +- The purpose of `Noop` is to help write more general code for the +different scaled GraphMatrix types. """ struct Noop end @@ -136,8 +161,10 @@ struct CombinatorialLaplacian{T} <: Laplacian{T} A::CombinatorialAdjacency{T} end -doc""" -Normalized Laplacian is \$\\hat{L} = I - D^{-1/2} A D^{-1/2}\$. +@doc_str """ + NormalizedLaplacian{T} + +The normalized Laplacian is ``\\hat{L} = I - D^{-1/2} A D^{-1/2}``. If A is symmetric, then the normalized Laplacian is also symmetric with positive eigenvalues bounded by 2. """ @@ -145,12 +172,20 @@ struct NormalizedLaplacian{T} <: Laplacian{T} A::NormalizedAdjacency{T} end -"""Laplacian version of the StochasticAdjacency matrix.""" +""" + StochasticLaplacian{T} + +Laplacian version of the StochasticAdjacency matrix. +""" struct StochasticLaplacian{T} <: Laplacian{T} A::StochasticAdjacency{T} end -"""Laplacian version of the AveragingAdjacency matrix.""" +""" + AveragingLaplacian{T} + +Laplacian version of the AveragingAdjacency matrix. +""" struct AveragingLaplacian{T} <: Laplacian{T} A::AveragingAdjacency{T} end @@ -164,8 +199,18 @@ size(a::GraphMatrix, i::Integer) = size(a.A, i) issymmetric(::StochasticAdjacency) = false issymmetric(::AveragingAdjacency) = false -"""degrees of a graph as a Vector.""" +""" + degrees(adjmat) + +Retun the degrees of a graph represented by the CombinatorialAdjacency `adjmat`. +""" degrees(adjmat::CombinatorialAdjacency) = adjmat.D + +""" + degrees(graphmx) + +Return the degrees of a graph represented by the graph matrix `graphmx`. +""" degrees(mat::GraphMatrix) = degrees(adjacency(mat)) adjacency(lapl::Laplacian) = lapl.A @@ -261,9 +306,10 @@ end """ -Symmetrize the matrix. -:triu, :tril, :sum, :or. -use :sum for weighted graphs. + symmetrize(A::SparseMatrix, which=:or) + +Returns a symmetric version of graph (represented by sparse matrix `A`) as a sparse matrix. +`which` may be one of `:triu`, `:tril`, `:sum`, or `:or`. Use `:sum` for weighted graphs. """ function symmetrize(A::SparseMatrix, which=:or) if which==:or @@ -286,6 +332,13 @@ function symmetrize(A::SparseMatrix, which=:or) end """ + symmetrize(adjmat, which=:or) + +Returns a symmetric version of graph (represented by `CombinatorialAdjacency` `adjmat`) +as a `CombinatorialAdjacency`. `which` may be one of `:triu`, `:tril`, `:sum`, or `:or`. +Use `:sum` for weighted graphs. + +### Implementation Notes Only works on Adjacency because the normalizations don't commute with symmetrization. """ symmetrize(adjmat::CombinatorialAdjacency, which=:or) = @@ -299,5 +352,9 @@ symmetrize(adjmat::CombinatorialAdjacency, which=:or) = -"""A package for using the type system to check types of graph matrices.""" +""" + LinAlg + +A package for using the type system to check types of graph matrices +""" LinAlg diff --git a/src/linalg/nonbacktracking.jl b/src/linalg/nonbacktracking.jl index 90d3a4150..9e86a2676 100644 --- a/src/linalg/nonbacktracking.jl +++ b/src/linalg/nonbacktracking.jl @@ -3,13 +3,16 @@ export non_backtracking_matrix, contract!, contract -""" -Given two oriented edges i->j and k->l in g, the -non-backtraking matrix B is defined as +@doc_str """ + non_backtracking_matrix(g) + +Return a non-backtracking matrix B and an edgemap storing the oriented +edges' positions in B. -B[i->j, k->l] = δ(j,k)* (1 - δ(i,l)) +Given two arcs ``A_{i,j}` and `A_{k,l}` in `g`, the +non-backtraking matrix ``B`` is defined as -returns a matrix B, and an edgemap storing the oriented edges' positions in B +``B_{A_{i,j}, A_{k,l}} = δ_{j, k} * (1 - δ_{i, l})`` """ function non_backtracking_matrix(g::AbstractGraph) # idedgemap = Dict{Int, Edge}() @@ -41,25 +44,26 @@ function non_backtracking_matrix(g::AbstractGraph) return B, edgeidmap end -"""Nonbacktracking: a compact representation of the nonbacktracking operator - - g: the underlying graph - edgeidmap: the association between oriented edges and index into the NBT matrix +@doc_str """ + Nonbacktracking{G} + +A compact representation of the nonbacktracking operator. The Nonbacktracking operator can be used for community detection. This representation is compact in that it uses only ne(g) additional storage and provides an implicit representation of the matrix B_g defined below. -Given two oriented edges i->j and k->l in g, the -non-backtraking matrix B is defined as +Given two arcs ``A_{i,j}` and `A_{k,l}` in `g`, the +non-backtraking matrix ``B`` is defined as -B[i->j, k->l] = δ(j,k)* (1 - δ(i,l)) +``B_{A_{i,j}, A_{k,l}} = δ_{j, k} * (1 - δ_{i, l})`` This type is in the style of GraphMatrices.jl and supports the necessary operations for computed eigenvectors and conducting linear solves. -Additionally the contract!(vertexspace, nbt, edgespace) method takes vectors represented in -the domain of B and represents them in the domain of the adjacency matrix of g. +Additionally the `contract!(vertexspace, nbt, edgespace)` method takes vectors +represented in the domain of ``B`` and represents them in the domain of the +adjacency matrix of `g`. """ struct Nonbacktracking{G<:AbstractGraph} g::G @@ -136,8 +140,10 @@ function *(nbt::Nonbacktracking, x::AbstractMatrix) return y end -"""contract!(vertexspace, nbt, edgespace) in place version of -contract(nbt, edgespace). modifies first argument +""" + contract!(vertexspace, nbt, edgespace) + +The mutating version of `contract(nbt, edgespace)`. Modifies `vertexspace`. """ function contract!(vertexspace::Vector, nbt::Nonbacktracking, edgespace::Vector) T = eltype(nbt.g) @@ -147,8 +153,11 @@ function contract!(vertexspace::Vector, nbt::Nonbacktracking, edgespace::Vector) end end -"""contract(nbt, edgespace) -Integrates out the edges by summing over the edges incident to each vertex. +#TODO: documentation needs work. sbromberger 20170326 +""" + contract(nbt, edgespace) + +Integrate out the edges by summing over the edges incident to each vertex. """ function contract(nbt::Nonbacktracking, edgespace::Vector) y = zeros(eltype(edgespace), nv(nbt.g)) diff --git a/src/linalg/spectral.jl b/src/linalg/spectral.jl index c7a632459..51e79e50e 100644 --- a/src/linalg/spectral.jl +++ b/src/linalg/spectral.jl @@ -7,13 +7,16 @@ coo_sparse, spectral_distance """ -Returns a sparse boolean adjacency matrix for a graph, indexed by `[u, v]` -vertices. `true` values indicate an edge between `u` and `v`. Users may + adjacency_matrix(g, dir=:out, T=Int) + +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`). -Note: This function is optimized for speed. +### 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) @@ -63,7 +66,9 @@ end adjacency_matrix(g::AbstractGraph, T::DataType) = adjacency_matrix(g, :out, T) """ -Returns a sparse [Laplacian matrix](https://en.wikipedia.org/wiki/Laplacian_matrix) + laplacian_matrix(g, dir=:unspec, T=Int) + +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. @@ -77,19 +82,33 @@ function laplacian_matrix(g::AbstractGraph, dir::Symbol=:unspec, T::DataType=Int return D - A end -doc"""Returns the eigenvalues of the Laplacian matrix for a graph `g`, indexed -by vertex. Warning: Converts the matrix to dense with $nv^2$ memory usage. Use -`eigs(laplacian_matrix(g); kwargs...)` to compute some of the -eigenvalues/eigenvectors. Default values for `dir` and `T` are the same as +@doc_str """ + laplacian_spectrum(g, dir=:unspec, T=Int) + +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 `laplacian_matrix`. + +### 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))) -doc"""Returns the eigenvalues of the adjacency matrix for a graph `g`, indexed -by vertex. Warning: Converts the matrix to dense with $nv^2$ memory usage. Use -`eigs(adjacency_matrix(g);kwargs...)` to compute some of the -eigenvalues/eigenvectors. Default values for `dir` and `T` are the same as +@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 `adjacency_matrix`. + +### 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) if dir == :unspec @@ -98,12 +117,19 @@ function adjacency_spectrum(g::AbstractGraph, dir::Symbol=:unspec, T::DataType=I return eigvals(full(adjacency_matrix(g, dir, T))) end -"""Returns a sparse node-arc incidence matrix for a graph, indexed by +""" + incidence_matrix(g, T=Int) + +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 directed graphs, a value of `-1` indicates that `src(e) == v`, while a value of `1` indicates that `dst(e) == v`. Otherwise, the value is -`0`. For undirected graphs, if the optional keyword `oriented` is `false`, -both entries are `1`, otherwise, an arbitrary orientation is chosen. +`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`. """ function incidence_matrix(g::AbstractGraph, T::DataType=Int; oriented=false) isdir = is_directed(g) @@ -132,16 +158,15 @@ function incidence_matrix(g::AbstractGraph, T::DataType=Int; oriented=false) return spmx end -""" -spectral_distance(G₁, G₂ [, k]) -Compute the spectral distance between undirected n-vertex -graphs G₁ and G₂ using the top k ≤ n greatest eigenvalues. -If k is ommitted, uses full spectrum. +@doc_str """ + spectral_distance(G₁, G₂ [, k]) -For further details, please refer to: +Compute the spectral distance between undirected n-vertex +graphs `G₁` and `G₂` using the top `k` greatest eigenvalues. +If `k` is ommitted, uses full spectrum. -JOVANOVIC, I.; STANIC, Z., 2014. Spectral Distances of -Graphs Based on their Different Matrix Representations +### References +- JOVANOVIC, I.; STANIC, Z., 2014. Spectral Distances of Graphs Based on their Different Matrix Representations """ function spectral_distance end diff --git a/src/persistence/common.jl b/src/persistence/common.jl index be696a122..936624e57 100644 --- a/src/persistence/common.jl +++ b/src/persistence/common.jl @@ -11,9 +11,8 @@ const filemap = Dict{Symbol, Tuple{Function, Function, Function, Function}}() """ loadgraph(file, t=:lg) -Reads a graph from `file` in the format `t`. - -Supported formats are `:lg, :gml, :dot, :graphml, :gexf, :net, :jld, :graph6`. +Read a graph from `file` in the format `t`. +Supported formats are `:lg`, `:gml`, `:dot`, `:graphml`, `:gexf`, `:net`, `:jld`, and `:graph6`. """ function loadgraph(fn::String, x...) GZip.open(fn,"r") do io @@ -31,9 +30,9 @@ end """ load(file, name, t=:lg) -Loads a graph with name `name` from `file` in format `t`. +Load a graph with name `name` from `file` in format `t`. -Currently supported formats are `:lg, :gml, :graphml, :gexf, :dot, :net`. +Currently supported formats are `:lg`, `:gml`, `:graphml`, `:gexf`, `:dot`, `:net`, and `graph6`. """ function load(io::IO, gname::String, t::Symbol=:lg) t in keys(filemap) || error("Please select a supported graph format: one of $(keys(filemap))") @@ -43,7 +42,7 @@ end """ load(file, t=:lg) -Loads multiple graphs from `file` in the format `t`. Returns a dictionary +Load multiple graphs from `file` in the format `t`. Return a dictionary mapping graph name to graph. For unnamed graphs the default names \"graph\" and \"digraph\" will be used. @@ -63,6 +62,8 @@ end """ + savegraph(f, g, ...) + Save a graph to file. See [`save`](@ref). """ savegraph(f, g::AbstractGraph, x...; kws...) = save(f, g, x...; kws...) @@ -73,13 +74,11 @@ savegraph(f, g::AbstractGraph, x...; kws...) = save(f, g, x...; kws...) save(file, dict, t=:lg) Saves a graph `g` with name `name` to `file` in the format `t`. If `name` is not given -the default names \"graph\" and \"digraph\" will be used. +the default names \"graph\" and \"digraph\" will be used. Return the number of graphs written. -Currently supported formats are `:lg, :gml, :graphml, :gexf, :dot, :net, :graph6`. +Currently supported formats are `:lg`, `:gml`, :`graphml`, `:gexf`, `:dot`, `:net`, and `:graph6`. For some graph formats, multiple graphs in a `dict` `"name"=>g` can be saved in the same file. - -Returns the number of graphs written. """ function save(io::IO, g::AbstractGraph, gname::String, t::Symbol=:lg) t in keys(filemap) || error("Please select a supported graph format: one of $(keys(filemap))") diff --git a/src/persistence/gexf.jl b/src/persistence/gexf.jl index 4f62f0c1c..ff85122e9 100644 --- a/src/persistence/gexf.jl +++ b/src/persistence/gexf.jl @@ -2,15 +2,12 @@ # TODO: implement readgexf """ -savegexf(f::IO, g::AbstractGraph, gname::String) + savegexf(f, g, gname) -Writes a graph `g` with name `gname` -to a file `f` in the -[Gexf](http://gexf.net/format/) format. - -Returns 1 (number of graphs written). +Write a graph `g` with name `gname` to an IO stream `io` in the +[Gexf](http://gexf.net/format/) format. Return 1 (number of graphs written). """ -function savegexf(f::IO, g::AbstractGraph, gname::String) +function savegexf(io::IO, g::AbstractGraph, gname::String) xdoc = XMLDocument() xroot = setroot!(xdoc, ElementNode("gexf")) xroot["xmlns"] = "http://www.gexf.net/1.2draft" @@ -40,7 +37,7 @@ function savegexf(f::IO, g::AbstractGraph, gname::String) m += 1 end - prettyprint(f, xdoc) + prettyprint(io, xdoc) return 1 end diff --git a/src/persistence/gml.jl b/src/persistence/gml.jl index 0ea706995..34cd2eea3 100644 --- a/src/persistence/gml.jl +++ b/src/persistence/gml.jl @@ -38,11 +38,10 @@ function loadgml_mult(io::IO) end """ -savegml(f, g, gname = "graph") + savegml(f, g, gname="graph") -Writes a graph `g` with name `gname` -to a file `f` in the -[GML](https://en.wikipedia.org/wiki/Graph_Modelling_Language) format. +Write a graph `g` with name `gname` to an IO stream `io` in the +[GML](https://en.wikipedia.org/wiki/Graph_Modelling_Language) format. Return 1. """ function savegml(io::IO, g::AbstractGraph, gname::String = "") println(io, "graph") @@ -68,9 +67,9 @@ function savegml(io::IO, g::AbstractGraph, gname::String = "") end -"""Writes a dictionary of (name=>graph) to a file `fn` - -Returns number of graphs written. +""" + savegml_mult(io, graphs) +Write a dictionary of (name=>graph) to an IO stream `io` Return number of graphs written. """ function savegml_mult(io::IO, graphs::Dict) ng = 0 diff --git a/src/persistence/graph6.jl b/src/persistence/graph6.jl index daec8dbb0..d8b81b8ee 100644 --- a/src/persistence/graph6.jl +++ b/src/persistence/graph6.jl @@ -69,7 +69,11 @@ function _g6_Np(N::Vector{UInt8}) end -"""Given a graph, create the corresponding Graph6 string""" +""" + _graphToG6String(g) + +Given a graph `g`, create the corresponding Graph6 string +""" function _graphToG6String(g::Graph) A = adjacency_matrix(g, Bool) n = nv(g) @@ -120,15 +124,20 @@ function loadgraph6_mult(io::IO) return graphdict end -"""Reads a graph from file `fname` in the [Graph6](http://users.cecs.anu.edu.au/%7Ebdm/data/formats.txt) format. - Returns the graph. +""" + loadgraph6(io, gname="g1") + +Read a graph from IO stream `io` in the [Graph6](http://users.cecs.anu.edu.au/%7Ebdm/data/formats.txt) +format. Return the graph. """ loadgraph6(io::IO, gname::String="g1") = loadgraph6_mult(io)[gname] """ -Writes a graph `g` to a file `f` in the [Graph6](http://users.cecs.anu.edu.au/%7Ebdm/data/formats.txt) format. -Returns 1 (number of graphs written). + savegraph6(io, g, gname="g") + +Write a graph `g` to IO stream `io` in the [Graph6](http://users.cecs.anu.edu.au/%7Ebdm/data/formats.txt) +format. Return 1 (number of graphs written). """ function savegraph6(io::IO, g::AbstractGraph, gname::String = "g") str = _graphToG6String(g) diff --git a/src/persistence/jld.jl b/src/persistence/jld.jl index 9fdb2d9cd..08aded632 100644 --- a/src/persistence/jld.jl +++ b/src/persistence/jld.jl @@ -1,5 +1,7 @@ using JLD """ + GraphSerializer + GraphSerializer is a type for custom serialization into JLD files. It has no use except on disk. This type supports JLD.writeas(g::Graph) and JLD.readas(gs::GraphSerializer). It is a form of Compressed Sparse Column format diff --git a/src/persistence/lg.jl b/src/persistence/lg.jl index 163cf2bb2..cef7fe7c9 100644 --- a/src/persistence/lg.jl +++ b/src/persistence/lg.jl @@ -35,7 +35,11 @@ function _lg_skip_one_graph(f::IO, n_e::Integer) end end -"""Returns a dictionary of (name=>graph) loaded from file `fn`.""" +""" + loadlg_mult(io) + +Return a dictionary of (name=>graph) loaded from IO stream `io`. +""" function loadlg_mult(io::IO) graphs = Dict{String, AbstractGraph}() while !eof(io) @@ -76,10 +80,11 @@ function loadlg(io::IO, gname::String) error("Graph $gname not found") end -"""Writes a graph `g` with name `graphname` in a proprietary format -to the IO stream designated by `io`. +""" + savelg(io, g, gname) -Returns 1 (number of graphs written). +Write a graph `g` with name `gname` in a proprietary format +to the IO stream designated by `io`. Return 1 (number of graphs written). """ function savelg(io::IO, g::AbstractGraph, gname::String) # write header line @@ -93,10 +98,11 @@ function savelg(io::IO, g::AbstractGraph, gname::String) return 1 end -"""Writes a dictionary of (name=>graph) to a file `fn`, -with default `GZip` compression. +""" + savelg_mult(io, graphs) -Returns number of graphs written. +Write a dictionary of (name=>graph) to an IO stream `io`, +with default `GZip` compression. Return number of graphs written. """ function savelg_mult(io::IO, graphs::Dict) ng = 0 diff --git a/src/persistence/net.jl b/src/persistence/net.jl index a2efdaf8f..15dc8fd7f 100644 --- a/src/persistence/net.jl +++ b/src/persistence/net.jl @@ -1,37 +1,40 @@ """ -Writes a graph `g` to a file `f` in the [Pajek -NET](http://gephi.github.io/users/supported-graph-formats/pajek-net-format/) format. -Returns 1 (number of graphs written). + savenet(io, g, gname="g") + +Write a graph `g` to an IO stream `io` in the [Pajek NET](http://gephi.github.io/users/supported-graph-formats/pajek-net-format/) +format. Return 1 (number of graphs written). """ -function savenet(f::IO, g::AbstractGraph, gname::String = "g") - println(f, "*Vertices $(nv(g))") +function savenet(io::IO, g::AbstractGraph, gname::String = "g") + println(io, "*Vertices $(nv(g))") # write edges if is_directed(g) - println(f, "*Arcs") + println(io, "*Arcs") else - println(f, "*Edges") + println(io, "*Edges") end for e in edges(g) - println(f, "$(src(e)) $(dst(e))") + println(io, "$(src(e)) $(dst(e))") end return 1 end -"""Reads a graph from file `fname` in the [Pajek - NET](http://gephi.github.io/users/supported-graph-formats/pajek-net-format/) format. - Returns the graph. """ -function loadnet(f::IO, gname::String = "g") - line =readline(f) + loadnet(io::IO, gname="g") + +Read a graph from IO stream `io` in the [Pajek NET](http://gephi.github.io/users/supported-graph-formats/pajek-net-format/) +format. Return the graph. +""" +function loadnet(io::IO, gname::String = "g") + line =readline(io) # skip comments while startswith(line, "%") - line =readline(f) + line =readline(io) end n = parse(Int, matchall(r"\d+",line)[1]) - for fline in eachline(f) - line = fline + for ioline in eachline(io) + line = ioline (ismatch(r"^\*Arcs",line) || ismatch(r"^\*Edges",line)) && break end if ismatch(r"^\*Arcs",line) @@ -40,16 +43,16 @@ function loadnet(f::IO, gname::String = "g") g = Graph(n) end while ismatch(r"^\*Arcs",line) - for fline in eachline(f) - line = fline + for ioline in eachline(io) + line = ioline m = matchall(r"\d+",line) length(m) < 2 && break add_edge!(g, parse(Int, m[1]), parse(Int, m[2])) end end while ismatch(r"^\*Edges",line) # add edges in both directions - for fline in eachline(f) - line = fline + for ioline in eachline(io) + line = ioline m = matchall(r"\d+",line) length(m) < 2 && break i1,i2 = parse(Int, m[1]), parse(Int, m[2]) diff --git a/src/shortestpaths/astar.jl b/src/shortestpaths/astar.jl index 49e32bd9d..c42779fed 100644 --- a/src/shortestpaths/astar.jl +++ b/src/shortestpaths/astar.jl @@ -4,7 +4,7 @@ # A* shortest-path algorithm function a_star_impl!( - graph::AbstractGraph,# the graph + g::AbstractGraph,# the graph t::Integer, # the end vertex frontier, # an initialized heap containing the active vertices colormap::Vector{Int}, # an (initialized) color-map to indicate status of vertices @@ -18,7 +18,7 @@ function a_star_impl!( return path end - for v in LightGraphs.out_neighbors(graph, u) + for v in LightGraphs.out_neighbors(g, u) if colormap[v] < 2 dist = distmx[u, v] @@ -36,12 +36,16 @@ function a_star_impl!( end """ -Computes the shortest path between vertices `s` and `t` using the -[A\* search algorithm](http://en.wikipedia.org/wiki/A%2A_search_algorithm). An -optional heuristic function and edge distance matrix may be supplied. + a_star(g, s, t[, distmx][, heuristic]) + +Return a vector of edges comprising the shortest path between vertices `s` and `t` +using the [A\* search algorithm](http://en.wikipedia.org/wiki/A%2A_search_algorithm). +An optional heuristic function and edge distance matrix may be supplied. If missing, +the distance matrix is set to `DefaultDistance` and the heuristic is set to +`n -> 0`. """ function a_star( - graph::AbstractGraph, # the graph + g::AbstractGraph, # the g s::Integer, # the start vertex t::Integer, # the end vertex @@ -49,10 +53,10 @@ function a_star( heuristic::Function = n -> 0 ) where T # heuristic (under)estimating distance to target - U = eltype(graph) + U = eltype(g) frontier = PriorityQueue(Tuple{T,Vector{Edge},U},T) frontier[(zero(T), Vector{Edge}(), s)] = zero(T) - colormap = zeros(Int, nv(graph)) + colormap = zeros(Int, nv(g)) colormap[s] = 1 - a_star_impl!(graph, t, frontier, colormap, distmx, heuristic) + a_star_impl!(g, t, frontier, colormap, distmx, heuristic) end diff --git a/src/shortestpaths/bellman-ford.jl b/src/shortestpaths/bellman-ford.jl index e7ef5ab18..2632c6b80 100644 --- a/src/shortestpaths/bellman-ford.jl +++ b/src/shortestpaths/bellman-ford.jl @@ -12,6 +12,11 @@ struct NegativeCycleError <: Exception end # AbstractPathState is defined in core +""" + BellmanFordState{T, U} + +An `AbstractPathState` designed for Bellman-Ford shortest-paths calculations. +""" struct BellmanFordState{T<:Number, U<:Integer}<:AbstractPathState parents::Vector{U} dists::Vector{T} @@ -54,10 +59,13 @@ function bellman_ford_shortest_paths!( return state end -"""Uses the [Bellman-Ford algorithm](http://en.wikipedia.org/wiki/Bellman–Ford_algorithm) -to compute shortest paths between a source vertex `s` or a set of source -vertices `ss`. Returns a `BellmanFordState` with relevant traversal information -(see below). +""" + bellman_ford_shortest_paths(g, s, distmx=DefaultDistance()) + bellman_ford_shortest_paths(g, ss, distmx=DefaultDistance()) + +Compute shortest paths between a source `s` (or list of sources `ss`) and all +other nodes in graph `g` using the [Bellman-Ford algorithm](http://en.wikipedia.org/wiki/Bellman–Ford_algorithm). +Return a `BellmanFordState` with relevant traversal information. """ function bellman_ford_shortest_paths( graph::AbstractGraph, @@ -86,14 +94,14 @@ function has_negative_edge_cycle(g::AbstractGraph, distmx::AbstractMatrix) return false end -function enumerate_paths(state::AbstractPathState, dest::Vector{T}) where T<:Integer +function enumerate_paths(state::AbstractPathState, vs::Vector{T}) where T<:Integer parents = state.parents - num_dest = length(dest) - all_paths = Vector{Vector{T}}(num_dest) - for i=1:num_dest + num_vs = length(vs) + all_paths = Vector{Vector{T}}(num_vs) + for i=1:num_vs all_paths[i] = Vector{T}() - index = dest[i] + index = vs[i] if parents[index] != 0 || parents[index] == index while parents[index] != 0 push!(all_paths[i], index) @@ -106,19 +114,21 @@ function enumerate_paths(state::AbstractPathState, dest::Vector{T}) where T<:Int all_paths end -enumerate_paths(state::AbstractPathState, dest) = enumerate_paths(state, [dest])[1] +enumerate_paths(state::AbstractPathState, v) = enumerate_paths(state, [v])[1] enumerate_paths(state::AbstractPathState) = enumerate_paths(state, [1:length(state.parents);]) """ -Given a path state `state` of type `AbstractPathState` (see below), returns a + enumerate_paths(state[, vs]) +Given a path state `state` of type `AbstractPathState`, return a vector (indexed by vertex) of the paths between the source vertex used to -compute the path state and a destination vertex `v`, a set of destination -vertices `vs`, or the entire graph. For multiple destination vertices, each +compute the path state and a single destination vertex, a list of destination +vertices, or the entire graph. For multiple destination vertices, each path is represented by a vector of vertices on the path between the source and the destination. Nonexistent paths will be indicated by an empty vector. For single destinations, the path is represented by a single vector of vertices, and will be length 0 if the path does not exist. +### Implementation Notes For Floyd-Warshall path states, please note that the output is a bit different, since this algorithm calculates all shortest paths for all pairs of vertices: `enumerate_paths(state)` will return a vector (indexed by source vertex) of diff --git a/src/shortestpaths/dijkstra.jl b/src/shortestpaths/dijkstra.jl index c9c1c189b..cb571f0ef 100644 --- a/src/shortestpaths/dijkstra.jl +++ b/src/shortestpaths/dijkstra.jl @@ -1,5 +1,3 @@ -abstract type AbstractDijkstraState<:AbstractPathState end - struct DijkstraHeapEntry{T, U<:Integer} vertex::U dist::T @@ -7,20 +5,29 @@ end isless(e1::DijkstraHeapEntry, e2::DijkstraHeapEntry) = e1.dist < e2.dist -struct DijkstraState{T, U<:Integer}<: AbstractDijkstraState +""" + struct DijkstraState{T, U} + +An `AbstractPathState` designed for Dijkstra shortest-paths calculations. +""" +struct DijkstraState{T, U<:Integer}<: AbstractPathState parents::Vector{U} dists::Vector{T} predecessors::Vector{Vector{U}} pathcounts::Vector{U} end -"""Performs [Dijkstra's algorithm](http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) -on a graph, computing shortest distances between a source vertex `s` and all -other nodes. Returns a `DijkstraState` that contains various traversal -information (see below). +""" +dijkstra_shortest_paths(g, srcs, distmx=DefaultDistance()); + allpaths=false + ) +Perform [Dijkstra's algorithm](http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) +on a graph, computing shortest distances between `srcs` and all other nodes. +Return a `DijkstraState` that contains various traversal information. -With `allpaths=true`, returns a `DijkstraState` that keeps track of all -predecessors of a given vertex (see below). +### Optional arguments +- `allpaths=false`: If true, returns a `DijkstraState` that keeps track of all +predecessors of a given vertex. """ function dijkstra_shortest_paths( g::AbstractGraph, diff --git a/src/shortestpaths/floyd-warshall.jl b/src/shortestpaths/floyd-warshall.jl index 88589e1aa..f6d6710a0 100644 --- a/src/shortestpaths/floyd-warshall.jl +++ b/src/shortestpaths/floyd-warshall.jl @@ -2,20 +2,25 @@ # licensing details. +""" + struct FloydWarshallState{T, U} + +An `AbstractPathState` designed for Floyd-Warshall shortest-paths calculations. +""" struct FloydWarshallState{T, U<:Integer}<:AbstractPathState dists::Matrix{T} parents::Matrix{U} end -doc""" -Uses the [Floyd-Warshall algorithm](http://en.wikipedia.org/wiki/Floyd–Warshall_algorithm) -to compute shortest paths between all pairs of vertices in graph `g`. Returns a -`FloydWarshallState` with relevant traversal information, each is a -vertex-indexed vector of vectors containing the metric for each vertex in the -graph. +@doc_str """ +floyd_warshall_shortest_paths(g, distmx=DefaultDistance()) +Use the [Floyd-Warshall algorithm](http://en.wikipedia.org/wiki/Floyd–Warshall_algorithm) +to compute the shortest paths between all pairs of vertices in graph `g` using an +optional distance matrix `distmx`. Return a `FloydWarshallState` with relevant +traversal information -Note that this algorithm may return a large amount of data (it will allocate -on the order of $\mathcal{O}(nv^2)$). +### Performance +Space complexity is on the order of ``\\mathcal{O}(|V|^2)``. """ function floyd_warshall_shortest_paths{T}( g::AbstractGraph, diff --git a/src/spanningtrees/kruskal.jl b/src/spanningtrees/kruskal.jl index 02ee51fd1..ea74634b7 100644 --- a/src/spanningtrees/kruskal.jl +++ b/src/spanningtrees/kruskal.jl @@ -6,9 +6,10 @@ end isless(e1::KruskalHeapEntry, e2::KruskalHeapEntry) = e1.dist < e2.dist """ -Performs [Quick-Find algorithm](https://en.wikipedia.org/wiki/Disjoint-set_data_structure) -on a given pair of nodes `p`and `q`, and makes a connection between them -in the vector `nodes`. + quick_find!(nodes, p, q) + +Perform [Quick-Find algorithm](https://en.wikipedia.org/wiki/Disjoint-set_data_structure) +on a given pair of nodes `p`and `q`, and make a connection between them in the vector `nodes`. """ function quick_find!(nodes, p, q) pid = nodes[p] @@ -20,15 +21,17 @@ function quick_find!(nodes, p, q) end end -"""Performs [Kruskal's algorithm](https://en.wikipedia.org/wiki/Kruskal%27s_algorithm) -on a connected, non-directional graph `g`, having adjacency matrix `distmx`, -and computes minimum spanning tree. Returns a `Vector{KruskalHeapEntry}`, -that contains the containing edges and its weights. """ -function kruskal_mst( - g::AbstractGraph, + kruskal_mst(g, distmx=DefaultDistance()) + +Return a vector of edges representing the minimum spanning tree of a connected, undirected graph `g` with optional +distance matrix `distmx` using [Kruskal's algorithm](https://en.wikipedia.org/wiki/Kruskal%27s_algorithm). +""" +function kruskal_mst end +@traitfn function kruskal_mst{T}( + g::::(!IsDirected), distmx::AbstractMatrix{T} = DefaultDistance() -) where T<:Real +) U = eltype(g) edge_list = Vector{KruskalHeapEntry{T}}() diff --git a/src/spanningtrees/prim.jl b/src/spanningtrees/prim.jl index 0337c23a2..0d330cc4e 100644 --- a/src/spanningtrees/prim.jl +++ b/src/spanningtrees/prim.jl @@ -6,13 +6,15 @@ end isless(e1::PrimHeapEntry, e2::PrimHeapEntry) = e1.dist < e2.dist """ -Performs [Prim's algorithm](https://en.wikipedia.org/wiki/Prim%27s_algorithm) -on a connected, non-directional graph `g`, having adjacency matrix `distmx`, -and computes minimum spanning tree. Returns a `Vector{Edge}`, -that contains the edges. + prim_mst(g, distmx=DefaultDistance()) + +Return a vector of edges representing the minimum spanning tree of a connected, undirected graph `g` with optional +distance matrix `distmx` using [Prim's algorithm](https://en.wikipedia.org/wiki/Prim%27s_algorithm). +Return a vector of edges. """ -function prim_mst( - g::AbstractGraph, +function prim_mst end +@traitfn function prim_mst( + g::::(!IsDirected), distmx::AbstractMatrix = DefaultDistance() ) pq = Vector{PrimHeapEntry}() @@ -39,8 +41,10 @@ function prim_mst( end """ -Used to mark the visited vertices. Marks the vertex `v` of graph `g` true in the array `marked` -and enters all its edges into priority queue `pq` with its `distmx` values as a PrimHeapEntry. + visit!(g, v, marked, pq, distmx) + +Mark the vertex `v` of graph `g` true in the array `marked` and enter all its +edges into priority queue `pq` with its `distmx` values as a PrimHeapEntry. """ function visit!( g::AbstractGraph, diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 4eaa4815f..308995be1 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -9,19 +9,19 @@ # ################################################# """ -**Conventions in Breadth First Search and Depth First Search** -VertexColorMap : + BreadthFirst + +## Conventions in Breadth First Search and Depth First Search +### VertexColorMap - color == 0 => unseen - color < 0 => examined but not closed - color > 0 => examined and closed -EdgeColorMap : +### EdgeColorMap - color == 0 => unseen -- color == 1 => examined +- color == 1 => examined """ - -mutable struct BreadthFirst <: AbstractGraphVisitAlgorithm -end +mutable struct BreadthFirst <: AbstractGraphVisitAlgorithm end function breadth_first_visit_impl!( g::AbstractGraph, # the graph @@ -87,8 +87,10 @@ end # Constructing BFS trees # ########################################### -"""TreeBFSVisitorVector is a type for representing a BFS traversal -of the graph as a parents array. This type allows for a more performant implementation. +""" + TreeBFSVisitorVector{T} + +A type for representing a BFS traversal of the graph as a parents array. """ mutable struct TreeBFSVisitorVector{T<:Integer} <: AbstractGraphVisitor tree::Vector{T} @@ -98,7 +100,11 @@ function TreeBFSVisitorVector(n::Integer) return TreeBFSVisitorVector(fill(zero(n), n)) end -"""tree converts a parents array into a DiGraph""" +""" + tree(parents) + +Convert a parents array into a `DiGraph` +""" function tree(parents::AbstractVector{T}) where T<:Integer n = T(length(parents)) t = DiGraph(n) @@ -139,10 +145,13 @@ function bfs_tree!(visitor::TreeBFSVisitorVector{T}, traverse_graph!(g, BreadthFirst(), s, visitor; vertexcolormap=vertexcolormap, queue=queue) end -"""Provides a breadth-first traversal of the graph `g` starting with source vertex `s`, -and returns a directed acyclic graph of vertices in the order they were discovered. +""" + bfs_tree(g, s) +Provide a breadth-first traversal of the graph `g` starting with source vertex `s`, +and return a directed acyclic graph of vertices in the order they were discovered. -This function is a high level wrapper around bfs_tree!, use that function for more performance. +### Implementation Notes +This function is a high level wrapper around bfs_tree!; use that function for more performance. """ function bfs_tree(g::AbstractGraph, s::Integer) nvg = nv(g) @@ -154,7 +163,12 @@ end ############################################ # Connected Components with BFS # ############################################ -"""Performing connected components with BFS starting from seed""" +""" + ComponentVisitorVector{T} + +A type of `AbstractGraphVisitor` that represents connected components with +BFS starting from a given seed. +""" mutable struct ComponentVisitorVector{T<:Integer} <: AbstractGraphVisitor labels::Vector{T} seed::T @@ -191,10 +205,9 @@ function examine_neighbor!(visitor::BipartiteVisitor, u::Integer, v::Integer, end """ - is_bipartite(g) - is_bipartite(g, v) + is_bipartite(g[, v]) -Will return `true` if graph `g` is [bipartite](https://en.wikipedia.org/wiki/Bipartite_graph). +Return `true` if graph `g` is [bipartite](https://en.wikipedia.org/wiki/Bipartite_graph). If a node `v` is specified, only the connected component to which it belongs is considered. """ function is_bipartite(g::AbstractGraph) @@ -221,10 +234,11 @@ function _bipartite_visitor(g::AbstractGraph, s::Integer; vmap=Dict{eltype(g),In return visitor end -""" -If the graph is bipartite returns a vector `c` of size `nv(g)` containing -the assignment of each vertex to one of the two sets (`c[i] == 1` or `c[i]==2`). -If `g` is not bipartite returns an empty vector. +@doc_str """ + bipartite_map(g) +For a bipartite graph `g`, return a vector `c` of size ``|V|`` containing +the assignment of each vertex to one of the two sets (``c_i == 1`` or c_i ==2``). +If `g` is not bipartite, return an empty vector. """ function bipartite_map(g::AbstractGraph) cc = connected_components(g) @@ -242,10 +256,11 @@ end ########################################### """ - gdistances!(g, source, dists) -> dists + gdistances!(g, source, dists) -Fills `dists` with the geodesic distances of vertices in `g` from vertex/vertices `source`. -`dists` should be a vector of length `nv(g)`. +Fill `dists` with the geodesic distances of vertices in `g` from `source`. +`dists` should be a vector of length `nv(g)`. Return `dists`. +For vertices in disconnected components the default distance is -1. """ function gdistances!(g::AbstractGraph, source, dists) T = eltype(g) @@ -275,10 +290,10 @@ end """ - gdistances(g, source) -> dists + gdistances(g, source) -Returns a vector filled with the geodesic distances of vertices in `g` from vertex/vertices `source`. -If `source` is a collection of vertices they should be unique (not checked). +Return a vector filled with the geodesic distances of vertices in `g` from +`source`. If `source` is a collection of vertices each element should be unique. For vertices in disconnected components the default distance is -1. """ gdistances(g::AbstractGraph, source) = gdistances!(g, source, Vector{Int}(nv(g))) diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index 4e815a2c4..4b1fe0756 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -10,19 +10,18 @@ # ################################################# """ -**Conventions in Breadth First Search and Depth First Search** -VertexColorMap : + DepthFirst +## Conventions in Breadth First Search and Depth First Search +### VertexColorMap - color == 0 => unseen - color < 0 => examined but not closed - color > 0 => examined and closed -EdgeColorMap : +### EdgeColorMap - color == 0 => unseen - color == 1 => examined """ - -mutable struct DepthFirst <: AbstractGraphVisitAlgorithm -end +mutable struct DepthFirst <: AbstractGraphVisitAlgorithm end function depth_first_visit_impl!( g::AbstractGraph, # the graph @@ -115,8 +114,10 @@ discover_vertex!(vis::DFSCyclicTestVisitor, v) = !vis.found_cycle """ is_cyclic(g) -Tests whether a graph contains a cycle through depth-first search. It -returns `true` when it finds a cycle, otherwise `false`. +Returns `true` if graph `g` contains a cycle. + +### Implementation Notes +Uses DFS. """ function is_cyclic(g::AbstractGraph) cmap = zeros(Int, nv(g)) @@ -181,10 +182,10 @@ function examine_neighbor!(visitor::TreeDFSVisitor, u::Integer, v::Integer, ucol end """ - dfs_tree(g, s::Integer) + dfs_tree(g, s) -Provides a depth-first traversal of the graph `g` starting with source vertex `s`, -and returns a directed acyclic graph of vertices in the order they were discovered. +Returns an ordered vector of vertices representing a directed acylic graph based on +depth-first traversal of the graph `g` starting with source vertex `s`. """ function dfs_tree(g::AbstractGraph, s::Integer) nvg = nv(g) diff --git a/src/traversals/maxadjvisit.jl b/src/traversals/maxadjvisit.jl index f628e5b82..6ff65664f 100644 --- a/src/traversals/maxadjvisit.jl +++ b/src/traversals/maxadjvisit.jl @@ -174,14 +174,17 @@ end ################################################# -"""Returns a tuple `(parity, bestcut)`, where `parity` is a vector of integer +""" + mincut(g, distmx=DefaultDistance()) + +Return a tuple `(parity, bestcut)`, where `parity` is a vector of integer values that determines the partition in `g` (1 or 2) and `bestcut` is the weight of the cut that makes this partition. An optional `distmx` matrix may be specified; if omitted, edge distances are assumed to be 1. """ function mincut{T}( g::AbstractGraph, - distmx::AbstractMatrix{T} + distmx::AbstractMatrix{T}=DefaultDistance() ) visitor = MinCutVisitor(g, distmx) colormap = zeros(Int, nv(g)) @@ -189,9 +192,11 @@ function mincut{T}( return(visitor.parities + 1, visitor.bestweight) end -mincut(g::AbstractGraph) = mincut(g,DefaultDistance()) -"""Returns the vertices in `g` traversed by maximum adjacency search. An optional +""" + maximum_adjacency_visit(g[, distmx][, log][, io]) + +Return the vertices in `g` traversed by maximum adjacency search. An optional `distmx` matrix may be specified; if omitted, edge distances are assumed to be 1. If `log` (default `false`) is `true`, visitor events will be printed to `io`, which defaults to `STDOUT`; otherwise, no event information will be diff --git a/src/traversals/randomwalks.jl b/src/traversals/randomwalks.jl index 30a533f14..148512f2e 100644 --- a/src/traversals/randomwalks.jl +++ b/src/traversals/randomwalks.jl @@ -1,5 +1,8 @@ -"""Performs a random walk on graph `g` starting at vertex `s` and continuing for -a maximum of `niter` steps. Returns a vector of vertices visited in order. +""" + randomwalk(g, s, niter) + +Perform a random walk on graph `g` starting at vertex `s` and continuing for +a maximum of `niter` steps. Return a vector of vertices visited in order. """ function randomwalk(g::AbstractGraph, s::Integer, niter::Integer) T = eltype(g) @@ -18,8 +21,12 @@ function randomwalk(g::AbstractGraph, s::Integer, niter::Integer) return visited[1:i-1] end -"""Performs a non-backtracking random walk on graph `g` starting at vertex `s` and continuing for -a maximum of `niter` steps. Returns a vector of vertices visited in order. +""" + non_backtracking_randomwalk(g, s, niter) + +Perform a non-backtracking random walk on directed graph `g` starting at +vertex `s` and continuing for a maximum of `niter` steps. Return a +vector of vertices visited in order. """ function non_backtracking_randomwalk end @traitfn function non_backtracking_randomwalk(g::::(!IsDirected), s::Integer, niter::Integer) @@ -83,9 +90,11 @@ end return visited[1:i-1] end -"""Performs a [self-avoiding walk](https://en.wikipedia.org/wiki/Self-avoiding_walk) +""" + saw(g, s, niter) +Perform a [self-avoiding walk](https://en.wikipedia.org/wiki/Self-avoiding_walk) on graph `g` starting at vertex `s` and continuing for a maximum of `niter` steps. -Returns a vector of vertices visited in order. +Return a vector of vertices visited in order. """ function saw(g::AbstractGraph, s::Integer, niter::Integer) T = eltype(g) diff --git a/test/flow/dinic.jl b/test/flow/dinic.jl index 0787964bc..1165c5769 100644 --- a/test/flow/dinic.jl +++ b/test/flow/dinic.jl @@ -34,14 +34,14 @@ for dst in collect(neighbors(residual_graph, source)) rem_edge!(h, source, dst) end - @test @inferred(LightGraphs.blocking_flow!(h, source, target, capacity_matrix, flow_matrix)) == 0 + @test @inferred(LightGraphs.blocking_flow(h, source, target, capacity_matrix, flow_matrix)) == 0 #disconnect target and add unreachable vertex h = copy(residual_graph) for src in collect(in_neighbors(residual_graph, target)) rem_edge!(h, src, target) end - @test @inferred(LightGraphs.blocking_flow!(h, source, target, capacity_matrix, flow_matrix)) == 0 + @test @inferred(LightGraphs.blocking_flow(h, source, target, capacity_matrix, flow_matrix)) == 0 # unreachable vertex (covers the case where a vertex isn't reachable from the source) h = copy(residual_graph) @@ -50,10 +50,10 @@ capacity_matrix_ = vcat(hcat(capacity_matrix, zeros(Int, nv(residual_graph))), zeros(Int, 1, nv(residual_graph)+1)) flow_graph_ = vcat(hcat(flow_matrix, zeros(Int, nv(residual_graph))), zeros(Int, 1, nv(residual_graph)+1)) - @test @inferred(LightGraphs.blocking_flow!(h, source, target, capacity_matrix_, flow_graph_ )) > 0 + @test @inferred(LightGraphs.blocking_flow(h, source, target, capacity_matrix_, flow_graph_ )) > 0 #test with connected graph - @test @inferred(LightGraphs.blocking_flow!(residual_graph, source, target, capacity_matrix, flow_matrix)) > 0 + @test @inferred(LightGraphs.blocking_flow(residual_graph, source, target, capacity_matrix, flow_matrix)) > 0 end flow_matrix = zeros(Int, nv(residual_graph), nv(residual_graph)) From 0a5b473a378ffd4daf99691d47ff8e22c8a74956 Mon Sep 17 00:00:00 2001 From: Seth Bromberger <github@bromberger.com> Date: Mon, 27 Mar 2017 13:03:42 -0700 Subject: [PATCH 2/2] nodes -> vertices, node -> vertex, and more doc consistency --- README.md | 125 +++++------------ src/LightGraphs.jl | 9 +- src/biconnectivity/articulation.jl | 2 +- src/centrality/betweenness.jl | 29 ++-- src/centrality/closeness.jl | 6 +- src/centrality/degree.jl | 4 +- src/centrality/pagerank.jl | 6 +- src/community/cliques.jl | 8 +- src/community/clustering.jl | 11 +- src/community/core-periphery.jl | 2 +- src/community/label_propagation.jl | 26 ++-- src/connectivity.jl | 12 +- src/core.jl | 20 +-- src/digraph-transitivity.jl | 9 +- src/edit_distance.jl | 9 +- src/flow/boykov_kolmogorov.jl | 9 +- src/flow/ext_multiroute_flow.jl | 6 +- src/flow/maximum_flow.jl | 16 +-- src/flow/multiroute_flow.jl | 44 +++--- src/flow/push_relabel.jl | 7 +- src/generators/euclideangraphs.jl | 30 ++--- src/generators/randgraphs.jl | 125 ++++++++--------- src/generators/smallgraphs.jl | 2 +- src/generators/staticgraphs.jl | 4 +- src/graphtypes/simplegraphs/simplegraph.jl | 3 +- src/interface.jl | 131 ++++++++++++++---- src/linalg/graphmatrices.jl | 12 +- src/linalg/nonbacktracking.jl | 14 +- src/linalg/spectral.jl | 4 +- src/matching/README.md | 2 - src/operators.jl | 148 +++++++++++++-------- src/persistence/graph6.jl | 2 +- src/shortestpaths/astar.jl | 2 +- src/shortestpaths/bellman-ford.jl | 2 +- src/shortestpaths/dijkstra.jl | 10 +- src/shortestpaths/floyd-warshall.jl | 6 +- src/spanningtrees/kruskal.jl | 22 +-- src/traversals/bfs.jl | 8 +- src/traversals/dfs.jl | 4 +- src/utils.jl | 19 ++- test/shortestpaths/dijkstra.jl | 4 +- 41 files changed, 500 insertions(+), 414 deletions(-) delete mode 100644 src/matching/README.md diff --git a/README.md b/README.md index 27514835a..15b207dc1 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ better-optimized mechanisms. Additional functionality may be found in the companion package [LightGraphsExtras.jl](https://github.com/JuliaGraphs/LightGraphsExtras.jl). + ## Documentation Full documentation is available at [GitHub Pages](https://juliagraphs.github.io/LightGraphs.jl/latest). Documentation for methods is also available via the Julia REPL help system. @@ -38,33 +39,19 @@ A graph *G* is described by a set of vertices *V* and edges *E*: *G = {V, E}*. *V* is an integer range `1:n`; *E* is represented as forward (and, for directed graphs, backward) adjacency lists indexed by vertices. Edges may also be accessed via an iterator that yields `Edge` types containing -`(src::Int, dst::Int)` values. +`(src<:Integer, dst<:Integer)` values. Both vertices and edges may be integers +of any type, and the smallest type that fits the data is recommended in order +to save memory. *LightGraphs.jl* provides two graph types: `Graph` is an undirected graph, and `DiGraph` is its directed counterpart. Graphs are created using `Graph()` or `DiGraph()`; there are several options -(see below for examples). - -Edges are added to a graph using `add_edge!(g, e)`. Instead of an edge type -integers may be passed denoting the source and destination vertices (e.g., -`add_edge!(g, 1, 2)`). +(see the tutorials for examples). Multiple edges between two given vertices are not allowed: an attempt to add an edge that already exists in a graph will result in a silent failure. -Edges may be removed using `rem_edge!(g, e)`. Alternately, integers may be passed -denoting the source and destination vertices (e.g., `rem_edge!(g, 1, 2)`). Note -that, particularly for very large graphs, edge removal is a (relatively) -expensive operation. An attempt to remove an edge that does not exist in the graph will result in an -error. - -Use `nv(g)` and `ne(g)` to compute the number of vertices and edges respectively. - -`rem_vertex!(g, v)` alters the vertex identifiers. In particular, calling `n=nv(g)`, it swaps `v` and `n` and then removes `n`. - -`edges(g)` returns an iterator to the edge set. Use `collect(edge(set))` to fill -an array with all edges in the graph. ## Installation Installation is straightforward: @@ -72,57 +59,6 @@ Installation is straightforward: julia> Pkg.add("LightGraphs") ``` -## Usage Examples -(all examples apply equally to `DiGraph` unless otherwise noted): - -```julia -# create an empty undirected graph -g = Graph() - -# create a 10-node undirected graph with no edges -g = Graph(10) -@assert nv(g) == 10 - -# create a 10-node undirected graph with 30 randomly-selected edges -g = Graph(10,30) - -# add an edge between vertices 4 and 5 -add_edge!(g, 4, 5) - -# remove an edge between vertices 9 and 10 -rem_edge!(g, 9, 10) - -# create vertex 11 -add_vertex!(g) - -# remove vertex 2 -# attention: this changes the id of vertex nv(g) to 2 -rem_vertex!(g, 2) - -# get the neighbors of vertex 4 -neighbors(g, 4) - -# iterate over the edges -m = 0 -for e in edges(g) - m += 1 -end -@assert m == ne(g) - -# show distances between vertex 4 and all other vertices -dijkstra_shortest_paths(g, 4).dists - -# as above, but with non-default edge distances -distmx = zeros(10,10) -distmx[4,5] = 2.5 -distmx[5,4] = 2.5 -dijkstra_shortest_paths(g, 4, distmx).dists - -# graph I/O -g = loadgraph("mygraph.jgz", :lg) -savegraph("mygraph.gml", g, :gml) -``` - ## Current functionality - **core functions:** vertices and edges addition and removal, degree (in/out/histogram), neighbors (in/out/all/common) @@ -155,37 +91,43 @@ symmetric difference, blkdiag, induced subgraphs, products (cartesian/scalar) - **community:** modularity, community detection, core-periphery, clustering coefficients -- **persistence formats:** proprietary compressed, [GraphML](http://en.wikipedia.org/wiki/GraphML), [GML](https://en.wikipedia.org/wiki/Graph_Modelling_Language), [Gexf](http://gexf.net/format), [DOT](https://en.wikipedia.org/wiki/DOT_(graph_description_language)), [Pajek NET](http://gephi.org/users/supported-graph-formats/pajek-net-format/) +- **persistence formats:** proprietary compressed, [GraphML](http://en.wikipedia.org/wiki/GraphML), [GML](https://en.wikipedia.org/wiki/Graph_Modelling_Language), [Gexf](http://gexf.net/format), [DOT](https://en.wikipedia.org/wiki/DOT_(graph_description_language)), [Pajek NET](http://gephi.org/users/supported-graph-formats/pajek-net-format/), [Graph6](http://users.cecs.anu.edu.au/~bdm/data/formats.html) -- **visualization:** integration with [GraphLayout](https://github.com/IainNZ/GraphLayout.jl), [TikzGraphs](https://github.com/sisl/TikzGraphs.jl), [GraphPlot](https://github.com/JuliaGraphs/GraphPlot.jl), [NetworkViz](https://github.com/abhijithanilkumar/NetworkViz.jl/) +- **visualization:** integration with +[GraphPlot](https://github.com/JuliaGraphs/GraphPlot.jl), +[Plots](https://github.com/JuliaPlots/Plots.jl) via [PlotRecipes](https://github.com/JuliaPlots/PlotRecipes.jl), [GraphLayout](https://github.com/IainNZ/GraphLayout.jl), [TikzGraphs](https://github.com/sisl/TikzGraphs.jl), [NetworkViz](https://github.com/abhijithanilkumar/NetworkViz.jl/) ## Core API -These functions are defined as the public contract of the LightGraphs.AbstractGraph interface. +These functions are defined as the public contract of the `LightGraphs.AbstractGraph` interface. ### Constructing and modifying the graph - -- add_edge! -- rem_edge! -- add_vertex! -- add_vertices! -- rem_vertex! +- `Graph` +- `DiGraph` +- `add_edge!` +- `rem_edge!` +- `add_vertex!`, `add_vertices!` +- `rem_vertex!` +- `zero` ### Edge/Arc interface -- src -- dst +- `src` +- `dst` +- `reverse` +- `==` +- Pair / Tuple conversion ### Accessing state -- nv::Int -- ne::Int -- vertices (Iterable) -- edges (Iterable) -- neighbors -- in_edges -- out_edges -- has_vertex -- has_edge -- has_self_loops (though this might be a trait or an abstract graph type) +- `nv` +- `ne` +- `vertices` (Iterable) +- `edges` (Iterable) +- `neighbors`, `in_neighbors`, `out_neighbors` +- `in_edges` +- `out_edges` +- `has_vertex` +- `has_edge` +- `has_self_loops` (though this might be a trait or an abstract graph type) ### Non-Core APIs @@ -202,7 +144,8 @@ This can be computed from neighbors by default `degree(g,v) = length(neighbors(g * Julia 0.3: LightGraphs v0.3.7 is the last version guaranteed to work with Julia 0.3. * Julia 0.4: LightGraphs versions in the 0.6 series are designed to work with Julia 0.4. * Julia 0.5: LightGraphs versions in the 0.7 series are designed to work with Julia 0.5. -* Julia 0.6: Some functionality might not work with prerelease / unstable / nightly versions of Julia. If you run into a problem on 0.6, please file an issue. +* Julia 0.6: LightGraphs versions in the 1.0 series are designed to work with Julia 0.6. +* Later versions: Some functionality might not work with prerelease / unstable / nightly versions of Julia. If you run into a problem, please file an issue. # Contributing and Reporting Bugs We welcome contributions and bug reports! Please see [CONTRIBUTING.md](https://github.com/JuliaGraphs/LightGraphs.jl/blob/master/CONTRIBUTING.md) diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 8e87a7fc9..3793034ac 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -117,7 +117,10 @@ kruskal_mst, prim_mst, #biconnectivity and articulation points articulation, biconnected_components -"""An optimized graphs package. +""" + LightGraphs + +An optimized graphs package. Simple graphs (not multi- or hypergraphs) are represented in a memory- and time-efficient manner with adjacency lists and edge sets. Both directed and @@ -131,6 +134,10 @@ explicit design decision that any data not required for graph manipulation (attributes and other information, for example) is expected to be stored outside of the graph structure itself. Such data lends itself to storage in more traditional and better-optimized mechanisms. + +[Full documentation](http://codecov.io/github/JuliaGraphs/LightGraphs.jl) is available, +and tutorials are available at the +[JuliaGraphsTutorials repository](https://github.com/JuliaGraphs/JuliaGraphsTutorials). """ LightGraphs include("interface.jl") diff --git a/src/biconnectivity/articulation.jl b/src/biconnectivity/articulation.jl index 9701b1e9f..9e8524a35 100644 --- a/src/biconnectivity/articulation.jl +++ b/src/biconnectivity/articulation.jl @@ -21,7 +21,7 @@ end Perform a depth first search storing the depth (in `depth`) and low-points (in `low`) of each vertex. -Call this function repeatedly to complete the DFS (see `articulation` for usage). +Call this function repeatedly to complete the DFS (see [`articulation`](@ref) for usage). """ function visit!(state::Articulations, g::AbstractGraph, u::Integer, v::Integer) children = 0 diff --git a/src/centrality/betweenness.jl b/src/centrality/betweenness.jl index 5c56fd717..ab9da8ed7 100644 --- a/src/centrality/betweenness.jl +++ b/src/centrality/betweenness.jl @@ -3,20 +3,19 @@ @doc_str """ - betweenness_centrality(g[, nodes]) + betweenness_centrality(g[, vs]) betweenness_centrality(g, k) Calculate the [betweenness centrality](https://en.wikipedia.org/wiki/Centrality#Betweenness_centrality) -of a graph `g` across all nodes, a specified subset of nodes `nodes`, or a random subset of `k` -nodes. Return a vector representing the centrality calculated for each node in `g`. - -### Optional arguments -* `normalize=true`: If true, normalize the betweenness values by the - total number of possible distinct paths between all pairsin the graphs. - For an undirected graph, this number is ``\\frac{(|V|-1)(|V|-2)}{2}`` - and for a directed graph, ``\\frac{(|V|-1)(|V|-2)}``. -* `endpoints=false`: If true, include endpoints in the shortest path count. +of a graph `g` across all vertices, a specified subset of vertices `vs`, or a random subset of `k` +vertices. Return a vector representing the centrality calculated for each node in `g`. +### Optional Arguments +- `normalize=true`: If true, normalize the betweenness values by the +total number of possible distinct paths between all pairsin the graphs. +For an undirected graph, this number is ``\\frac{(|V|-1)(|V|-2)}{2}`` +and for a directed graph, ``\\frac{(|V|-1)(|V|-2)}``. +- `endpoints=false`: If true, include endpoints in the shortest path count. Betweenness centrality is defined as: `` @@ -25,20 +24,20 @@ bc(v) = \\frac{1}{\\mathcal{N}} \sum_{s \\neq t \\neq v} ``. ### References -* Brandes 2001 & Brandes 2008 +- Brandes 2001 & Brandes 2008 """ function betweenness_centrality( g::AbstractGraph, - nodes::AbstractVector = vertices(g); + vs::AbstractVector = vertices(g); normalize=true, endpoints=false) n_v = nv(g) - k = length(nodes) + k = length(vs) isdir = is_directed(g) betweenness = zeros(n_v) - for s in nodes + for s in vs if degree(g,s) > 0 # this might be 1? state = dijkstra_shortest_paths(g, s; allpaths=true) if endpoints @@ -77,7 +76,7 @@ function _accumulate_basic!( # make sure the source index has no parents. P[si] = [] - # we need to order the source nodes by decreasing distance for this to work. + # we need to order the source vertices by decreasing distance for this to work. S = sortperm(state.dists, rev=true) for w in S coeff = (1.0 + δ[w]) / σ[w] diff --git a/src/centrality/closeness.jl b/src/centrality/closeness.jl index b15702796..2bd734525 100644 --- a/src/centrality/closeness.jl +++ b/src/centrality/closeness.jl @@ -4,9 +4,9 @@ Calculate the [closeness centrality](https://en.wikipedia.org/wiki/Centrality#Closeness_centrality) of the graph `g`. Return a vector representing the centrality calculated for each node in `g`. -### Optional arguments -* `normalize=true`: If true, normalize the centrality value of each -node `n` by ``\\frac{|δ_n|}{|V|-1}, where ``δ_n`` is the set of nodes reachable +### Optional Arguments +- `normalize=true`: If true, normalize the centrality value of each +node `n` by ``\\frac{|δ_n|}{|V|-1}, where ``δ_n`` is the set of vertices reachable from node `n`. """ function closeness_centrality( diff --git a/src/centrality/degree.jl b/src/centrality/degree.jl index 23c9401a4..b0b267612 100644 --- a/src/centrality/degree.jl +++ b/src/centrality/degree.jl @@ -23,8 +23,8 @@ end Calculate the [degree centrality](https://en.wikipedia.org/wiki/Centrality#Degree_centrality) of graph `g`. Return a vector representing the centrality calculated for each node in `g`. -### Optional arguments: -* `normalize=true`: If true, normalize each centrality measure by ``\frac{1}{|V|-1}``. +### Optional Arguments +- `normalize=true`: If true, normalize each centrality measure by ``\frac{1}{|V|-1}``. """ degree_centrality(g::AbstractGraph; all...) = _degree_centrality(g, 0; all...) indegree_centrality(g::AbstractGraph; all...) = _degree_centrality(g, 1; all...) diff --git a/src/centrality/pagerank.jl b/src/centrality/pagerank.jl index 41c588633..df860ce77 100644 --- a/src/centrality/pagerank.jl +++ b/src/centrality/pagerank.jl @@ -6,9 +6,9 @@ Calculate the [PageRank](https://en.wikipedia.org/wiki/PageRank) of the directed graph `g` parameterized by damping factor `α`, number of -iterations `n`, and convergence threshold `ϵ` (default 1.0e-6). Return a -vector representing the centrality calculated for each node in `g`, or an -error if convergence is not reached within `n` iterations. +iterations `n`, and convergence threshold `ϵ`. Return a vector representing +the centrality calculated for each node in `g`, or an error if convergence +is not reached within `n` iterations. """ function pagerank end diff --git a/src/community/cliques.jl b/src/community/cliques.jl index c995df4bc..30a72f16f 100644 --- a/src/community/cliques.jl +++ b/src/community/cliques.jl @@ -7,7 +7,7 @@ """ maximal_cliques(g) - + Return a vector of vectors representing the node indices in each of the maximal cliques found in the undirected graph `g`. @@ -59,7 +59,7 @@ function maximal_cliques end # Start main loop while !isempty(smallcand) || !isempty(stack) - if !isempty(smallcand) # Any nodes left to check? + if !isempty(smallcand) # Any vertices left to check? n = pop!(smallcand) else # back out clique_so_far @@ -90,7 +90,7 @@ function maximal_cliques end continue end # find pivot node (max connected in cand) - # look in done nodes first + # look in done vertices first numb_cand = length(new_cand) maxconndone = -1 for n in new_done @@ -110,7 +110,7 @@ function maximal_cliques end continue end # still finding pivot node - # look in cand nodes second + # look in cand vertices second maxconn = -1 for n in new_cand cn = intersect(new_cand, nnbrs[n]) diff --git a/src/community/clustering.jl b/src/community/clustering.jl index ba71ab10c..ef31fb250 100644 --- a/src/community/clustering.jl +++ b/src/community/clustering.jl @@ -3,7 +3,7 @@ local_clustering_coefficient(g, vs) Return the [local clustering coefficient](https://en.wikipedia.org/wiki/Clustering_coefficient) -for node `v` in graph `g`. If a list of nodes `vs` is specified, return a vector +for node `v` in graph `g`. If a list of vertices `vs` is specified, return a vector of coefficients for each node in the list. """ function local_clustering_coefficient(g::AbstractGraph, v::Integer) @@ -15,12 +15,12 @@ local_clustering_coefficient(g::AbstractGraph, vs = vertices(g)) = [local_clustering_coefficient(g, v) for v in vs] -""" +@doc_str """ local_clustering(g, v) local_clustering(g, vs) -Return a tuple `(a,b)`, where `a` is the number of triangles in the neighborhood -of `v` and `b` is the maximum number of possible triangles. If a list of nodes +Return a tuple `(a, b)`, where `a` is the number of triangles in the neighborhood +of `v` and `b` is the maximum number of possible triangles. If a list of vertices `vs` is specified, return two vectors representing the number of triangles and the maximum number of possible triangles, respectively, for each node in the list. @@ -55,7 +55,7 @@ end triangles(g, vs) Return the number of triangles in the neighborhood of node `v` in graph `g`. -If a list of nodes `vs` is specified, return a vector of number of triangles +If a list of vertices `vs` is specified, return a vector of number of triangles for each node in the list. If no vertices are specified, return the number of triangles for each node in the graph. """ @@ -65,6 +65,7 @@ triangles(g::AbstractGraph, vs = vertices(g)) = local_clustering(g, vs)[1] """ global_clustering_coefficient(g) + Return the [global clustering coefficient](https://en.wikipedia.org/wiki/Clustering_coefficient) of graph `g`. """ diff --git a/src/community/core-periphery.jl b/src/community/core-periphery.jl index dd77849ea..7831a85ac 100644 --- a/src/community/core-periphery.jl +++ b/src/community/core-periphery.jl @@ -2,7 +2,7 @@ core_periphery_deg(g) Compute the degree-based core-periphery for graph `g`. Return the vertex -assignments (1 for core and 2 for periphery) for each node in `g`. +assignments (`1` for core and `2` for periphery) for each node in `g`. References: [Lip](http://arxiv.org/abs/1102.5511)) diff --git a/src/community/label_propagation.jl b/src/community/label_propagation.jl index 4d491d391..ebcba9ae7 100644 --- a/src/community/label_propagation.jl +++ b/src/community/label_propagation.jl @@ -1,30 +1,30 @@ """ label_propagation(g, maxiter=1000) -Community detection using the label propagation algorithm. Return two vectors: -the first is the label number assigned to each node, and the second is the -convergence history for each node. Will return after `maxiter` iterations -if convergence has not completed. +Community detection using the label propagation algorithm. +Return two vectors: the first is the label number assigned to each node, and +the second is the convergence history for each node. Will return after +`maxiter` iterations if convergence has not completed. -References: - [Raghavan et al.](http://arxiv.org/abs/0709.2938) +### References +- [Raghavan et al.](http://arxiv.org/abs/0709.2938) """ function label_propagation(g::AbstractGraph, maxiter=1000) T = eltype(g) n = nv(g) label = collect(one(T):n) - active_nodes = IntSet(vertices(g)) + active_vs = IntSet(vertices(g)) c = NeighComm(collect(one(T):n), fill(-1,n), one(T)) convergence_hist = Vector{Int}() random_order = Vector{T}(n) i = 0 - while !isempty(active_nodes) && i < maxiter - num_active = length(active_nodes) + while !isempty(active_vs) && i < maxiter + num_active = length(active_vs) push!(convergence_hist, num_active) i += 1 - # processing nodes in random order - for (j,node) in enumerate(active_nodes) + # processing vertices in random order + for (j,node) in enumerate(active_vs) random_order[j] = node end range_shuffle!(1:num_active, random_order) @@ -34,10 +34,10 @@ function label_propagation(g::AbstractGraph, maxiter=1000) label[u] = vote!(g, label, c, u) if old_comm != label[u] for v in out_neighbors(g, u) - push!(active_nodes, v) + push!(active_vs, v) end else - delete!(active_nodes, u) + delete!(active_vs, u) end end end diff --git a/src/connectivity.jl b/src/connectivity.jl index 85d592892..cc703db7a 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -34,7 +34,7 @@ end components_dict(labels) Convert an array of labels to a map of component id to vertices, and return -a `Dict{Integer,Vector{Int}}` with each key corresponding to a given component id +a map with each key corresponding to a given component id and each value containing the vertices associated with that component. """ function components_dict(labels::Vector{T}) where T<:Integer @@ -157,7 +157,7 @@ end """ strongly_connected_components(g) -Computes the strongly connected components of a directed graph `g`. +Compute the strongly connected components of a directed graph `g`. """ function strongly_connected_components end @traitfn function strongly_connected_components(g::::IsDirected) @@ -189,7 +189,7 @@ function is_strongly_connected end """ period(g) -Return the (common) period for all nodes in a strongly connected directed graph. +Return the (common) period for all vertices in a strongly connected directed graph. Will throw an error if the graph is not strongly connected. """ function period end @@ -246,7 +246,7 @@ Return the condensation graph associated with `g`. The condensation `h` of a graph `g` is the directed graph where every node in `h` represents a strongly connected component in `g`, and the presence -of an edge between between nodes in `h` indicates that there is at least one +of an edge between between vertices in `h` indicates that there is at least one edge between the associated strongly connected components in `g`. The node numbering in `h` corresponds to the ordering of the components output from `strongly_connected_components`. @@ -299,7 +299,7 @@ end Return a vector of the vertices in `g` at a geodesic distance less or equal to `d` from `v`. -### Optional arguments +### Optional Arguments - `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`. """ @@ -316,7 +316,7 @@ end """ isgraphical(degs) -Check whether the degree sequence `degs` is graphical, according to +Return true if the degree sequence `degs` is graphical, according to [Erdös-Gallai condition](http://mathworld.wolfram.com/GraphicSequence.html). ### Performance diff --git a/src/core.jl b/src/core.jl index d12dc4595..a22ecc297 100644 --- a/src/core.jl +++ b/src/core.jl @@ -51,40 +51,40 @@ degree(g::AbstractGraph, v::AbstractVector = vertices(g)) = [degree(g, x) for x """ Δout(g) -Return the maximum `outdegree` of vertices in `g`. +Return the maximum [`outdegree`](@ref) of vertices in `g`. """ Δout(g) = noallocextreme(outdegree,(>), typemin(Int), g) """ δout(g) -Return the minimum `outdegree` of vertices in `g`. +Return the minimum [`outdegree`](@ref) of vertices in `g`. """ δout(g) = noallocextreme(outdegree,(<), typemax(Int), g) """ Δin(g) -Return the maximum `indegree` of vertices in `g`. +Return the maximum [`indegree`](@ref) of vertices in `g`. """ Δin(g) = noallocextreme(indegree,(>), typemin(Int), g) """ δin(g) -Return the minimum `indegree` of vertices in `g`. +Return the minimum [`indegree`](ref) of vertices in `g`. """ δin(g) = noallocextreme(indegree,(<), typemax(Int), g) """ Δ(g) -Return the maximum `degree` of vertices in `g`. +Return the maximum [`degree`](@ref) of vertices in `g`. """ Δ(g) = noallocextreme(degree,(>), typemin(Int), g) """ δ(g) -Return the minimum `degree` of vertices in `g`. +Return the minimum [`degree`](@ref) of vertices in `g`. """ δ(g) = noallocextreme(degree,(<), typemax(Int), g) @@ -115,8 +115,8 @@ degree_histogram(g::AbstractGraph) = fit(Histogram, degree(g)) neighbors(g, v) Return a list of all neighbors reachable from vertex `v` in `g`. -For DiGraphs, the default is equivalent to `out_neighbors(g, v)`; -use `all_neighbors` to list inbound and outbound neighbors. +For directed graphs, the default is equivalent to [`out_neighbors`](@ref); +use [`all_neighbors`](@ref) to list inbound and outbound neighbors. ### Implementation Notes Returns a reference, not a copy. Do not modify result. @@ -126,8 +126,8 @@ neighbors(g::AbstractGraph, v::Integer) = out_neighbors(g, v) """ all_neighbors(g, v) Return a list of all inbound and outbound neighbors of `v` in `g`. -For undirected graphs, this is equivalent to `out_neighbors` and -`in_neighbors`. +For undirected graphs, this is equivalent to both [`out_neighbors`](@ref) +and [`in_neighbors`](@ref). ### Implementation Notes Returns a reference, not a copy. Do not modify result. diff --git a/src/digraph-transitivity.jl b/src/digraph-transitivity.jl index 96686dec3..bcea8fb73 100644 --- a/src/digraph-transitivity.jl +++ b/src/digraph-transitivity.jl @@ -4,10 +4,11 @@ Compute the transitive closure of a directed graph, using the Floyd-Warshall algorithm. If `selflooped` is true, add self loops to the graph. -This version of the function modifies the original graph. - ### Performance Time complexity is \\mathcal{O}(|V|^3). + +### Implementation Notes +This version of the function modifies the original graph. """ function transitiveclosure! end @traitfn function transitiveclosure!(g::::IsDirected, selflooped=false) @@ -31,8 +32,8 @@ end transitiveclosure(g, selflooped=false) Compute the transitive closure of a directed graph, using the Floyd-Warshall -algorithm. Return a graph represetning the transitive closure. If `selflooped` -is true, add self loops to the graph. +algorithm. Return a graph representing the transitive closure. If `selflooped` +is `true`, add self loops to the graph. ### Performance Time complexity is \\mathcal{O}(|V|^3). diff --git a/src/edit_distance.jl b/src/edit_distance.jl index 774cb1864..d20746048 100644 --- a/src/edit_distance.jl +++ b/src/edit_distance.jl @@ -1,5 +1,5 @@ @doc_str """ -edit_distance(G₁::AbstractGraph, G₂::AbstractGraph) + edit_distance(G₁::AbstractGraph, G₂::AbstractGraph) Compute the edit distance between graphs `G₁` and `G₂`. Return the minimum edit cost and edit path to transform graph `G₁` into graph `G₂``. @@ -11,7 +11,7 @@ representing vertex operations: - ``(u>0,v>0)``: substitution of vertex ``u ∈ G₁`` by vertex ``v ∈ G₂`` -### Optional arguments +### Optional Arguments - `insert_cost::Function=v->1.0` - `delete_cost::Function=u->1.0` - `subst_cost::Function=(u,v)->0.5` @@ -24,7 +24,6 @@ search, for example: ``` edit_distance(G₁, G₂, subst_cost=MinkowskiCost(μ₁, μ₂)) ``` - - `heuristic::Function=DefaultEditHeuristic`: a custom heuristic provided to the A* search in case the default heuristic is not satisfactory. @@ -116,7 +115,7 @@ For labels μ₁ on the vertices of graph G₁ and labels μ₂ on the vertices of graph G₂, compute the p-norm cost of substituting vertex u ∈ G₁ by vertex v ∈ G₂. -### Optional arguments +### Optional Arguments `p=1`: the p value for p-norm calculation. """ function MinkowskiCost(μ₁::AbstractVector, μ₂::AbstractVector; p::Real=1) @@ -128,7 +127,7 @@ end Return value similar to `MinkowskiCost`, but ensure costs smaller than 2τ. -### Optional arguments +### Optional Arguments `p=1`: the p value for p-norm calculation. `τ=1`: value specifying half of the upper limit of the Minkowski cost. """ diff --git a/src/flow/boykov_kolmogorov.jl b/src/flow/boykov_kolmogorov.jl index c92e2e4c9..c42772758 100644 --- a/src/flow/boykov_kolmogorov.jl +++ b/src/flow/boykov_kolmogorov.jl @@ -7,11 +7,12 @@ using the Boykov-Kolmogorov algorithm. Return the maximum flow in the network, the flow matrix and the partition `{S,T}` in the form of a vector of 0's, 1's and 2's. -References: - BOYKOV, Y.; KOLMOGOROV, V., 2004. An Experimental Comparison of - Min-Cut/Max-Flow Algorithms for Energy Minimization in Vision. +### References +- BOYKOV, Y.; KOLMOGOROV, V., 2004. An Experimental Comparison of +Min-Cut/Max-Flow Algorithms for Energy Minimization in Vision. -Author: Júlio Hoffimann Mendes (juliohm@stanford.edu) +### Author +- Júlio Hoffimann Mendes (juliohm@stanford.edu) """ function boykov_kolmogorov_impl end @traitfn function boykov_kolmogorov_impl( diff --git a/src/flow/ext_multiroute_flow.jl b/src/flow/ext_multiroute_flow.jl index d01b783ee..4d13a0817 100644 --- a/src/flow/ext_multiroute_flow.jl +++ b/src/flow/ext_multiroute_flow.jl @@ -10,8 +10,8 @@ Boykov-Kolmogorov max-flow algorithm is used as a subroutine. Otherwise, return the vector of breaking points of the parametric multiroute flow function. -### Reference: -* [Extended Multiroute Flow algorithm](http://dx.doi.org/10.1016/j.disopt.2016.05.002) +### References +- [Extended Multiroute Flow algorithm](http://dx.doi.org/10.1016/j.disopt.2016.05.002) """ # EMRF (Extended Multiroute Flow) algorithms function emrf( @@ -49,7 +49,7 @@ function auxiliaryPoints end ) # Problem descriptors λ = maximum_flow(flow_graph, source, target)[1] # Connectivity - n = nv(flow_graph) # number of nodes + n = nv(flow_graph) # number of vertices r1, r2 = minmaxCapacity(capacity_matrix) # restriction left (1) and right (2) auxpoints = fill((0., 0.), λ + 1) diff --git a/src/flow/maximum_flow.jl b/src/flow/maximum_flow.jl index 3ca8cd932..9dc5ef931 100644 --- a/src/flow/maximum_flow.jl +++ b/src/flow/maximum_flow.jl @@ -15,7 +15,7 @@ struct EdmondsKarpAlgorithm <: AbstractFlowAlgorithm end """ DinicAlgorithm <: AbstractFlowAlgorithm -Forces the maximum_flow function to use Dinic\'s algorithm. +Forces the maximum_flow function to use Dinic's algorithm. """ struct DinicAlgorithm <: AbstractFlowAlgorithm end @@ -34,7 +34,7 @@ struct PushRelabelAlgorithm <: AbstractFlowAlgorithm end """ DefaultCapacity{T} -Structure that returns 1 if a forward edge exists in `flow_graph`, and 0 otherwise. +Structure that returns `1` if a forward edge exists in `flow_graph`, and `0` otherwise. """ struct DefaultCapacity{T<:Integer} <: AbstractMatrix{T} flow_graph::DiGraph @@ -53,7 +53,7 @@ ctranspose(d::DefaultCapacity) = DefaultCapacity(reverse(d.flow_graph)) """ residual(flow_graph) -Returns a directed residual graph for a directed `flow_graph`. +Return a directed residual graph for a directed `flow_graph`. The residual graph comprises the same node list as the orginal flow graph, but ensures that for each edge (u,v), (v,u) also exists in the graph. This allows @@ -61,7 +61,7 @@ flow in the reverse direction. If only the forward edge exists, a reverse edge is created with capacity 0. If both forward and reverse edges exist, their capacities are left unchanged. -Since the capacities in `DefaultDistance` cannot be changed, an array of ones +Since the capacities in [`DefaultDistance`](@ref) cannot be changed, an array of ones is created. """ function residual end @@ -120,14 +120,14 @@ end end """ -maximum_flow(flow_graph, source, target[, capacity_matrix][, algorithm][, restriction]) + maximum_flow(flow_graph, source, target[, capacity_matrix][, algorithm][, restriction]) Generic maximum_flow function for `flow_graph` from `source` to `target` with -capacities in `capacity_matrix`. Uses flow algorithm `algorithm` and cutoff restriction -`restriction`. +capacities in `capacity_matrix`. +Uses flow algorithm `algorithm` and cutoff restriction `restriction`. - If `capacity_matrix` is not specified, `DefaultCapacity(flow_graph)` will be used. -- If `algorithm` is not specified, it will default to `PushRelabelAlgorithm`. +- If `algorithm` is not specified, it will default to [`PushRelabelAlgorithm`](@ref). - If `restriction` is not specified, it will default to `0`. Return a tuple of (maximum flow, flow matrix). For the Boykov-Kolmogorov diff --git a/src/flow/multiroute_flow.jl b/src/flow/multiroute_flow.jl index cdae9845e..90449150b 100644 --- a/src/flow/multiroute_flow.jl +++ b/src/flow/multiroute_flow.jl @@ -8,14 +8,14 @@ abstract type AbstractMultirouteFlowAlgorithm end """ KishimotoAlgorithm -Forces the multiroute_flow function to use the Kishimoto algorithm. +Used to specify the Kishimoto algorithm. """ struct KishimotoAlgorithm <: AbstractMultirouteFlowAlgorithm end """ ExtendedMultirouteFlowAlgorithm -Forces the multiroute_flow function to use the Extended Multiroute Flow algorithm. +Used to specify the Extended Multiroute Flow algorithm. """ struct ExtendedMultirouteFlowAlgorithm <: AbstractMultirouteFlowAlgorithm end @@ -98,7 +98,7 @@ end ### TODO: CLEAN UP THIS FUNCTION AND DOCUMENTATION. THERE SHOULD BE NO NEED TO ### HAVE A TYPE-UNSTABLE FUNCTION HERE. (sbromberger 2017-03-26) """ -multiroute_flow(flow_graph, source, target[, DefaultCapacity][, flow_algorithm][, mrf_algorithm][, routes]) + multiroute_flow(flow_graph, source, target[, DefaultCapacity][, flow_algorithm][, mrf_algorithm][, routes]) The generic multiroute_flow function. @@ -116,44 +116,44 @@ returned as a third output. When the input is a network, it requires the following arguments: -- flow_graph::DiGraph # the input graph -- source::Integer # the source vertex -- target::Integer # the target vertex -- capacity_matrix::AbstractMatrix{T} # edge flow capacities with T<:Real -- flow_algorithm::AbstractFlowAlgorithm # keyword argument for flow algorithm -- mrf_algorithm::AbstractFlowAlgorithm # keyword argument for multiroute flow algorithm -- routes::R<:Real # keyword argument for the number of routes +- `flow_graph`: the input graph +- `source`: the source vertex +- `target`: the target vertex +- `capacity_matrix`: matrix of edge flow capacities +- `flow_algorithm`: keyword argument for flow algorithm +- `mrf_algorithm`: keyword argument for multiroute flow algorithm +- `routes`: keyword argument for the number of routes When the input is only the set of (breaking) points and the number of route, it requires the following arguments: -- breakingpoints::Vector{Tuple{T, T, Int}}, # vector of breaking points -- routes::R<:Real, # number of routes +- `breakingpoints`: vector of breaking points +- `routes`: number of routes When the input is the set of (breaking) points, the number of routes, and the network descriptors, it requires the following arguments: -- breakingpoints::Vector{Tuple{T1, T1, Int}} # vector of breaking points (T1<:Real) -- routes::R<:Real # number of routes -- flow_graph::DiGraph # the input graph -- source::Integer # the source vertex -- target::Integer # the target vertex -- capacity_matrix::AbstractMatrix # optional edge flow capacities (T2<:Real) -- flow_algorithm::AbstractFlowAlgorithm # keyword argument for algorithm +- `breakingpoints`: vector of breaking points +- `routes`: number of routes +- `flow_graph`: the input graph +- `source`: the source vertex +- `target`: the target vertex +- `capacity_matrix`: matrix of edge flow capacities +- `flow_algorithm`: keyword argument for flow algorithm The function defaults to the Push-relabel (classical flow) and Kishimoto (multiroute) algorithms. Alternatively, the algorithms to be used can also -be specified through keyword arguments. A default capacity of 1 is assumed +be specified through keyword arguments. A default capacity of `1` is assumed for each link if no capacity matrix is provided. -The mrf_algorithm keyword is inforced to Extended Multiroute Flow +The `mrf_algorithm` keyword is inforced to Extended Multiroute Flow in the following cases: - The number of routes is non-integer - The number of routes is 0 or non-specified ### Usage Example : -(please consult the max_flow section for options about flow_algorithm +(please consult the [`max_flow`](@ref) section for options about flow_algorithm and capacity_matrix) ```jldoctest diff --git a/src/flow/push_relabel.jl b/src/flow/push_relabel.jl index 1e2ab297e..803af8f98 100644 --- a/src/flow/push_relabel.jl +++ b/src/flow/push_relabel.jl @@ -73,8 +73,9 @@ end """ push_flow!(residual_graph, u, v, capacity_matrix, flow_matrix, excess, height, active, Q) -Using `residual_graph` with capacities in `capacity_matrix`, push as much flow as possible through the given edge(`u`, `v`). -Requires preallocated `flow_matrix` matrix, and `excess`, `height, `active`, and `Q` vectors. +Using `residual_graph` with capacities in `capacity_matrix`, push as much flow +as possible through the given edge(`u`, `v`). Requires preallocated `flow_matrix` +matrix, and `excess`, `height, `active`, and `Q` vectors. """ function push_flow! end @traitfn function push_flow!( @@ -106,7 +107,7 @@ end """ gap!(residual_graph, h, excess, height, active, count, Q) -Implement the push-relabel gap heuristic. Relabel all nodes above a cutoff height. +Implement the push-relabel gap heuristic. Relabel all vertices above a cutoff height. Reduce the number of relabels required. Requires arguments: diff --git a/src/generators/euclideangraphs.jl b/src/generators/euclideangraphs.jl index d68702ef1..58e12adbe 100644 --- a/src/generators/euclideangraphs.jl +++ b/src/generators/euclideangraphs.jl @@ -1,11 +1,9 @@ @doc_str """ -euclidean_graph(N, d; seed = -1, L=1., p=2., cutoff=-1., bc=:open) + euclidean_graph(N, d; seed=-1, L=1., p=2., cutoff=-1., bc=:open) -Generates `N` uniformly distributed points in the box ``[0,L]^{d}`` -and builds a Euclidean graph. - -Return a graph, a Dict containing the distance on each edge and a matrix with -the points' positions. +Generate `N` uniformly distributed points in the box ``[0,L]^{d}`` +and return a Euclidean graph, a map containing the distance on each edge and +a matrix with the points' positions. """ function euclidean_graph(N::Int, d::Int; L=1., seed = -1, kws...) @@ -14,25 +12,23 @@ function euclidean_graph(N::Int, d::Int; return (euclidean_graph(points; L=L, kws...)..., points) end -### TODO: finish this documentation. sbromberger 2017/03/26 """ - euclidean_graph(points, ) + euclidean_graph(points) + +Given the `d×N` matrix `points` build an Euclidean graph of `N` vertices and +return a graph and Dict containing the distance on each edge. -Given the `d×N` matrix `points` build an Euclidean graph of `N` vertices -according to the following procedure. +### Optional Arguments +- `L=1`: used to bound the `d` dimensional box from which points are selected. +- `p=2` +- `bc=:open` +### Implementation Notes Defining the `d`-dimensional vectors `x[i] = points[:,i]`, an edge between vertices `i` and `j` is inserted if `norm(x[i]-x[j], p) < cutoff`. In case of negative `cutoff` instead every edge is inserted. For `p=2` we have the standard Euclidean distance. Set `bc=:periodic` to impose periodic boundary conditions in the box ``[0,L]^d``. - -### Optional arguments -- `L=1`: used to bound the `d` dimensional box from which points are selected. -- `p=2` -- `bc=:open` - -Return a graph and Dict containing the distance on each edge. """ function euclidean_graph(points::Matrix; L=1., p=2., cutoff=-1., bc=:open) diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 7738bad87..70536433c 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -35,10 +35,10 @@ end erdos_renyi(n, p) Create an [Erdős–Rényi](http://en.wikipedia.org/wiki/Erdős–Rényi_model) -random graph with `n` nodes. Edges are added between pairs of nodes with +random graph with `n` vertices. Edges are added between pairs of vertices with probability `p`. -### Optional arguments +### Optional Arguments - `is_directed=false`: if true, return a directed graph. - `seed=-1`: set the RNG seed. """ @@ -56,9 +56,9 @@ end erdos_renyi(n, ne) Create an [Erdős–Rényi](http://en.wikipedia.org/wiki/Erdős–Rényi_model) random -graph with `n` nodes and `ne` edges. +graph with `n` vertices and `ne` edges. -### Optional arguments +### Optional Arguments - `is_directed=false`: if true, return a directed graph. - `seed=-1`: set the RNG seed. """ @@ -74,7 +74,7 @@ Return a [Watts-Strogatz](https://en.wikipedia.org/wiki/Watts_and_Strogatz_model small model random graph with `n` vertices, each with degree `k`. Edges are randomized per the model based on probability `β`. -### Optional arguments +### Optional Arguments - `is_directed=false`: if true, return a directed graph. - `seed=-1`: set the RNG seed. """ @@ -172,7 +172,7 @@ graph with `k` vertices. Each new vertex is attached with `k` edges to `k` different vertices already present in the system by preferential attachment. Initial graphs are undirected and consist of isolated vertices by default. -### Optional arguments +### Optional Arguments - `is_directed=false`: if true, return a directed graph. - `complete=false`: if true, use a complete graph for the initial graph. - `seed=-1`: set the RNG seed. @@ -189,7 +189,7 @@ graph with `n0` vertices. Each new vertex is attached with `k` edges to `k` different vertices already present in the system by preferential attachment. Initial graphs are undirected and consist of isolated vertices by default. -### Optional arguments +### Optional Arguments - `is_directed=false`: if true, return a directed graph. - `complete=false`: if true, use a complete graph for the initial graph. - `seed=-1`: set the RNG seed. @@ -213,14 +213,14 @@ random graph with `n` vertices. It is grown by adding new vertices to an initial graph `g`. Each new vertex is attached with `k` edges to `k` different vertices already present in the system by preferential attachment. -### Optional arguments +### Optional Arguments - `seed=-1`: set the RNG seed. """ function barabasi_albert!(g::AbstractGraph, n::Integer, k::Integer; seed::Int=-1) n0 = nv(g) 1 <= k <= n0 <= n || throw(ArgumentError("Barabási-Albert model requires 1 <= k <= n0 <= n" * - "where n0 is the number of nodes in graph g")) + "where n0 is the number of vertices in graph g")) n0 == n && return g # seed random number generator @@ -236,20 +236,20 @@ function barabasi_albert!(g::AbstractGraph, n::Integer, k::Integer; seed::Int=-1 # expand initial graph n0 += 1 - # add edges to k existing nodes + # add edges to k existing vertices for target in sample!(collect(1:n0-1), k) add_edge!(g, n0, target) end end - # vector of weighted nodes (each node is repeated once for each adjacent edge) - weightedNodes = Vector{Int}(2*(n-n0)*k + 2*ne(g)) + # vector of weighted vertices (each node is repeated once for each adjacent edge) + weightedVs = Vector{Int}(2*(n-n0)*k + 2*ne(g)) - # initialize vector of weighted nodes + # initialize vector of weighted vertices offset = 0 for e in edges(g) - weightedNodes[offset+=1] = src(e) - weightedNodes[offset+=1] = dst(e) + weightedVs[offset+=1] = src(e) + weightedVs[offset+=1] = dst(e) end # array to record if a node is picked @@ -259,11 +259,11 @@ function barabasi_albert!(g::AbstractGraph, n::Integer, k::Integer; seed::Int=-1 targets = Vector{Int}(k) for source in n0+1:n - # choose k targets from the existing nodes - # pick uniformly from weightedNodes (preferential attachement) + # choose k targets from the existing vertices + # pick uniformly from weightedVs (preferential attachement) i = 0 while i < k - target = weightedNodes[rand(1:offset)] + target = weightedVs[rand(1:offset)] if !picked[target] targets[i+=1] = target picked[target] = true @@ -274,8 +274,8 @@ function barabasi_albert!(g::AbstractGraph, n::Integer, k::Integer; seed::Int=-1 for target in targets add_edge!(g, source, target) - weightedNodes[offset+=1] = source - weightedNodes[offset+=1] = target + weightedVs[offset+=1] = source + weightedVs[offset+=1] = target picked[target] = false end end @@ -287,11 +287,11 @@ end @doc_str """ static_fitness_model(m, fitness) -Generate a random graph with ``|fitness|`` nodes and `m` edges, -in which the probability of the existence of edge `(i, j)` is proportional -to `fitness[i]*fitness[j]`. +Generate a random graph with ``|fitness|`` vertices and `m` edges, +in which the probability of the existence of ``Edge_{ij}`` is proportional +to ``fitness_i × fitness_j`. -### Optional arguments +### Optional Arguments - `seed=-1`: set the RNG seed. ### Performance @@ -304,14 +304,14 @@ function static_fitness_model(m::Integer, fitness::Vector{T}; seed::Int=-1) wher @assert(m >= 0, "invalid number of edges") n = length(fitness) m == 0 && return Graph(n) - nodes = 0 + nvs = 0 for f in fitness # sanity check for the fitness f < zero(T) && error("fitness scores must be non-negative") - f > zero(T) && (nodes += 1) + f > zero(T) && (nvs += 1) end # avoid getting into an infinite loop when too many edges are requested - max_no_of_edges = div(nodes*(nodes-1), 2) + max_no_of_edges = div(nvs*(nvs-1), 2) @assert(m <= max_no_of_edges, "too many edges requested") # calculate the cumulative fitness scores cum_fitness = cumsum(fitness) @@ -321,13 +321,13 @@ function static_fitness_model(m::Integer, fitness::Vector{T}; seed::Int=-1) wher end @doc_str """ -static_fitness_model(m, fitness_out, fitness_in) + static_fitness_model(m, fitness_out, fitness_in) -Generate a random graph with ``|fitness_out + fitness_in|`` nodes and `m` edges, -in which the probability of the existence of edge `(i, j)` is proportional with -respect to `i ∝ fitness_out` and `j ∝ fitness_in`. +Generate a random graph with ``|fitness_out + fitness_in|`` vertices and `m` edges, +in which the probability of the existence of ``Edge_{ij}`` is proportional with +respect to ``i ∝ fitness_out`` and ``j ∝ fitness_in``. -### Optional arguments +### Optional Arguments - `seed=-1`: set the RNG seed. ### Performance @@ -342,15 +342,15 @@ function static_fitness_model(m::Integer, fitness_out::Vector{T}, fitness_in::Ve @assert(length(fitness_in) == n, "fitness_in must have the same size as fitness_out") m == 0 && return DiGraph(n) # avoid getting into an infinite loop when too many edges are requested - outnodes = innodes = nodes = 0 + noutvs = ninvs = nvs = 0 @inbounds for i=1:n # sanity check for the fitness (fitness_out[i] < zero(T) || fitness_in[i] < zero(S)) && error("fitness scores must be non-negative") - fitness_out[i] > zero(T) && (outnodes += 1) - fitness_in[i] > zero(S) && (innodes += 1) - (fitness_out[i] > zero(T) && fitness_in[i] > zero(S)) && (nodes += 1) + fitness_out[i] > zero(T) && (noutvs += 1) + fitness_in[i] > zero(S) && (ninvs += 1) + (fitness_out[i] > zero(T) && fitness_in[i] > zero(S)) && (nvs += 1) end - max_no_of_edges = outnodes*innodes - nodes + max_no_of_edges = noutvs*ninvs - nvs @assert(m <= max_no_of_edges, "too many edges requested") # calculate the cumulative fitness scores cum_fitness_out = cumsum(fitness_out) @@ -382,7 +382,7 @@ end Generate a random graph with `n` vertices, `m` edges and expected power-law degree distribution with exponent `α`. -### Optional arguments +### Optional Arguments - `seed=-1`: set the RNG seed. - `finite_size_correction=true`: determines whether to use the finite size correction proposed by Cho et al. @@ -396,7 +396,7 @@ Time complexity is ``\\mathcal{O}(|V| + |E| log |E|)``. * Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in scale-free networks under the Achlioptas process. Phys Rev Lett 103:135702, 2009. """ function static_scale_free(n::Integer, m::Integer, α::Real; seed::Int=-1, finite_size_correction::Bool=true) - @assert(n >= 0, "Invalid number of nodes") + @assert(n >= 0, "Invalid number of vertices") @assert(α >= 2, "out-degree exponent must be >= 2") fitness = _construct_fitness(n, α, finite_size_correction) static_fitness_model(m, fitness, seed=seed) @@ -409,7 +409,7 @@ Generate a random graph with `n` vertices, `m` edges and expected power-law degree distribution with exponent `α_out` for outbound edges and `α_in` for inbound edges. -### Optional arguments +### Optional Arguments - `seed=-1`: set the RNG seed. - `finite_size_correction=true`: determines whether to use the finite size correction proposed by Cho et al. @@ -418,12 +418,12 @@ proposed by Cho et al. Time complexity is ``\\mathcal{O}(|V| + |E| log |E|)``. ### References -* Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution in scale-free networks. Phys Rev Lett 87(27):278701, 2001. -* Chung F and Lu L: Connected components in a random graph with given degree sequences. Annals of Combinatorics 6, 125-145, 2002. -* Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in scale-free networks under the Achlioptas process. Phys Rev Lett 103:135702, 2009. +- Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution in scale-free networks. Phys Rev Lett 87(27):278701, 2001. +- Chung F and Lu L: Connected components in a random graph with given degree sequences. Annals of Combinatorics 6, 125-145, 2002. +- Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in scale-free networks under the Achlioptas process. Phys Rev Lett 103:135702, 2009. """ function static_scale_free(n::Integer, m::Integer, α_out::Real, α_in::Float64; seed::Int=-1, finite_size_correction::Bool=true) - @assert(n >= 0, "Invalid number of nodes") + @assert(n >= 0, "Invalid number of vertices") @assert(α_out >= 2, "out-degree exponent must be >= 2") @assert(α_in >= 2, "in-degree exponent must be >= 2") # construct the fitness @@ -457,7 +457,7 @@ Create a random undirected [regular graph](https://en.wikipedia.org/wiki/Regular_graph) with `n` vertices, each with degree `k`. -### Optional arguments +### Optional Arguments - `seed=-1`: set the RNG seed. ### Performance @@ -493,16 +493,16 @@ function random_regular_graph(n::Integer, k::Integer; seed::Int=-1) end @doc_str """ - random_configuration_model(n, ks; seed=-1, check_graphical=false) + random_configuration_model(n, ks) Create a random undirected graph according to the [configuration model] (http://tuvalu.santafe.edu/~aaronc/courses/5352/fall2013/csci5352_2013_L11.pdf) containing `n` vertices, with each node `i` having degree `k[i]`. - -### Optional arguments +### Optional Arguments - `seed=-1`: set the RNG seed. -- `check_graphical=false`: if true, ensure that `k` is a graphical sequence (see `isgraphical`). +- `check_graphical=false`: if true, ensure that `k` is a graphical sequence +(see [`isgraphical`](@ref)). ### Performance Time complexity is approximately ``n \\bar{k}^2``. @@ -538,12 +538,12 @@ end Create a random directed [regular graph](https://en.wikipedia.org/wiki/Regular_graph) with `n` vertices, each with degree `k`. -### Optional arguments +### Optional Arguments - `dir=:out`: the direction of the edges for degree parameter. - `seed=-1`: set the RNG seed. ### Implementation Notes -Allocates an ``n \times n`` sparse matrix of boolean as an adjacency matrix and +Allocates an ``n × n`` sparse matrix of boolean as an adjacency matrix and uses that to generate the directed graph. """ function random_regular_digraph(n::Integer, k::Integer; dir::Symbol=:out, seed::Int=-1) @@ -578,7 +578,6 @@ end @doc_str """ stochastic_block_model(c, n) -stochastic_block_model(cin::Real, coff::Float64, n::Vector{Integer}; seed::Int = -1) Return a Graph generated according to the Stochastic Block Model (SBM). @@ -587,10 +586,10 @@ Return a Graph generated according to the Stochastic Block Model (SBM). determined by ``c[b,a] = c[a,b] * \\frac{n[a]}{n[b]}``. `n[a]` : Number of vertices in block `a` -### Optional arguments +### Optional Arguments - `seed=-1`: set the RNG seed. -For a dynamic version of the SBM see the `StochasticBlockModel` type and +For a dynamic version of the SBM see the [`StochasticBlockModel`](@ref) type and related functions. """ function stochastic_block_model(c::Matrix{T}, n::Vector{U}; seed::Int = -1) where T<:Real where U<:Integer @@ -634,9 +633,9 @@ end Return a Graph generated according to the Stochastic Block Model (SBM). -Samples from a SBM with `c[a,a]=cint`, and `c[a,b]=cext`. +Samples from a SBM with ``c_{a,a}=cint``, and ``c_{a,b}=cext``. -### Optional arguments +### Optional Arguments - `seed=-1`: set the RNG seed. """ function stochastic_block_model(cint::T, cext::T, n::Vector{U}; seed::Int=-1) where T<:Real where U<:Integer @@ -656,7 +655,7 @@ The assignement is stored in nodemap and the block affinities a `k` by `k` matrix is stored in affinities. `affinities[k,l]` is the probability of an edge between any vertex in -block k and any vertex in block `l`. +block `k` and any vertex in block `l`. ### Implementation Notes Graphs are generated by taking random ``i,j ∈ V`` and @@ -724,14 +723,14 @@ end const biclique = ones(2,2) - eye(2) #TODO: this documentation needs work. sbromberger 20170326 -""" +@doc_str """ nearbipartiteaffinity(sizes, between, intra) Construct the affinity matrix for a near bipartite SBM. `between` is the affinity between the two parts of each bipartite community. `intra` is the probability of an edge within the parts of the partitions. -This is a specific type of SBM with k/2 blocks each with two halves. +This is a specific type of SBM with ``\\frac{k}{2} blocks each with two halves. Each half is connected as a random bipartite graph with probability `intra` The blocks are connected with probability `between`. """ @@ -752,7 +751,11 @@ function nearbipartiteSBM(sizes, between, inter, noise; seed::Int = -1) end -"""Generate a stream of random pairs in 1:n""" +""" + random_pair(rng, n) + +Generate a stream of random pairs in `1:n` using random number generator `RNG`. +""" function random_pair(rng::AbstractRNG, n::Integer) f(ch) = begin while true @@ -805,7 +808,7 @@ Graph(nvg::Integer, neg::Integer, sbm::StochasticBlockModel) = """ blockcounts(sbm, A) -Count the number of edges that go between each block +Count the number of edges that go between each block. """ function blockcounts(sbm::StochasticBlockModel, A::AbstractMatrix) # info("making Q") diff --git a/src/generators/smallgraphs.jl b/src/generators/smallgraphs.jl index a3c061895..d1865e2a6 100644 --- a/src/generators/smallgraphs.jl +++ b/src/generators/smallgraphs.jl @@ -22,7 +22,7 @@ doc""" smallgraph(s) smallgraph(s) -Creates a small graph of type `s`. Admissible values for `s` are: +Create a small graph of type `s`. Admissible values for `s` are: | `s` | graph type | |:------------------------|:---------------------------------| diff --git a/src/generators/staticgraphs.jl b/src/generators/staticgraphs.jl index bf5f4a9cb..40bebcaf8 100644 --- a/src/generators/staticgraphs.jl +++ b/src/generators/staticgraphs.jl @@ -22,7 +22,7 @@ end CompleteBipartiteGraph(n1, n2) Create an undirected [complete bipartite graph](https://en.wikipedia.org/wiki/Complete_bipartite_graph) -with ``n1+n2`` vertices. +with `n1 + n2` vertices. """ function CompleteBipartiteGraph(n1::Integer, n2::Integer) g = Graph(n1+n2) @@ -175,7 +175,7 @@ end Create a ``|dims|``-dimensional cubic lattice, with length `dims[i]` in dimension `i`. -### Optional arguments +### Optional Arguments - `periodic=false`: If true, the resulting lattice will have periodic boundary condition in each dimension. """ diff --git a/src/graphtypes/simplegraphs/simplegraph.jl b/src/graphtypes/simplegraphs/simplegraph.jl index d1357611b..e064a6fef 100644 --- a/src/graphtypes/simplegraphs/simplegraph.jl +++ b/src/graphtypes/simplegraphs/simplegraph.jl @@ -98,7 +98,6 @@ badj(g::SimpleGraph, v::Integer) = fadj(g, v) Return the adjacency list of a graph. If `v` is specified, return only the adjacency list for that vertex. - ### Implementation Notes Returns a reference, not a copy. Do not modify result. """ @@ -162,7 +161,7 @@ end """ add_vertex!(g) -Add a new vertex to the graph `g`. Return true if addition was successful. +Add a new vertex to the graph `g`. Return `true` if addition was successful. """ function add_vertex!(g::SimpleGraph) T = eltype(g) diff --git a/src/interface.jl b/src/interface.jl index 404ec30dd..96d215a61 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -2,13 +2,25 @@ _NI(m) = error("Not implemented: $m") -"""A type representing a single edge between two vertices of a graph.""" +""" + AbstractEdge + +An absract type representing a single edge between two vertices of a graph. +""" abstract type AbstractEdge end -"""A type representing an edge iterator""" +""" + AbstractEdgeIter + +An abstract type representing an edge iterator. +""" abstract type AbstractEdgeIter end -"""An abstract type representing a graph.""" +""" + AbstractGraph + +An abstract type representing a graph. +""" abstract type AbstractGraph end @@ -20,15 +32,28 @@ abstract type AbstractGraph end # Interface for AbstractEdges # -"""Return source of an edge.""" +""" + src(e) + +Return the source vertex of edge `e`. +""" src(e::AbstractEdge) = _NI("src") -"""Return destination of an edge.""" +""" + dst(e) + +Return the destination vertex of edge `e`. +""" dst(e::AbstractEdge) = _NI("dst") Pair(e::AbstractEdge) = _NI("Pair") Tuple(e::AbstractEdge) = _NI("Tuple") +""" + reverse(e) + +Create a new edge from `e` with source and destination vertices reversed. +""" reverse(e::AbstractEdge) = _NI("reverse") ==(e1::AbstractEdge, e2::AbstractEdge) = _NI("==") @@ -37,71 +62,127 @@ reverse(e::AbstractEdge) = _NI("reverse") # # Interface for AbstractGraphs # -"""Return the type of a graph's edge""" +""" + edgetype(g) + +Return the type of graph `g`'s edge +""" edgetype(g::AbstractGraph) = _NI("edgetype") -"""Return the type of the graph's vertices""" +""" + eltype(g) + +Return the type of the graph's vertices (must be <: Integer) +""" eltype(g::AbstractGraph) = _NI("eltype") -"""Return the number of vertices in `g`.""" +""" + nv(g) + +Return the number of vertices in `g`. +""" nv(g::AbstractGraph) = _NI("nv") -"""Return the number of edges in `g`.""" +""" + ne(g) + +Return the number of edges in `g`. +""" ne(g::AbstractGraph) = _NI("ne") -"""Return the vertices of a graph.""" +""" + vertices(g) + +Return (an iterator to or collection of) the vertices of a graph. + +### Implementation Notes +A returned iterator is valid for one pass over the edges, and +is invalidated by changes to `g`. + +""" vertices(g::AbstractGraph) = _NI("vertices") -"""Return an iterator to the edges of a graph. -The returned iterator is valid for one pass over the edges, and +""" + edges(g) +Return (an iterator to or collection of) the edges of a graph. + +### Implementation Notes +A returned iterator is valid for one pass over the edges, and is invalidated by changes to `g`. """ edges(x...) = _NI("edges") is_directed(x...) = _NI("is_directed") is_directed{T}(::Type{T}) = _NI("is_directed") -"""Add a new vertex to the graph `g`. -Returns true if the vertex was added successfully, false otherwise. +""" + add_vertex!(g) + +Add a new vertex to the graph `g`. +Return true if the vertex was added successfully, false otherwise. """ add_vertex!(x...) = _NI("add_vertex!") """ -Add a new edge `e` to `g`. -Will return false if add fails (e.g., if vertices are not in the graph), true otherwise. + add_edge!(g, e) + +Add a new edge `e` to `g`. Return false if add fails +(e.g., if vertices are not in the graph, or edge already exists), true otherwise. """ add_edge!(x...) = _NI("add_edge!") """ -Remove the vertex `v` from graph `g`. -Returns false if removal fails (e.g., if vertex is not in the graph), true otherwise. + rem_vertex!(g) + +Remove the vertex `v` from graph `g`. Return false if removal fails +(e.g., if vertex is not in the graph), true otherwise. """ rem_vertex!(x...) = _NI("rem_vertex!") """ -Remove the edge `e` from `g` -Returns false if edge removal fails (e.g., if edge does not exist), true otherwise. + rem_edge!(g, e) + +Remove the edge `e` from `g`. Return false if edge removal fails +(e.g., if edge does not exist), true otherwise. """ rem_edge!(x...) = _NI("rem_edge!") -"""Return true if `v` is a vertex of `g`.""" +""" + has_vertex(g, v) + +Return true if `v` is a vertex of `g`. +""" has_vertex(x...) = _NI("has_vertex") -"""Return true if the graph `g` has an edge `e`.""" +""" + has_edge(g, e) + +Return true if the graph `g` has an edge `e`. +""" has_edge(x...) = _NI("has_edge") """ + in_neighbors(g, v) + Return a list of all neighbors connected to vertex `v` by an incoming edge. -NOTE: returns a reference, not a copy. Do not modify result. + +### Implementation Notes +Returns a reference, not a copy. Do not modify result. """ in_neighbors(x...) = _NI("in_neighbors") """ + out_neighbors(g, v) + Return a list of all neighbors connected to vertex `v` by an outgoing edge. -NOTE: returns a reference, not a copy. Do not modify result. + +# Implementation Notes +Returns a reference, not a copy. Do not modify result. """ out_neighbors(x...) = _NI("out_neighbors") """ -Return a zero-vertex, zero-edge version of the same type of graph. + zero(g) + +Return a zero-vertex, zero-edge version of the same type of graph as `g`. """ zero(x...) = _NI("zero") diff --git a/src/linalg/graphmatrices.jl b/src/linalg/graphmatrices.jl index 64a0c9e2b..cf24eb783 100644 --- a/src/linalg/graphmatrices.jl +++ b/src/linalg/graphmatrices.jl @@ -202,7 +202,7 @@ issymmetric(::AveragingAdjacency) = false """ degrees(adjmat) -Retun the degrees of a graph represented by the CombinatorialAdjacency `adjmat`. +Return the degrees of a graph represented by the [CombinatorialAdjacency](@ref) `adjmat`. """ degrees(adjmat::CombinatorialAdjacency) = adjmat.D @@ -308,7 +308,7 @@ end """ symmetrize(A::SparseMatrix, which=:or) -Returns a symmetric version of graph (represented by sparse matrix `A`) as a sparse matrix. +Return a symmetric version of graph (represented by sparse matrix `A`) as a sparse matrix. `which` may be one of `:triu`, `:tril`, `:sum`, or `:or`. Use `:sum` for weighted graphs. """ function symmetrize(A::SparseMatrix, which=:or) @@ -334,12 +334,12 @@ end """ symmetrize(adjmat, which=:or) -Returns a symmetric version of graph (represented by `CombinatorialAdjacency` `adjmat`) -as a `CombinatorialAdjacency`. `which` may be one of `:triu`, `:tril`, `:sum`, or `:or`. +Return a symmetric version of graph (represented by [`CombinatorialAdjacency`](@ref) `adjmat`) +as a [`CombinatorialAdjacency`](@ref). `which` may be one of `:triu`, `:tril`, `:sum`, or `:or`. Use `:sum` for weighted graphs. ### Implementation Notes -Only works on Adjacency because the normalizations don't commute with symmetrization. +Only works on [Adjacency](@ref) because the normalizations don't commute with symmetrization. """ symmetrize(adjmat::CombinatorialAdjacency, which=:or) = CombinatorialAdjacency(symmetrize(adjmat.A, which)) @@ -355,6 +355,6 @@ symmetrize(adjmat::CombinatorialAdjacency, which=:or) = """ LinAlg -A package for using the type system to check types of graph matrices +A package for using the type system to check types of graph matrices. """ LinAlg diff --git a/src/linalg/nonbacktracking.jl b/src/linalg/nonbacktracking.jl index 9e86a2676..131b4c513 100644 --- a/src/linalg/nonbacktracking.jl +++ b/src/linalg/nonbacktracking.jl @@ -6,13 +6,13 @@ export non_backtracking_matrix, @doc_str """ non_backtracking_matrix(g) -Return a non-backtracking matrix B and an edgemap storing the oriented -edges' positions in B. +Return a non-backtracking matrix `B` and an edgemap storing the oriented +edges' positions in `B`. -Given two arcs ``A_{i,j}` and `A_{k,l}` in `g`, the +Given two arcs ``A_{i j}` and `A_{k l}` in `g`, the non-backtraking matrix ``B`` is defined as -``B_{A_{i,j}, A_{k,l}} = δ_{j, k} * (1 - δ_{i, l})`` +``B_{A_{i j}, A_{k l}} = δ_{j k} * (1 - δ_{i l})`` """ function non_backtracking_matrix(g::AbstractGraph) # idedgemap = Dict{Int, Edge}() @@ -46,17 +46,17 @@ end @doc_str """ Nonbacktracking{G} - + A compact representation of the nonbacktracking operator. The Nonbacktracking operator can be used for community detection. This representation is compact in that it uses only ne(g) additional storage and provides an implicit representation of the matrix B_g defined below. -Given two arcs ``A_{i,j}` and `A_{k,l}` in `g`, the +Given two arcs ``A_{i j}` and `A_{k l}` in `g`, the non-backtraking matrix ``B`` is defined as -``B_{A_{i,j}, A_{k,l}} = δ_{j, k} * (1 - δ_{i, l})`` +``B_{A_{i j}, A_{k l}} = δ_{j k} * (1 - δ_{i l})`` This type is in the style of GraphMatrices.jl and supports the necessary operations for computed eigenvectors and conducting linear solves. diff --git a/src/linalg/spectral.jl b/src/linalg/spectral.jl index 51e79e50e..4cb8a23fa 100644 --- a/src/linalg/spectral.jl +++ b/src/linalg/spectral.jl @@ -87,7 +87,7 @@ end 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 -`laplacian_matrix`. +[`laplacian_matrix`](@ref). ### Performance Converts the matrix to dense with ``nv^2`` memory usage. @@ -101,7 +101,7 @@ laplacian_spectrum(g::AbstractGraph, dir::Symbol=:unspec, T::DataType=Int) = eig @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 -`adjacency_matrix`. +[`adjacency_matrix`](@ref). ### Performance Converts the matrix to dense with ``nv^2`` memory usage. diff --git a/src/matching/README.md b/src/matching/README.md deleted file mode 100644 index d6c2545f1..000000000 --- a/src/matching/README.md +++ /dev/null @@ -1,2 +0,0 @@ -## Please note that the matching functions have been moved to [LightGraphsExtras](https://github.com/JuliaGraphs/LightGraphsExtras.jl). - diff --git a/src/operators.jl b/src/operators.jl index 603b62a68..16fea3bb1 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -1,8 +1,10 @@ """ complement(g) -Produces the [graph complement](https://en.wikipedia.org/wiki/Complement_graph) -of a graph. +Return the [graph complement](https://en.wikipedia.org/wiki/Complement_graph) +of a graph + +### Implementation Notes Preserves the eltype of the input graph. """ function complement(g::Graph) @@ -30,13 +32,16 @@ function complement(g::DiGraph) end """ - reverse(g::DiGraph) + reverse(g) + +Return a directed graph where all edges are reversed from the +original directed graph. -Produces a graph where all edges are reversed from the -original. +### Implementation Notes Preserves the eltype of the input graph. """ -function reverse(g::DiGraph) +function reverse end +@traitfn function reverse(g::::IsDirected) gnv = nv(g) gne = ne(g) h = DiGraph(gnv) @@ -47,11 +52,12 @@ function reverse(g::DiGraph) end """ - reverse!(g::DiGraph) + reverse!(g) -In-place reverse (modifies the original graph). +In-place reverse of a directed graph (modifies the original graph). """ -function reverse!(g::DiGraph) +function reverse! end +@traitfn function reverse!(g::::IsDirected) g.fadjlist, g.badjlist = g.badjlist, g.fadjlist return g end @@ -59,10 +65,10 @@ end doc""" blkdiag(g, h) -Produces a graph with $|V(g)| + |V(h)|$ vertices and $|E(g)| + |E(h)|$ -edges. +Return a graph with ``|V(g)| + |V(h)|`` vertices and ``|E(g)| + |E(h)|`` +edges where the vertices an edges from graph `h` are appended to graph `g`. -Put simply, the vertices and edges from graph `h` are appended to graph `g`. +### Implementation Notes Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. """ @@ -81,9 +87,10 @@ end """ intersect(g, h) -Produces a graph with edges that are only in both graph `g` and graph `h`. +Return a graph with edges that are only in both graph `g` and graph `h`. -Note that this function may produce a graph with 0-degree vertices. +### Implementation Notes +This function may produce a graph with 0-degree vertices. Preserves the eltype of the input graph. """ function intersect(g::T, h::T) where T<:AbstractGraph @@ -100,8 +107,9 @@ end """ difference(g, h) -Produces a graph with edges in graph `g` that are not in graph `h`. +Return a graph with edges in graph `g` that are not in graph `h`. +### Implementation Notes Note that this function may produce a graph with 0-degree vertices. Preserves the eltype of the input graph. """ @@ -119,9 +127,10 @@ end """ symmetric_difference(g, h) -Produces a graph with edges from graph `g` that do not exist in graph `h`, +Return a graph with edges from graph `g` that do not exist in graph `h`, and vice versa. +### Implementation Notes Note that this function may produce a graph with 0-degree vertices. Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. @@ -143,7 +152,10 @@ end """ union(g, h) -Merges graphs `g` and `h` by taking the set union of all vertices and edges. +Return a graph that combines graphs `g` and `h` by taking the set union +of all vertices and edges. + +### Implementation Notes Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. """ @@ -169,8 +181,10 @@ end """ join(g, h) -Merges graphs `g` and `h` using `blkdiag` and then adds all the edges between -the vertices in `g` and those in `h`. +Return a graph that combines graphs `g` and `h` using `blkdiag` and then +adds all the edges between the vertices in `g` and those in `h`. + +### Implementation Notes Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. """ @@ -188,7 +202,10 @@ end """ crosspath(len::Integer, g::Graph) -Replicate `len` times `g` and connect each vertex with its copies in a path. +Return a graph that duplicates `g` `len` times and connects each vertex +with its copies in a path. + +### Implementation Notes Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. """ @@ -227,7 +244,11 @@ function *(g::DiGraph, v::Vector{T}) where T<:Real return y end -"""sum(g,i) provides 1:indegree or 2:outdegree vectors""" +""" + sum(g, i) + +Return a vector of indegree (`i`=1) or outdegree (`i`=2) values for graph `g`. +""" function sum(g::AbstractGraph, dim::Int) dim == 1 && return indegree(g, vertices(g)) dim == 2 && return outdegree(g, vertices(g)) @@ -236,13 +257,25 @@ end size(g::AbstractGraph) = (nv(g), nv(g)) -"""size(g,i) provides 1:nv or 2:nv else 1 """ +""" + size(g, i) + +Return the number of vertices in `g` if `i`=1 or `i`=2, or `1` otherwise. +""" size(g::Graph,dim::Int) = (dim == 1 || dim == 2)? nv(g) : 1 -"""sum(g) provides the number of edges in the graph""" +""" + sum(g) + +Return the number of edges in `g` +""" sum(g::AbstractGraph) = ne(g) -"""sparse(g) is the adjacency_matrix of g""" +""" + sparse(g) + +Return the default adjacency matrix of `g`. +""" sparse(g::AbstractGraph) = adjacency_matrix(g) #arrayfunctions = (:eltype, :length, :ndims, :size, :strides, :issymmetric) @@ -254,7 +287,10 @@ issymmetric(g::AbstractGraph) = !is_directed(g) """ cartesian_product(g, h) -Returns the (cartesian product)[https://en.wikipedia.org/wiki/Tensor_product_of_graphs] of `g` and `h` +Return the (cartesian product)[https://en.wikipedia.org/wiki/Tensor_product_of_graphs] +of `g` and `h`. + +### Implementation Notes Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. """ @@ -280,7 +316,10 @@ end """ tensor_product(g, h) -Returns the (tensor product)[https://en.wikipedia.org/wiki/Tensor_product_of_graphs] of `g` and `h` +Return the (tensor product)[https://en.wikipedia.org/wiki/Tensor_product_of_graphs] +of `g` and `h`. + +### Implementation Notes Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. """ @@ -302,38 +341,39 @@ end """ induced_subgraph(g, vlist) + induced_subgraph(g, elist) -Returns the subgraph of `g` induced by the vertices in `vlist`. +Return the subgraph of `g` induced by the vertices in `vlist` or edges in `elist` +along with a vector mapping the new vertices to the old ones +(the vertex `i` in the subgraph corresponds to the vertex `vmap[i]` in `g`.) The returned graph has `length(vlist)` vertices, with the new vertex `i` corresponding to the vertex of the original graph in the `i`-th position of `vlist`. -Returns also a vector `vmap` mapping the new vertices to the -old ones: the vertex `i` in the subgraph corresponds to -the vertex `vmap[i]` in `g`. +### Usage Examples +```doctestjl +julia> g = CompleteGraph(10) - induced_subgraph(g, elist) +julia> sg, vmap = subgraph(g, 5:8) + +julia> @assert g[5:8] == sg + +julia> @assert nv(sg) == 4 -Returns the subgraph of `g` induced by the edges in `elist`, along with -the associated vector `vmap` mapping new vertices to the old ones. +julia> @assert ne(sg) == 6 +julia> @assert vm[4] == 8 -### Usage Examples: -```julia -g = CompleteGraph(10) -sg, vmap = subgraph(g, 5:8) -@assert g[5:8] == sg -@assert nv(sg) == 4 -@assert ne(sg) == 6 -@assert vm[4] == 8 +julia> sg, vmap = subgraph(g, [2,8,3,4]) -sg, vmap = subgraph(g, [2,8,3,4]) -@assert sg == g[[2,8,3,4]] +julia> @assert sg == g[[2,8,3,4]] -elist = [Edge(1,2), Edge(3,4), Edge(4,8)] -sg, vmap = subgraph(g, elist) -@assert sg == g[elist] +julia> elist = [Edge(1,2), Edge(3,4), Edge(4,8)] + +julia> sg, vmap = subgraph(g, elist) + +julia> @assert sg == g[elist] ``` """ function induced_subgraph(g::T, vlist::AbstractVector{U}) where T<:AbstractGraph where U<:Integer @@ -384,17 +424,21 @@ end """ g[iter] -Returns the subgraph induced by `iter`. Equivalent to [`induced_subgraph`](@ref)`(g, iter)[1]`. +Return the subgraph induced by `iter`. +Equivalent to [`induced_subgraph`](@ref)`(g, iter)[1]`. """ getindex(g::AbstractGraph, iter) = induced_subgraph(g, iter)[1] """ - egonet(g, v::Int, d::Int; dir=:out) + egonet(g, v:, d) + +Return the subgraph of `g` induced by the neighbors of `v` up to distance +`d`. +This is equivalent to [`induced_subgraph`](@ref)`(g, neighborhood(g, v, d, dir=dir))[1].` -Returns the subgraph of `g` induced by the neighbors of `v` up to distance -`d`. If `g` is a `DiGraph` the `dir` optional argument specifies -the edge direction the edge direction with respect to `v` (i.e. `:in` or `:out`) -to be considered. This is equivalent to [`induced_subgraph`](@ref)`(g, neighborhood(g, v, d, dir=dir))[1].` +### Optional Arguments +- `dir=:out`: if `g` is directed, this argument specifies the edge direction +with respect to `v` (i.e. `:in` or `:out`). """ egonet(g::AbstractGraph, v::Integer, d::Integer; dir=:out) = g[neighborhood(g, v, d, dir=dir)] diff --git a/src/persistence/graph6.jl b/src/persistence/graph6.jl index d8b81b8ee..1b29ef790 100644 --- a/src/persistence/graph6.jl +++ b/src/persistence/graph6.jl @@ -72,7 +72,7 @@ end """ _graphToG6String(g) -Given a graph `g`, create the corresponding Graph6 string +Given a graph `g`, create the corresponding Graph6 string. """ function _graphToG6String(g::Graph) A = adjacency_matrix(g, Bool) diff --git a/src/shortestpaths/astar.jl b/src/shortestpaths/astar.jl index c42779fed..6725e0c26 100644 --- a/src/shortestpaths/astar.jl +++ b/src/shortestpaths/astar.jl @@ -41,7 +41,7 @@ end Return a vector of edges comprising the shortest path between vertices `s` and `t` using the [A\* search algorithm](http://en.wikipedia.org/wiki/A%2A_search_algorithm). An optional heuristic function and edge distance matrix may be supplied. If missing, -the distance matrix is set to `DefaultDistance` and the heuristic is set to +the distance matrix is set to [`DefaultDistance`](@ref) and the heuristic is set to `n -> 0`. """ function a_star( diff --git a/src/shortestpaths/bellman-ford.jl b/src/shortestpaths/bellman-ford.jl index 2632c6b80..9e468851a 100644 --- a/src/shortestpaths/bellman-ford.jl +++ b/src/shortestpaths/bellman-ford.jl @@ -65,7 +65,7 @@ end Compute shortest paths between a source `s` (or list of sources `ss`) and all other nodes in graph `g` using the [Bellman-Ford algorithm](http://en.wikipedia.org/wiki/Bellman–Ford_algorithm). -Return a `BellmanFordState` with relevant traversal information. +Return a [`BellmanFordState`](@ref) with relevant traversal information. """ function bellman_ford_shortest_paths( graph::AbstractGraph, diff --git a/src/shortestpaths/dijkstra.jl b/src/shortestpaths/dijkstra.jl index cb571f0ef..f20a59b53 100644 --- a/src/shortestpaths/dijkstra.jl +++ b/src/shortestpaths/dijkstra.jl @@ -8,7 +8,7 @@ isless(e1::DijkstraHeapEntry, e2::DijkstraHeapEntry) = e1.dist < e2.dist """ struct DijkstraState{T, U} -An `AbstractPathState` designed for Dijkstra shortest-paths calculations. +An [`AbstractPathState`](@ref) designed for Dijkstra shortest-paths calculations. """ struct DijkstraState{T, U<:Integer}<: AbstractPathState parents::Vector{U} @@ -22,11 +22,11 @@ dijkstra_shortest_paths(g, srcs, distmx=DefaultDistance()); allpaths=false ) Perform [Dijkstra's algorithm](http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) -on a graph, computing shortest distances between `srcs` and all other nodes. -Return a `DijkstraState` that contains various traversal information. +on a graph, computing shortest distances between `srcs` and all other vertices. +Return a [`DijkstraState`](@ref) that contains various traversal information. -### Optional arguments -- `allpaths=false`: If true, returns a `DijkstraState` that keeps track of all +### Optional Arguments +- `allpaths=false`: If true, returns a [`DijkstraState`](@ref) that keeps track of all predecessors of a given vertex. """ function dijkstra_shortest_paths( diff --git a/src/shortestpaths/floyd-warshall.jl b/src/shortestpaths/floyd-warshall.jl index f6d6710a0..b7ddcb287 100644 --- a/src/shortestpaths/floyd-warshall.jl +++ b/src/shortestpaths/floyd-warshall.jl @@ -5,7 +5,7 @@ """ struct FloydWarshallState{T, U} -An `AbstractPathState` designed for Floyd-Warshall shortest-paths calculations. +An [`AbstractPathState`](@ref) designed for Floyd-Warshall shortest-paths calculations. """ struct FloydWarshallState{T, U<:Integer}<:AbstractPathState dists::Matrix{T} @@ -16,8 +16,8 @@ end floyd_warshall_shortest_paths(g, distmx=DefaultDistance()) Use the [Floyd-Warshall algorithm](http://en.wikipedia.org/wiki/Floyd–Warshall_algorithm) to compute the shortest paths between all pairs of vertices in graph `g` using an -optional distance matrix `distmx`. Return a `FloydWarshallState` with relevant -traversal information +optional distance matrix `distmx`. Return a [`FloydWarshallState`](@ref) with relevant +traversal information. ### Performance Space complexity is on the order of ``\\mathcal{O}(|V|^2)``. diff --git a/src/spanningtrees/kruskal.jl b/src/spanningtrees/kruskal.jl index ea74634b7..8cde79b8b 100644 --- a/src/spanningtrees/kruskal.jl +++ b/src/spanningtrees/kruskal.jl @@ -6,17 +6,17 @@ end isless(e1::KruskalHeapEntry, e2::KruskalHeapEntry) = e1.dist < e2.dist """ - quick_find!(nodes, p, q) + quick_find!(vs, p, q) Perform [Quick-Find algorithm](https://en.wikipedia.org/wiki/Disjoint-set_data_structure) -on a given pair of nodes `p`and `q`, and make a connection between them in the vector `nodes`. +on a given pair of vertices `p`and `q`, and make a connection between them in the vector `vs`. """ -function quick_find!(nodes, p, q) - pid = nodes[p] - qid = nodes[q] - for i in 1:length(nodes) - if nodes[i] == pid - nodes[i] = qid +function quick_find!(vs, p, q) + pid = vs[p] + qid = vs[q] + for i in 1:length(vs) + if vs[i] == pid + vs[i] = qid end end end @@ -36,7 +36,7 @@ function kruskal_mst end U = eltype(g) edge_list = Vector{KruskalHeapEntry{T}}() mst = Vector{Edge}() - connected_nodes = collect(one(U):nv(g)) + connected_vs = collect(one(U):nv(g)) sizehint!(edge_list, ne(g)) sizehint!(mst, ne(g)) @@ -50,8 +50,8 @@ function kruskal_mst end v = src(heap_entry.edge) w = dst(heap_entry.edge) - if connected_nodes[v] != connected_nodes[w] - quick_find!(connected_nodes, v, w) + if connected_vs[v] != connected_vs[w] + quick_find!(connected_vs, v, w) push!(mst, heap_entry.edge) end end diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 308995be1..6682c0bdb 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -103,7 +103,7 @@ end """ tree(parents) -Convert a parents array into a `DiGraph` +Convert a parents array into a directed graph. """ function tree(parents::AbstractVector{T}) where T<:Integer n = T(length(parents)) @@ -147,11 +147,12 @@ end """ bfs_tree(g, s) + Provide a breadth-first traversal of the graph `g` starting with source vertex `s`, and return a directed acyclic graph of vertices in the order they were discovered. ### Implementation Notes -This function is a high level wrapper around bfs_tree!; use that function for more performance. +This function is a high level wrapper around [`bfs_tree!`](@ref); use that function for more performance. """ function bfs_tree(g::AbstractGraph, s::Integer) nvg = nv(g) @@ -236,8 +237,9 @@ end @doc_str """ bipartite_map(g) + For a bipartite graph `g`, return a vector `c` of size ``|V|`` containing -the assignment of each vertex to one of the two sets (``c_i == 1`` or c_i ==2``). +the assignment of each vertex to one of the two sets (``c_i == 1`` or c_i == 2``). If `g` is not bipartite, return an empty vector. """ function bipartite_map(g::AbstractGraph) diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index 4b1fe0756..723c3fec7 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -114,7 +114,7 @@ discover_vertex!(vis::DFSCyclicTestVisitor, v) = !vis.found_cycle """ is_cyclic(g) -Returns `true` if graph `g` contains a cycle. +Return `true` if graph `g` contains a cycle. ### Implementation Notes Uses DFS. @@ -184,7 +184,7 @@ end """ dfs_tree(g, s) -Returns an ordered vector of vertices representing a directed acylic graph based on +Return an ordered vector of vertices representing a directed acylic graph based on depth-first traversal of the graph `g` starting with source vertex `s`. """ function dfs_tree(g::AbstractGraph, s::Integer) diff --git a/src/utils.jl b/src/utils.jl index e70dce06e..7e5e5c4cc 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,8 +1,13 @@ """ -sample!([rng,] a, k; exclude = ()) +sample!([rng, ]a, k) Sample `k` element from array `a` without repetition and eventually excluding elements in `exclude`. -Pay attention, it changes the order of the elements in `a`. + +### Optional Arguments +- `exclude=()`: elements in `a` to exclude from sampling. + +### Implementation Notes +Changes the order of the elements in `a`. For a non-mutating version, see [`sample`](@ref). """ function sample!(rng::AbstractRNG, a::AbstractArray, k::Integer; exclude = ()) length(a) < k + length(exclude) && error("Array too short.") @@ -24,9 +29,15 @@ end sample!(a::AbstractArray, k::Integer; exclude = ()) = sample!(getRNG(), a, k; exclude = exclude) """ -sample([rng,] r, k; exclude = ()) + sample([rng,] r, k) + Sample `k` element from unit range `r` without repetition and eventually excluding elements in `exclude`. -Unlike `sample!`, does not produce side effects. + +### Optional Arguments +- `exclude=()`: elements in `a` to exclude from sampling. + +### Implementation Notes +Unlike [`sample!`](@ref), does not produce side effects. """ sample(a::UnitRange, k::Integer; exclude = ()) = sample!(getRNG(), collect(a), k; exclude = exclude) diff --git a/test/shortestpaths/dijkstra.jl b/test/shortestpaths/dijkstra.jl index 5c1216122..dcb480d7c 100644 --- a/test/shortestpaths/dijkstra.jl +++ b/test/shortestpaths/dijkstra.jl @@ -56,7 +56,7 @@ for g in testgraphs(G) ds = @inferred(dijkstra_shortest_paths(g,2,w)) - # this loop reconstructs the shortest path for nodes 1, 3 and 4 + # this loop reconstructs the shortest path for vertices 1, 3 and 4 @test spaths(ds, [1,3,4], 2) == Array[[2 1], [2 3], [2 1 4]] @@ -65,7 +65,7 @@ w[2,2] = 10.0 ds = @inferred(dijkstra_shortest_paths(g,2,w)) shortest_paths = [] - # this loop reconstructs the shortest path for nodes 1, 3 and 4 + # this loop reconstructs the shortest path for vertices 1, 3 and 4 @test spaths(ds, [1,3,4], 2) == Array[[2 1], [2 3], [2 1 4]]