From 2c45b459f29661fbdf489fddbb9a880d6bbd6ffb Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Wed, 8 Mar 2017 21:26:27 -0800 Subject: [PATCH 01/56] benchmarks --- bench/connectivity.jl | 51 ------------------- bench/max-flow.jl | 12 ----- benchmarks/LightGraphsBenchmarks.jl | 51 +++++++++++++++++++ {bench => benchmarks/attic}/bfstree.jl | 2 +- .../attic}/nonbacktracking.jl | 0 benchmarks/centrality.jl | 23 +++++++++ benchmarks/connectivity.jl | 13 +++++ benchmarks/core.jl | 41 +++++++++++++++ benchmarks/max-flow.jl | 11 ++++ benchmarks/traversals.jl | 12 +++++ 10 files changed, 152 insertions(+), 64 deletions(-) delete mode 100644 bench/connectivity.jl delete mode 100644 bench/max-flow.jl create mode 100644 benchmarks/LightGraphsBenchmarks.jl rename {bench => benchmarks/attic}/bfstree.jl (98%) rename {bench => benchmarks/attic}/nonbacktracking.jl (100%) create mode 100644 benchmarks/centrality.jl create mode 100644 benchmarks/connectivity.jl create mode 100644 benchmarks/core.jl create mode 100644 benchmarks/max-flow.jl create mode 100644 benchmarks/traversals.jl diff --git a/bench/connectivity.jl b/bench/connectivity.jl deleted file mode 100644 index d7347aa27..000000000 --- a/bench/connectivity.jl +++ /dev/null @@ -1,51 +0,0 @@ -using LightGraphs -using MatrixDepot -using Base.Profile - -"""Find the largest connected component of graph and return it as a vector of indices.""" -function symmetrize(A) - println("Symmetrizing ") - tic() - if !issymmetric(A) - println(STDERR, "the matrix is not symmetric using A+A'") - A = A + A' - end - if isa(A, Base.LinAlg.Symmetric) - A.data.nzval = abs(A.data.nzval) - else - A.nzval = abs(A.nzval) - end - #= spA = abs(sparse(A)) =# - toc() - return A -end - -function loadmat(matname) - println("Reading MTX of $matname") - tic() - A = matrixdepot(matname, :read) - A = symmetrize(A) - tic() - g = Graph(A) - return g -end - -names = ["Newman/football" , "Newman/cond-mat-2003", "SNAP/amazon0302","SNAP/roadNet-CA"] -sucesses = [] -failures = [] -for matname in names - println("Working on $matname") - g = loadmat(matname) - println("Finding Components") - visitor = LightGraphs.TreeBFSVisitorVector(zeros(Int, nv(g))) - label = zeros(Int, nv(g)) - @time label = LightGraphs.connected_components!(label, g) - @time components = LightGraphs.connected_components!(visitor, g) - fill!(visitor.tree, 0) - @time LightGraphs.bfs_tree!(visitor, g, 1) - @show length(components) - #= - @time components_slow = LightGraphs.connected_components(g) - @assert length(components) == length(components_slow) - =# -end diff --git a/bench/max-flow.jl b/bench/max-flow.jl deleted file mode 100644 index 67067562f..000000000 --- a/bench/max-flow.jl +++ /dev/null @@ -1,12 +0,0 @@ -using LightGraphs -function bench_maxflow(n) - p = 8.0/n - A = sprand(n,n,p) - g = DiGraph(A) - cap = round(A*100) - @time maximum_flow(g, 1, n, cap) -end - -for n in 3:14 - bench_maxflow(2^n) -end diff --git a/benchmarks/LightGraphsBenchmarks.jl b/benchmarks/LightGraphsBenchmarks.jl new file mode 100644 index 000000000..967c3e42d --- /dev/null +++ b/benchmarks/LightGraphsBenchmarks.jl @@ -0,0 +1,51 @@ +module LightGraphsBenchmarks + +using BenchmarkTools +using JLD +using Compat +using LightGraphs + +import Compat: UTF8String, view + +BenchmarkTools.DEFAULT_PARAMETERS.seconds = 1.0 +BenchmarkTools.DEFAULT_PARAMETERS.samples = 10000 +BenchmarkTools.DEFAULT_PARAMETERS.time_tolerance = 0.15 +BenchmarkTools.DEFAULT_PARAMETERS.memory_tolerance = 0.01 + +const PARAMS_PATH = joinpath(dirname(@__FILE__), "params.jld") +const SUITE = BenchmarkGroup() + +testdatadir = joinpath(dirname(@__FILE__), "..","test","testdata") +println("testdatadir = $testdatadir") + +dg1fn = joinpath(testdatadir, "graph-5k-50k.jgz") + +DIGRAPHS = Dict{String, DiGraph}( + "complete100" => CompleteDiGraph(100), + "5000-50000" => LightGraphs.load(dg1fn)["graph-5000-50000"], + "path500" => PathDiGraph(500) +) + +GRAPHS = Dict{String, Graph}( + "complete100" => CompleteGraph(100), + "tutte" => smallgraph(:tutte), + "path500" => PathGraph(500), + "5000-49947" => Graph(DIGRAPHS["5000-50000"]) +) + +MODULES = [ + "core.jl", + "centrality.jl", + "max-flow.jl", + "connectivity.jl", + "traversals.jl" +] + +for m in MODULES + include("$m") +end + +run_benchmarks() = run(SUITE, verbose=true) + + +end diff --git a/bench/bfstree.jl b/benchmarks/attic/bfstree.jl similarity index 98% rename from bench/bfstree.jl rename to benchmarks/attic/bfstree.jl index 24042d64c..7199d3fe6 100644 --- a/bench/bfstree.jl +++ b/benchmarks/attic/bfstree.jl @@ -3,7 +3,7 @@ using MatrixDepot function symmetrize(A) println("Symmetrizing ") tic() - if !issymmetric(A) + if !issymmetric(A) println(STDERR, "the matrix is not symmetric using A+A'") A = A + A' end diff --git a/bench/nonbacktracking.jl b/benchmarks/attic/nonbacktracking.jl similarity index 100% rename from bench/nonbacktracking.jl rename to benchmarks/attic/nonbacktracking.jl diff --git a/benchmarks/centrality.jl b/benchmarks/centrality.jl new file mode 100644 index 000000000..2cddd99fe --- /dev/null +++ b/benchmarks/centrality.jl @@ -0,0 +1,23 @@ +bg = BenchmarkGroup() +SUITE["centrality"] = bg + +for (name, g) in GRAPHS + bg["centrality","degree_centrality","graph","$name"] = @benchmarkable LightGraphs.degree_centrality($g) + bg["centrality","closeness_centrality","graph","$name"] = @benchmarkable LightGraphs.closeness_centrality($g) + if nv(g) < 1000 + bg["centrality","betweenness_centrality","graph","$name"] = @benchmarkable LightGraphs.betweenness_centrality($g) + bg["centrality","katz_centrality","graph","$name"] = @benchmarkable LightGraphs.katz_centrality($g) + end +end + +for (name, g) in DIGRAPHS + bg["centrality","degree_centrality","digraph","$name"] = @benchmarkable LightGraphs.degree_centrality($g) + bg["centrality","closeness_centrality","digraph","$name"] = @benchmarkable LightGraphs.closeness_centrality($g) + if nv(g) < 1000 + bg["centrality","betweenness_centrality","digraph","$name"] = @benchmarkable LightGraphs.betweenness_centrality($g) + bg["centrality","katz_centrality","digraph","$name"] = @benchmarkable LightGraphs.katz_centrality($g) + if nv(g) < 500 + bg["centrality","pagerank","digraph","$name"] = @benchmarkable LightGraphs.pagerank($g) + end + end +end diff --git a/benchmarks/connectivity.jl b/benchmarks/connectivity.jl new file mode 100644 index 000000000..fd19e0805 --- /dev/null +++ b/benchmarks/connectivity.jl @@ -0,0 +1,13 @@ +bg = BenchmarkGroup() +SUITE["connectivity"] = bg + + +for (name, g) in DIGRAPHS + bg["connectivity","connected_components","digraph","$name"] = @benchmarkable LightGraphs.connected_components($g) + bg["connectivity","strongly_connected_components","digraph","$name"] = @benchmarkable LightGraphs.strongly_connected_components($g) + +end + +for (name, g) in GRAPHS + bg["connectivity","connected_components","graph","$name"] = @benchmarkable LightGraphs.connected_components($g) +end diff --git a/benchmarks/core.jl b/benchmarks/core.jl new file mode 100644 index 000000000..24fa36850 --- /dev/null +++ b/benchmarks/core.jl @@ -0,0 +1,41 @@ +bg = BenchmarkGroup() +SUITE["core"] = bg + +function bench_iteredges(g::AbstractGraph) + i = 0 + for e in edges(g) + i += 1 + end + return i +end + +function bench_has_edge(g::AbstractGraph) + srand(1) + nvg = nv(g) + srcs = rand([1:nvg;], cld(nvg, 4)) + dsts = rand([1:nvg;], cld(nvg, 4)) + i = 0 + for (s, d) in zip(srcs, dsts) + if has_edge(g, s, d) + i += 1 + end + end + return i +end + + +EDGEFNS = [ + bench_iteredges, + bench_has_edge +] + +for fun in EDGEFNS + for (name, g) in GRAPHS + bg["edges","$fun","graph","$name"] = @benchmarkable $fun($g) + end + + for (name, g) in DIGRAPHS + bg["edges","$fun","digraph","$name"] = @benchmarkable $fun($g) + end + +end diff --git a/benchmarks/max-flow.jl b/benchmarks/max-flow.jl new file mode 100644 index 000000000..d8743b6af --- /dev/null +++ b/benchmarks/max-flow.jl @@ -0,0 +1,11 @@ +bg = BenchmarkGroup() +SUITE["max-flow"] = bg + +for n in 9:5:29 + srand(1) + p = 8.0 / n + A = sprand(n,n,p) + g = DiGraph(A) + cap = round(A*100) + bg["maximum_flow","digraph","$n"] = @benchmarkable LightGraphs.maximum_flow($g, 1, $n, $cap) +end diff --git a/benchmarks/traversals.jl b/benchmarks/traversals.jl new file mode 100644 index 000000000..ea4c68998 --- /dev/null +++ b/benchmarks/traversals.jl @@ -0,0 +1,12 @@ +bg = BenchmarkGroup() +SUITE["traversals"] = bg + +for (name, g) in GRAPHS + bg["traversals","bfs_tree","graph","$name"] = @benchmarkable LightGraphs.bfs_tree($g, 1) + bg["traversals","dfs_tree","graph","$name"] = @benchmarkable LightGraphs.dfs_tree($g, 1) +end + +for (name, g) in DIGRAPHS + bg["traversals","bfs_tree","digraph","$name"] = @benchmarkable LightGraphs.bfs_tree($g, 1) + bg["traversals","dfs_tree","digraph","$name"] = @benchmarkable LightGraphs.dfs_tree($g, 1) +end From 811c5093ec95dee6ea1e49894b944f655c99fc3b Mon Sep 17 00:00:00 2001 From: James Fairbanks Date: Thu, 9 Mar 2017 22:22:54 -0500 Subject: [PATCH 02/56] add edgetype benchmark katz centrality is broken. --- benchmarks/LightGraphsBenchmarks.jl | 8 +++-- benchmarks/edgetype.jl | 46 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 benchmarks/edgetype.jl diff --git a/benchmarks/LightGraphsBenchmarks.jl b/benchmarks/LightGraphsBenchmarks.jl index 967c3e42d..d70bb985c 100644 --- a/benchmarks/LightGraphsBenchmarks.jl +++ b/benchmarks/LightGraphsBenchmarks.jl @@ -34,8 +34,9 @@ GRAPHS = Dict{String, Graph}( ) MODULES = [ - "core.jl", - "centrality.jl", + "core.jl", + "edgetype.jl", + # "centrality.jl", "max-flow.jl", "connectivity.jl", "traversals.jl" @@ -47,5 +48,6 @@ end run_benchmarks() = run(SUITE, verbose=true) - +run_benchmarks() end + diff --git a/benchmarks/edgetype.jl b/benchmarks/edgetype.jl new file mode 100644 index 000000000..bffbb4624 --- /dev/null +++ b/benchmarks/edgetype.jl @@ -0,0 +1,46 @@ +bg = BenchmarkGroup() +SUITE["edgetype"] = bg +import Base: convert +typealias P Pair{Int, Int} +immutable Edge + src::Int + dst::Int +end + +function fille(n) + t = Array{Edge,1}(n) + for i in 1:n + t[i] = Edge(i, i+1) + end + return t +end + +function fillp(n) + t = Array{P,1}(n) + for i in 1:n + t[i] = P(i, i+1) + end + return t +end + +function tsum(t) + x = 0 + for i in 1:length(t) + u,v = Tuple(t[i]) + x += u + x += v + end + return x +end + +src(e) = e.src +dst(e) = e.dst +convert(::Type{Tuple}, e::Edge) = (src(e), dst(e)) +convert(::Type{Tuple}, e::Pair) = (e.first, e.second) + +n = 10000 +bg["edgetype", "fille", "$n"] = @benchmarkable fille($n) +bg["edgetype", "fillp", "$n"] = @benchmarkable fillp($n) +a, b = fille(n), fillp(n) +bg["edgetype", "tsume", "$n"] = @benchmarkable tsum($a) +bg["edgetype", "tsump", "$n"] = @benchmarkable tsum($b) From 8e4489b888a2a3ec2c7453ec78372a360be31529 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Fri, 10 Mar 2017 15:02:10 -0800 Subject: [PATCH 03/56] simplegraphs abstraction --- src/LightGraphs.jl | 14 +- src/core.jl | 150 ++++++------------ src/graphtypes/simpleedge/SimpleEdges.jl | 29 ++++ src/graphtypes/simplegraph/SimpleGraphs.jl | 131 +++++++++++++++ .../simplegraph/simpledigraph.jl} | 60 +++---- .../simplegraph/simplegraph.jl} | 60 +++---- test/graphdigraph.jl | 8 +- 7 files changed, 284 insertions(+), 168 deletions(-) create mode 100644 src/graphtypes/simpleedge/SimpleEdges.jl create mode 100644 src/graphtypes/simplegraph/SimpleGraphs.jl rename src/{digraph.jl => graphtypes/simplegraph/simpledigraph.jl} (58%) rename src/{graph.jl => graphtypes/simplegraph/simplegraph.jl} (69%) diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index a4b2e3893..d64e13a5e 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -13,15 +13,14 @@ import Base: write, ==, <, *, ≈, convert, isless, issubset, union, intersect, sum, size, sparse, eltype, length, ndims, transpose, ctranspose, join, start, next, done, eltype, get, issymmetric, A_mul_B! - # core -export AbstractGraph, Edge, Graph, DiGraph, vertices, edges, src, dst, +export AbstractGraph, AbstractEdge, Edge, Graph, DiGraph, vertices, edges, src, dst, fadj, badj, in_edges, out_edges, has_vertex, has_edge, is_directed, nv, ne, add_edge!, rem_edge!, add_vertex!, add_vertices!, indegree, outdegree, degree, degree_histogram, density, Δ, δ, Δout, Δin, δout, δin, neighbors, in_neighbors, out_neighbors, common_neighbors, all_neighbors, has_self_loops, num_self_loops, -rem_vertex!, +rem_vertex!, is_ordered, # distance eccentricity, diameter, periphery, radius, center, @@ -131,9 +130,14 @@ more traditional and better-optimized mechanisms. LightGraphs include("core.jl") - include("digraph.jl") + include("graphtypes/simpleedge/SimpleEdges.jl") + typealias Edge SimpleEdges.SimpleEdge + + include("graphtypes/simplegraph/SimpleGraphs.jl") + typealias Graph SimpleGraphs.SimpleGraph + typealias DiGraph SimpleGraphs.SimpleDiGraph + include("digraph-transitivity.jl") - include("graph.jl") include("edgeiter.jl") include("traversals/graphvisit.jl") include("traversals/bfs.jl") diff --git a/src/core.jl b/src/core.jl index 70d6b167e..b1175b80c 100644 --- a/src/core.jl +++ b/src/core.jl @@ -1,3 +1,5 @@ +_NI(m...) = error("Not implementedxxx") + abstract AbstractPathState # modified from http://stackoverflow.com/questions/25678112/insert-item-into-a-sorted-list-with-julia-with-and-without-duplicates # returns true if insert succeeded, false if it was a duplicate @@ -5,46 +7,29 @@ _insert_and_dedup!(v::Vector{Int}, x::Int) = isempty(splice!(v, searchsorted(v,x """A type representing a single edge between two vertices of a graph.""" abstract AbstractEdge -immutable Edge <: AbstractEdge - src::Int - dst::Int -end -show(io::IO, e::Edge) = print(io, "Edge $(e.src) => $(e.dst)") +"""An abstract type representing a graph.""" +abstract AbstractGraph + +"""Return the type of a graph's edge""" +edgetype(g::AbstractGraph) = _NI() """Return source of an edge.""" -src(e::Edge) = e.src +src(e::AbstractEdge) = _NI() """Return destination of an edge.""" -dst(e::Edge) = e.dst - -convert(::Type{Pair}, e::Edge) = Pair(src(e), dst(e)) -convert(::Type{Tuple}, e::Edge) = (src(e), dst(e)) - -reverse(e::Edge) = Edge(dst(e), src(e)) -is_ordered(e::Edge) = src(e) <= dst(e) - -==(e1::Edge, e2::Edge) = (src(e1) == src(e2) && dst(e1) == dst(e2)) +dst(e::AbstractEdge) = _NI() -"""An abstract type representing a graph.""" -abstract AbstractGraph +# The following functions should be implemented for each edge type. +Pair(e::AbstractEdge) = _NI() +Tuple(e::AbstractEdge) = _NI() +reverse(e::AbstractEdge) = _NI() +==(e1::AbstractEdge, e2::AbstractEdge) = _NI() -"""A type representing an undirected graph.""" -type Graph <: AbstractGraph - vertices::UnitRange{Int} - ne::Int - fadjlist::Vector{Vector{Int}} # [src]: (dst, dst, dst) -end +is_ordered(e::AbstractEdge) = src(e) <= dst(e) -"""A type representing a directed graph.""" -type DiGraph <: AbstractGraph - vertices::UnitRange{Int} - ne::Int - fadjlist::Vector{Vector{Int}} # [src]: (dst, dst, dst) - badjlist::Vector{Vector{Int}} # [dst]: (src, src, src) -end """Return the vertices of a graph.""" -vertices(g::AbstractGraph) = g.vertices +vertices(g::AbstractGraph) = _NI() """Return an iterator to the edges of a graph. The returned iterator is valid for one pass over the edges, and is invalidated by changes to `g`. @@ -70,29 +55,26 @@ The optional second argument take the `v`th vertex adjacency list, that is: NOTE: returns a reference, not a copy. Do not modify result. """ -fadj(g::AbstractGraph) = g.fadjlist -fadj(g::AbstractGraph, v::Int) = g.fadjlist[v] +fadj(g::AbstractGraph) = _NI() +fadj(g::AbstractGraph, v::Int) = _NI() +badj(g::AbstractGraph) = _NI() +badj(g::AbstractGraph, v::Int) = _NI() + +adj(g::AbstractGraph) = _NI() +adj(g::AbstractGraph, v::Int) = _NI() """Returns true if all of the vertices and edges of `g` are contained in `h`.""" -function issubset{T<:AbstractGraph}(g::T, h::T) - (gmin, gmax) = extrema(vertices(g)) - (hmin, hmax) = extrema(vertices(h)) - return (hmin <= gmin <= gmax <= hmax) && issubset(edges(g), edges(h)) -end +issubset{T<:AbstractGraph}(g::T, h::T) = _NI() +is_directed(g::AbstractGraph) = _NI() +add_vertex!(g::AbstractGraph) = _NI() """Add `n` new vertices to the graph `g`. Returns true if all vertices were added successfully, false otherwise.""" -function add_vertices!(g::AbstractGraph, n::Integer) - added = true - for i = 1:n - added &= add_vertex!(g) - end - return added -end +add_vertices!(g::AbstractGraph) = _NI() """Return true if the graph `g` has an edge from `u` to `v`.""" -has_edge(g::AbstractGraph, u::Int, v::Int) = has_edge(g, Edge(u, v)) +has_edge(g::AbstractGraph, u::Int, v::Int) = _NI() """ in_edges(g, v) @@ -100,7 +82,7 @@ has_edge(g::AbstractGraph, u::Int, v::Int) = has_edge(g, Edge(u, v)) Returns an Array of the edges in `g` that arrive at vertex `v`. `v=dst(e)` for each returned edge `e`. """ -in_edges(g::AbstractGraph, v::Int) = [Edge(x,v) for x in badj(g, v)] +in_edges(g::AbstractGraph, v::Int) = _NI() """ out_edges(g, v) @@ -108,24 +90,24 @@ in_edges(g::AbstractGraph, v::Int) = [Edge(x,v) for x in badj(g, v)] Returns an Array of the edges in `g` that depart from vertex `v`. `v = src(e)` for each returned edge `e`. """ -out_edges(g::AbstractGraph, v::Int) = [Edge(v,x) for x in fadj(g,v)] +out_edges(g::AbstractGraph, v::Int) = _NI() """Return true if `v` is a vertex of `g`.""" -has_vertex(g::AbstractGraph, v::Int) = v in vertices(g) +has_vertex(g::AbstractGraph, v::Int) = _NI() """ nv(g) The number of vertices in `g`. """ -nv(g::AbstractGraph) = length(vertices(g)) +nv(g::AbstractGraph) = _NI() """ ne(g) The number of edges in `g`. """ -ne(g::AbstractGraph) = g.ne +ne(g::AbstractGraph) = _NI() """ add_edge!(g, u, v) @@ -133,7 +115,7 @@ ne(g::AbstractGraph) = g.ne Add a new edge to `g` from `u` to `v`. Will return false if add fails (e.g., if vertices are not in the graph); true otherwise. """ -add_edge!(g::AbstractGraph, u::Int, v::Int) = add_edge!(g, Edge(u, v)) +add_edge!(g::AbstractGraph, u::Int, v::Int) = _NI() """ rem_edge!(g, u, v) @@ -142,7 +124,7 @@ Remove the edge from `u` to `v`. Returns false if edge removal fails (e.g., if edge does not exist); true otherwise. """ -rem_edge!(g::AbstractGraph, u::Int, v::Int) = rem_edge!(g, Edge(u, v)) +rem_edge!(g::AbstractGraph, u::Int, v::Int) = _NI() """ rem_vertex!(g, v) @@ -155,56 +137,19 @@ 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. """ -function rem_vertex!(g::AbstractGraph, v::Int) - v in vertices(g) || return false - n = nv(g) - - edgs = in_edges(g, v) - for e in edgs - rem_edge!(g, e) - end - neigs = copy(in_neighbors(g, n)) - for i in neigs - rem_edge!(g, Edge(i, n)) - end - if v != n - for i in neigs - add_edge!(g, Edge(i, v)) - end - end - - if is_directed(g) - edgs = out_edges(g, v) - for e in edgs - rem_edge!(g, e) - end - neigs = copy(out_neighbors(g, n)) - for i in neigs - rem_edge!(g, Edge(n, i)) - end - if v != n - for i in neigs - add_edge!(g, Edge(v, i)) - end - end - end - - g.vertices = 1:n-1 - pop!(g.fadjlist) - if is_directed(g) - pop!(g.badjlist) - end - return true -end +rem_vertex!(g::AbstractGraph, v::Int) = _NI() """Return the number of edges which end at vertex `v`.""" -indegree(g::AbstractGraph, v::Int) = length(badj(g,v)) +indegree(g::AbstractGraph, v::Int) = _NI() """Return the number of edges which start at vertex `v`.""" -outdegree(g::AbstractGraph, v::Int) = length(fadj(g,v)) +outdegree(g::AbstractGraph, v::Int) = _NI() indegree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [indegree(g,x) for x in v] outdegree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [outdegree(g,x) for x in v] +"""Return the number of edges (both ingoing and outgoing) from the vertex `v`.""" +degree(g::AbstractGraph, v::Int) = _NI() + degree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [degree(g,x) for x in v] "Return the maximum `outdegree` of vertices in `g`." @@ -243,12 +188,12 @@ degree_histogram(g::AbstractGraph) = fit(Histogram, degree(g)) NOTE: returns a reference, not a copy. Do not modify result. """ -in_neighbors(g::AbstractGraph, v::Int) = badj(g,v) +in_neighbors(g::AbstractGraph, v::Int) = _NI() """Returns a list of all neighbors connected to vertex `v` by an outgoing edge. NOTE: returns a reference, not a copy. Do not modify result. """ -out_neighbors(g::AbstractGraph, v::Int) = fadj(g,v) +out_neighbors(g::AbstractGraph, v::Int) = _NI() """Returns a list of all neighbors of vertex `v` in `g`. @@ -256,16 +201,17 @@ For DiGraphs, this is equivalent to `out_neighbors(g, v)`. NOTE: returns a reference, not a copy. Do not modify result. """ -neighbors(g::AbstractGraph, v::Int) = out_neighbors(g, v) +neighbors(g::AbstractGraph, v::Int) = _NI() "Returns the neighbors common to vertices `u` and `v` in `g`." -common_neighbors(g::AbstractGraph, u::Int, v::Int) = intersect(neighbors(g,u), neighbors(g,v)) +common_neighbors(g::AbstractGraph, u::Int, v::Int) = _NI() -@deprecate has_self_loop has_self_loops +all_neighbors(g::AbstractGraph) = _NI() "Returns true if `g` has any self loops." - has_self_loops(g::AbstractGraph) = any(v->has_edge(g, v, v), vertices(g)) "Returns the number of self loops in `g`." num_self_loops(g::AbstractGraph) = sum(v->has_edge(g, v, v), vertices(g)) + +density(g::AbstractGraph) = _NI() diff --git a/src/graphtypes/simpleedge/SimpleEdges.jl b/src/graphtypes/simpleedge/SimpleEdges.jl new file mode 100644 index 000000000..c1b5a80fa --- /dev/null +++ b/src/graphtypes/simpleedge/SimpleEdges.jl @@ -0,0 +1,29 @@ +module SimpleEdges + +import Base: Pair, Tuple, show, == +import LightGraphs: AbstractEdge, src, dst, reverse +export AbstractSimpleEdge, SimpleEdge + +abstract AbstractSimpleEdge <: AbstractEdge + +immutable SimpleEdge <: AbstractSimpleEdge + src::Int + dst::Int +end + +# Accessors +src{T<:AbstractSimpleEdge}(e::T) = e.src +dst{T<:AbstractSimpleEdge}(e::T) = e.dst + +# I/O +show{T<:AbstractSimpleEdge}(io::IO, e::T) = print(io, "Edge $(e.src) => $(e.dst)") + +# Conversions +Pair{T<:AbstractSimpleEdge}(e::T) = Pair(src(e), dst(e)) +Tuple{T<:AbstractSimpleEdge}(e::T) = (src(e), dst(e)) + +# Convenience functions +reverse{T<:AbstractSimpleEdge}(e::T) = T(dst(e), src(e)) +=={T<:AbstractSimpleEdge}(e1::T, e2::T) = (src(e1) == src(e2) && dst(e1) == dst(e2)) + +end diff --git a/src/graphtypes/simplegraph/SimpleGraphs.jl b/src/graphtypes/simplegraph/SimpleGraphs.jl new file mode 100644 index 000000000..b8f79ccb5 --- /dev/null +++ b/src/graphtypes/simplegraph/SimpleGraphs.jl @@ -0,0 +1,131 @@ +module SimpleGraphs + +import Base:show, ==, Pair, Tuple, copy +import LightGraphs: _insert_and_dedup!, AbstractGraph, edges, vertices, +nv, ne, fadj, badj, adj, degree, indegree, outdegree, in_neighbors, out_neighbors, +all_neighbors, neighbors, common_neighbors, issubset, add_vertex!, add_vertices!, rem_vertex!, +has_edge, in_edges, out_edges, has_vertex, +add_edge!, rem_edge!, is_directed, num_self_loops, has_self_loops, density + +import LightGraphs.SimpleEdges: AbstractSimpleEdge, SimpleEdge, src, dst, reverse + +export AbstractSimpleGraph, AbstractSimpleDiGraph, +SimpleGraph, SimpleGraphEdge, +SimpleDiGraph, SimpleDiGraphEdge + + +""" +AbstractSimpleGraphs must have the following elements: +- vertices::UnitRange{Integer} +- fadjlist::Vector{Vector{Integer}} +- ne::Integer +""" +abstract AbstractSimpleGraph <: AbstractGraph +typealias AbstractSimpleGraphEdge AbstractSimpleEdge + +function show(io::IO, g::AbstractSimpleGraph) + if is_directed(g) + dir = "directed" + else + dir = "undirected" + end + if nv(g) == 0 + print(io, "empty $dir simple graph") + else + print(io, "{$(nv(g)), $(ne(g))} $dir simple graph") + end +end + +vertices(g::AbstractSimpleGraph) = g.vertices +nv(g::AbstractSimpleGraph) = length(vertices(g)) + +fadj(g::AbstractSimpleGraph) = g.fadjlist +fadj(g::AbstractSimpleGraph, v::Int) = g.fadjlist[v] + + +badj(g::AbstractSimpleGraph) = _NI +badj(g::AbstractSimpleGraph, v::Int) = _NI + +indegree(g::AbstractSimpleGraph, v::Int) = length(badj(g,v)) +outdegree(g::AbstractSimpleGraph, v::Int) = length(fadj(g,v)) +degree(g::AbstractSimpleGraph, v::Int) = _NI() + +in_neighbors(g::AbstractSimpleGraph, v::Int) = badj(g,v) +out_neighbors(g::AbstractSimpleGraph, v::Int) = fadj(g,v) + +neighbors(g::AbstractSimpleGraph, v::Int) = out_neighbors(g, v) +common_neighbors(g::AbstractSimpleGraph, u::Int, v::Int) = intersect(neighbors(g,u), neighbors(g,v)) + +function issubset{T<:AbstractSimpleGraph}(g::T, h::T) + (gmin, gmax) = extrema(vertices(g)) + (hmin, hmax) = extrema(vertices(h)) + return (hmin <= gmin <= gmax <= hmax) && issubset(edges(g), edges(h)) +end + + +function add_vertices!{T<:AbstractSimpleGraph}(g::T, n::Integer) + added = true + for i = 1:n + added &= add_vertex!(g) + end + return added +end + +has_edge{T<:AbstractSimpleGraph}(g::T, u::Int, v::Int) = has_edge(g, edgetype(g)(u, v)) +in_edges{T<:AbstractSimpleGraph}(g::T, v::Int) = [edgetype(g)(x,v) for x in badj(g, v)] +out_edges{T<:AbstractSimpleGraph}(g::T, v::Int) = [edgetype(g)(v,x) for x in fadj(g,v)] +has_vertex{T<:AbstractSimpleGraph}(g::T, v::Int) = v in vertices(g) + +ne{T<:AbstractSimpleGraph}(g::T) = g.ne +add_edge!{T<:AbstractSimpleGraph}(g::T, u::Int, v::Int) = add_edge!(g, edgetype(g)(u, v)) +rem_edge!{T<:AbstractSimpleGraph}(g::T, u::Int, v::Int) = rem_edge!(g, edgetype(g)(u, v)) + +function rem_vertex!{T<:AbstractSimpleGraph}(g::T, v::Int) + v in vertices(g) || return false + n = nv(g) + + edgs = in_edges(g, v) + for e in edgs + rem_edge!(g, e) + end + neigs = copy(in_neighbors(g, n)) + for i in neigs + rem_edge!(g, edgetype(g)(i, n)) + end + if v != n + for i in neigs + add_edge!(g, edgetype(g)(i, v)) + end + end + + if is_directed(g) + edgs = out_edges(g, v) + for e in edgs + rem_edge!(g, e) + end + neigs = copy(out_neighbors(g, n)) + for i in neigs + rem_edge!(g, edgetype(g)(n, i)) + end + if v != n + for i in neigs + add_edge!(g, edgetype(g)(v, i)) + end + end + end + + g.vertices = 1:n-1 + pop!(g.fadjlist) + if is_directed(g) + pop!(g.badjlist) + end + return true +end + +include("simpledigraph.jl") +include("simplegraph.jl") + + + + +end # module diff --git a/src/digraph.jl b/src/graphtypes/simplegraph/simpledigraph.jl similarity index 58% rename from src/digraph.jl rename to src/graphtypes/simplegraph/simpledigraph.jl index 8b19a2e06..1f51269c2 100644 --- a/src/digraph.jl +++ b/src/graphtypes/simplegraph/simpledigraph.jl @@ -1,28 +1,32 @@ -function show(io::IO, g::DiGraph) - if nv(g) == 0 - print(io, "empty directed graph") - else - print(io, "{$(nv(g)), $(ne(g))} directed graph") - end +typealias SimpleDiGraphEdge SimpleEdge + +"""A type representing a directed graph.""" +type SimpleDiGraph <: AbstractSimpleGraph + vertices::UnitRange{Int} + ne::Int + fadjlist::Vector{Vector{Int}} # [src]: (dst, dst, dst) + badjlist::Vector{Vector{Int}} # [dst]: (src, src, src) end -function DiGraph(n::Int) +edgetype(::SimpleDiGraph) = SimpleDiGraphEdge + +function SimpleDiGraph(n::Int) fadjlist = Vector{Vector{Int}}() badjlist = Vector{Vector{Int}}() for i = 1:n push!(badjlist, Vector{Int}()) push!(fadjlist, Vector{Int}()) end - return DiGraph(1:n, 0, badjlist, fadjlist) + return SimpleDiGraph(1:n, 0, badjlist, fadjlist) end -DiGraph() = DiGraph(0) +SimpleDiGraph() = SimpleDiGraph(0) -function DiGraph{T<:Real}(adjmx::SparseMatrixCSC{T}) +function SimpleDiGraph{T<:Real}(adjmx::SparseMatrixCSC{T}) dima, dimb = size(adjmx) isequal(dima,dimb) || error("Adjacency / distance matrices must be square") - g = DiGraph(dima) + g = SimpleDiGraph(dima) maxc = length(adjmx.colptr) for c = 1:(maxc-1) for rind = adjmx.colptr[c]:adjmx.colptr[c+1]-1 @@ -36,11 +40,11 @@ function DiGraph{T<:Real}(adjmx::SparseMatrixCSC{T}) return g end -function DiGraph{T<:Real}(adjmx::AbstractMatrix{T}) +function SimpleDiGraph{T<:Real}(adjmx::AbstractMatrix{T}) dima,dimb = size(adjmx) isequal(dima,dimb) || error("Adjacency / distance matrices must be square") - g = DiGraph(dima) + g = SimpleDiGraph(dima) for i in find(adjmx) ind = ind2sub((dima,dimb),i) add_edge!(g,ind...) @@ -48,31 +52,31 @@ function DiGraph{T<:Real}(adjmx::AbstractMatrix{T}) return g end -function DiGraph(g::Graph) - h = DiGraph(nv(g)) +function SimpleDiGraph(g::AbstractSimpleGraph) + h = SimpleDiGraph(nv(g)) h.ne = ne(g) * 2 - num_self_loops(g) h.fadjlist = deepcopy(fadj(g)) h.badjlist = deepcopy(badj(g)) return h end -badj(g::DiGraph) = g.badjlist -badj(g::DiGraph, v::Int) = badj(g)[v] +badj(g::SimpleDiGraph) = g.badjlist +badj(g::SimpleDiGraph, v::Int) = badj(g)[v] -function copy(g::DiGraph) - return DiGraph(g.vertices, g.ne, deepcopy(g.fadjlist), deepcopy(g.badjlist)) +function copy(g::SimpleDiGraph) + return SimpleDiGraph(g.vertices, g.ne, deepcopy(g.fadjlist), deepcopy(g.badjlist)) end -==(g::DiGraph, h::DiGraph) = +==(g::SimpleDiGraph, h::SimpleDiGraph) = vertices(g) == vertices(h) && ne(g) == ne(h) && fadj(g) == fadj(h) && badj(g) == badj(h) -is_directed(g::DiGraph) = true +is_directed(g::SimpleDiGraph) = true -function add_edge!(g::DiGraph, e::Edge) +function add_edge!(g::SimpleDiGraph, e::SimpleDiGraphEdge) s, d = Tuple(e) (s in vertices(g) && d in vertices(g)) || return false inserted = _insert_and_dedup!(g.fadjlist[s], d) @@ -83,7 +87,7 @@ function add_edge!(g::DiGraph, e::Edge) end -function rem_edge!(g::DiGraph, e::Edge) +function rem_edge!(g::SimpleDiGraph, e::SimpleDiGraphEdge) has_edge(g,e) || return false i = searchsorted(g.fadjlist[src(e)], dst(e))[1] deleteat!(g.fadjlist[src(e)], i) @@ -94,7 +98,7 @@ function rem_edge!(g::DiGraph, e::Edge) end -function add_vertex!(g::DiGraph) +function add_vertex!(g::SimpleDiGraph) g.vertices = 1:nv(g)+1 push!(g.badjlist, Vector{Int}()) push!(g.fadjlist, Vector{Int}()) @@ -103,7 +107,7 @@ function add_vertex!(g::DiGraph) end -function has_edge(g::DiGraph, e::Edge) +function has_edge(g::SimpleDiGraph, e::SimpleDiGraphEdge) u, v = Tuple(e) u > nv(g) || v > nv(g) && return false if degree(g,u) < degree(g,v) @@ -113,7 +117,7 @@ function has_edge(g::DiGraph, e::Edge) end end -degree(g::DiGraph, v::Int) = indegree(g,v) + outdegree(g,v) +degree(g::SimpleDiGraph, v::Int) = indegree(g,v) + outdegree(g,v) "Returns all the vertices which share an edge with `v`." -all_neighbors(g::DiGraph, v::Int) = union(in_neighbors(g,v), out_neighbors(g,v)) -density(g::DiGraph) = ne(g) / (nv(g) * (nv(g)-1)) +all_neighbors(g::SimpleDiGraph, v::Int) = union(in_neighbors(g,v), out_neighbors(g,v)) +density(g::SimpleDiGraph) = ne(g) / (nv(g) * (nv(g)-1)) diff --git a/src/graph.jl b/src/graphtypes/simplegraph/simplegraph.jl similarity index 69% rename from src/graph.jl rename to src/graphtypes/simplegraph/simplegraph.jl index 25c0c3141..3ad95dc7d 100644 --- a/src/graph.jl +++ b/src/graphtypes/simplegraph/simplegraph.jl @@ -1,12 +1,15 @@ -function show(io::IO, g::Graph) - if nv(g) == 0 - print(io, "empty undirected graph") - else - print(io, "{$(nv(g)), $(ne(g))} undirected graph") - end +typealias SimpleGraphEdge SimpleEdge + +"""A type representing an undirected graph.""" +type SimpleGraph <: AbstractSimpleGraph + vertices::UnitRange{Int} + ne::Int + fadjlist::Vector{Vector{Int}} # [src]: (dst, dst, dst) end -function Graph(n::Int) +edgetype(::SimpleGraph) = SimpleGraphEdge + +function SimpleGraph(n::Int) fadjlist = Vector{Vector{Int}}() sizehint!(fadjlist,n) for i = 1:n @@ -14,17 +17,17 @@ function Graph(n::Int) # sizehint!(o_s, n/4) push!(fadjlist, Vector{Int}()) end - return Graph(1:n, 0, fadjlist) + return SimpleGraph(1:n, 0, fadjlist) end -Graph() = Graph(0) +SimpleGraph() = SimpleGraph(0) -function Graph{T<:Real}(adjmx::AbstractMatrix{T}) +function SimpleGraph{T<:Real}(adjmx::AbstractMatrix{T}) dima,dimb = size(adjmx) isequal(dima,dimb) || error("Adjacency / distance matrices must be square") issymmetric(adjmx) || error("Adjacency / distance matrices must be symmetric") - g = Graph(dima) + g = SimpleGraph(dima) for i in find(triu(adjmx)) ind = ind2sub((dima,dimb),i) add_edge!(g,ind...) @@ -32,7 +35,7 @@ function Graph{T<:Real}(adjmx::AbstractMatrix{T}) return g end -function Graph(g::DiGraph) +function SimpleGraph(g::SimpleDiGraph) gnv = nv(g) edgect = 0 @@ -50,7 +53,7 @@ function Graph(g::DiGraph) end end iseven(edgect) || throw(AssertionError("invalid edgect in graph creation - please file bug report")) - return Graph(vertices(g), edgect ÷ 2, newfadj) + return SimpleGraph(vertices(g), edgect ÷ 2, newfadj) end """Returns the backwards adjacency list of a graph. @@ -58,8 +61,8 @@ For each vertex the Array of `dst` for each edge eminating from that vertex. NOTE: returns a reference, not a copy. Do not modify result. """ -badj(g::Graph) = fadj(g) -badj(g::Graph, v::Int) = fadj(g, v) +badj(g::SimpleGraph) = fadj(g) +badj(g::SimpleGraph, v::Int) = fadj(g, v) """Returns the adjacency list of a graph. @@ -67,23 +70,23 @@ For each vertex the Array of `dst` for each edge eminating from that vertex. NOTE: returns a reference, not a copy. Do not modify result. """ -adj(g::Graph) = fadj(g) -adj(g::Graph, v::Int) = fadj(g, v) +adj(g::SimpleGraph) = fadj(g) +adj(g::SimpleGraph, v::Int) = fadj(g, v) -function copy(g::Graph) - return Graph(g.vertices, g.ne, deepcopy(g.fadjlist)) +function copy(g::SimpleGraph) + return SimpleGraph(g.vertices, g.ne, deepcopy(g.fadjlist)) end -==(g::Graph, h::Graph) = +==(g::SimpleGraph, h::SimpleGraph) = vertices(g) == vertices(h) && ne(g) == ne(h) && fadj(g) == fadj(h) -"Returns `true` if `g` is a `DiGraph`." -is_directed(g::Graph) = false +"Returns `true` if `g` is a `SimpleDiSimpleGraph`." +is_directed(g::SimpleGraph) = false -function has_edge(g::Graph, e::Edge) +function has_edge(g::SimpleGraph, e::SimpleGraphEdge) u, v = Tuple(e) u > nv(g) || v > nv(g) && return false if degree(g,u) > degree(g,v) @@ -92,7 +95,7 @@ function has_edge(g::Graph, e::Edge) return length(searchsorted(fadj(g,u), v)) > 0 end -function add_edge!(g::Graph, e::Edge) +function add_edge!(g::SimpleGraph, e::SimpleGraphEdge) s, d = Tuple(e) (s in vertices(g) && d in vertices(g)) || return false @@ -106,7 +109,7 @@ function add_edge!(g::Graph, e::Edge) return inserted end -function rem_edge!(g::Graph, e::Edge) +function rem_edge!(g::SimpleGraph, e::SimpleGraphEdge) i = searchsorted(g.fadjlist[src(e)], dst(e)) length(i) > 0 || return false # edge not in graph i = i[1] @@ -121,7 +124,7 @@ end """Add a new vertex to the graph `g`.""" -function add_vertex!(g::Graph) +function add_vertex!(g::SimpleGraph) g.vertices = 1:nv(g)+1 push!(g.fadjlist, Vector{Int}()) @@ -130,10 +133,9 @@ end """Return the number of edges (both ingoing and outgoing) from the vertex `v`.""" -degree(g::Graph, v::Int) = indegree(g,v) - +degree(g::SimpleGraph, v::Int) = indegree(g,v) doc"""Density is defined as the ratio of the number of actual edges to the number of possible edges. This is $|v| |v-1|$ for directed graphs and $(|v| |v-1|) / 2$ for undirected graphs. """ -density(g::Graph) = (2*ne(g)) / (nv(g) * (nv(g)-1)) +density(g::SimpleGraph) = (2*ne(g)) / (nv(g) * (nv(g)-1)) diff --git a/test/graphdigraph.jl b/test/graphdigraph.jl index acbff9cf6..0903d39f9 100644 --- a/test/graphdigraph.jl +++ b/test/graphdigraph.jl @@ -1,5 +1,5 @@ -@test sprint(show, h1) == "{5, 0} undirected graph" -@test sprint(show, h3) == "empty undirected graph" +@test sprint(show, h1) == "{5, 0} undirected simple graph" +@test sprint(show, h3) == "empty undirected simple graph" @test Graph(DiGraph(g3)) == g3 @@ -33,8 +33,8 @@ h = DiGraph(5) LightGraphs.adj(g)[1] == LightGraphs.adj(g,1) == [2,3,4] -@test sprint(show, h4) == "{7, 0} directed graph" -@test sprint(show, h5) == "empty directed graph" +@test sprint(show, h4) == "{7, 0} directed simple graph" +@test sprint(show, h5) == "empty directed simple graph" @test has_edge(g, e1) @test has_edge(h, e1) @test !has_edge(g, e0) From 5765fa0e3fc42855d9816d63ae3484e46f62e9d7 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 5 Mar 2017 16:41:48 -0800 Subject: [PATCH 04/56] Edge is no longer a Pair --- .gitignore | 1 + benchmarks/LightGraphsBenchmarks.jl | 63 +++++++++++++++++++++++++---- src/LightGraphs.jl | 2 +- src/core.jl | 20 ++++++--- src/digraph.jl | 4 +- src/edgeiter.jl | 2 +- src/graph.jl | 4 +- src/operators.jl | 13 +++--- src/persistence/gml.jl | 3 +- test/biconnectivity/biconnect.jl | 8 ++-- test/core.jl | 7 +++- 11 files changed, 95 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index 633cd6c90..b083fa31b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ *.jl.mem docs/build/ docs/site/ +benchmarks/data diff --git a/benchmarks/LightGraphsBenchmarks.jl b/benchmarks/LightGraphsBenchmarks.jl index d70bb985c..339c6c943 100644 --- a/benchmarks/LightGraphsBenchmarks.jl +++ b/benchmarks/LightGraphsBenchmarks.jl @@ -16,7 +16,11 @@ const PARAMS_PATH = joinpath(dirname(@__FILE__), "params.jld") const SUITE = BenchmarkGroup() testdatadir = joinpath(dirname(@__FILE__), "..","test","testdata") +benchdatadir = joinpath(dirname(@__FILE__), "data") +paramsfile = joinpath(benchdatadir, "params.jld") + println("testdatadir = $testdatadir") +println("paramsfile = $paramsfile") dg1fn = joinpath(testdatadir, "graph-5k-50k.jgz") @@ -34,20 +38,63 @@ GRAPHS = Dict{String, Graph}( ) MODULES = [ - "core.jl", - "edgetype.jl", + "core", + "edgetype", # "centrality.jl", - "max-flow.jl", - "connectivity.jl", - "traversals.jl" + "max-flow", + "connectivity", + "traversals" ] for m in MODULES - include("$m") + include("$(m).jl") +end + +function save_benchmarks(results, datadir=benchdatadir) + ts = string(now()) + for m in MODULES + d = joinpath(datadir, m) + isdir(d) || mkdir(d) + f = joinpath(d, "$(ts).jld") + JLD.save(f, m, results[m]) + end end -run_benchmarks() = run(SUITE, verbose=true) +function _getlastresults(datadir=benchdatadir) + files = Vector{Tuple{String, String}}() + for m in MODULES + d = joinpath(datadir, m) + f = sort(readdir(d))[end] + push!(files, (m, joinpath(d,f))) + end + return files +end + +function load_benchmarks(files::Vector{Tuple{String, String}}=_getlastresults(), datadir=benchdatadir) + b = BenchmarkGroup() + for (m, f) in files + println("loading module $m from file $f") + b[m] = JLD.load(f, m) + end + return b +end -run_benchmarks() +function load_benchmarks(dts::String, datadir=benchdatadir) + fs = [joinpath(datadir, m, "$(dts).jld") for m in MODULES] + files = collect(zip(MODULES, fs)) + return load_benchmarks(files, datadir) end +function run_benchmarks() + if isfile(paramsfile) + loadparams!(SUITE, BenchmarkTools.load(paramsfile, "suite"), :evals, :samples) + end + run(SUITE, verbose=true) +end + +function tune_and_save!(b::BenchmarkGroup=SUITE) + tune!(b) + BenchmarkTools.save(paramsfile, "suite", params(b)) +end + +end diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 6b91045b8..a4b2e3893 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -8,7 +8,7 @@ using EzXML using ParserCombinator: Parsers.DOT, Parsers.GML using StatsBase: fit, Histogram -import Base: write, ==, <, *, ≈, isless, issubset, union, intersect, +import Base: write, ==, <, *, ≈, convert, isless, issubset, union, intersect, reverse, reverse!, blkdiag, getindex, setindex!, show, print, copy, in, sum, size, sparse, eltype, length, ndims, transpose, ctranspose, join, start, next, done, eltype, get, issymmetric, A_mul_B! diff --git a/src/core.jl b/src/core.jl index b8dd85598..70d6b167e 100644 --- a/src/core.jl +++ b/src/core.jl @@ -4,18 +4,26 @@ abstract AbstractPathState _insert_and_dedup!(v::Vector{Int}, x::Int) = isempty(splice!(v, searchsorted(v,x), x)) """A type representing a single edge between two vertices of a graph.""" -typealias Edge Pair{Int,Int} +abstract AbstractEdge +immutable Edge <: AbstractEdge + src::Int + dst::Int +end -@deprecate rev(e::Edge) reverse(e) +show(io::IO, e::Edge) = print(io, "Edge $(e.src) => $(e.dst)") """Return source of an edge.""" -src(e::Edge) = e.first +src(e::Edge) = e.src """Return destination of an edge.""" -dst(e::Edge) = e.second +dst(e::Edge) = e.dst + +convert(::Type{Pair}, e::Edge) = Pair(src(e), dst(e)) +convert(::Type{Tuple}, e::Edge) = (src(e), dst(e)) - is_ordered(e::Edge) = src(e) <= dst(e) +reverse(e::Edge) = Edge(dst(e), src(e)) +is_ordered(e::Edge) = src(e) <= dst(e) -==(e1::Edge, e2::Edge) = (e1.first == e2.first && e1.second == e2.second) +==(e1::Edge, e2::Edge) = (src(e1) == src(e2) && dst(e1) == dst(e2)) """An abstract type representing a graph.""" abstract AbstractGraph diff --git a/src/digraph.jl b/src/digraph.jl index f647db496..8b19a2e06 100644 --- a/src/digraph.jl +++ b/src/digraph.jl @@ -73,7 +73,7 @@ end is_directed(g::DiGraph) = true function add_edge!(g::DiGraph, e::Edge) - s, d = e + s, d = Tuple(e) (s in vertices(g) && d in vertices(g)) || return false inserted = _insert_and_dedup!(g.fadjlist[s], d) if inserted @@ -104,7 +104,7 @@ end function has_edge(g::DiGraph, e::Edge) - u, v = e + u, v = Tuple(e) u > nv(g) || v > nv(g) && return false if degree(g,u) < degree(g,v) return length(searchsorted(fadj(g,u), v)) > 0 diff --git a/src/edgeiter.jl b/src/edgeiter.jl index acb85f58b..81334d1ad 100644 --- a/src/edgeiter.jl +++ b/src/edgeiter.jl @@ -48,7 +48,7 @@ end function _isequal(e1::EdgeIter, e2) for e in e2 - s, d = e + s, d = Tuple(e) found = length(searchsorted(e1.adj[s], d)) > 0 if !e1.directed found = found || length(searchsorted(e1.adj[d],s)) > 0 diff --git a/src/graph.jl b/src/graph.jl index b5e041ae2..25c0c3141 100644 --- a/src/graph.jl +++ b/src/graph.jl @@ -84,7 +84,7 @@ end is_directed(g::Graph) = false function has_edge(g::Graph, e::Edge) - u, v = e + u, v = Tuple(e) u > nv(g) || v > nv(g) && return false if degree(g,u) > degree(g,v) u, v = v, u @@ -94,7 +94,7 @@ end function add_edge!(g::Graph, e::Edge) - s, d = e + s, d = Tuple(e) (s in vertices(g) && d in vertices(g)) || return false inserted = _insert_and_dedup!(g.fadjlist[s], d) if inserted diff --git a/src/operators.jl b/src/operators.jl index 9011436fe..10cda7e73 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -244,14 +244,15 @@ Returns the (cartesian product)[https://en.wikipedia.org/wiki/Tensor_product_of_ function cartesian_product{G<:AbstractGraph}(g::G, h::G) z = G(nv(g)*nv(h)) id(i, j) = (i-1)*nv(h) + j - for (i1, i2) in edges(g) + for e in edges(g) + i1, i2 = Tuple(e) for j=1:nv(h) add_edge!(z, id(i1,j), id(i2,j)) end end for e in edges(h) - j1, j2 = src(e), dst(e) + j1, j2 = Tuple(e) for i=1:nv(g) add_edge!(z, id(i,j1), id(i,j2)) end @@ -267,8 +268,10 @@ Returns the (tensor product)[https://en.wikipedia.org/wiki/Tensor_product_of_gra function tensor_product{G<:AbstractGraph}(g::G, h::G) z = G(nv(g)*nv(h)) id(i, j) = (i-1)*nv(h) + j - for (i1, i2) in edges(g) - for (j1, j2) in edges(h) + for e1 in edges(g) + i1, i2 = Tuple(e1) + for e2 in edges(h) + j1, j2 = Tuple(e2) add_edge!(z, id(i1, j1), id(i2, j2)) end end @@ -344,7 +347,7 @@ function induced_subgraph{T<:AbstractGraph}(g::T, elist::AbstractVector{Edge}) vmap = Vector{Int}() for e in elist - u, v = e + u, v = Tuple(e) for i in (u,v) if !haskey(newvid, i) add_vertex!(h) diff --git a/src/persistence/gml.jl b/src/persistence/gml.jl index 22d5e1c7f..9ff0a5b2d 100644 --- a/src/persistence/gml.jl +++ b/src/persistence/gml.jl @@ -55,7 +55,8 @@ function savegml(io::IO, g::AbstractGraph, gname::String = "") println(io,"\t\tid $i") println(io,"\t]") end - for (s,t) in edges(g) + for e in edges(g) + s, t = Tuple(e) println(io,"\tedge") println(io,"\t[") println(io,"\t\tsource $s") diff --git a/test/biconnectivity/biconnect.jl b/test/biconnectivity/biconnect.jl index fe2674581..47de50e74 100644 --- a/test/biconnectivity/biconnect.jl +++ b/test/biconnectivity/biconnect.jl @@ -15,8 +15,8 @@ add_edge!(g, 9, 10) add_edge!(g, 11, 12) bcc = biconnected_components(g) -ans = [[3=>5,4=>5,2=>4,3=>4,2=>3], [9=>10], [6=>9,8=>9,6=>8], [1=>7,6=>7,2=>6,1=>2], [11=>12]] -@test bcc == ans +a = [[Edge(3, 5),Edge(4, 5), Edge(2, 4),Edge(3, 4),Edge(2, 3)], [Edge(9, 10)], [Edge(6, 9),Edge(8, 9),Edge(6, 8)], [Edge(1, 7),Edge(6, 7),Edge(2, 6),Edge(1, 2)], [Edge(11, 12)]] +@test bcc == a g = Graph(4) add_edge!(g, 1, 2) @@ -34,5 +34,5 @@ G = blkdiag(g, h) add_edge!(G, 4, 5) bcc = biconnected_components(G) -ans = [[5=>8,7=>8,6=>7,5=>6], [4=>5], [1=>4,3=>4,2=>3,1=>2]] -@test bcc == ans +a = [[Edge(5, 8),Edge(7, 8),Edge(6, 7),Edge(5, 6)], [Edge(4, 5)], [Edge(1, 4),Edge(3, 4),Edge(2, 3),Edge(1, 2)]] +@test bcc == a diff --git a/test/core.jl b/test/core.jl index 591fdf70f..018b18ffb 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1,6 +1,9 @@ -@test e1.first == src(e1) == 1 -@test e1.second == dst(e1) == 2 +@test e1.src == src(e1) == 1 +@test e1.dst == dst(e1) == 2 @test reverse(e1) == re1 +@test sprint(show, e1) == "Edge 1 => 2" +@test Pair(e1) == Pair(1,2) +@test Tuple(e1) == (1,2) e2 = Edge(1,3) e3 = Edge(1,4) From 6124998293a2e145801df87583b5d51622dce60f Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 12 Mar 2017 14:19:54 -0700 Subject: [PATCH 05/56] pkgbenchmarks --- .gitignore | 2 +- ...c2f3d620f89970e811af97da4ddf73ebe43933.jld | Bin 0 -> 5517282 bytes benchmark/.tune.jld | Bin 0 -> 35274 bytes benchmark/benchmarks.jl | 35 ++++++ benchmark/centrality.jl | 26 +++++ benchmark/connectivity.jl | 13 +++ {benchmarks => benchmark}/core.jl | 28 ++--- benchmark/edges.jl | 39 +++++++ benchmark/max-flow.jl | 10 ++ benchmark/traversals.jl | 14 +++ benchmarks/LightGraphsBenchmarks.jl | 100 ------------------ benchmarks/attic/bfstree.jl | 69 ------------ benchmarks/attic/nonbacktracking.jl | 48 --------- benchmarks/centrality.jl | 23 ---- benchmarks/connectivity.jl | 13 --- benchmarks/edgetype.jl | 46 -------- benchmarks/max-flow.jl | 11 -- benchmarks/traversals.jl | 12 --- 18 files changed, 154 insertions(+), 335 deletions(-) create mode 100644 benchmark/.results/73c2f3d620f89970e811af97da4ddf73ebe43933.jld create mode 100644 benchmark/.tune.jld create mode 100644 benchmark/benchmarks.jl create mode 100644 benchmark/centrality.jl create mode 100644 benchmark/connectivity.jl rename {benchmarks => benchmark}/core.jl (54%) create mode 100644 benchmark/edges.jl create mode 100644 benchmark/max-flow.jl create mode 100644 benchmark/traversals.jl delete mode 100644 benchmarks/LightGraphsBenchmarks.jl delete mode 100644 benchmarks/attic/bfstree.jl delete mode 100644 benchmarks/attic/nonbacktracking.jl delete mode 100644 benchmarks/centrality.jl delete mode 100644 benchmarks/connectivity.jl delete mode 100644 benchmarks/edgetype.jl delete mode 100644 benchmarks/max-flow.jl delete mode 100644 benchmarks/traversals.jl diff --git a/.gitignore b/.gitignore index b083fa31b..07eef31a3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ *.jl.mem docs/build/ docs/site/ -benchmarks/data +.results/ diff --git a/benchmark/.results/73c2f3d620f89970e811af97da4ddf73ebe43933.jld b/benchmark/.results/73c2f3d620f89970e811af97da4ddf73ebe43933.jld new file mode 100644 index 0000000000000000000000000000000000000000..4928fe2fe8e05239e86fe85c3c65dc8b4516a7b3 GIT binary patch literal 5517282 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

Rc9E7-H@X>RXr@#XRP)fHs!V!x*z~)cFWAqm^se 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�P;&sh;Q7tq3a-nPs} zt(@mAVa{u3&fAIEQNU8powPf38!Tte>tP;1y>&Q<>qdQ?4=tdd^Sl+z0U9jeM9z(d zIG;wtbY=k~oLAA9!36U%x{9V~x_}!v&(NAW+{E?V4!V{5j5eHx=h0-ah}mc{=Xu*Q zTWJZk(N46KI;qQG8ME79IkU&$P-Z_3&`KJl6Agx#jfOd&X>cxctbhwS&%2a4L6Zj4 z%o_}5n2lyR&lyzr@IT+shP0TrrPc!4IPYN4$y}G+xi4=SvzwOF0n}^I$LyySG(am0 zIFa)Z4bz!)AzezB(IidLH3l~@=glx1-NgA;S~MN+gP9f^EMd;;V0KcE!9mP6M~#vGwlbS_QMWi&}sbdA9@bB3yW`8`1`)J8keQiE=0KOISfbRrGWFs-6dx{R)& zHMA&<*Ok}I>@_%uIY=Y4ipJdY;Y!X-Z=9zYPz32)3($~OR1B(sGAO;Ug|ek$sD3pbS_;; z6EsVUX5w|(431=;NFy{(m(gmvfoAC@x|NncfcsR^5RK85R6U4&cIu!38m7zW8rtw7 zTvtNN=^(n2rm2cx|G6|kN6<&<6LcB7B!erNH_;p|nuY6I(1WR;PN(V- zr>gyPFzrNLw2YohFQM1cS#%AxRpGp=>C^OO>X?o7vGlD+k>97b$B_MW9lbD$`3xGR z<~f*8qhseHd*{LB^cT8eKIZSmVCHGqV*y+nhnB@~@C&eK0#2nCzk7K zUsoe{TA`ab=UvPr?_qvGO4mKLVOJ%8qycF_8juF00ck)QkOrgyX+Ro~2BZOLKpKz+ zqycF_8juF00ck)QkOrgyX+Ro~2BZOLKpKz+qycF_8juF00ck)QkOrgyX+Ro~2BZOL zKpKz+qycF_8juF00ck)QkOrgyX+Ro~2BZOLKpKz+qycF_8juF00ck)QkOrgyX+Ro~ z2BZOLKpKz+qycF_8juF00ck)QkOrgyX+Ro~2BZOLKpKz+qycF_8juF00ck)QkOrgy zX+Ro~2BZOLKpKz+qycF_8juF00ck)QkOrgyX+Ro~2BZOLKpKz+qycF_8juF00ck)Q zkOrgyX+Ro~2BZOLKpKz+qycF_8juF00ck)QkOrgyX+Ro~2BZOLKpKz+qycF_8juF0 z0ck)QkOrgyX+Ro~2BZOLKpKz+qycF_8juF00ck)QkOrgyX+Ro~2BZOLKpKz+qycF_ z8juF00ck)QkOrgyX+Ro~2BZOLKpKz+qycF_8juF00ck)QkOrgyX+Ro~2BZOLKpKz+ zqycF_8juF00ck)QkOrgyX+Ro~2BZOLKpKz+qycF_8juF00ck)QkOrgyX+Ro~2BZOL zKpKz+qycF_8juF00ck)QkOrgyX+Ro~2BZOLKpKz+qycF_8juF00ck)QkOrgyX+Ro~ z2BZOLKpKz+qycF_8juF00ck)QkOrgyX+Ro~2BZOLKpKz+qycF_8juF0f&bD#J=Iv% z+cvk={$typddfs~R0Cz+_CHG1um5frOz_V}+xFF7Qomk({YsOv>kAE(O>K;|(tqyw zH0-tjS{vfp+Vk>%TKwDnK7a95k5ZBR(mrjzH%Fbljp+LCzsP^~=FpcLsE*uL>@>H- zk39Ncm)BD>?yLRtc$Zs?pX~MNh{|p6$ba@1{n!4#!Vmc#^@nNO3$CXu`pd3Y55N7e z#`rDrHrTdayKeN(q`kfEezxu78e=c!egL+5$4$+VDpNtdn&;RT%@bAle z{O4}vWj$4+d+(0Csv2$kJ&V=i>I;^uMMbqg9x*7gT(#DxSB+Wly1J`v?X*>9@$2gK zZNFu)@{dWquHMu)JnQSbLM_s#S6(#r4fSdB+6~`7&}yY>uOGH>!o0WD;sa`@rgKB^LaLcr$8r7)w$KEZbzoU-Vf2^(={;q1>vUb|8LB+eOWwY8J zTdaTKJ@w|cx1d;^Q2y(CYLtHNs*xR6so96tPS@V%S*32#U(P>T_gbwE*!GQ3tlm!L zR;&KlJG=6I^=6aW=@m0?`9M9azsO&Ic=$s#dB57}sPj(wNIksm8>Cp3{o3;*^*U}m zqus}BQ=|7gg zKJioauzpyV3E@vwqt>;1f1musr|Nk9$38DDS+82^Rlk{gdA+)(Y3&C4w+%m2gY{qN zijyz+Ox>by@Z5amXKIxGTO8OZ^zk2fdiLDsVaok~h0jC2k6roq(n|2_6!@*}(&YCSe#-xk{ICuA>ON{cpQJ|C(# znUCFq^Lo-=^i8P!#9W!<^*>|(x3u>!$o=UhbT-r){fg@r(95?XkEH*A>OSU1|HOIa zP`i=Npf&WLwB>KuKZ4GspFr(%TB5q^&(rmEKK%-6t?TLQniZ-enFrI6^aZZ3WxldL z&YcXk2btS9zTx zg#F(_t<3?r-?dOZ&3s@p%x{M3HRd&RVsos24b`vAP0g6kr(Ye2+_D8cmCmEjL)GOV zeO)c0tLVp2+sIr}jB`$)4myI)q18}*M!%+IEpeYobTZWDGe1L%4#vI`dMeb)nTOEf z^j^9cYOm2FTH&0_pf-*9J?0}USnmt9%jvMzm@lBKp>}v1%#VRudwLS*J(+JiMBk@v zf~rGXT-Ot7y_x;Y`yYz^*U$>6=FlZH!}Ws>!+A$TtsV2tbUIx~uRR>+&wwgPH_!%F zoHrb5_b|tppQbO-m0Zs-Q}qq=;3IH9jfUw%^b`6WwH=B3pGME8UV0_H0ctlg2brsw z>m7yrz5>-oT2zAhAgHckzK4E6FF6|fE~DL!LB0s8%b17IS2(w~!@kp@>cM;=9n1NB z%n#E=G|BbGHl71jyY|R4=>n);qzTT~(Q(J>`_(+CJxiabzjFPQ~9WLTjs-=+cTd= zFQbdN{u2F=*3e&|c8(qA_uF1|DY6>K`4~Ef^QFuy>D%;k`a3N?Nk3m5N-u)iJgTEKO zfR2QkMyFEq>Db?zI-xd(&ZG6u!0R6Z)q~6*GXKEbt26dp1J!Kiqe^j~)1fw)j;E8j zK8txd^RP2<-a}B$r*Ct9_*ppj1lj{?E?UNUklw@j3i=iOo|bmO{cfcXK=l}Nls>`v z{!Z+-(Ua&oPz_`rL~r1HB%Q$deEKqN&=u!i4b?r&Pti8ru-=3Iod&j7-H)sm&}V3d z`@W{l&&GKz=#5ZKXMTiU)Lq}FUV++c^e4_YGh2G#JSV*!s)w21rC)H~sVC0u0=1sZ zH!)A6t2r<3h5bjFRnWYYE*S&zMa0qeJ7r)@7IPx^&WG# z^KectsEws}aXy>*CAx-g;CeSVu785QMvp%q^BbWyl6fBef%E45ux}&OPP+iPI}Ot+ zI-Aa;tD)AQ9M|1SC(?EF;{I4)LBFTRUx;}ZsEwl&IbTONao+JFeZS_SQ|Sj#ZKY>; zaIOz((`Xf4K|h5`U5x$BpjJ);oL4e`$$ao7cpc|J<)Py_f0lVQ{eXVK^|LO;{f5wM z=v`2o#Jr06Bl;74b^z}G9BpwK@+nZ2G2cpOaNgi@+}A|ALTv=|Bg~I8|G?bk3Y_IPVDB zkB)}glgv#9V_!Sk0cyw7QhE*7N6`74FJVs7wbXPq&OM2K0JUR=U>=}v(o&y3*FK@9 zYmkqD%0^G7OK6j8ab7DroF0B1uD=he2#wJf>1$A1!TcrjM&>(*;+zTe8K^#CZgoA@ z&!XL-dWb$tw{pFD81}D*>I>#?X#KzG>#8Nxs_AO_1J^tIan5F_x(>&6AzJ?iux~{aGZh>0U5x9N?RJYMhoR7a1>-9#$aZt4wg=nDgi9 zFPtAf8rO}XmfMg=(3$jUx)iGVn!fK}4no$3(=gYs8iRdZoMorruDy`BbWg5Gc!&aI#mY1>JdPlMW0=2w{;-;MJJ(iklY zVg3beJ{jkf&}LJx-ja@_*GQK%x*e>^ZS`UVD35-=Xhy{2au0_5a$ht+7#w{=p&qOVt(Zz zT(=skcm&t4gW7`+WBnJXeq&xR3;Whm`y<#lgtnZG^#GktAE8e{t%i9sZS^S5u|wr& zp33|J^Wl$SU+XCDGm&2VIPy%WHJF2aZD@Zwo<2vHLiGuA6%9xb1Td?meyRzY>_ zlUS$fN#^h9p!ryzAA?<zleRU60i-_I#D<0%b3?Nn_j~H4p6&=Zsq)|SFk?oRoG&=uC|1(pr^cs z^$Y1e^sLu0f1Dn<0y#$qzk&0*tb`Mx+Q{7EP0VkBs?S@v{w1h&ejC}5#J-!MYW5E1 z?VPI-7enk)4g#9C-s$%y3g#DFJ`<(gP&DdZ1GuAJrw?gd++Wr@;e+so0zv8;? zwBc5)KlB@{_#K|Bder{kO|>|U)I+XmpsVUNL_UiSZiKv^YE7{3O{g7XLay2m`<69D zPDAbPW|&{u9P^oG_z~@RAo4n>ozjB)=_j<^LD=Uh#`^nEZD@(S;$W=*LTg%K-pT?G zY7O1=iniE))e)Fyp}OQq%%?+b#8Fu9UxN7?M8iTv4F@T4wqE?we89@SM>JGvY4 zg=Zssy2ClWFyHSS>BKQ zYd!3_0rMd>^gH;=4_IHi878;FHT8Px-!sGO!{rTiRkR86#HPs4wSrf*#=K!$7*ry%2Kisj%NY*mu$+$Td~) zJE-oShxJDmVLtykm`!3{@ec9}tC81#g53Et1wHOksIZZkN*`OqR!FZ@6k$moWw_k|)9aq8EX`?~N;cKwZHWa>iE9U7c>Q9^4mx;->v!*+shQgNtGE z3V6|Atj`~Uoc6)XhGKsAF!6oXh zV7td*tGQTjmVl$7HhMYM2Y!V4v+Lo4FEBrIBXaa>nBD|uY=ymlgIgPv>F?jQjdWG6 z3G)2?k!LqU{<1mp1+6)6gZcYM!4DH zym%}eHV(cF)wv<;TNZ|I%!cPZg?;BQKrVR&`I)zor+fe#ugAQ{Z}9m8`s%;`r3b^e zTj{DL7UVJQ;1%65FLlEg&c}T8062dT>^vCjr(6rKzYg=;M#Gn;VSe>Y*kTF1|7EOK zyoT)g1i5`0`TDQmVc%f>W&LyY*L^}`U3Jj$$e*2#JmzfJxEJOpT!-9uIP%@2pl2NB z=T1jnwh(#vm#}*c=2t=0qRn}=_f?}0feX)vk6)py)m#IQzYFup_rRN~Fn{wgM~7vEJqjtoQy(H?d?h=5yuJXlvfHbhsuWf#mhnz_1(GZ!Gg2(^XJU_SbJU2P6M>jmul^(Cx3UctI`Ip&wW zhWULoT#faU-^2Q~tFhiB4acm9SNsM$)xSW0-xlnzn<(lFdq05ra|?CVF|EsM_o?HK z)z$XDQ8&?c4Dy5s^gRRLT88yYztdGye$aKk`782KGyCiJjeZ2{&ACWFzxE#TM;ed@ zqycF_8juF00ck)QkOrgyX+Ro~2BZOLKpKz+qycF_8juF00ck)QkOrgyX+Ro~2BZOL zKpKz+qycF_8juF0fnCu+n~#su|NDs>eu4QjPxuabV;T-v1Dn;r=sU1+y%PQUCx1fD zU!S-4u7@uo2RJm!zChpsZr&lnFYXJY<-74jEJv_i-e!u)Y zIBENHFMJ(2%3jw!j`P~If}`iaY3;YaPt1WliG56e4cXfk=Pi4C`};VCWBqbI2M_Z1 zov;e~dLD=MhCd@Ge?!jANA5Qgo?8v`zt4je$QvGk6W+mfhfUvpU*g^E^H83*d@<%L z@4|JClaJP)v%?<1+^UfC_lteO*L61>=7qfnVgK@u$et(Q%X2Yb=SS}MA>4e%G5__y zubb-~D|2i&Yzdh$~|N3kfV_(qR?!TVjtOf4tUxV{!j)!Nxf^&{p zgxv3f?eCZ1=P-Z&yw>}$Z(s`7<>z^;f|yUc4X-EB3LZNHucMluhy4D{pW?hqzP|i? zNq5Z4=5PPHmVCbb?>}(N_BnrjG2ic+6Su$qqm}>tb?2}1@qNht9s3R&u>JM;vQ?zJ9~@f5qbOasDN*Y=57BZ7}@M zvv~Xa7?xFwCdX~}I*jXoHpltB@@LPf>w3qJcs}gm?fWhs^WS?)KDGUPe|~NL zzODRx8rLUWwf*~V;op;q#&{j$zwe;;yK43J`;6z*l}nD^{__}G_}|YPcqe+WezpDY zH~quyb6xlQ|Jb|ZXgTtE@AqW{g`kX$LeSVK1cjimQ8R)i|enwTz`K(>_h79IQ#$nb2iQ|`p+}>g%|zv?74=1Uf&tX zgRj(kuRB9IER+xLlBYjfpL?UL>mBzE7k%%PS6uY(>l42E=iE;{`#gQmFYJ=_E9EH% z<>_Pj{(s2d-CR27%NxBxUhy2c`1u^+-!IP^>mQ#lJWKoevz1pKd(r;j3z`@F>*~}0 zsC>rb<&=N_zICfV_aC38`|`o^rTpu~#~+~nUHJEpa!vWr+4|S3OP{O!boPV2davTo z`HDpEZMva)r@!MCC;z@Z|8@GgJ(EB8bMB?Pw_HoT%iTwL=NWQ8Kac9O`rMbF=bn7W z`SeBqb#w9a>c8oRr!9Ve(EtB?+7>^R|Cx)Q-sz4 z-j#gVH)Pnm+{q7lB%k%4@ZbOb7@h3=zx{t)!nn-Ie@aFF^>e%McgkJ(*W&jdUii!A zPX4vfc|MP7Z|A5`?>~qe%#c5AI_dhT7|MNfoZ~trY&sXcee$E&7 zdrm|B1Hd=>?>}Fy|MPoMPyNh=f2<4tI`3N-|8x1@<@cHL$Nm2#>F+Lk+JF9F_8(=@ z%H;2IU|jCxete27H%R|7GP|q{uPn!>O2=l7G3MBye@*oUSYwO9Che2!$^v`WQ;x1L zE3|H)JjLF!^1^J9Z^%84af&V0H&V~Ov5Yap1}%rrK@Ur`Z^9lM^fvP@CO20e;sgtf zZ=rp7OPQkODtlOAd@Ic}3~sHQ;26EzXgfW`Z&hkUA1?y zzzXZd-lvBVCLL39jV+p|>Ar^`iRM;iuVYJg@2-8^u^=1w(7uO01~^3b zp6VrN1iagEK%S#{FZISayti_LEn4@{Ji-)nOtxvCqH$mPm|~6-wC|^09|xFqY{=IA z)ekYk5stCNLCE*Q8t3ReK>HlWXq}GJkPO<7Zc!>HbwrED0_i)^?BAX9Y&&D3QIK=2->Lm}qs4dyrp}B(r zPOx6gAE91^L!96gtB%eib?0M*F%B?A^HJ(A49PKOIK&YaSfbI_J0AKNVT>vE9Fq4`+N2UwxClh47ZW1pPj5GOdt7M(No-a?<8;smY7spnxIGaO@q zQ!G0gkJme9N0;oOk0TtT6YD<07;_xs1Z(u4pu2_6S<3y61M;||_eAw$Ofkn1)@bcg z-$Ne*9N-vBY%zI~?guzR|H*vsjw!jsIYwuzKf@Z$MDre|IL7EH+9#Of80~YkcQEca zAdhg2H5yOVy@gK4lsv>1gH_!v%*Z*GI7jM!)k5e~6N_vz|KSfld{&0X{`#37Dw zh7I}yzIVp~dET*iuKFHk9cSb@n$J|n$&(?j4ITkp@66a_C`PO-)o?M&~+nBx$Oj&pK@#!GZ>V~816Sflk) zel8edf&)x3#{%7#=|0CP&d}VWeGfAnFXk`TKEfDtoMMFywrIUV_aii3sqA8i3644% zx%vsFIL4-<{VMeX3~`LstF<3siW!#Zy+*x-5jn#gORR8);a1e${_dV?6xZ{Le;0#-|-l%s%oZ;Y2 znh&wS85;Yv_b|YI$C#Yr2*+4rg)_9?%%88LM;>5`b8Ikri|!W|;}FLkOLB!XoTKqBz3-skF(8K>V{(QQoMMgkSntP};TR|AzgxWw zt@kLW=)70i#{>tMVvXJb^#dGWjw5W*dY}3|^wD}hbM!Fon3AiGE!q5l-f=L%K2ESe zd&1{pA8Rx|sJ(>&7FeNuzIqE|@&I!jW8JYO+aJ<90fyMe7;`MK#q6N&$2h?f=h&e4 zVLlJ1SYm_5N7OSr4#^WNaEjJPb?0M_Q*6*K)XQ*$Q=FsyG4*C>ew_C)zz9>EVDA(3 zv5zBk53$FnW1k#jfllzj9O4Y!&*?7c7?TGb?ZbQy<~YFud!JX@3;~1wMOLB!Z8eh=6 zeJpW~#uxeXV1fgzagIi*zJ)f1m|}q~T3^zgjWH(Ze3|cwITkp>2Cc8~y)ncXQ_OJO zu_Vv2cY)q3utfW-nui$U2q!p2>uc)w(C;`PFHFfpw7;%<9|xFXfzyt8rMofO-{5;= zhS4`QUzm||9ODcdY|;7_-wz|qaD)>qu)P?~b-lMRBM&>y$UmgUjSU9hS1-l^4zb1-?H}-cF~J#{N3`!_j0KiB z!~PG|AK?^xKhiwx7?XQH*51bu3$$wOZFJGY7z>=@9G#!&-o*ez%rM6}+CSqC1MH)H zRQm{H%yEdlpR4EL92+!$p?x0{%yEb#jDM+qhW$DBIKl$$Uui!=_t*4sgblXn{6;+w zV|0G2d4x3@ztcR#1cx}rdNKdK`X$b>*Jz%g`3GeO`#8WF%|EJVqmMJJaquVg#yII% zksGv+tKUNxCs?8RXZ1>)q45{Z2RQB6`>XaY`j}ybH8$wAy6a<%S;sNi{Tts8D>VM0 zdDoD6?}YXR&ap-FpV~*5VUA<0(EpeEIZm*~=-=9>IK>L*I4~}M@;^UKag5d_H1DB{ zAx47jK<~Yp@$PJ(Yic6%rM7U$C^CcsJjumS5TgIEXl?d z`5cTe#uRIuV~gIEbU(xrO-u70PSCxw<}sS5D!b@mhLgqqD(d-|V2iya?FTr-s-tyP z^~|eD8wWVWcCo*@dSmo#cwbYmpcqFMx&>Ff+c#_(|n4?^_6X$VR8e_hiES=JLqA9#tpUa zV~k@oZp1xK(YUeZE_xVaf)gwp^;`6Aq8#90k#DMbA6x9*O!Elm*kFssX7xM_aEjK= zwa?JIg|d$+POwDpmg*%q!2+k~xO_hPIKT;3IKvi`Tj{>-=-*oN07ERX!trg?E70Ad zoT7hQ_88qxd5qJJHF=?Zd-VfMafp$py>SOw;~cF!YVKf;6~=ecKEVQKn6GGW-C6oL z#2K1*(LTfo6SVHCeSjH`u|?D8^RSPDjwA8}TQpD8eIEl70z*bH|@=>7j?ME zch}rT2NP^CyN7x$I`>p|v5x~RFb&j;?LV{mWHBW%#RkLEpeFv1jP9qUDJ8=s34 zEYQ5K_6r?yfx-Q>U)U$lF}}a{8IEy+6*{4MQ*1GLfaWP0XDB;3z!dEVYHw_pW3147 zkmf#)(0j1v3v06Z5X}>eBIOjv=si^P0DBMPJ?uT4J*U3AdLKAN$5K89FggVqz&^RU1v=4WX?ej=Z{OJ+F4IW}lNNxc|*Pgc&cLGx_Q z$7m(WKE~MMxmed73AfVY!$;U;7k?IKdKoFHq0H97kAUgXwPd>lexf%@--V=wY^)zgYVrMwxPg zDGt$piS{vO=)IIX%yIHE%?ph7DCaMi6{fFXj#WqNmD;-)_Q}nwwQsTi z8ZwUeDi>Iz^;*q)=wlx%oMZMn^@muY|9Z^>OmKn~&d?gF@1lnzoMZk5^{h8a2LnuS zgw~tX>tTQ)COF3i&3$}MN0%I8f+>!0f)%zneY4&z(S3{Z0NuALXIP>)(%gI-eJruU zwxfHV`XTl)db{Q^4seDwI{VdI*e4fQqx%l^Ds0hwC-)d*g2ubF_pp!7SaTN>9HISg z?L!=PwBMtBj2RXYW6KIKtp_+NYRdg*8Tp)oZZ#dF2389HIRM_88$1 zdtcN(>==C;P4z<@V2T-57<^0ph4#9# zkHd}yxx^Y9w7#wTJ_g@Wo}=|$ZoS`+>eS{4fzvBB~geA_fMf2Bu zPmD3c85+M)&p{t^w2o3cbcbIVU2UNey?5+r&wduXkViB z2jzt^d4vTTe^hUP6D-jCllJj(+5huJ9g!#4b~OH?{=$?zLHn=T2N+>_G1knjR(C#* z(EOX`c1NEaVulkm|E@b1Q}q6!`9hx@;t(e|!@)mw*EMHu{EN@S08=cnMd#n@M;$Zr z5G$Nx-`IHa|9^XgQ*6+>g!T!J(LP0U2XnM8skw_D&T!t*y_EVfCYYjeY2L#SV@z<2 z6D)9s_GR==h%wrx=2M)Zd0EXpOmTuUY|*$J{f-HFj1!#U9R17deuTyql*5HrR33H= zuB7=CYYZ&SBkbbJ|>u= ze;w^(OmKn)&M>&H`Z*49gl><|!x$%6Vufu-<9fO`(ZU`&IK~?7>+3GV9Q_;ceQ}Hx zHs~y?7hsGfwu}9Z)C+OYvAVJLGpx~YG+)>w2bf@q#ZA<&F}SI6j#G4Rrg@C!&6N|( z(YgimjwA8}y<4j1V~QD0(RS5a=#X8EaexI{x6<7Zr`VuzYwaVPV2KSHw^7erxJB8+ z8jag(p5q*?+i4!-2y1MycYF2bXnD#u#@J$X2R;ucSYg}Iy`%a*1{mWAtvjjb;sA#@ z!ZEg(t?+$tdS~StTO8j-^R}aTSIvi*ou+J@E>mpprfhDNJ_cB$b9e1S%(2D>&3mY4 zbsUfjoZ%dUd+IL2A&vsgOPr%|FU?b&V2#newKwh~d+1?+5yn_xh5c>1A9S3P8w~Et z_rVOk`)Qt`d4FXGQ=DKw zv6`ot?Bs6Ynaa`Q2eE%Mo# z=Qzd&Tl5q40<3V>(R(VNgAEp|noqGo`)QgxPnZ5PxF5&_^F=;a^Au<3Jd-<|;1tbg zX&+*`raZ+4t!HcQ;T*%~XdYpVCA!bmK1(m^h-^Pk^C=q7S9WoL#tSs>?W z)QdaL$lhM<^VdrEbuz>h3v^zu{Q$k8a)bkHF@K}>`J1G%Pxjv;V=Qq#(!6<_^v{za zW;kBV->$v6Uk=|PTlC+lY`secIK$DqH4on-YjoeMTw!uRIYs9K$}alYpf}Nej3w6R zYwmwYPH>9hLCsSf;o!r(hx3IWVg6BR7IK6|$C5l-%s-*NcSu%fe^NRBlpJGks+?hg z=4UlevBKWxnByGn!<7kn8tA6r3 z8T?)ju|lVzhYdP^)ZF=#jIc%TxaJFU@(|6xXy3!|ue^f|rhn7i{=0N=fWbewL*s<9 zx$s}g8K(bM&O7#uE1dk#`D*kop&Vd-it_l9(%o=TC*zdYiQOuPyu7yN+^z6;7|KdDxQ$Rv2DS^C@Q6S9We7BlMP)b1ZJC+~W8~ z%KnYH!y!&?qWR>evOwcz%Jya%-dx7m;OrKfo435Eo~t~=2EAKp-p4uGx7OUfjVx~~ z&D%*IhuEUyX&>H^J8Ut(ljgmZi<*)%jPA@m_I>5*G+ASKx^jlYyD3jG-Kt#NgF7_u zsT^a0{=GEMvBJr{H8;1(2<`hSJJ@1;Kg|ai+@D@3T`aIc`vL4RN9PR9L+s-a3$z|c z4`_9HB?#onX2$01se z(LCyy?9{x$=uG8e;p3EN=sZC=!vd?bG;g0M=TDN>lVyl~tkFDM`#~Zzw4TEJ9BDpP zjxm3la*Nh8lzZpO7>78<{F&MhF?el7kn^97mb5`w}_C{H0`UF?|L1*y8+^nuocJ(0>*0yjsrDc#X1; z89J|JjzgScg_GCOd%euCM*j_(TW^vH8vB%cIC``43>(bfqIvsPS&wA=HaWl&{qr%E9?^gx*J#L(Fi5`A4;{G5(lx z@8hz->XXVd^gpGXq5Em&WGZV6KBGLr;4pifqx*Ty8*H)sqUL!idta8pS7nGX`d`z$ z!K6|i;S}9(Xr5v8O?uyw{<@rD_HE@Mw%=8@zb74RzONh}kvUG#{vmx#f22IapjOV% z{E2e#Gg+Z=RN2G$=gK)2zf^89nk!qslA~kN`F+PfC=W3EBYkxKq?}@jHTr*MkHKG* z3#>8ttL7mNaDsE3w(13clhHqA{x4}7S3LQD*SnZpLb<}|6y+3a9Bt6txs)8?82w9Y z9$<1A<(|nLXO~l+UtZ=L<>(61zM?d*BpploSC+x4GF+0@Ri%wBMpx5(p?h^@4>K&V z#lEdxehoRq7Dw0A+`bn3P13x!%yEPTPSLoIdNcH{tL%3S$#KWBr+$Uz^^`q~uCJV8 zj_wUKAK-9Vd5rN5l^0IPGi2Vit(MbZ_&Jq za@a8=4{?ff%~BjeDqPqK6p{JC4XT8u!$_hdvf)1biPHV2&l$I7j|Q4o28?H1DIHjRA(}Y|}o*5msp4m+yx@4luJ`OO$65R)?AK(C;?V6`J#W}WUJV-qc2ROkB?FXyp zpoc!DIKm0evF&I)MDO*`!2xDC!5Plcjdb6~1V=c=36>q_Wb2`N$Hxf!SYV6p!{}p- zL!99pn~u@Lb)RF271lWKXztLRgC541;RNSsKSFmgCOE<|8jn=ZMIU1vV1ZLCJDQKu zJ6^{=d4dI29i6`JeH`Nytw(Dgbd1O`rX36N3>&l`qxT&2FvI~4ae`BvVT;yd^bdA)hC`fSffd@1*L@Ft3^2wNGo0cK=V-)w&&L2W%&|f1 z34H&H(Ptjx6uq-_m*WsyjGm}{ABQ-)7^ln^&d75#cIiD2L+oRW1I%!Q1wdQ?#F|cM=?Q9FZqjVcl^~?yc&*0ahJnFSNK z#5o$z(0-vw?srVclSO}^I}=kJ;TR_!D{_Ozxq7FE0fsoi5@%SW{Y>3Qn4|M7&3z0p z#sp_*uJJw4dA72LeN1qGGip8j)F~$^g9Akkcn$OjJfDy)+;|OORt(4El2nSeT ziFL<@Y&}o!xESIPCpg6l)934MhI8z{K=T|&SYnM0hP%~IF~=d69fvREbFo4DMVj|; zh!bqle6e~K+8E;yD{OI+>8`@Mi;H>q!;gFdGF zw68A4hPm@*^<4CDf(15czJ-erF(*$jdxv^MY|wtE<}UhJW3$-5OTDmT zL{6~m=#F()qy28>02{R5qj`ckPOw4iz3TZGVuEwb4yZRm>wU@&y6@*546u(e<{c+w z?*qCIaf&6@9p~hHqPrHO4=NYvd`LOP0xN8>cTl}4+8Lv_7KV1PknaRC5o* zjsx;!(JRzXI#y)sW7_x7#R8p=^LaSM3d>JuU*Y(Wa)A}rXn#`s03)ibf6o0cxclWr&B*3l^Mx*XgtLz3uhb7Q__cB$GaO>Q*#Abo1XIkh!4~6V>JM;? zbM$`8dsw3LJIz}(ey^NijYgw+fH``9(7cZ^4zNVykLuYNVAZiEx0wA&cSAIeld;0! z&&<*Ji*kz2UzHb@e=XFA5(NLt)7choTGOc?E?&Pge^v z@bc>SagHr|8@12SzJhWOT?{e93Wrxz-@cM`(Z>KooMMfR#T}*{bMo;2!ZG_5My|0zV~Nj49|MfB#0Gm;)7>1?t1H*oqG4+ubd1Rv zj~tXkAZtHV(1C3XSWlXJLft4KyF&46S9&-HtxlyrK3n<~LTZ&~TJZv~Y^vP3U8T z)=f2cFv7TFMxNv3X1Z(8->e*WtjWEbYwx0mL$q(9eGeV%;|#M~s%N<}!5n7`Z>7C? z>x-I^bF9(1je0Id7~=>_oMW&>cU@EFBb?$4hqu*T|8}y)`1Z;JtguDH)84`q+eN;E z_BFQH+)?w%on(X7in5Ct7TBVBXZ1oXvFbP{+jmvpK_3S=!ZDUOL(AtLJxp+nqtnz| zSdeGfVtl%K@!c+JLLOp^##Z$_Ogffi_wMRN9aD0N4fgKA=VRW{x~KLF19E{?#~IlT zbQfTbV>IujeGgsqafH^r>0uvd9b583>pr@3(Z?FiZS2v<^*`$rZ~aqQQD7jj&@&j2Xh?a9F0e- z=ivZ{=sbqc#|h5Rc&zpTCLR5q+V?TRY%xDm`x0vm9;bPPamS3@qWO6BdsyHcTl8b~ zV$5-b6D+aD;0d~07?Jx}UX0OMx|^Z-L}dpf%+cPZeGdc7agOPe)XT9&_sNVf;Ggm|~7CMz2?|k8#J0Ji-!tnJ2RP`Mk}DkVQ-6-uo0Z3CzlHa(LSv-)LW>-BG~cFP zxbQq>=j}4>I3(NqwO?qygLg1PY7X159y*Q*1Flpt}~0_bEF#e82Jt=V*OE^BgA=W%Gm5!yLz0U~;~C8Rl4G z{2|`Q60L)pFLcQ>9Di8*-bduPN=g0h7X_Hp_}?JI0BEHz)) zC#PT1e1avqU)J2mq~n-8L;EZ0_i=<{EOGEv^)hVG{+i|qPO-rjt*@&W;sgtvbxbPt zhdAq4ljmrBLwy?)Y_RuDJ|7Eg(Epb938py05*u{a)nAyA8#KSI{X&nNVU9zru*MdR z@9+-xFvTH8-&HTe9Ea%4wD+-(2@Y|B#`o0kVTu{%Saz()ExO<5bFq&ldOy%U#Jpog zwvVV6VTu`!ae@WTutoESdS{_ccG36|dvvf|%zvzXfkv%tV-ExD;{+?Lf1gfGky#gz2I;Ou+FZrc3=hDYMW;j~Rf2CfI1lEo2AJUp$2h?fD;)k#?-UsPo{V!e8$J(5e^4$v`hV0s z{F6+vcU(Ea4BbC#KEVdfzwl1SJ~_n<8?^tbyBLR9;T)Y-Jr_Mpae6V9%+0^)KEM!T zEV05GTQvW!`-K5H#29m|uw0z$wnqKEXW>FvAhn*r54O-HkE$7a0rm z|E>8D$2i3q+J<%VzYoyE?bydP%>|I8^ zEx3Z)aTl)Q0M~GehN*WJTI5aGEcTaG&%`Bk@yKF-IrV%Da379vf(P+%$D?HP@_Nt4 z9`30^X34zOPAucm&C8ScZSt7{+P4vaCu)sFkfrmcGmx8MqH#T_{8 zI3qW>;Tn3+$DNp=aZSE2PVgWWIK!j3d@bGW>DZ8sP1>*ER@{ym9zgHf>TkmV?#2Ch z5RYPm#&vY>VS;;bh(~aa-gWt&m|>1vdfG==V1vuo)85AbBTR6BDQ>vF?m~=kwc{T0 zKAhsl8|Z!u?!)~!!?xq@W!+V{-f`)M+ArfFJcccvz~vjMzZG}lDmK`=G4J9w+=&Gq zMblA#9cMVlru7EYY}`<|b~$W!#QCafIe(-EG62xQYjH9rxc{ z{e=bj0<3YpN~t+Zdq7R_60zJgnEFYd<~*67?u zcNvawf`{+~ZrY-LiR-xWwwn7G;7;6yt61SN3~s0Ug{$No3!LHb_IwYl(eyNru#X3E z+VLpax`XaE;bt7*8cuQLj_Pm69k?4OSYnOKchcQv+=hLep}E5M!2#NL)_eNPVu^EXal`3)C&mMK98cip zyQ#N|M{tfUnp@S|j4QYu8#M2(o`uUe!o%3$rhD)%Zp9FDJcRZ=>EkLM#uAUA6R1DI zgLoJ#H14IIi+k`eX7|>99i96qFWf`!Z_|8$Q(VUyjr*#%19LopHEy_{dYjP21XG-2 zi|+k(x259>ImA9DIK+K;0?knGZN{CrhWqg#8V}&}aVM_g1RFHYP=5{g;5r5m)P5EB z;sg)jQFOPfzYW)LH_ov^>p^^93~)ORaUT}A`e5Dd#vz&y(R?F%7~_6iiqzYLn=!x$ z`1)v3?|XkCFlIzzH74qu659*Ij|zAFaFx*D-yJ=DTr-hj5DPm_AnhA@0W# zFF<3bdI#_j+GlFMa68$2oaQ?*#t|OG8aF;(eG6SoaX0SADIUSivF;;0fa_S}o+qfc z_nQEvrfT*U$I#$&kg9Nn#Ajzc_*?o-vDn)&k11vy*U7zs?zUox5stCIBj}#1yL}xmAn$ml_9;&A zD4xLOXQ?;9!#G1{P5TIQJc7&5);_=#4`GYebJg44aSwSP9>J}t`YGl(#|_WZek*Rn zy_h^-`-2@1ksI9j0`->A!>xD#r&yuCTXzFYvBV?T;0auPA$K^%88*25BJ~{HjeBwQ zV(kxfJVtgh?N@OP3#_pB67}}uK|G8naLY^8OYtCXc$wx)xQv@I!9BPa51_e6_dQ&} zBe>z^ypJA6IKbsssORBM+>aYysr?WSVvuXT3rn}Y*l`)#-bH-19%t(aql{vqvGJMJa7=zmhZAs)mT9>rasQg4DKnxEEu zGxl*G?wV@98^`EF|x1&7$++IO_iI?l**-1}Yi zj^O5*@&FH^^*zn^q4j;`K4w2qUdI-<9nn0-b=>?z%@=Mbul`7L`^U0}J8{!bxWfRq zVTngD{;B$_Ka&NnV|G;Y!#Km^xaH^C_i-l$#54cdR!zK3f#!DDFsg?BK<-HZHJ?bmTbtGtAJ(f*s}368Kp^Y7ZnIKe|` z{6l;9gbZUaUl^0uaA0bmVutQzHIHzFyDz8tq07r@$D`zp z8@2b)x`J}=iqgR?xDD5^#od!1d(} zmu{dO;{iO1`7(VR-B9^38aGm2!5Eirta%SNW94Xm95>!XImdk%-jq3Bfa|#NX4+5i zAXa!3mo}>xqIq*=8+T%f(Ji#!@5(9Gw^DX)Ej`?dJ21x*S8t>K7&mWGUcvOX$`d?_ zz1wN-;5x3{Uh`Gljkc%x9^8usmbl|i>g~b;D_mO9KEu7}-&u3>E^-rY!L7K8=3UkE zvB0C~_}UN9IZb&pR;QEiCU@f=jJImObaxq|a}VXMnBdNPYCgp+f${_!+;lI^OI*6Q za)vpk+cZCn{rf7{xbJ?-Cvb3o-oY96Ld`d0?*U|VaSIL}sQto}ym7ncOSluaK1lPu z=sZ~YIL;rUys#k;9;*3Kj2@=EXYShG|pC@B=Q6< zpQF4Fqo*owUzKz0KTUZZkE8K)&9~q-Jb}(Lv|qtK?!q-ZgnQ3bzr-5jXKKFuEZLyD zro0^!+=B=45MF?5&(___bL1N4xEGV>YQG<+xRz=jK2HwuFm8Ij=Fx6>0D~7Q@5UOv z7i%73@)G3~otN?smbm$4nrFBNORO>2qh5nMU#`6Cm2xj$fJ?dNIS%mxJc5H)sW-$i z_V#M-V(+!e86Lynb(*Jm5bdGnV?2x--k^Dmd+`wNe53YrZ1KpOG(WUY_TDU)(Rd3P zSMV?%Tg>08-Zl(I%6qVQoARdfWFL3DU3nKaxN*Pco6*Gz^LJ=JeWzSU_g%^f9>l{~ zu*ZELQyyXOlH9wAnQaSmuY;e<8ltWy_ zDLNNuKgM<3{8i08-1{}<8Kz%XUipUH@-4XsSJstxeOs1T;qrGhKZf>ql_$7srtE)D z?!qpnp`^ z__=g(D@NGju3xCP?Uyo`%M$HhDQ6h{S~)Pg zcnAl7;Qmk2J1#@avBaf6YrpQ>u-anNdi0os34-t>3r;AR{w@;|iSjR&yC_=NT| z9Q;#x*T1CsZ!}Il`Jaz$x`YgHwc~E`KCE!%6y0sZ4VUD-4e|sUmr~w>#Ufu?^PQKG z8%(^MT)VuquP8TR&rU$UD?5H*HGS#N3pu5 z<}D7cr98q5u*Rd9ZBlO!p1`GRYu>+(OmWL~l{4ITedWVwEi3Q9@`lRWZY1~JSdQ=j zt~#0rH<1T%`DV(Sae1?{kH>M#Ei@nFE>}6j{pj6V^S#(&?>3q{IKj0onoscp9Nkv) z3$VrGx6|Cdyd!FX2SmCxiXucme-BEdp&YhGS+_R!=-$ic5e39>}`3lCk)7Sh6 zPES)_I$dtXHPpY|DU zy1(*LD9s1R{pg>e>^)G1xVBx{eULOBA~!~I>7mkmn4II09m>`t<%YftaQV^7y~oHD zmmjN~;qINvGjz{XK7!8Ul!uR(JD(trpCywg%C#rS{ZE$L&XysjiSlvupQ4en4+Alpvn$MM6o+s@W z$Zc5T&KGLF^dh+z598>?nqPp!mng5}+DnzaJ@OE4dAafwhp$xL@G9oG^lIe zccQ!vlMnI^uAi@b;6w7rL3teQk0_5mD!q?MAJ;yvyc>g0C?Ce1pHiOS=1-GxJXH=p zE4N{a+drpygzGrNqc}gT-rDEo@)u-)<`B5v1zZ%F3b%hp^F9`M9Cv?D`|A6$_X8Q?cAPK#vG&WgT*W=O;b)q= zxaa4}`|uE^ztr5A%OmLjN_nCAYvsLY|3>-nZ{?=n%K*3Io<{SfKS~={aQmM$caF<3 z7C6Ptf7br+U*zDg(re}Bzsu?$a`^;%T*1j={!i^|JdPXwrTIpj8do{_f5)$!A}1SU zcqw@VlglXY#T}Pb-gh~P;|j{wm1J|O+;tThEwR6uc}cKpt4e8%pm+ayK5p%F%q~rg9bk5BBQ-+K%!5_y33> z2;DRYQbq(R!H6I@We^0x&>#q=8XAO734&l~szJ(>;Lu=b+NPT7AT&sIq(Q1F(~1g$ z(MClDgM*Zm%l^Ed-s}0TxyM@n-~HYHy?3ltFYoU&d-j~~-Y0Yl&cuG(=zKbM4pO_@ z$^<*MSI0OT7vQ8FbnXn66R-;>4$*uP_TEXI?=1W5B6|;&+3s>4j_FW4Uz77O*i+rd zmpKM|t0&@QT(OVly@tttIBQ?^l>OvVEDlgF#w8=vod?SjM+R!=FgY2Q9Ioy&QjR}T zR!4o_KBLq#aq`jX+1T$G^$3hH!Sq<2PZ=%eW9M;d?*usnr-$ks-4i*-j&G_LV{nps zG)}|eCu`nktgNutDe5J-411rd`B0pWIZhg<^Agk3x$haW;$}zYUN8F_OkRN^Ce$&(wu;(6rZQ*z`&IsGrppOM8Px%_zdBAs4x*6OlrP3CLK7WQ0Q?cu1FdO9w} zY3pb{`pcZ-&_3#g*l#`cNL;+WdfoC5H#D`nRd>0B)r z{7_E6MwYk~JFnHe?^HPgXJ5zsMmY@Ao7D4fmiaAmN-BHbCez#HY+R0gf2{c=95P)! z4txDnJ#vN|jZ<*^9hw)vlwlAqV}Leo791S`PS&T=b0W`K+AtoGhQ0qZZ3PFUsC8$w4@^RF7C9N8=1E zU(vkpYjQje{D(SNCTC&KH`G1fk~6XMZS}Bs@GRxJvsJ$8RLZIYWE|q`&fp~ zdhLHNjO`)kuPWWuWCwO)&t96(#1U(&y_O7dJ`Pz|^YqJdNgvsPThid;SHVOjo8_I^Z;pD$yajUyLmJ`2bEQSCh`L!9)y zy5j||e?=~NRZeT{&k1nQtK%Y$=CtC5H^eugWPn6$fpt`3fAmoqCWb z2ks=D-Q@VM$tiot1=w>>^%$S?z2xZq3#i<9XixF};&OTH<@-R8^a5-g^43CrZ zz9~zbbdtK`WH|}@j#V!?P0l)tK2NsJmqRX)ow4k7iOg}BufH(IUeBmo&&!c7$e~N+Vsu|uJ8#GeJKj`}e^)O0Q1<(Kib^PU^De{Ywp zDy!Ax{MF^aHRQ71a?zLM$S=#NkDRiBoUxJY!dYKY58GUh!Lc}NOU(yt#p}K*t8L_{ zL2?1Q+p5Q7yq$W+j$FT!9D@_F*j@9n`^nDXavlynK;8EsIRFiBfI0Ec{A-RDfX7)P9?o_Mxg zc#fQYuIxBZ4!S@to+zhXBv(w5)4wn0T`A||w5!#lr^zY z6|(r3?D#}Z!^r8&f8WcZhYZ({gL=!>x-#!0JJypkamj}2&W&W+Px_n4kvPLukK2s% zEo8J6*A0}zx0Zc&l(VoLqMkQYCcDaDH#reU?WuP5mc0*<^Re?l^$H9RQfG(A=xEt> zjO=r~oR5o6P%r+5biOHFjBqwi8>923C(F@eWhX8;MLpmLD}a(4WiM zcgmi3%Lxz45xT>29oUbjHVXzL@tuF`kmHjrB z@mJ)yP2`}j%5rNtbQ?JxhYV8p-BB(YDo5`o$Kb#Y^|Y_aXb(Blmn*PqA9cL1EOG21 z>Nz+&P)|Ne7C7e^^^nnWNhqg%Lr(anOfCDIuK9v9KTADgyzFaiEg1Y4JK{bh2@_vMT$<;1Jx@UR+oNnnSV(RT2~J5D_a}O#r>r771_0^4ExK@&1K&Ka@rPh{#Tg~ zk||E#Ry}7FcmkCU@;z_-=oF^|+0jvcSg&yfQ!mfaOc#aD!}h%E{>7 ztR9KuZ&lCwiR}F|&S%IuGnwBdr=t5C^_02zJK5(kIqh+oE|BA%k;x0vc}ezsne)HN z@z{Dr9sgYxugdf_*@a`4sh8vEH`MdrmVG~vOR?8-_2iG_v=y?#ksqsPIvcmY=SzFY zWj*E4Rperv)?2-B9XWhm8T65T`^izRbT^la2FN~J%4q{-I!F%KP8LJtz@20#4%}7U zyF)JCOAgydjvglG50?`H8IP1BM$0*4vG&kS)3yKo+{nbWS?;|Jxg{@l&uS8 zc!})uT{#Kk@2MAGCOf{*byMWbtL1`gWqO_Lyg~L%rFVzyJ4*)l$@%l-fXC#BKgj%P zIr@3oXR(~~ifsK|hRfuL_vOIlaw0DNr@Hl_^uF|!_UGMa9XV`0IlrGAvZJFblK-pIm$R$?O!7oUN5J1 z$`pG}Q;)($x2f|V%Xu^80t|ny&T#Y{YCn?`AC$=)IqNsldqfU=M#eA7)=P54t8&Oc zW$>Y#x5_5%{ZCy}cJ-E%){?_FmcGaNPIAD`%y*U3cazTUa>kyrz{SJ4?jY$NECU>P zn0f_{JW@UTXt^9aMysbDFZ~l`*U56$>2lc_(v4;3_vLaN_#^eOY0{Z4$No%q+$Bdn zB|Be`%U+S?-{qLM#v?UKrX}z zrwrtE+sIynWDC>5>dsx{=-uT)?6;?S#zAuA!E#w3-DBl!oPUaX*m=@BUoN;%4!B$v zSI7}lP?=r%d(C$hX(E`LaN&6UIdBC}`Z^ylP~=jp|q|4q(+Q;v9x z`G<1BM{?8(`9Kf1{rQxu$gY;$u8&-_p`78$37g5dzg)J3oVlf(vx8i9Cpim8?X1pm z*&gb-d&xWYk(&&YTka>{$83Lf{{!T6hsfkG8H|$d(X!XEa_tl46Q{_nzb&VqB{v!` zk3UnS%4(xTa`o3G_nYYRlXUgUG%ERxM$%C^0LvlMDI9GiP zb`|P17Rq7I%PSVk=mj|)yIxce`K$c!O?l*UdB;ccr~i_-th8DC^Iy_KZn>IVwubEW zW!bl{d=z&Zq~2&Z*=M*Mj-4aaHytLUugh7-N_UJLdY0@vUQWjGXRF6tBG0%~mU#PQ z^_z)2{2KZG?K02g!n@_856I(lIqFe)&L8E%Kg++oD0`RkjF;sM9QjxEflK8-K9KjV zkni{C-`?k}HDvET^2AN${4M0LfwF6myli{<*WKjU4!O}Da{UozTf&la1vf37LmHAsEaju<)}NPKSz#xNlyG&4j;5-`~FAnCW`~*vZLkLi)7a& za^1`1Wz*z|kITDXkbS(Z+V|^qh#VQn<8k?6>gUFDexAJZcKP8ua_~p;0cT+Qet+*F zr>-hzts$@9PNuuaWA>0UhSSH%lfNNHpCpeTCl5SF-W1EhlbCm6D&LtUH~F<3_ke6Y zF6S(eeV>*^$@xce=Z|IAC$d-1ueN`#zN^dk*OPblmxt^kAKhC{IGFiJ`QZ`r#G~cH zZ^-jcl^vEsFDG3nM_(lKOXS^`%13d#N$MHjlOIl&{VwOa zE9KyyO7G`#=}b9fwjBSEJSLZ`&XW^x#bat`ft>!N?Efb@<|!G!DCho7PJ2!Ee?tau z$p!Do58s#9FPBSuZr%PouIMfOwdAEOx!(q|YeRW*Kl$M%@}~asm~G|SJIT?z$wzmW zmyM9u94qHy&rsbpR<1Zh?s1;n8)soMN%Q3Ua>!&EUnxgkEsI31Dg^d*^WEg#=TjvXvN-brq>i|iUISKUpXv5)N7U-tOAoIXlk^G!K^jGTIs z^v|ZxmHj8lxp?aL)V(f~t6nL$oFd(;WRFBnyiWGMUY0k=UN_4ta0TA<3(fDIDLe0x zLubokjvSoJadYLsN9af8*!i*xhy7kX761ICdVV3(Kg*k*mII%c7ri8>EtR(~lk?w@ z`@Jdsx8?Eg$lmYC%RZJ1Kan@}9Mt~2SF9@6?k#8Ierv1G$17UuY3s`2UzQiGFX!}? z$8Re8Z7wg{QcmAW<^$#YugU>iOMe^r=(h4X{5y8-p!prU$?+X>p)ZH;EiWA=ryf8b zDEB@@b_TNRFnP!^a_+IR?`Sy-=NzYgAD=s3ot_|)0{Ig|xj=XGwoO7NWcD_6k=V6bFG(QKAyjZ=-W%7{AW$!EGoma}6 zrpO1blKcHoW;pp8^$dLCTJ@0Y<@q4t%8%#CX^+Ukzn5D+F1P-pJO-CNsh;|jES{F@KO=8> zPUcJGrLW2({~>4Kyh{Df+j75m1)etz9jEkPd<))Hdar;CtUT1Tga|0<)E$QHQUJvgXN{W$zyyu zW0<^XU-|fcvd;nX44i?BMrc0xV3`K;_QU1QBjqA|{7CiEqh!BR!C^zyf5z#1sb?N4 zw+!U*xW`EK^*H`$_1|&KvFde?lUJM|hmMh>@UoNC51cIXQ{{^n%k?jj#T9bSRq~LV ztQ(%XUA<+_{8%eIuWal|(2bGDa@c9k1_P4*uq-``Kh2gpf*9Dk&|YLtBQc-cRcv&PCp z&X6CDmwnHbz0Z>c4!=OX^CTHfmTO!gx4TkyUL{YyhQ@g}swYm958N!hTjVWw(D&l6 z<%bW*iyo459+n;R<@jgifzQbqf0fH$m-FA2(Yx}@_vHH@%dx9??ayx_9`t2(xSrf^ zV|mtBm~SS>Z7zS0Z{mRiG#|K)+Mg4_PP&yeN-*S?>OK`N8Y*qGd98Oa7&j>wF|%Tp{0b zc4&W|VNdC=B1fz#@5jy8RlmB4JYsYCr9hsiNG=m_;@N6IsglKn=>z45rC)&DqF z?l4+@e6l?BV)^H*67Fs-<2y8xxtU+kA5K+z9ctZA)_64YTx&~bLFDRa?KCrVXd9p z=ilE_E_y&7^E>&(LV5jPtSu{`e;xzB!IYu{(($@14v$c>iBTL$jYKHp+%`SC&Wpipl9ExGn3 z@}9S4?;d-$UpIAg`Pu+^&a?8wcjWkYna&uFF(FX#`D`cn?0@G`ycWW?ERk3_wKoO`+d^2<-&E_##_t^0=a{bN1il`mf1TyX47x?bE*Rb(hK+SIG0H$nQ*T8?S$zJm+4`54cZW z|DbeoxyFLF@k472Ykyu&ANj^6@+YqR(zdc|XE|x8T-k5y3^_zS?l5_abRIrJ=La37 z`8LO?pS(=Y{h8*!o2~AgD^Gb;^CKUVzy5=~q}2K0U2@3#nn$bc+kXD1)|8ulN!~d? z{&6dL+aP)WF!`r_<@o*OMCt55s@={ir)a*$8S4AS%M&h^LoS!Q|3EIjT7K^a+5g72 z@o6{9rL#0Y;(qn+kI1h)#(ch<|5y37zw3Ofx77D{sdxEE{m3f&wZ9Lu*KQjR@1q{M zf%`D{N#~OO>NN(mb<(fOgFVe}+gZKUuIkfw zmv?n&e%vs5<^HlbT<7^oGWeFf>U5p2a<2N`^VAPss?M&IFI_EHy-w$?Th#7!b@&r` z&`kO3J9WNVroQlT^`Ir{W0%SYyX5N2Wse@i+xuF1t+w&f_0@N8B!9k%<_8Z{{{uJN zUh_FS$@PZn{FlSjWA~Gj_Lo-<*ZE!t$oCJC#~v+jKc;Q`>rfu>ZTY9~$UV=KBQKJd zeODfQmAwClZJlGTmEZ4_*UR{{+trhPCG$ty#y@&iePyZs_21OTye6-DU%vW*&b>7c zXz%L}Uv3-k{Z)DGfpWlMI$!@{^<6irFM3J7QfWTp1NGKD4{YD>AUvv{dh8MM?W5$q zW7@|5yg+@~74qBv()^%94r<@$@5i-`(`)6Hsl2i0h|kXBXSSA)Z!b^Xv8{8?f$B-; zspnrJx0@|j`KQhgU-{tneQrIYt#j)6hqT{!jeF(HwGM5c$LF_oj($wN=Cf`6nd=6h zo#$H&XzQ1rJa35RFF(^ZzUC$M&v!Yjef@(SZJiyzD`!64)_?H0!#_LsC&q2_y|4ay z`+DcrBig<^=cxAi&0CIYJ8ao8pBeu7?y%jL-#+Fu-QOO1&S&;?HoNgNo4=Y3G#h9( z&}^XDK(m2n1I-4S4Ky2QHqdOK*+8>_W&_Oznhi7?Xg1JnpxHpPfo22E2AT~t8)!Dr zY@pddvw>y<%?6qcG#h9(&}^XDK(m2n1I-4S4Ky2QHqdOK*+8>_W&_Oznhi7?Xg1Jn zpxHpPfo22E2AT~t8)!DrY@pddvw>y<%?6qcG#h9(&}^XDK(m2n1I-4S4Ky2QHqdOK z*+8>_W&_Oznhi7?Xg1JnpxHpPfo22E2AT~t8)!DrY@pddvw>y<%?6qcG#h9(&}^XD zK(m2n1I-4S4Ky2QHqdOK*+8>_W&_Oznhi7?Xg1JnpxHpPfo22E2AT~t8)!DrY@pdd zvw>y<%?6qcG#h9(&}^XDK(m2n1I-4S4Ky2QHqdOK*+8>_W&_Oznhi7?Xg1JnpxHpP zfo22E2AT~t8)!DrY@pddvw>y<%?6qcG#h9(&}^XDK(m2n1I-4S4Ky2QHqdOK*+8>_ zW&_Oznhi7?Xg1JnpxHpPfo22E2AT~t8)!DrY@pddvw>y<%?6qcG#h9(&}^XDK(m2n z1I-4S4Ky2QHqdOK*+8>_W&_Oznhi7?Xg1JnpxHpPfo22E2AT~t8)!DrY@pddvw>y< z%?6qcG#h9(&}^XDK(m2n1I-4S4Ky2QHqdOK*+8>_W&_Oznhi7?Xg1JnpxHpPfo22E z2AT~t8)!DrY@pddvw>y<%?6qcG#h9(&}^XDK(m2n1I-4S4Ky2QHqdOK*+8>_W&_Oz znhi7?Xg1JnpxHpPfo22E2AT~t8)!DrY@pddvw>y<%?6qcG#h9(&}^XDK(m2n1I-4S z4g8mGV9>N}_WCb}px&E}edf7U9zVk2F{a2r+`8J=(Q@818 z-)G<&^4gCyFIH1ubh6y{4$XI+B8U8q*9ST`&zTNWzwxMCX`r6(v`xO&exF}WkuP<~ z*DsK3T`kRh{%xlF9P<;M-*@F6?fcx{Uz+Q_@e7?V-A429Zn@R-?Rk9MHh%kc&u=SN z>MPCbdtRvC@vS}E*KL^UeituP-@B)bSKI5e^LXysa<7l|y2IB}@7urK@t00)x6}Js z_4hx}bw{Y3=Wf;Y8(b=n{Hd;g@LTGckILTr%eUXs`S};B-+WP?znA7KzNzQiVeP%! z&v*43dcWoG>HL>n^0t$7{-uTLVXL;=xn&*AC+(^(j#vNrPr9y;-p~2oxjOH4upIdd z`Ow4is3YYndcE^a&Esb;XwT!l-_`l^d#Xo|RS%e|-fZ=K+WXHd&3}_?9=|W&xJmQ5 zZ>V40M%OPsPvVq2}ofx$}E^|GqEDi@vS-%4h2O(`L#meyaJ{W$L@{kl)!v^Ak55*51RVPpY#e z@`cOgyZz6#C}qOCJ!aC;ta|AL<9w5QrSbI0oXGrptqQw~@2`w*|X zlluM%^2u{_-3c%2KDV8$`M_i4z30hao~rjhDbn?4ohpa+)A_|`$r0mq-HO$8{^(@Q ziyftTj^!8W{N^(?|JL2=F+Y(T|4~1ucdL5l)w=HbN98^XR~vbs`wFUu*9X>V72 zL-#*%kmkn~^1Hu~{dK)FcbWQ{v)ej9T&`a06TSZC)AhaBV6FYyfBt++_l;kEP1hgZ zU-Lfu$YBrY&lR(;b#`vgoqO)o^&Jy*-DKXk`kUt2Nt&N=h_2sYw!C&t%@6p6{Djxf zdQ0cuyHlF?+vO=;clts5x1aYH8>z2)T)!VX-LLL^PUnkPXnxi9>a8-lS}ad~qOEh` z5Ow^XUO!gfbLW(AY2WX>G`#)2y8Ds?+V9)>o3_pgnfjJ%blpy`Yrap_?)bg?+OLaq z{T{?SKd$S4HC5N$+wY+EeXp7;&%0LtK6rD6y7%64`s4%K?|W^izToQzx4ZQ%J?}PM z?Rh--8@jLA*Ev6Izs}k9p7!-->`WdQv|sOjSJw}`Q{Myc+{4<>^YXqU+y46n`iFSP zywBgqI&bGn&Z^EzpZ-(pv%h`%*Gf(=Y~!r#v_AcxEGA=AMX3gZyy=uIO)I7{{Q?BD>HR+4`Kf1*JLMbhN#n+! z7oFsM`O_PG_AY#Q&OYBh^Q7(eI^(?0UJ`!xxBumTeev&4pvCb-|FgYpG)NcqKcIdu z|L1&v{_}soo*StB?0w+>4g1mWm-z~tKYu>`eQN(ob^rUMKd(OfGwQRy{_}o+BHi%6 z`0wWTsTaS}W*_D^-0b7C-~3fSeYXGjd-UnSr+=;d>FF8n*WAIow(~#i#c^in;Qx@n z|7Y)IhyMrnvdX8Qb^Z5cmG<|AwKU(CPyajSv%k#m(GEk}+3dyI%i#ZO_M(3e4gB|? zyPyB>yw84jKKsjP{v59V?>w)4{mRZj=bD4{@98@Zk#i4~&js?G!{lc#_|NC&kOq8x&O=OPulZ8?(P57f4}|r{|@^6_s{IYxbmm}f9tcotmE$4{(k_D<2ysv zbcTGwe+d11zdfw~f40(1N9x-an~$u$R{MMBI1|je|F`MqEByAGKPtn=r2hx$Es*Jx z()o)Fv0S7Mp8LGs^E4JOsGS$3k5Q>kFnC#AV2RFOdHvsH3lof%Xl`u%UF~9s75cC0 zJjTq@S*r8wHJSWFdaug>lVx1@hIHPPA!gWmOLHFs46!itiu=4R1N7dZ(S28)V2UOB zT{@33vCQZk-S>69hgprS4|H9K6*|i`_c6o_b9Da6`(W!swTm7mm|^viu5(s!JqFnN z7x%@)^vBFC6FU4v=PCNmzuLbK5muP4q`Dy4B+SF|>^61aqv=Sx2w;Eo0i4(iytz z^1c{aMs$qMm-)HS#}G3t&AgAU4>86BQ_GAlYjoDr{aP4djukrVa~%d)p|gR`GxYkZ zZGGm(fHsD-F{Tr&u(hG?A6dq$Z{6U?!|3S(EV z%PmW~LU%J=7hs49mgx4^bsmP@7;&ChrgVlCwl?SfmLVNuiW%luV2REE-Ot#fJ@hfe z2xCkzwJd383!WE!3@k%B!W=7XZOMHwuncKqOedIPj-{oumG1ANhk<2A7naUI_J}PE zEJHfN7*i~;#0pzq<@wOF4Cx4COt8SxvbDADYmDd^6U?x(^taLLLyR!N409|ky+L}t z(Wi|eZA|C_OKfe+e$dCnGNTJDE&c6uA7e-xQ#!*OOROxN?RhRskM=Ra)H0)OOXfz$ z)B6}(v}YO9iDgC`bGkrh2liqa&@m>K8J%N=t--pV(WL{7Fs?CWo>}I!ZNa?63Y{JK zd@McM7|;0uRu~V_{S(Y;EScL@%)Om-pU^U*V@ztym={>0vooIqx)@kSbZnW@ zB|5usKXlQz4CokhEYTUN`_$=iZVc!UV@xo`3`=x&<$bVa>Crw07-NE|Wk%;%SUS7u zeLV~@#>6tCD@$j0p5L-X8(rGBjA+}Kd1{%_#)_^p?9lVsM$Bzv=6N?(oY(1mjlH0U zJ_eQ%9b{cpS<%MU9(sQleGDTw}Ea~oS?WO1N(6{in>Fmq%q1%l<=e7a!u*QhFF{Tqt zv9K)Z3Z4Dvp@U7R*f!mzZ(P2jUjD}=-4u)Z8PS%WkDND z+8M5&$3+kQ8YAX0rrnrxUZ8UTpC7syScbGQqGL=kvn=S!vUQ-|*R#y%)A zZ7k^0(m7V|XLM zF`+nzTIgDOv|nS$+!)cugibN5F=uWpXxoyxa{`~6Ws5etwAYP3 z=K+Q_M$C;domi%HhPh=ymsnXkA)iY(x||z5+P4ho&@!T9Oe|Bnuq^2cTi@V&Zt2qj zMwr%^GcUTa@SOOH1Cbbuko zmI<9=j-{nDhVP-JN89?$L(7OxF~g$9lDV;>TPN|kqSuW9=f;qZFt*I-+_I!?os;=| z&_%x+1I|Osgf^yhR%6b*z!JT0>A7ru<^hJ5F`ZzFd5r~g+lqOe&RD)zmM&eV$GOp` z1Iv&$Ms$pcWlCq5V^L$t+~}Ob_YyrzpEd?`h!G|=X3TTTf-ccHmAzWJv}YO6Ax0Qu zf+^;f1zpwXp2l7)SjJdX_#NScY`;1x>lmn9(IxmQKWTp^p*9m{_KChPh=y8!OuR4&S31UFJT9 zHO9=132jViV@Bs#d_haDv#ppLoip`w8C$f`qkRl8tTAS8Oz0G|8gu5xf-bQ_=PW)4 zOP4l!be%ruA;y?krgUbR(`Ahnb7N~f-&YJU#0X={jLxyZ%F;QTz0~M2H~Msdk!4I9 z6FRlb>B6$4s~X*N*oUP@+xpA{3^A%PW}b9o%DFM4ZFA;@Wl0-b=kjx5@C6OI&KS|g zn65M7+?dg&r87a#XKc}~rAHe>x;rDTOE9f5V{V%>Hx_h>6*}kfb6B=$w;O%VBg>fX z&V=hS%bd0?n77X7`%$CE+{d6BW6l#yvB1i*b%EYLuncM2ha9xSj7u30s-v@NjLmvankWMhI zF=JkKW5v015zm7z`rR0CZVc(zGNDtVNHsWD}4n=#L8ESTGt z%qvTG5}&7~PX`#*7%`7Au}taQvY^XutT;D1-{W(#Y|*wpb7Me8He;S+ zh0bMq-j=0L8v{D5F=n1%iUpSFe4qVbt45!>ZNS_Z(or`ioToKr%yTTTtg&KlbSCq2 zbz_Tj*V3bX3^1~c=>${EYb=>pmd@ooFS_Vk26Tui=2%#kw6UV=bgs~IxRxGWr_Z@D zphL@uj%!Sq8&let(Z-xM7PM`}+}630y<4_u_Y3NAoiU(8jB8Ap+h)vdbLO@Mb7M(Y zmd+G@UdtA3>oPZbw9%(+L*`M9G4tdLnsHs71?Qz@MLR#>=f+ky`kaTDU}{;=rDa7M zovZZzEp*Yd4Cn}B%Y;rXGrF{_Xyo)24= zE^X^E_bmfD#K4}Hskjxff=GNo-Z=G|Fvow1~i6>W5`=jX6&(RI3u*R5qohj$W zjLvH;m>WylSkZO1Zq#0U%YY6oBRZ)uV{Xjp!m^}oE9TBkd|uebqFqamHu|(}z}y(p#)vk?bb=WcHI~dPOJ^ECPmL|+Mwhnr zm7rIrP4ENNp!8=af^c`RGB(WU)v3^)%hBRa<1vZRd_?cBoiS+;1SNBbC9 z#Ujc{diEm+0Ki&w(xUYV?`c8E_tAgt28pXO;zB zS~@@G`-v`kmOdR}Qe(>8n9;dqK^rUDna7Ic+`WzGXxkW7;-ho?2#fcjjDISeA5Eqw`aKo*Dz@#)ytF!PGLN3#`!j8Q&9h zEj`-*f(Bd{))+IlO_ztYF5nJe52DEL+Ji^#Ap;OE-#{x@qe#!Ho zi++s(b7M$HHO9=137yrLGcPPl+O}d|XX{SBALycwag7P{w8o5iodxH%CG*PC`4yi7 zw$Mf2GN5fk=C%=YV@w+pI>qe&qdBiP7IbM@(XEWn$)goVl@}D|GJVc`aSq=+VAq zNZTgN3rpudK3{C1XX(>{WkRQzVQyK{#)`Iee$CHcqs!dG$TFrAOfj>}>B6$2ZCkT> zF7(jv#(?t>6U?!|vc`&ecRKg$`CRnSw+v`wNJkiBQe(>8He+rqX=6p#={&%GEnT`! zkMqDXq$A6OPBF90X=6#-R?LmggX|x@8hz%r0rLKbJ(+`OB+49 zPM>qzfVpkR+!)a@CYBjpXU=(1W69iD(XHR`^P^j%$2_PpWNwV;xWvTCcdbE!Lh8WkFFgK=j_604t&bDN3tZ1Y2TRsPLEj`-k(}86~$K9B6o>}H}VOh~m z&gX!hrBB-i%xy#F5yqA&ZJRMKu*AyJd6?(5Y|%!S_ACS17}CawwvCx5m|5nuv7{?Y zZ?2x-uQ6b53~Aeld4j2BM(0>!W$FBm?`e%LbKf$cjUjD}Xk$WWH5SZEbms9nql=!U zPum8}jUjCtGp{q@+?dkFoG!Yt-7#*((Jn71C~`OvfU zX=6y+M$GGsIX5P>ZOYuXWL{^*xvldU-z#*{LmxwoF|kZ(V@4Zux zv~9{f!yF52{ekyK7d`Ybu#D)~GNIENGv+xKmd*k`AN0_-4CoLej4`pyXxp5*ZNa?6 z%F_8GdqWR>3@lSR!yJnmOXkLkcAnsU(XG*A?qk@E5$AD@3G<@HlDTcg+bb)0zI?wRELeJ8tL(7PcYfPADnAcb`H&(Q>i0>ErmI2+JA=gD1V~W`q zH0Qda#)`S~ET5~TM;im$7}2&db7M+pmN{Kui50rf>A5`gF{m+Q9${jc(#DLo&6yY7 zSaI$=&-W2M3@lSRw=C$g#)`SKnD19Nwm5gut1)06VQiVu#*8)=v~9)Qd4cCe*D|2P zFKEnl#)M95%$Vm`SeA5!&Wk)Rwrcd4`xsz|5yqI+m@}^|otN|+Ep*Yt2xCkz#}X@a zO73Ur(}873+eXY|Ofbb93(JzOu=TQ@$L&U+b7M$H7-M3Y(is*tmdqVNH zwoK>@i*BqqH#)EIeXG%D9%6(sCYV}gv@xe`3+5$O==`01VhdeMkM?T}nMW97f~jRj z=av=iyvlP~wrHbEdzLj4cz|n9?~GSYn0F2RsM5mLBa}26Tuq=9UFrp|hO#! zdzJwmS|)UAnbEmrL6_+Llh55Uplw6ubw->gm|}&k5A}XVk2VH$SYyOIt}$Vr)mSh$ zmb7if-1&&-MHhX`fVK^pN0u>dOz0Fd%q>gWwzWdf=~?=8fFVX0V`7=oITkgR%qw*M zrROlZbc8V`HKxpM3+B$pd>-hbZyC@jW|lc!V2REr+`mSTd0-jS#*EIf=*E(BTgUm> zcRu~Ag|4MX8zb5n(+Q?E=FDvi=0!J5C26Tk6WkTCJ zE9*W+pAImzjA&y_8&let(*;&FwtDJ0T=dY#2vf{z%$XN8R?KakRrLO?8eQhbfDSDq z+8EPGjVW_uL6_*P%I9q9(@{6ZoZBYMjX7OdwpP>o`<4N18#0eE!4z{WutcX9`^Nxd zOfa>~Xk$T_SfR7J?r-#HV@SuCV2arnH0Qd)vZO0ZXAM1Q3MHf8`F~S%VOtHWcD@$i> zy^n2+xmTmlJjB>Cp^Yh>V_{j+#)@{n#Peax(xp91pN=gPI>Qp37Vm>CbZhjP2bLk7 zVul4)=&Zy0Sq5}iW5nDT(5F^Yi3))$apBG&WFvJK`%Z$z~3%bP0(pg{c>!ObV#+YDgnbQT9 z-B@w%ZlL$|Y7CegBRa+$D|Gs@KMX7*+8EOb=2+BNGOy6zQ12IDXc^J5Wl9&=+K4@% zj{$}lVS*V}H98yXzCMN+VTu`+mTo_u3nPpz6FRlbXk$TF=zK-@H@0Zc(x(H=(Ak97 zTefJo8$-^!Gvd0~GNp4%XHz{_3w;bMBRa+eGb}AD+Hv{0(Y5qxV?c+N5p9g=1XC=q z!f-Rae`J}^DP~w$Ryh0f-BfA0(Gb6toLrj|KfqBDT^ zK_4T`EDO3sXA54BK86@uCbTi7Z8PQtmX;OmY^nD*wrJlnqGQakKxZrNgDv!G448*C zM$C-~U1!R=%8@h%T_QY<-p2V~CMuN*6Vj%$=>-6SgqK7)#5FHnz6WeT**c zp^pKE7-51bW|(7%6*_}>zZzZUMvo3^44E4vI;$~fUSL_Hvn`(&h8S7KbYhv)#)5XY zr<;EG$dfwqow=sP{3tw1++hmNA`RVOi0(!4SP)RAb6K!yJol ztT=af()~RQEOWX-XJ=jSV}K#%*xE(cxt1Pn^l4*28$;R{(J|&&pg&aa8(@ryWlCpQ zp|h)AZ**zTGN40@Efd<9(Z-xM7IcNJ-SiwDMwSU}Olf0Iw|3Wkd<<%gnHv*2!>~j5 zNiem{=*rUh8vDc+dNl^jW6ZEbe-GU+#sqV0?WuFGMxS|v31*f#U38=4^M2@KfDtB` zVs2T`#)@w3#r-XP+8EFwMi^s`1y+{M-g+OSPe+(q=CrY*oqf0ux)@=M38t81fhATo zI>YonE(RE3j?TVZXX(+#fHsD7Y?;!TWw4*#C&B~^EU~q}uJbX*#4@FAbLItBmdSu^!q|3|XtK#7G9q0OCREENCc-7Vk<_Ye zCc-ApeI?wC=y8Z+96VIdI~-!bh+|CHdl57zmBqr!$^)M?RoE}|wp%USUPC0P&gu| zLi1?ehrPmzY+D@AI~)}za_aG+)At^udmPs2F%*u-*+?E^YO$d=kLA0u z!U5LkF%;TaJwL#RV@zlcFUFRIug`ON* z9MNYRa{n3pPIOq~sBkc+^PzA=&YEX(FIw~%aEuAfv-DgKhd9Cp`$uqp;gFmiksCD6 z=D9*k&U*3?1CB9agVl4m7cCCZ7Y@lI9Aj$HJeR#-RX8BmEe`2Ng^Aptf1chGFk-J( zKPYtMA)4p&omk@#M;Nj90?y;GFpwiQXkMu2vK2Y&$TfN#V!$z)BXyrek3$R?F`;>p zp6lbNFp?9R7wde5{lWn`>&T@weZUb$OxU1%iQemRgb@>(dA_f(PqtX2#}UTDF}cD1 zOLf03bmSU6_Fks*LmXr8<$Na&3IjRibFbjN=n6eK6h^XrrJk?R;|LQr*ngGIJ4|Sf z;=2p`WLxOSbzvZnutDSbPBgFPeQ0rjqrynej>%d78r_%ByjHn~7Ka!xV!{TiqxHNi ztjXCCIoo?3zY86X(Y#*sev1S88a)m%;20A&h5a|^JsyV`ag3?3TF`R=8#Hg^ccDdx zbzvZnu|e}D-IMK;vz{DVZ1VGO<{lgt26DpQTljvo7%`!FtIqY&W5k3_p?RC0>)`-v z^f)Svlg$b+HoaX7?)qrylYWB*t^KPc=S$Gtej2F=?wuh5~#A&xL& z!s>XQM~ee=g*7=F$m7C>Y~G>!vpsSjhuENfr_N^w=jmIUl_=v!bnbN z7j^#tJqC<8#->GcqVDMx4#}g!F}bw=F5NT4fc3w$tm}z>fE5vlgEYCM>&rkhlLHgJtnNq;2b72pHQFekq1~~?~^(=E^Nrz-kCZ##DL~g zJdgFKm9w6l9g-U~pV9f!KD{lh$q}2vYFYPLbXc9Get-^}!og=bhY`ov`yBV<03G%} zuX&9gBf7JhqsJkRFrhg|=X+RTA00N>J6Go`>|>3o&_uqcFp9=%?_*sU z$>Ga7mmQO{iEPf}eb_^bLyS1aguSom`3eVEqsM>^ny>O4_Hlqi3^*zrlN;=g_u&ACIKnYDh34COet-c-7;$)^&V|AeIimlL&W$jk`L6mt))=t*p5_*7 z3^>As{`)#V#s=*V)DH@4a>RrU_Ez;=g?+3s;usq=Kh$&Ciaf-Cqrx#cVgDlCW3l%m zz6ULa7Gpktv7Q?g)?|;voPVtIp)iuih1Df|Cl0Vij{y@l*t=BE_i@-_m3R-<*yR2) z%_EM{T&}){4r?4-p}9lf;+Q@aHsn(C6TTNcHihP=IyWc`~Ez!#DED$TXP@XZ5|p0XgOVCOX$gi#7VfNH)9h96C%`-&AumDFco%VS~M!>0E^lYc#uR z-ori)u*M-K?A=_?^)VIN-8A=@(A+|Owoe}72qU`Pb-u<1%`JI9dK_ZF278sxS7_uCMTdhsXx_V{bXa3Tb0_Y> z0s6wKuX6(&WAD!DYYf=Ci+YO=>$`Fu1NQb+pRji~$yIa}v^pw4?t*uSs(A;!Yq{WKq7jR8lP3e7>>ivc4hY|!3c=Od1>_W<=4 z2k3E>=MUsN(P5249AP!Xy;x(!2Kx`vxgkb04_2S8$XQEvg`PabgbmgY)xExOOg0bG zyhe{h429<5IzPaOW9%KQxx*R*jxZHA$E$ zd5Zzfc|1K1aD?VC&3ib)gv}E)ub(JA4sneACuu&w5yrwq?m3XffajBPQ(6 z>A68+ASY}J-81|UwfzDf?z6^_XX8#G7hIpet>`)Dy1npg8a^ev8GqjTBbYk3YGR!6HJwHWEE z*Xdlu>h;Qf9Ad-gbn%=H6LRCUCIt4ChWai^QzF3?Rzwjn9#hJ=ddm`CutsVgoF2~cj&Rf ze&9K@?^mv|{{dx-1FW%svgQr;KB(NsL7^u%Sbs?8Jtj1#sLu|`0aKoTSm$~;!h~^2 z^D&x_C|B6WgyvN4`zYtIM*lJOS#z3lX^*~-0mu3L$MxK>Fp}-*nh!8wLO0U9cZQ6g zkYlVqsocjJJ@(Gj+~E-Ir__6Fu>WcG0jtmOJdV*W^E{5Qf0p_htIsMAu*QI6Y+4+D zPS5u~FD*K3usU0FhaN+rIY;OE=y8M*t8=*zhuENr%rRi^i`0CCDN0_j| z!IyR3o_B2rTAjY6INeW@36*rf%=5KZ*U(bY|ws_ z`L|@i5suM}H6P%x#s0Us4?PYqRG%G_6ZXENd5r;6VMDII%lDxBo^trU9AWZO!QQXc4{+3CLmz*m^TS3)9Ao&c`tW->!ieb)>g^vn_h-&wM6<5m zV}ql=s2^kRugVDrf8#!Eu)11()|1`e)qCv!Q+dLe1F!k#tWOu3E!0op0Oty8vd4wO zMRLGpoZC|O57BI;+{cJ>TdVij+eX=H^tg!YIDG@1b2yJ9OjzGg=N2&F3jXUK zxWDn&!&KoSIlDy8PTWY(_0Zx{;R@O8#Ce>@{*BdV$7HiJ--Z3cIr8L9bgssLOE|lW z=JU9M4Vs&3Uf}>ejUAoVbO~+rk<0EIM4o z)jZ#w@4*qSVDFZiJDf*fI3x#5SXFvH+b7Q!2J$$c-$T#MpvOfV;WDn`U{5_a!gZXu z75CvRF5(JK?WJ=yj?mm%y~8ms-9~*xx3}^FMqHaxpPjs|a_@H6c9J~6d0Z%5C9k8o zz3!>7w~zAR4sui&$-O&jK7~UJh2~B=*F%dlIK&ao_jNuyB%3>{pTs2`W5W5n=-fJ* zyDB^M`zl8q&KFQd7;vc-hv9_p8-xd$7p_ESHD3pm6@T*ftY_tbN9xP;3%y}#xO zm+q}R!WA_4Q9q9h7;uaUCl1j09!}#N4l(7q)p>`r2P!XOz^VJHpT`AU#o7C5o^bO1 z$`vl*GNwF#fX+2I`#@!fGc(EqoOzIPc7bdjtbQ6DF5&PYny=wH`iH7F50jHv6*lDc zhwEH+e6aG&03RW19O5YFM`|81JX*PTh@5_m^f<(5)Ws3uJxP~)N(z#i5Xq@^XP8_bBP2~9} zs~_PqMjYch_MW2ibGU*H_MWP_Ev(4_$LOA>^EIwx@9F9XxPtv>sQ0*t0jpDAhJcs^uoPWKX$B8#6_i=#pxPT*E#)xa^7Ie=#F1$&3j1BrXtFPW7XK+xsK%RZ8 z&IL4YQ|{p;R=A8)$MD=xuHe+M$~8vp9jAT~6V`9%9vq=LUi}32G2#l2vG)${$0eM2 zr}{q5;v9M$;nWE_KaBxL=odA2C(3!8eHZU}H_zk5dz5Ez0Ryg~d9TimFrB2lUf6q| z`WhoPg)>3tJTBqf`_(Vt2nQcfzl!!`<$y~#^+EM3IQS6f(43;&#{tgaGERJ0=cbqB z7%+ZReg9)J;@WA-)yL^^c)D_SaiqM2lV>OoaOo4waT!-}>XVw=Go`~J z)}L0t@)TD)zproN#JIc@_gU=)TVLXf9ChVTJP;zoGfm zH)S6OW91PxIe%MygM$l|Yn;bLocxaF6^^jMmG5dk`#ovC&v|UHURA$_!yhU)7sAy*j)z!*_!ayGVUGv^Q zWWtGmDo^18&Y3MX{_lbf_O?)tIJuSbsBmIy^>gTPW*hahI9Hg+4K8e}^I@t9OLYb zHJ{vBu3>{SH&H*tsa=$3ad1=R1)Q2x9^FhPtaeon*kJ$W>X)#$oAN47?5;e819UiZ zOU*qtm@4&l582yOPU0BDt<*=H*-LqdBQ&>GKaC4GdmHu3INn>?Ovwqf*xXkAI!@e9 zIpW0al`CAt^?lS&-9awn7|k7d?oM*pmk|@r-dX+JUFAw)wXgcgyU9gd!u~zfSJQGH z8(hbw{WKrp_?|p>FBxzN-TvwuboW+XyAM5@1C(dc;TWf^=H|d_JD>A?)i2}H{gnIn zmm^$#fb!AmtaOH^ZVC6KB6OWJqmvI&A zM{3?b2VDgpQPS8xsJWVmHm_D_^ERCX)@r#)0Nk7?itE%POhVWrt%2auzHsIMGUx(Ge>A% zW59`LtGC$T#B937`T@pd^lUOF7TQ+XZJqH_O4IsYy>!WHblTfKXata17z&g1m^l!v(Ze&yMd zkD z`EufGa{lXhfn2~vocxCR#c#=ki(};m=f15x#;FUHXTB>ZzbBWl!L=W#A6+E-Kazus zWyFO`lvijQZv6NEw1u4AQqJMxR?6e8rQ1&Sc97Neq{WpR z(BDu_p}mpv0Q);DH#d=P7g=MRq`w*7T+Zw!r+1e%&ZFByeZaLnmDh3dHpD6d_`bB#wW0&Bv&!^l~-`#&dN*s%F*3r|DJOB z-m-Uq9OIN#UcIkeJcx7mC*$lxl_wr1*B>scgJo|ZC$V{y^4eo$|FLrA@pAnz84jn% z@iUYs=j7~j}QnyvTV+g<8$T0m*w<%a=vg(p8SgD_N#LFYjWxW+5e{W zIDet?-1p@82eP*+?GKq>j6ar(m&kajoV`r0Vsp9j@Cy7XuF3RExrWWJ$iI^-e~^Pe zO7|zZjMbl&JV8! zM!Lh~80Vg-Jo_Zscd~c5tTABk$?6xMB3GX(m!2-oGvwMc<@B@UEG{3Ry!>pr{v5gV zT)Fr>e7>A{x$M1CE*y1j*U3w-Ro_2a#@ETYH_G%Tx%L)0^;S8J_1lz}j+HaV$%(hq zA1`O$DHl(W;oWlbB)RZDX@Z=`(fgI{2jwCTKBSyZk@bf;w1+h=F4*Bt8!*VE}So$ugUaHS&e1?+tObs7rxJY6@Msa zE|SxiNOP&IF`>CkeeVi6z*$`WiF$vfoc)EI$1&EwQXhXKdyTCBAj6;J5)Rju<6mUL zrN1e!;=OiZjjXqo6Wht@?d1?Bc2J($ksc>*zE+7)RdVJi z*&Iz?kTse&DzD-2O`LzT>>VQ)(GQhZusT+G?Ko-QE|-s&qjxaJ=@XRQiTGX_FkypJ zCo%tkocN#|pCU&emeoh4!^zW?mp>r~pOn*Q%9+o|gw?Y00ydvho;_PG;xewBqkcKc zwXeyEugkd$WQ~&-DlcO5edV~y`5#GpiCo4i(c{`>%&(9U$3Ic-{ZzU&Id$c=t$v~G zadegP>aXSCH?sFzIfskCSDyWYT*uzJvi%D^&iqw5;oRSp$5+eV-{qv)R{wnw$6J!O zkrUg?nH}Whj(C09$9gB_guR`a-&C5L$2u1Tq5#;B|$y&x2 z%F&T>_N8R3U(PukzM47u*D5a^Ehkmc3xXYVD~u-;#J35N$MPdz}+&&Y_whbvD$LOPs(v~u$px$s!Ibf}zuyj(p@PCP-5 zpD3rEBxj$_{26i{S8L^U9KBF^af0A@T z_D+_IAEaNBLtH*xIi4lQxO$HA^tsYT@)u>q(TeiY`Eq3}>kDQ2j_iM5HaPV|<>AGg z!|KP%_2tZeCTD&wt1IO?4t}A$it%^KlYfxQf0oUIGn>?Y0bvUf`v_moSwmep;p?U?LutG;(T&fi{!J78a~-9=9B zD{Hh5P#)tNR*zJ_aEP3Ej9fijE{DS_Ma`Y=X`D@v4Ta^0k;8-K>Z9cHAu>KyHqVsP z&%$TR;dABM^Q3u!oO_|H=jF`nuDxH*eMC;5il@u@Gh}s^T>YF}KAZWuGMp#fSLEb(dpJ)Lg3uV zGLED@Q;yG(vtMC8mV@uexmD&rmg|?u(NE>nRkHU-{EIXb*VBJL+Cj#f$Z&JHc1!8@ zkj>uG-%-|gl1q1yV_ex!+1`_L_mSxU*}Jb?If(h8av6tDpnr<&&&l4i<)D_s7s~h+ zxp0h}I!O*bEc+jmOQ*}>r)2LeIarbLJ92GRE?p#7FP8o?>8_CRXEI@Il$Y1#^xx&! zTz_NFD>snU&E?`A^n2o+NPnDMI6*Eh%K8Ix zuq1nD%Gq<}!WX3bvK)O=`fthcsx()~;m>6MS90a1H`w^S&F*sMmU8OOGCWMqKT4*< z;(hY4Aa{Ph zJpKdn>N92iDcO8lo_V$m6FY6ZZ@iP-XFu88NA7QBc%=ONv*q<(D6jsU{NOj`FiCU3 zjrkpN^ho)Kqvg^&<;PBw^;hN5-RzM zJ7Z?U3od!k#<>Ta`QQy-@wA6*^xsSynyJq`^qTy;BQ8DVz%9(SW{Ye7p?e$ub`6u) zB(ud9DtdcwM3dd>U-N4wH>wUW|N7_k{p(-W?7O4c_n%vB1hdV*+H2-pZn@>g1ryx2 z)fT&+aLbJ&ep)x{{7&3*P50At&zm)7UW5Nj|DN|T#Yb-OZ{K&#CvEkwe<?d)a~i{!{+z-v0GZfBe_Kwla6&*v>ZpwfApp+L&!&-gV+N|N6)QFW>p&54`MU zNB`?j8<#eeU=ul}3=adn^9ZJ^sgw}Ea0 z-3Gc1bQ|b4&~2dGK(~Qz1KkF?4RjmmHqdRL+d#L0ZUfy0x(#$2=r+)8pxZ#Vfo=oc z2D%M&8|XIBZJ^sgw}Ea0-3Gc1bQ|b4&~2dGK(~Qz1KkF?4RjmmHqdRL+d#L0ZUfy0 zx(#$2=r+)8pxZ#Vfo=oc2D%M&8|XIBZJ^sgw}Ea0-3Gc1bQ|b4&~2dGK(~Qz1KkF? z4RjmmHqdRL+d#L0ZUfy0x(#$2=r+)8pxZ#Vfo=oc2D%M&8|XIBZJ^sgw}Ea0-3Gc1 zbQ|b4&~2dGK(~Qz1KkF?4RjmmHqdRL+d#L0ZUfy0x(#$2=r+)8pxZ#Vfo=oc2D%M& z8|XIBZJ^sgw}Ea0-3Gc1bQ|b4&~2dGK(~Qz1KkF?4RjmmHqdRL+d#L0ZUfy0x(#$2 z=r+)8pxZ#Vfo=oc2D%M&8|XIBZJ^sgw}Ea0-3Gc1bQ|b4&~2dGK(~Qz1KkF?4Rjmm zHqdRL+d#L0ZUfy0x(#$2=r+)8pxZ#Vfo=oc2D%M&8|XIBZJ^sgw}Ea0-3Gc1bQ|b4 z&~2dGK(~Qz1KkF?4RjmmHqdRL+d#L0ZUfy0x(#$2=r+)8pxZ#Vfo=oc2D%M&8|XIB zZJ^sgw}Ea0-3Gc1bQ|b4&~2dGK(~Qz1KkF?4RjmmHqdRL+d#L0ZUfy0x(#$2=r+)8 zpxZ#Vfo=oc2D%M&8|XIBZJ^sgw}Ea0-3Gc1bQ|b4&~2dGK(~Qz1KkF?4RjmmHqdRL z+d#L0ZUfy0x(#$2=r+)8pxZ#Vfo=oc2D%M&8|XIBZJ^sgw}Ea0-3Gc1bQ|b4&~2dG zK(~Qz1KkF?4RjmmHqdRL+d#L0|4|$G&ayr5UmxsWe2V=1XErpq`nvM%w>$8fxw*%a zG@s%5 zWzJ*D`7HGw112<|V~*zY$`#t|+1!7QjOTJL;uo&1C)+Qo4_JMPj1kS3)yMO$Z6dp` z(0^6>6vR4#&tu5>LiG_7 zhVO9CcdxDap0aJx(VOqhQW=zpa13EjoY5zUX49Y!>l zsLxum&;6yES7I&YA799pmSp9_i3IjQz`6>6J$ABsK zKhwE@{^#6}0qq(+nk$vFj_fgD%IAK;`9e$1x}1Nh^R`7tAJJT;^A%e3IsZy?R~X0< zQ(^UMzN65S!*$gBM)zbZvPFj;BPKKr&tp~S$=N{8Msn8uR`-R%M6Q0v??#USBbq9c{HjrqA9bdN=c9s@>9Xm;fNXwjiB4CGX3uE+b)p)U;NRA{fy^XM?PnCQ(7_O+)Ve{79G7WO!@q-d>6(R6MeS2IqxlWWM3G_3C(VL zzG~6an_K8ywj!5W`m85s139AEoqMrDSLn&1Fp{fVvWLPzj+hEf#rs>V=-noI<`Gk& z*@O3Bg(>%YYVI&%LUSw4s}?Q2EA-^jK%b4|w25{veMi=j6PjByFN|b!8-52`bcKN& z3lrJw%{_&l99m5DSu@2wEn0eqzQstNP2}pf?4!_;112=L)A`bhK5NMiJ%$z|y}3R2 zV^wI$4t^Hmf`8xvV8;9XS*xvbht_qeEX9$k|BF zCUQ3Pb#Fp*XWmn2$yrB^h2}1LK3kD(i;mu7#DwOqy3ZCma;c{em|8UZ@;eJHxzy8V z139*s=(E+`cputAPmY)h&E45Ui;h0)$(tECmyKj|552FnqR(1#o1S@TppTgHJ=1!x zLyxgAkyrkx0#rmd$JF#3In;#$h^(O+}w-bgBBh7!ay!H`*TmBBm2TY z&PH;Z=HA?ownayu_2kk(-)3Z zxlPYJ8_2ORkxR{idZ7cW1)E<&tZkO(2?8p%u54(n~`}o zkxQ!?eWxvS@b@Zj4J{!nwM&_xodN6w_bmXijX9GF5Xdc4v z#%dESb64oerGY-1$XWAH-jCIPsO5atkxK)8n~}MB7~g|cp(VQ(J$=~3#5`*ruJ=|g zTKcRbmwNhaAZH^vwP+6JJJ1z+a=?fQ%|OpP3>XU&Icpxl-U};o){?W1T&TnwIaiwKv*xjSe}xuZi;+HEN6jqvqb>C0Y#?VNxzru1 zd$WNYH!(3UHIL)Zp~Z?mYss!fPam$Mk#nVq-aMY~!>UC~?+QIR8_3y6&L(o3<}m(T zTeS2JJ%&w;%oCa?@SYYOeVd+nHjuNCoK56X^F+P_ZJ{Ik>uBIyHj+ydy?GMvZ_(11 zdipj4^U_40HBRr(R^(Dk?^^Wq0plhn=H_tT{~ua$-nQuIePJMHBe~7QJZqlJ_hPk) zmU$>lWb+j6E3C*_OU^oSX`oLnny2!<7AyL!C1)Ku>&crLIhR^APt$i*g_hi=WA0lF z^rewLo5)%FbbU|Ok+YthjpS@1XYDg|pKH<6mj?QH9Zj68=GbSUCA-2v-pt6kHWTyf zs(B`RZqd=l788B-EPgNALPySea=?hG&>X?NEn51lBYO;(HqktrJ)kcPgh`ZecZ&vywtpy@7~0Uxh;(3HWTw~^%A|W)Y7{a zJ$=B`qM7ISU{z?zo9Q^0_2kk(pN-_yqIoI5A8nx{2aGKydh;@VZ(&6)we(p>F7@=; zL@qTi=l7s3bmUS`pAF<#n8>B(75whPid<^xePJNS!bHxRSMolr3N5+R(FcqzCi<*- z74JjaqNC4xa+`s9X`(MRNAVpkR`hLJ=57-`^U^?{jpS@18?W!I3M0AA#N51E&)Y&r z&U$h-kYiyYo7b>EtO_kT8_01J&1-e9EA-^hVx-R|a<)2}Jr+80si%*a(7cX&&~Bn* z?lD|P6X&w#_3RrfbcKQ3W@MgC9dJ!-pqSjtmtiv zj(#&e=K{tS6Mfdag}-iEwDhHpzSPra1NpidIiEFe<@*XP*|!+zvyq(8yp2B>tTxdy z&pNU%4CGjt$XRm?@5c&li;g}NCbAiFU!f(p>6n*#`hc-8k*j0%UR&tMSx?ReayF6^ zn&Y^)(39H?%(Ic4P2{Y3J9{gv$fcIP)X|rE`cN3j*_59@Uf<&}V8n#>9qb2Pi=I9k z$g#!how~P8%iLqYh^a+$0()+;qPK;Pob}|=KpzVexzsH3J%tt7qHEF9Z)V_JX{664 zvN@6cqixaAXFa(z&}SpLG|^|xyVy@*Mcz!$xojY3BRQMMS@Uk*j}_V$9evi5vw@tA zWb+>0gVlA^a;{CsyiL!%G|-nu`fMU+&3pOlW)m%QSLn&HFp;z7B=*%}Mc<}no^@o8 zp)itDi{^d&ZnP~r`m84hjD?AuHG%gOTJm+(ao%IVhzZU6*>j5(y+zler!Nik5mSri z1AIq|75!#f&b8^7mwNim44jLFiQJ|+ncr7v$qs#CAZH^vwP-%bUq72@nYZbfX9GDK z$*ItMh~HUQk!_(PdyIvNY);``tkAaT=sgCEo0yoFRv*^;U7;tJ2KtBz%@Th;Xj=^Q z*+@>;(dr|-r$tBKre_`sBRQctl|Prlid<^x9s2y-N4W>9LQ5`n^jS~N26EiQ#5`Ml zOyA>L^z@-Hk`q>^=^k6?$pKTIe_ZDyn$y_}R)v=A3j;Z}nCPn!-*+8#ob#I)m}et- zGtC+NUaSf&IqS$-PtFE%EKKBV^$EQeJkd79DyF7z-2Gd`99gjG{JugGV^JU(%iI#b(qxXf8Y|i7)rNxTgqTj^8JR8Z`L^faHy@eGyYsqbT=Gj1wm|Cp9 zs`uL#9lghZ5z{7`6@K6UM=j5_>6mY3i zTKcRbdkojn$hkHX^Q`$g-;cIMPoE9sgysV7X|bYj(=vB0div5pp9;-4`0Jw3l3k0Q zJ{!nwM&?=bP4<8lT6E|!v>55LiEO^5`)!MkzSPriX5w5mW`Af49XYfZ=~JQkHv2(~ zzA%s@rt7G=kncu&9d(?`dU7_9vyq$%&3E{HSY1ag=dzxh4dhsu$XWAUem`3D7+W;o zk2(N8_3y6PH29ldu)r2KI_Q=6Pkg@K$+&U(^kV_N2xsvx3R%DN< z#p)M&-fp5}?puuX*+e$KWY2|`>^9Lek1Zy8dlkO}9s0sRj)mq|>=7%pg^rx{HwLCp3T1a~55pCvT?tqwdL8%^f9m;c zMJ~1UuF#W1i;=#~#Jo*oezo!c&!Iw#zA%v6jLfrU3*B2<(U&^eyXFWM$LbEmZVYP{td7F;8$53dt;rF5|^yF+Hr$V!>?x_kbIqS%w z#Ymq`(64RxPIw~2vyHj=Z6YOO~ExJt%%r`S~E^GGS&$UHMpLOJ{Cr3ux)YFH;NKS>-UhE5PVI-$~?$(?~i>@$`OB21h4ST=}ZHqpi+gs1s zLPz$6fxMZKbJ;{LHB)>yT6Be;955Cpvbin4r_hlDraZqL=h30ZfU(6yZ*H%9DzxYe z134SXrHS6`!@Vs!`fMOaOoi1QxDPG5LQl>Ha+{HPDl~W0`?3|;7CLe)Ok{H>?m>sX zFp{&0Z2J6ebm$8MITcoS=6Q4&3M08R(VM&QJFr4q=*S+!CPwDjL^gNjeOMLxocGl^ ziw=EZ$mi~+^A3HBfj+jF=*``AUxltkPhT47vyt3pVs7ri??%_6r!S54W?J|Br&c^? z3mv)5#5`;EBD3L`nSXsq7nF<>lASlv(a zQcLe!4D{JZPG}DLf7rVh@LQ*H|Np~zInR)!@W^RNX(B99hMX3n>6E3&kkfLWlq@1c zIV^IR6*-0ByyVbysFw3+=aj-kq>!P6NG76{L;m&syr2HRHTHgfbAG=|*Z=ps{*UY0 zz53kuJ;pQUjdr_h?`!kjOivCKM(So`-i5Ao6V)Ah44GzA&ZEtAM66>hToCt$i=qmI}8{xq1ldm&|$2w*k1D< zLuMqW3eD&E9;QsY1AX+Eu$ZKNsU_z+>ZP6>%kw*OPlcAc!+_@VI#*>uKV~L!uK5D* zKwF`wUYf|IW+&d2X~{0rlVdr*v(8&|nSmTJVev)YkFLT%J!U3yu?zQPT5@S1M>LLe zXfr)IH&Rbn?8@)YbmY=RE;V1$xhgIFL3-v=dEaij*P+i$WV1VS=rLqQa&Dq-_u%(u zdU9zX$IL`7_S8MaUc3tfMl2@ty-Y_g_2k?@Jz_$$x9%-28U+zPj z>B%uOk&FHKo#-+HIilI0cVMirI6!j_eP$p>GzaoK(4xY&L*&NQjXb0%%dkh#cq5B%YJ2R2Z5$Zei7%*Z&cO>^=$c*INM7{Vr zzcbU49Y(ZAX+C9I&$}}{IbuR{G~dm%I$vPGRMyW?-($k!`>H$i7%`zem-Cs4Y|m4_)RBE=B%AX!Ut~IRsVCJbx~z`bZOVk-L=XwG54h~`52ndTzZExHQL#hUwXUP2uU3}`RqyP2LG(O#yxkXcMq z-J;73vO<}(91Vetdrg+4Qq6PhbDUs{k$9XVpkG*{|;%yd_&9x@|2p}AUf_J`7C zdUC*ormOkVf^5-a%(U0=9rP7O>gHO$gKmJaoWD-z3iN2MSKVhOa`7Y1VahZ=rl0A_ z0b^z&o1f@>uB9H!{&eOt3v#ZdUh2rDo?IHp35y%}U1%|4LUSYULWikBa})DuGd($E znwvS7>Byyl95c-=I&aaTuP{(AHMerULQg%Sxs7>rnUS0_i>PzCj=K3N{R%C0j}cQj z|6e-q(EN;yJ~NO@BiY=pbESb?8p*{S+>a4cW^t$Hd}bi0%;GNQF=EOzcWbVw&{HoB zWb<>*p+%P&$)$;0+{61YV9G3h!5qdi|5EjU5$(OqW59^Ted^mxbHD0ErX%~zNKWN^ zk9*Of$AA$Nng?{Q)RIdb*=I&_Li3>RaTqX_`62ZKMl62CyD^}7SoH#JrYGkH>ZP$f z_XzL9fGN}bnz={${%@qsbmUS`E`G~=W+2DRL@plVdzqH(F`#*z@1Vzk5zQ0an`y}o zeP$$=CUWsR-4`+w**wX+&||=unaH^&aSu8S7%`!FO7o=!xzv&)rn3Kgoh#5`EbD(z z-(ko!Pjf!gkt3!|^GEK*fDsd#XEayp$fcfKn#%cSIfn&0^cc`Sr}>m={-nA^ml?|Z zyyhHw44ANZfjRUTvG}w4rIze71KGT&`O<=HGd($A%rt-DU0A%NZ2QzJ^wc9JG%xcm zwCFKn$~3R&T!8@-x>wcr7%*bWw14IO7%-uEjXruzXkO<%Xwjkh8~w~cj+tf#^O=_H zD)iJNhQI5c(nv0~Z>aAx13993llP#@^yJb=9#->~?k^4Gh^a#JHs{c1Msh0W{=qyJ znU?I(V?gr`d&qQTj{#$5A{+A;{_hl}BjwD$2}M_&HVJyW59%F z0p>CTIc6rZU66NS#Dv8{>X$mQ&kW>Pp;?&sVMNnVz0{F?W+LaB_i!F9x=c?l4djHy zBK*!wM=mWE<#%L8aze8h_o2stDbu}I^AQu8#rYn3jG69zn)4ViVnVY7-@%0D{i>H* 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%0D8TAfxP5Z3o9?kRYqsNFDL%;Si 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�ni? z6H||BzkmUT82gpZJ&ZTh8O&lCYn}67vxg3rv4R0MG4Yu0bxhJ7Q*<6(tYC=o$MqZ= z%UHoGHZilx`=Wy$=AO`b5fi^r2Uy3AZ;{#{Z~!4(*WVU%lr3^4I$&CO?J1|4*|dmf9ws-KqlXPN z|EhBfbLe4!$-l9O>A$P97+@3QZJlS(#xhp^q4U^3c`k<7#`M2*p4gPBh(6uIHk$v| zejbhg@Hv?IueyW*+Qv>V{_n6HEMN=UXl}(GmbO+`Fx92@u!-i&HSd_AZOoyA>20*% zF++Q3ZL4z^V=;9C+vsnnc@<;ZtF2c^8_QV58dkcs7vDj8=woU}&9i9kq%L48uC}m@ z$(=P%VF6tXF}{oT(r9BI%b4oXo`)5zVTg%cc^|aVL31~q+Yuew#{ioVjoo!Wh6VJn z9MPw%7^0ETbMk2Jq0XR*CqWdePyVElDD53r7YzveZpAEdSpeyQo#s~ahqIYj2LjmbA? zp2iGX7-I8K?U@6ze3*Y@+>U?YUUNI=0Y1R(ows4yj96KTe%L{-qY_8ip8Twbw%P z1aLc$Z9I8AG(r z)_DQlcdI>2!v2w1uj?Q^%D=%%#V}K#X->*Fv8)$z( z^8%JJHm-RJi)dV+dGSK&Ve%rigAL4Ita-;09lJ#H&Nh-EY`)4YDUj9(#>XrhZA23W)Nr0!)uBFku8sji^+QFX^E9WQF0#?n>X!`R2v zX2djI#^lx7bFdh(HKp_X$7KnPPpHcgQ!aCie_EZv5;ib3t@Aos*QlH5UaRhCT>= zjcrf!#Ajs-jnAn&=C4=#Si|a!=1oj|UY*7qX1<_#1xqD$9TWdYW8wyNfY}?>Idn0= z!mQ5Q7{5uK#4uv!W}UlO{*pRTmKpT0_+`zFTVxu`=ws|wo%@)dQu&c^z9AzeDpZR_|1&=B4#DSwQ11bp~7Le_itm*3qhH zZe#u%>W=9J`diYvNBWq)SM6dILu{i_)m|FCj`wMvy#J*d-)0{(i|QQaG4=rWFpnOZ z59-`P2TK@Y>^nRM%UH);pz{j0F#TQ415ABSoy9WNF!p_&$I(Uyi|AtY2ik97;)m+| zL(;_>W@?&yXgsVAvH2sly(~*Vk!_6ql=&kvTbFq(VDe|0dsxACMC<3;bFqvK46*bJ z?bR??QFruysV-xHO)NdC^W?8yYK|^qs-g4juceEr$JACto6ckEaqXosgYi|(J#?Q? z=YJ!eCuI#oG=Hmk6$`&pdzk*cI*Ubg*EDZq;g9MTnxVRk0UA$h?qC7UKWW~;E>+8Iwl*#sA#Q!W`yt0js!#$*pwH!#P~Ro~?DB zei_fl8657?ykm|ozFhMrZsPnln#Z=4NgTusj$k&%^KlF((8Cf2nA%SFY|NvFB`o7Q zCbrkTDfF<6%eaaIuh4!5vsl0;wz0Qc`;*wW1C3MIh`2@%?5O=TS~!7CT*K5(+Rx)M z8gb2gaj?^`0Sa;n8jfn z$1<*9i1y)n&J31t0RwDf_YvA3L^G}K$0E*P3;T}Lo{bF*ap)+WPvR7MIFIWc->Ch> zpiJW`_Pt5-DV)I?b{(yA8_T$gn;1JrdqX&in-S+T+Ut9>%-}dq<0`f>@fPi;(ZK=+ zxPirEwO_&BA$1Pzx2hdn#>8=&4`2mrm^@zRMeNP0N3nzz>^VW_8MH8uRSYA>PSm|# z^dc_M>$r(sZ_~XcY+?)jVV#eiBx@LA;$%J-O&r1rEZ`(gW7jFV-;F~!ie;R~0MnN4 zbu80uoOrv=6Q{})&SCBynip^i*RhSgBift8{?lmO#NN|2uSRUr-Dl{07^@hwHIHKo z9bCaC8fR+1W13#T4a}aUJrCz_5r^NYa|;9P8P$9N*Ki%X-^DpLu!$|~JzIN4oW@0T z->vg`%;eOQSjHNz;lMfC8^j7WvGg9DuVEXz-mAHVZFI&o?|z@`!66*O^@)T$%u72{sEmQu!+XF<|d|a9eXd(`8X~{%v`8*2PZF5 z7jXs`G3DsoLI*dn>tdbz*ma4z2h$VW#~ixY?DVDDTl|o$<2q(Ptoa;<7%ym^!~q<| z8C=55W!hiGHYP6DybnzrLkDX(dxiF`N!jxe*^99&)!jIW6Cc$)Rg|%-H8el3 zc|Q(*LG5AUi|T2tVhtz$Pv?_p+@Q{5fMYjm-p1UldI76Aa+BuP%`*Na8b@&q*Dzkz z-Vmn0thUj`HEd(-7VR0g${KFs?40HSPJBf@k0W1IkKrVG7~H0F!*Cv&OMyLIqa?IyoK>^sLR-Qw>pdC-&D_Ic|jdu z>|5#?+{Bf8H8-nr78h{*KFteQL=T&2->s_@-RK_`lT+ zH2$NmVC=u@0bDX(`Qm>*-^8)4)T_7_v9-0%?U%_}T*SF9&1)E9^5vSFIEWM5XkNkY zZPlZ=h{G|>SFwLPbq43QR|i;m1^d|SR>yXbJy^!{PMSLyi>rrUDXpEQkBc35(Y%3X zk9q(HF@u|E?XJBMoWV7mPUt+bhn#$s%5U#&KA7U$90OXm(Yv4zFg=)8Y# zY2hN4_R&1R*uLr>T)^Hw%}pG}94=x1emoDm_gANI^8od*DJ$6XTJ~`CK(&v#*Qw{R z(68=4NM>;gCl1!!#c52XH19e@wsH6k>eQjq!KDFp>@YcuGl#1OkB}D5VLYvQ{wO){ zMw!MLEDmb!;|9i#)_eh1k5Px1%&0dpe6xD#E$rdgvFb_O#L*$m$8iQ1aRtk7)!qhX zj#Dq<^zrHdH*qwp`3er7pw68r=W+CH>h58=fYT?bH?aF;bpof*KSlGjB@4KO!P_+- zJyjObe201vi#UU`BRU^DO$NA$6Q^tL;lvqg)0R0bVStNg>b!-^?^Gv7txO2923{8 zyJzGQWMcl-tn>6=tmg8l)hK(<)r*D<+S7h<4vW`QysmIX2 zQ{AyWuO7ckT3?qzMb3Uh&fF~*aO0cmz6Cjm)7bSb%^j@a@I9L6v3Re#jj5`-fy4Ky zEnL9{4t!hZxkWks0R5oM;|$JXInenE=D({BaUGN2(>%cC?{jZSrhXtD?EazJ#f68| zLpAB+0`@(ux%DGi{;^!g^0GSr6Pf&}%wgjZwON-HT*UsLY3^YD=jw@H$c2t8>fuK@ zZ^#m^qW^2oACtw$(IQB?q3Di&)s8vG`Z@6t1?_UH_1|e@f@yvj4xF8#}-F@BhnK z+DbjNwH(C)y10phFW24%E^njmjY$)2Y+?U)Iv>JuT-}~~JIPpF7IAQA^$1R5aTm>J zca?24cT=Z#ml^C%s8iV7L%oi@d#YD@Wdp5Ot4DDL!@V>gdkrS#^u98^pBy|u_L{PY zT?eWIT*uVwG#|qzru#MTI!G=aEE_oT26gYDvS&b!Vd^k-73U6DuN)~G*v7G=G+)4h zH>x|f>Fl89V{ej)qh7Nk9l4$V&D7Kwe#i7IOk|xpf)d* z!#IW$xQNRaX>Zz*3l~e{5;=#-52~lIHlZHBR66Kl&xbT$k9vjX-lWWaM9$zG+E;2` z{ixh5O6w{)f&RzT=GD@}zA1I-<1+P0IfJ>+sC%xFBe-&{+P+S@o=ksMX0U+EXnjuS z3)p+TdJ+Si_&?1zv2}wwb)&3cfTOdTmu{B!m!yNOvU=(kIex30!J#>I>#MSGn_NKC zSFd5>cD09tcdA#vCO6Uey1Il7?5=2@#o!xi^KO~L8cu#w^EDj(mU`$O>0#e}>K6Ln zR>v0c0hxPHPGAvdzoU68kly#@>XKag0sTW+en@WCWcFdXjL9FV2QmL+^&BoPtJ^=3 z-9MEcF5t=|n%C+w|1-Jzb7}oTE@9t_dia;J_^5Po8H0xA(~n7GRhDqz3H9o4WQg@A z)l0vX#otRG-8JkTYr?bQ1(16>$r*|f6_dMwP)08=&q|ro|UV(j>+dV zw=nZJ^&&3+T|L#7bC~&;I{t4t^dDI+@_KDmgSo!(#b0aIqMiF2>h-0GL}m^(@i9@7um~7+3;pz&e((E54y9ebc zw%^45(Q*Tu$Ed4s=KNUc56Pal$^~3IP8~m98d(|O8upx^c@q0hR4>0xCQgzgC(D^r zEZvACg&|#^uiZ!#Xcv3nwqrJb#6p#@M8K z6z6f}Bbtw0DHpMMmAe09c(p9z!Y9g;VY zhYQ%}Yi`{xYq)~e9hy(z@}26zd6~s!?E9ML<%%5ohRk6NW8c(#6elpXp!vYJhQ-o*IN)nmAfD>$^GbNiRl z!|JcprN=mbT!yPM|Ach0{9ASWcXAL@PpON4lv96}i`c@-vzoVY{W>}e+cMach5yK|t$JSk*WLM-$#Rz*c)1+hR!(3Q$6}g0SlCWo$BFIL1*~+d z$9I(OE9GX7T-sG8c9SM%u!h#|I`v5&e7$1%IF z<^iVq)PtDE=W>A1=!{ za)i2=mfn%F|0rp`N!Brcw7P*!>^nyDSVj)KS@ym~4&unM>iTgqb-YaD+zIO8x5)*p zPZaGPzSbL!WypOI*y#F zJsW%8rLMhO_T}hvPAtvaP}(oI_5v79=cjCyRz{qX?{kQF+I&W`q!vy*GbQl z>Ceg0>tz%D&#PCzAS+*#A+DFy>$7qLt((*}T*u&M&AY!OhrTSUx5)CXvi~;bx6844 zS-|XF>RLq(eS>qHx?4TEAY0hHN4<{8d)0kaIdY$z!omC1<^wYRp!CrT)HB~@{yn+y zec4!&u^-C#L$ZK&O+AlwTzXjZ6`ZZBS3CYfU0jiC*hb?~%@=SL3%}C5(BM91o=_*( z{%uIwhKcbD@C z*}a#{U>P?$^VjHn6P=_wyN_JiR}S~d?)~H#P8_J7#J+>n?!nT>H5`1s=3OaiW6vS# zl{d(VLuGtGwsH6{_1F=z|43PXqx6rF;~6>dW;r}0dybP{R&Jai`%jc}!*c2*X`Uhn zEIBzspDvw<^Ji*4{4VL?^x5i6PWHW5E~7i9&c09jm^)AHykE{?;zD&E;}@wDIDE0X zd5MgDP_BPS=07Z#E_igz8)bV|mT!_i#&1?v%Cd#UIrV@qy*uRkopSXqxml6jcgrcP zWBwk^1MIz5?cs7&J$Sz~9+X|U`U7?ThjQp4S;fYW)T_&K@F#KxgP*Fibvg1gIrVdy z`GqY1Qf@vf*Bi3_>z8Uird~tyado&VyPl96PrlUQKd3WLN%K!~;?HvWIqA3L3NF5& zb~ofyTP|X1Q$2_Ef2)iCkriXN7ymh1+Dc}(mM(6-Ox@ol8#uBJ^KIoIc5SB~@0L?I zyo0*8M`qAQb9d$m>0`L3y3i|2ua=wW?xha)mc4zlw!d7#{sYvTIBBZm{jz}8LFy@t zyD;F>`#NJ!w%yBYz5__k}_Ng-a4moq0Or9<)wlvO^ zo7i)fdKqWlsW#sw?Xx*QN0!c&eeaj+m^)uR^Z_|ME*lrf)J3w1qmH_W$qDuNrE=*q z*?aj*P0}WoaQ;f|Sw)$+N>1a%$JAp}vi&KU{)`-)mU&#hMm=z?ocCnmvvTEn+4Du2 znw4EQ$pRL!^kwF^NEb(LRZrsr#^y91zD;ItmsMOw=WCi9RXKQ{T*3IFdg;5;T$0ug z_%!Fw z$i})HeO@{^|AKnpFS7ryvgaSNu=Vcx?=@Z~n_V)qtz6hn+S|*<4svWqIg8nyIFHM| zSITNa8n2R5*y>fs_u_nSIkTUf?3bBCWebM})Klo8ahT>S7#^k0zDdr#Sq=@!#__W2 z1erKl`j|dNy^b?)S0_)CRa<7xl=i#i)O)1=J{dcY&ddJuWjHREE|T4j9KgneI(Dfv zKP1<1xu7<#lv7vJQ*s)6KcOz;tgBx8wCw(j46c#JwQ}5(E*3tgp1)p>%}5WYzMu|n zlryt(;3oF4?`E}wJ#*^bugc-ukTf+$a6}W!Iuyd_blH z>3&ZJnERo+_aPZ}tf_~7gv)a35t*&a*>O#mM~;oj-t%Pz$1YG0II?=NY-8Ug>dg;wJ|QPBm9?v+{V_R@%Tww#v_Gk?e_E!d zW!jTd*GuyYas|7;s9r6}ksD+kmu^(g-7Gi0EGKW3g*j<|Mb^J6t=pvObN)3s|8+T0 zk>lTx#RWNq$$Ql6IDVgc;M+2@BvU_-tsly{hh+R=xq)-PRF{7(V~@$T-^k|g$r&%&*{8`3or1T4Y`8mU)A})$q>y==KqoIf8`7|jXhrc z`z`Y_Io>5_(bz_9ZZG3KvY3!-NjbldZ0{?p`%CixX&=ZObBCziLuG)YhpU&5lwEI< z8<;;@J)M#3Xdlb`t+H{P%p5NhC(3o4A6ECCBCDs$)ai2SELk{54!u{}W3qwc=c!8= zT%?}9LJm*L#7CrawakB#zD|yKvWDSj)nnJo=^JJLtZd#S$IG(o%X0b_Sw;VLb@>ii zzf&&WCA${n*aLFzyK=fF2bN{&r?OC&{lAdmirhrMq3-#$9K!;Jn0Z|1!&v@}dgDpy z{a%)yqC+|Jv>bXyj{aE|(RfZB{FV9NrHAvl`A^M9|0P$pdew`6AI$77TR69;x_@t3 z-$#b~$$3+jUMss^CpY@#)WLG?P&s*oOdlyjEFYzAy-7|SO}|ABA19sTJ8FOtQJIsc$cPRQv?W&1MOxLlf7 z$o@&rKO$pAxpJ+vJ}3LXAnRY0tsCUTjm)t)s}6Dct7;D$cc?3O%fWBS@q1*?y>bD) z2h^np`&x6)_6VQDLzZ!Kt36-*>o2{v47QQ=on?2A z98SoUUYSoyXFs`ifE+!9K2#14NcV7=JwjS&A4wm@`5UD@D3_0x`Qv5J33BK}IgC{_ z-llm1XD~CYdGASb_GGznigYbGgO#_dS4U*#bZMO-7i_t5mh|66r2izPXAgDm0fjq3WW9K1;`epwF9$ret1Mcs3owC<2Acgbo+_E%;0 zemU}eSzeOE%QD2ppR)Ie%=}9BJ|>4BmxG~fVequt{k zd`CJiS6?a3U1Z;`a%6WIzDjPsS`NQP4kYC&_Pti!e7&4FM9v&7?PFvK!?&sv$4T#a zIXx`rPLb=DoH||BaL86C&yjQQm5DJtR}P;i$Iq9&ACNtl$m&Pr5*k;kQ&-6>ZlHg) z=8MguIGIg39#V%W2KTA%H%CYlg`2uNODrYX2iyxIkSIPP( zW!#nB*U34od`a!!Dob;6`Zn2nyDZ)-hkq=`ej<~P$n*;HhMdKX$JFaj$PLW?PF>oN z@wW80-s{Cbr+F;CTZgOyU&J(izYB_L#oavX1H^{|f_`TV>zza`;3!a=L6|-d1PMlG9^y@O(Lbpn;hMg?=kj$@q7PnTlwhD^6p*b)Sl8mK;C7_9bYRm z{c;Y^K1e-w1oO1~^jqcfaq^y%<&LBBXq?BLFVp<;TjZ6Cau#p?p8CWe%LjiZk9$o1 zZB@?yMs7YScl?7aJSU%eUZ#w`7k`K5%jAQvlH0#V#`c!UedIu&43Cz}$I8W1W&Sj| zn=Q{B{r}jz3-GA2ZEe&OcL+|Sg*yaySXhuof;$xM5Zqw`!7aEq1$1ML1b2Gj&{%MX z!X1J;+|>BK>a&yX{`Y_HbI!SYpEG%$-EY1z##*atM0cnvVCuFoQ#&}XKfFB}`i+Bw zY2)$8uMWT&$Ki-)aMMfZ_{Mtl%yK`1lfn@hVci^XvIP%Sf;DQx9d+P=&MIHMyh*&oK$Fw1!O;V`WK z67HcV)8>-%r_KU@?gw)Xg9j$US7%}TRJmpT=VH)Z9`0xft4)XYQP?g7x|8OS@W{Ylpyr(_pTvFxpEvB~v~*&bI=* z@gt1h5^nwi@A&7Jxzb}`*{N`&e*vjaXbNNZfg7j7kX5jD2rP3MW-U=rj{7hUE=^cS z^12f6)MEH16fS!Sr==?_bD4|4>I0#3c^OY#_b6vc|M4qPuPs$p@~DI5#FeLDrb}?c zOHp5bj~tMwyqxE7Vo@zAi0oes-Y*TOl!33yimFLvNL`O!gAp1%b9iEIUs{QfcsRYn31^hWP%#&Z#(TX8o zDG6_uLH$%6Sid36*9hisE~>OGVIim!wnwhf3sx8gA8ApK4Md(h7P_awZ!_WI1@QeM zICTR&vPsk}4#dGx2zJaqop#M2WMfrG3nh`$FCF-|DkTVuVb{0c!SQ1Vw z4STjge}NMo?uvSe0NA(>EIt?g=8NEqt*Cc8j-2WOtoIJ}Y44Gren-xb!cX3R)pCle zQB^oi!^I0=zD3YywW#Z^MJ~S?`Nlrj>n!SXAHdVk*#C^&I(;R%-iv9XIyVd6-v`H? zfi?~JjvSDxvh4FvR#Dd}21^!)DQdvxokZ1iCd_k){Ri;bE7Vh{Dsnrv6%tjIlCX6X zShyeTFhbPLRv~ZR50Ab@eNXzTa=T{a6Lps=@cUZS4}6BZWBe#{&+>>mSzY+GDf0;Q zFHMJ;kD%W1BwY6y9>`x!&eL%@OcGdK@)DP*^LMWydC<+89(6xoe~qP6|V^L2W zSzG2h{#e(e<=0KLcMlYGXrX#ikCd#rN1gMW=<2iE>B;T{`$Q|GaZ8!&93VQZJ*_<1 zem0}EN5^8HHXh9%vjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXK zvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXK zvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXK zvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXK zvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXK zvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXK zvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXK zvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXK zvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXK zvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXK zvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXK zvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvjMXKvw?rR4J^xL zmG#`{14&`gZ1C`JqORH<`P)h46AO@E4}<;>Vbo3N$GU)=peuZG1odH<*98NRf4&4~ zUWZrCiz@kTc{{m*rgkFY=J4eB4aqCT#a9ItAXJRtwt$&JLNv?nCnW+8hR`z7=h=Ck(O|pt}2v!<_>%{AU^CT~cw?;GK>TGc$+4nF4Z(JI)dw&a^VL_J}=s5V@d zMC=lmgtv&x^gSz<+MW^oAGsAm*|21xt=gt zFZ4S@-McsHJD`fz7r6vf)tNP2NH4ITtRK#ok+z_k9)P-Ff1EdXAm*zKf~5z;W^@a! zIt23p)Nd%}20}fY-;Xx%>t$Sx{#06aIF9d3k3zKwU+>k{k(iGXh`HUg)F{-a(>G98 z9*uefs3*|>lD(*97bW3D?9{r*s` zrZ?!6Nw`k!$(U;n)fQ^gXj3p(jEDVj^NysnV8=T^)dRE^*poCUkG)V*|=^Gs_87K7Be4XK2Fom;c-w6 zqnByCxwuYMdKc>A^H4uV-$UIUKToLH^k;esYBzmA0bQ0^c>F=z2e?CjI z5ZA9yd(o>Ra>+>y;K`t`q&4u7mmlbDAYG zrvvF?dJ3wCH2zYY?*+SuU(i>qf2QqL;5tL;7pSwWL_H7GRp=&qhyBQ_aJvdawV3%Z{X~EG73a%Lb3+}- z{Fb@OYRomI*Vo|u51@Lo7X6&-U>VwmCJRQrAk+()PtotR%6iO=rE};qx)$oh8)Qxo zqzj-r$b5;uVZF{qoVOm-!Ery( zA8CK62GWbHUuUkq73X;fb<%CfIcenW$aUyoI+`wk+D)I(%sX&gRoWHm0Geqh`q!cM z-z8c1qnGImI$<~Z-)WpZIDRf&3-xXKm3H_I#|@;Xp?X0x*f>us8US@a=Cd^BUYutO zJxO2F4^Tzhhq?GPC2a@Q5#}@W73;6*r2V)(8=yWy@3EfY0QwoBuEacoZe{%@EpZUH zryW#Rnd2P7dEy>MKR#4WdXDw`wC@qjEr%+YCOwLJAzFpjrk$V~L1(ib>33W|Hq?2j zllAFzH;s23^V^_)NFURJC(tiO%R$|Tj-Wm#aeMt}bEpED6Q06(($cI@2hjtp|IU1j zzGdA%1m|A{)lPbu_1Db4r!iL$s-mqD4#Ge2iedj`j4rY)da#e9=_?pfTA)$|P1 zQO}`XfVQMW`5d(XLPpdxH98x}9F237+D5^Po=m3_10481Ds+PXSdr>ZFZBF*h3O?abCo)Em<$ zP)B=(dPdrurhkp=WS}`{U#NC6-=I<6;5>I})VDa^Ycb}I)I|%w!*QMI6dFwT((Cjs z)b-!v{MYHo4>-?asDqhPeMG$=tp@dIx{mej%sD^dyhUjURBxEue8Kq!)2UFerJq@k z_7&$zPRr81P>p0h#C(y){D$+#r5T_eN$0Y@i@C^m%pa$v6@Jh6E8Py2n+8V0@8`DB zGf-E-?@9G&{=Re{J*eV`FX~RHte8IH*sePiR0~sjG_d z(BDBj#77=Nw?Y-lTr&ZVTSJf0dp@W?qsbHEJpJh;sJ=5t!tdpEMfyWx%+IHB@OyT( ziob7vMeU@x&OWF^Xv}1&Cx&_ry}RU62I$B2LmGlsen+f%-bSBi_X~)bsz8h5IsEhRsSl%c`wv{MI`IS z^gW$b6!lZIQ8DDr)J;^{o`4gHXx(t4GU zZ_(_Pk;g;*Gp+xl)OD3=ust0_2UbTtZw=TIs`hjk>ywx>)RZ|jnC^u-nm>*&2~~&M zm^($^L!GP+=CVLljyZsN33am`xi02&()=`p=B|h9)Q7q;^GLdo^`B|3`k1dmThih5 z8GS>iH^BAx(B2J^57DBHkZaQ>jdA`qG!W` zbwvKP6MRM!c1B)7gP}^&1@-b!mFg;44WnD3-cFCPzM>nB+ecM*oPwuH(>;|##uP`aHi8i@0h8-#ve zsDkM4tlwpR${fmEZ!nH)Ot%j~zCrI%Jrvg&OE*IOkiMolhvE2nP{q{9RiUa)H?y8} zIQrA*H~PZ})YFU<)nurr(@m^jVgACLZWNAd4An2R&1jrw64bHAAU}Zm5sfnz#}^t0 zi_y~KG2b8R5zMQp&jj?7Lmf=}OvHIUK$UY6a!;tv(b!W^-$IYm+fX%{iu0$R7TzAn zI@NR>-+l)ATcNHn6FKQDn3Cp&I@@g2{b}1dl67~QVlHxKnwK`FQ|PF9IL~vahc7_B zN59e-3sH|r=h4@+{7>j_hPsqXvhGROdh=q`6E4C0KAK@E>J^~s#eDT=%;oz9_NQ5v zArY?zJ@yc z2Go;mh70Icx^fHZJLyNLD{qy${sL9PZI~NQ=j=d#`cBl>L)~c?@^`4y?nYis*Z+om zfL^qbx9)`<_lvsK0py|wk!#c6p^ADK{f4yo5#+Zt*HIid6ROz9(Ekp#<2d?dPN3cq z>T}e667|BTFc(VCg&^Oj`A;Lap+jlobExm3$Lag?sAsw$s?T)uCDfx_h8>|wdIk9w zRMoB`x40&%i%_S(f%@TF@CFUIjk%$BP~QSo^?S$#?n8g5K2zTZIIiMD^oKoytD(9| z3qC>pAXKTJB6o$V5A)n-sK2A_pG#I9p}xhO^abV$LKVQgo%tQD7mB&NP%V3f^ZiaM zyhUyab%A%74|xxpd_n&a)O)_7KKL8zGob$UJ92hD9_t2F<0IkmN97{JR#8M9L{CLW z{UOxPna{^SJ$_8gPmc|o$A#zOq2Db&a^3{U6`>yEgL>_R@CZ~16CwXd2S7EH`6Jbd zalVDLWfB}0KtrHPpA_}Y$uRGe93H22QlK9KRi+=1zfn6CazI)fH!dAK0QIsA=nu{a z^JhZ8NM<+$>hxKVi$NVJD{^2q=*j^v<`nhjJjn6$!b-kyH~mH*P0eG^IsISrm zg;Aed1ocWqk;fE+SBj&)y9B%j)ya~`XXp?-4o?>-@OC#X`@Mtv02 zjq4zHf;w7V)u4it0^!5E*pAN)Wo_ecN10Mva4B2T6j2O+l_0>9BqLowfR80v{NOhd;FM;Crz8RpSiE*D_+RLuPm&3+g^uk*Cn<*-*a+RpspH598yvFGAfd zC+247LjMTVsq>(I8>-;E=qq11kKU%y3Sz!sA=sZTf+}M%99O>tJYN!XRw?8o7CZ@c zlXA#&D`Ku*B{&!=7tLP{b4{UcSQGgQ)Ol(l_n_&UB44KEnn~6N>3h1l1?n|hVm=7! z4a|{RqwhzjwZU9)TNtGS`a3$po%9(^)d_QJY4QNft?2_R_Cx<1)DH)ses&-%Jp?uz zhPf;wk#7YeCmjVlLLHBfi*GOnbH9#-Gbh4MQ(&&CFw!(pt(ghG&q96UYiO34f2q+=wDoitk)x#*?^pAD{`fsaNQo%KiaUs zUewDSLUtU5!;iu0Ct%$W81J;Ge9s|wf$GtDyHn}FMP&bTo8}$x% z;8v)7?;>Bg2V374RePu}KH&UA)bBrr$)BR`dJeD9S2V+0)F08AACOOd64i%q@Rulf zK11fHc%DMrXfR$ZSdVUujk$|7UL5qZ#)ZSZHiQ$&eqWgelX*%yeT0 z%st2RI&`&6$aOMf?x!rs!C7IhZ17zU*gB`Et-Q#QeBpt7sPD}Wj}}BdV{zn0P|am7 zYoWf9cEa;hbT_CnRK)SuD`D;-t@k791**Xq{^*Z|>S}$|4>v%4a}&4^sainn+)}y~stkdszaNG392$q5emtxN)lTLL6ESyl68gobzyb8yRP;+vgP~9_ zosPLYvrz8@^^1kbKP`e$T$tOw1U6a)$I~vWk;iNjRo~6X6}BV4{SA4`UgRPBVao$B z`62XQ9)Zf4q*oz~3X zZ$*{&BXW<=aMX9yOGm-;in8%}ODCfv?~DnP#}?HVS}G25w7BTkqzMwBUeO1xhx#@h zkqGt9iDBj>qV7R^r$H`~1%9AbPRxhogFh7H9Nkk0^(IANrlRmpF?hC;s2f#=n`*%^ zbz!=CtV301)rTm{WX1f9eJ=DF1ZUcJ%-Dlz;d5OT@$K< z-;h^+N554hJparc74D1y=f{Sx;=qLo;h;pYdTLRpOM~p^fVFeL{CUuS?hDUUgjK7< z?=)ErWLHfX*{$IH4WP-9^-udqQVF)UEz7^8nN@4}<%L!zv?T zim{@~Fc}t|3MWiQfBQVxU_Q*h5YB{Z!ZOJ!%5u~vtVGVU3U*kJ`s5ABA2-5xn^FH| z7hJp>R@nmsZBZ@Wi~RgJ3^)Z7oQ8SMi0b=Uc<2i1-scsDx>1k%5cL`~_jBY{uh1|4 z7W%z|ixr;FxRs8Jg6Aui;qxE;;vuh13J0df{KRaqZ*JJfLceqc*CkOLMXr&5Eoo5)q}A%A*n+RJ?8`L}w>52C%VD*UM% z>UspsGYR!Avyk7miYnGaRAH$a4UwsvpE>>U8x9=&mJ2sHx-bQce(VkbVvGl{JWtKqm(Y0z(_U{(IRRSTD+U$zEbXE#+}IbQpH#@yTFaODxqb!v(^o?pMf z>oBX6FK}GdT6q1vA^Bxa|5yvh_u#)u@f-?#QWruq|KrF6cF~_jbmaB3Vzw>n(cMqvDOib-abrcwHA= zd?;SWVdZMSvXNAD6)9{Wo991ae_U&heuNL=6e3DEHfy{+Df+u28O*y{=B~xf6V_oyy4yB ziWuJeoI>7+6bbkKJnw|pC@O_~+$D6B;YW_83dT_dzk5DN!~XnvzroQx^(bnEcxZcf z&-=sO(AMhu9) zN7N@yBA34^TKQu3@Lb1#UTiUBath>q4$+#JQ?xtziaKr))W4S$1D=%;-Dk^*DsL6h zS*9iGuiJ=@N>jxEYr1GTe-iEetHfaURye>GeODesf7e4XK)(=mi%;lp{w%sr#OUd{ z{i=Ih(OD_I=({PC7*ai(=-QS`R80zr0Z|K!R@IuKvt>ikoo9&XDmMyF7%lpzn(eAuKbeG#C1{dBc`exn%quVg=elgU2P;_j*Cb}ct zMxIP(f07)0IC3xfxbcr61}u&*>H)b$wX3-3IM)U_MmsUUe<<>b>7q}e8KS$=Li8^z z6=*K;Q`nU(8Gs#OaV8%z(OUCK#xjoj$WMV-1^r9YAO>`8jA*#l0#Nd`N z=wfHduAj$>*7#s}Y`f@F;SlYScmu2^kD?&l2s%A)@p98PR|8N6``OhXJ1R zINuc&)#UzSK+FX4eKM$bCedd`E-_&4X3>91|3RMPg6?b-U5|H(&VGFcd+H(Qmx_+q zn?)V;Ky(l9JH*rXAKQMY$AH@ThIv$1(rb@L?m5MxeWr`Po86+@r`d2%-*J45=$tiG zRORQ3z6pO8?S?zVm>tfD&Yb7Pkhr%+m+PGvkaOJ#&-p%l^9%I&@ZIcD;?6yzJy!la zd8|ig%+ixR{>At8v7Yk3%%A`6@7w<}cih~N|J559T^_HU_IFDd`4+F07xM&OgYNI< zHg?2ox{aBP*NBTSr#oACjkgH(|J$f{ZpHPh4Z?ZGU-tKWUD27&;E7-4gYuOJMBVs87iwuZ5>- zmBQ`yUUyxD+u^P6v}-Z^rHV_)N}kmpLZyfAwP#IZl_D zB=^_uU%0Nk|HjtCagpPNdq3G5n&ac<_(^$fKb5MW+z-2b^Llbz#Ov>PDt#;TLH)n# zd&l+pU3LDdulG*C$J4tcxSscb2-;s0;Qp}guKvRLkLRhhEa^ACK43FNWK@Rc5~2Put#z^Zoh%kN<0a-_8~Z z_w+yG_dQFlRgsfDz7Ch%gFZixk^0W%!rJ-*Z=pw?^%(=bA~9&CC<1jUuW#gG34uv?eFW5bFN3Ovq4l% zHliMR3tash>U9sn@h4G_e?_$8-9$anZRE%I;4!||XU*rx1zuyWG;IN`RCp~=<@koV z%il$7WK?{;yc9#!xnjcUu|%tILiBG!eLp$+wNi@q9;hd!#$3KM@FBEMJ1{pti)bCn zh8)Wmxlk!MyOOB;Rz`mbv|?11to*8@-vHWmYN1}Yu4pZ-hdi+%^1UXaeYYvR-U3c) zEvj^#kaKi~!M#PRY+pDNs;>PdYYpwAgCwhvp{PgGm`^(#xh~B)O0qp;H2hA7kCCi8 zjzvAsc+na>1$hp%22Mx4#0*hqpADZbK>dzOwD&I+)rhcvblUUzR{9tCU}8@G$ruwAf@4L3p+vKRU7VbN}QL{#yAM?L9rQFlBE zOP&_(f#>15E27GA4LLuwCf-3k&wbI_@Cf}JPmz;`in{Gv!WkqdQg|~i$d8&zOZEew7PzUwN^-$m47WJ|1kn?mvt^@5(osk2&pntHdXfF*A zb@pDUU+*JYx%#5Nb1>>-hoJrp+Koma&!NjlBflFX>WyPX)qWE40BBF1g8Ig(q7`Mj zX#X}3mdDR&)@Zt85#|~!6;;e%L|tV$>h?M~c|9DtRkYh}6V)tY6Lrpy@aQ+uDjBVT zd|qygE?T~^ke9?3^|s`wZ%BdsC=K$!OfYU%I1{S*Igw-L5_PG3qTLnh`~@Ye4fH@E z%(p7adMPpN-=0K%R|)=DS+urS744NZVDWnB_ig}xf!2d2$UU2jdOYpa4*6zhSf`t4 zM-32Fxjv|;8ism;5y+LsATJn;+5hsEb~P_pXcfyPL@QABt-GW7K;+MZNtq(aQc7e*TR9z3=GnQS$$9 z>_>RbY_&Tf^58^peqvD-N`hRBR!b_`I+zOer|B@4A_H={Owc#8sDICbe(pTTIekT| zZ9cf4ZYzTvyS%8T_`~zHV4>QgU9O&Jy`vY~BDZaix$<3Lz5$}{GgP$Gj2EqPGteKi z5XN03YUdKs9<>AY(}$6l{|>*L6|H1f&>waUI&X^l;A_!N^#S>XYUKI6vP#AfRoSG- zkut+=S>cGhqP|!`v@g~~{Xr{H=`QHM?16rxKB(`5)|Gyet#3myS7jXL(z)QurRe|m z8}frg$iE(j6^@H4-yKmOd;*Jn6YZ%n8+-1D{aZ3o{bq^Qk7ZG>TMcpb*(&qsglO7zdIfkAuWcWCX{kGaN=QLp+!)Gv}Zk*{0zQ;1fnv~Xi_ zIITMR={q9d?uEQ%5ZnQ+HGz`t;y+=o{u=la>Vl`yA8}q(AFrXm`>Cj-e?mXaceo;U zQ`wiDSk#45A+LveZ+_%N1<@Z>T2yr`N`u3+pS@L1M17R zimJyh)MxF1ZTF)-_<(3fKPKw=r;%6QME~<$r!@6waW#s z6@_X2M0;Z;ICzj~$eNGS@I3M+KOGVpW38$?=|Jqv7 zO12;Mpabv_9sRpxec>EDe;)m`7vanMqV@1040tK(f^X2DnXtKh9?!}IPg$b*%>>KzY>cKsvhXFP-cfXk@gf~xB^)N?;a{cxyg zo&F@MJ+b8XS9bk)a7A&^I#2_7L~Z0-W0A`)f#=r4aof>zLG=tw-qTX+;=q@rImYgVBnP!OU(`?bTehKQ6 zmy7lT==iurvZLoVQ5D)Q>Txv6Ddb*HMR&_rqH}YCmY(;gdsN zQ6K(IbdTCAy51c|PIW|7_9@YFhM+&{0{UexiS}^lesoQ;`V6hq4*J{N;en%3adr;lJ&^FkD*`alIWP_MvnPFbQgOmS~_xT&-+_2-^JGEPqm&aJYdav{ z?JZi)DWZN#4p<8BkJJNHCay-%Wk zm#LlSc<0mHqWx=OxV4h#h+R{3->oOQ-t-oo7lw$I8U~lFL4VV4qP~6uxns8Wp7Yx& zI*YEiL87zLbWu6yi8|#X*58PZ-BCKqao?khuB_vcKTHyxDStxFbysw}E!ausqgbMI zN_SEJJV5Y`|>5 zY`|>5Y`|>5Y`|>5Y`|>b|KkS29tTiX{{J?Q*P9}j`^T(aWDeK&UPd?E5WYUxFMRe6 z{uA}C92LV|@1M;7sr{^2_}lxZ0TlF4e*fp|JlPuV_Ulbn1<&o$KSdE$=04>mcUmj{ zaTyo?4RdcHv~?x9KP6+~IB)$@Za=BtI~DFa-nN>)O>UoFH5$jWp$p_hKKLPgy?G+s zj#gi#u4e~?zh1pU;T}J$Yqbq`|Ml`<~P*{*8Ejr~~F+rN?n4isJm<`+Z^`t`qHx^tFzJ z`;#pL<`xV?eQH)5U&UYgdiFuo-(SJ)zVQObg=~}Cp(j5>y~j74XWCfI$C-!hy`5eo z$H#f?knrz!@9lhf9{cN2S=75*;W}&2;kZ4g!@a-t*c#!o+IcSAb#*6y+@6bgys%2Y z43DdgmJj=SAGd~QpSpA&Tz4A&ZE{_G3a(f76ZSFURJikc?~Yn|E!^XIy>-3fA|5|p z|H5B?KAlXD>*c#|g?oQ)$cFuTPvrG!GP$3+{AJu8Z+%M-WZeqyU)J@lSm7SG>>byG z@&5E?l{|U)^QB*jkFW19u&0#ya67#RjK;r}?u~z#Ia&CR%YA%&dVvABKbQD8{{=VX z@y>r7_o-<3zmvz{iqQh7yd_sZ>&u%w?|j^m&dPLBZ}hjSLp+B-oppN-4C6oJ^ubLM_;@j()r29 zgEQqY`FkWa;iw#^r$m?i+nGwVl8=KVndNac|2EId$`%O=`p<*Yf=L&%e)h{YUd(_^n7PhVrb;_m91We_XOWc|5G|H40zHs?y?UAv_Zat&*H?%acD=CIUg*@POUIUu zwq1G;2xzMk``}ja#;fw(d$!F}w%Y*DnVg>g{FndnK0e;(kNtVJ;(0v@{P~a9k?_Z2 zS^x9i(Duyd|5R3{dZzySz5KhqUc^7&PlrTYjqrcm&nxU_Lz@2y`@!emf9xmx=O3RI zpuGO`&%gipI7;*qdpeW$@Ave-@$>6Ut)lWV_A1@q@8y5<=hw6v*iWML{}c9u&oA#6 zWbe-fVV_U(DfQ1kpYZjZFSGKW`28v1AHUrFx4r$(zh8xYj}5yup6{%2!~FBt;jl*7 zpD1D7e7<3SiaXkM?9!!e#P8c-4+)UDf4Beq7wo5ZuRdJ^%zh$!KCHrig8S3Qk7qyT zOcRKt6R-aMSDClb1yY1wo*w{`mb4fsoY`POQQ0{EZIwKj_-LhbZxy} zs$MkDo2v_U?4@3YeeF)7qHdbjOYIK(xjKp3G0)jstqJ>oZAsMZqly8Hf)Z(xYz9g#F#^3v?qjI&)bJF!!9p!38(j^|K-X`=6IP2{mq$Y*E z3Dv7F7;A)_{70dJH{r)va zsgEf=Z(d6I(`eOEs%f9U8?8>H@l-EQO*2N_k~4IQ*I}$WDmVRjJf8{bZP?RzlcbpP95Z%=->kv)e0y5oKA zL9hSU&%ciB{l13tMTyiY55C%H@4HOiA%Fh<-~S)qUH`wlt#=-_ETz9$vMA{nd06-A?z={ZJpJm*@@l zZ!zCyzDpm_N9;eRuULQ0{D%20^LzS{exjf07y1<{byW7Ly+&g88WUN?VLdKQzr~Zbu0?Bet`4);MyxksovP-nw`9Ey zZAZJX-;KFDa{x0{J(>HlKadWl!>Fdip&r3Jl6iD^)j0Miuuj!P=E>|&p;KA+I*oZc zoyoa5%=73%)_-EAY7y&8=t}m3nK#kR5!}W(ue;c{S*L10v)7}{UPE}?X?lj9<=i=X zfnK6lpt?%0(OdLBeFpV&=C|}6eNR6?{e}5k1iv#=t$xSXt4K5ojY?yNS2OF_sOvcF zC!k4Krz#oiDOjiK2iDWl4D4rN&KAL3>_<@LYG$v&tgmO? z>qgeM(5-Yk=k_q~Wj;g?)8FZFdJ3u#W~$Dyeu4GN%r}|6-e&zS>r_2r{VD4&>1)>C z(l0djF?@gYfhrMA#(Hv^mi2VZ8E7`vbJD!57oY_rSeUsOEfHQ-lDP~mPb)%IIf7N$ zugQ9CW~%D4UXS(q%ng{SYRKFuys9zhscz1G2il2t=3H0WjrL%ls@}|f!mIjnz8~xT znFld@9n3t04yD7WhH5nP80K-zlbNT`AUccv+0@1QV!9-}+MAbh{%7`=(-o{!wTgAB zy{_imI=Y^7o9Je`g?+EPnQgk4a|h@l*1aBO{+%A@+zEPyo}(A&MW`>)%k(O}2GtFE zll5E7cbM-nKVtU!oH@pEd>+PuDju`f_{<5|PsE&tW~NzaR;aQu=c0L8FHXy_Zqc%= z`!Rd1!g_Vq{Ucb5{kp7ERgbv=`wgj+^|rJl?M%DU?lb_Z9<(Rz#lF|xtoNn;>0mlE zym|t%p2GT6X0Ov&pU(PRx-^2zm{-tsP;I2!=q`E!>eJy>7nm>7oAefaM4yINJ!g)f zdcitXADBNv75M~yev1lqH0Br)jK#hW>xq~X(`2ltU``!D2m2XV&&-@Ff(6(wMvJpw zf|g~yTzFMQ_WfuT_G?DapSeD5L0d!Bj`m=ks@}}~*&j#;vF>#u>oZt)MQ}O$t7$O% z>zQ{kQ?;Af>j~zw^a@nhnIF=}tUqCX#{4>hpP0YVNGI{}5R=A++G_&V)3cs|IU93M znwRzbv=r-9)n>f`>+NYz*89`JtZU{#<}u9Um?tn#q|;fS!@Q9GM3=C?nt2`bM&{kj z`iv^x7W!mDaA*W+9Z zW~#cd-jfbxe=MCwXRtq$c_Cd*ceB5bo}icM8~PDyuV0xXhv55ORA^HbojET1KFm}( zm@~6q2&%%&MVX7y63{LWt*W#J)QzF)&Uzo_fy~39ruJxLdo1V1u|I)%BKtE~p9QVC z>@Q+{G4oRTGqirCtLbL;chX(qwe}#}HnjG!f1LFb%%`C~!+eeTI`a)^QFRm9zQeh@ z%+HwLKOg^~0=(uzrS~hxT3i2I%%h+^noeeaI{WikUk253=3w?WKx-HCZsy;Z57A>#oq_f>*6%VupwFQSWnZ1g z$75`0#bwR}?ZV6@nQJk(gt{H;{pkqSCooTAo=+FhpQsC38==|??LF+l)l_l9;LeZu}T_CGOyXTS7i ze0*1cb^~a&q$62(L-mUF>{sw{R1sROnLE+j(9Uxeb6ufYK@UUgBF%73=Imi~BXwSv zy43;NOPDXvSFHEHfw_&)J`8pKn^ISeXk52sos?#Vc0QUPI!i(K9;hC&9_5xC?}!g| zQRb4+s>*r;=0?zta$AmbM}^K9(2)_^lb~7vook`>4!Yh$JINhOh5d0Pr`e%Z7rHvq z9#CIk|0#4*o$s!k$JzxQ&!Kt=wd0=jolT%U8>*eq@eaCwxR3K>fUZo?Q5&lIP&AkPB+&T-bZ3AnKeXpVYZ?1zS-$~w?1yqb zoe4U#Lpv|DhC}yc=xFgs=3Ra033~c5ZdaZsqWv1GOixj71YPZ*<1@|w4Cm_x?dZ=X zs~FG`3+lMcDWR1fx-+q!jb?|g0?=teRS{}GXjfsb4qepYkL;+;dL7O+gf>-;kln4J zY6G2Zp`#;oQL87i)tmJ`v|o7DK+aK{I)k__BTS8*UiXo>fC~?soIL{q&9VJ<8jorop}dTJE8Wv zi~Zf(pfNvqZb)97Y6zd_- zMcv+f8g;kVvz$8z^?9f+K#RJl;}Ysls;+RJI;s0A>Rz4Kc)shVE7w>eLp z)aA|hcpTLaIR6y7sPzn4Q=7V}^#XlIDCekp%{ouk=k6;GQQJo3d znFVU<$jUitWkXikp@X`p%7MB?9l4luLmfet2X!ZPQ!6j)zR;Bq+SFA5*-;RxLd=Ds zi#m%SJBvcQ7_^E*RRX%Go9dFBD-9iGp-mm-cpSB|Nr`IaXRe7FjP*sO^ zP3q5jEvTuB+O^rQ10B>^m-TwkMQv);N8M@&-PF|>SvTQv)J+{tIZs{9kW~vFN3E8~ zj#kjsnsaTS)s}VYY{zgtN@>;~P`>Va(agsM+?-OOHHeL3HcbN!){ zItC%z)J>g3QFl?BI)|aIsik>5Rl|{;BX~S@2J$%S7=`Q}1FdnKr)mPSng}&jlaQU% zrg}1up90-f2eD4oG-Stg)~P)M*_sKR)IAGX&knC+4(F-U>pUJ$HMPCYN8hm^f(wx~ zby1tDpU}6ca}l!3#rk6Crj8}Z+Uw8AY8mGvXfwO1V>#w*>R8G1QMWg*LfyGKylM@y zMK!glV=d1|RWP!fy4EAB4LlFk)D=NDv)YJx7gd{hJauhGc2n0DWan1aw?oGcsHw~A zPR>!i3)w|&>h`*u=h*`tzj2=0RNJV#sdF!~O|5-Ajym?UPt^e)e~@#B*r)0+vV&Sj zIQKhr9)p^?sPhEpsPiP}sf)V3`4svp1X@&6b(($Zpl)iPW&a$Hr_S@pHg#TPrq(4M zcbRqSxPoj^o2sjvr%tM=`x^SL>*2MT-QM~Q^c^>$MV(Z+*{A9jGj&B!-$vg>9d|iT zE$X87J@nQ6@H&|_wH~0asd|X)q&kAmN9bFRq5E&No^X!Z)a}hr(YK%RJkOb5Ku2hJ zHM4$+y7dZbYI}W+x_Seh)PBqP_s~t%2V@7;RDDEUed2LceMWXtH+6jFd8kD-by4Rx z%(@ zlv*)ar<%HAp&lGTn>m#F#>SjYL#T>_dH@ZjDlV@_L#R7~q0GMVFz=+As`#k;Qh(~C zLEe4>%vlk1G6zsil@I0})Sm{^5N|yp=0d1SgzQ5d)bi%U==)Q51f5CH*VIizsY;5z zMg6Ihx~Ok5%mqEln%$))Zc*|5Wx^;l^%Ux zYEl0PYGxM=rtS!aG6!YAd4nTpGl$Snsxo5Uhq}FHLfuV6sc&Y~{b@)9{j;F&q&k8& zvm-0dLxUq|GkbMp!@Mhk!OS)drM}sDJE%?F)HesuM{OEP{d1xpOx@JVg?a#Wd2?>m zgQ$zTsc#-0M}w)(%j2j`eSJ~a)Ez;eeCT_%n7sxwyQ#{Lc?b2Sng&r94W^;grvQ(q zHuWutxL|=PYEdT*p*}@;d#Eq9sDA`q%)!)Al*dzl8bE`o zoB9;v?V|qFMQ!R+9CKbRX8#BVFh^9g9zxyJQ3B@;rM@MREvjiS4WXe_mE!H9PHO*+ zj?$QS(trpCF^5FZ%^Vs*pEA6?e`65qZW>B`EFMQ)G??1dO|7z+chO*~%JDoA3}ANC zP^!vf-m4F@Mg6Ih>ImA*z7=quVCts+6;b!vXslV4ss0Yv>>Y}04 zQ5kbOf+5US74)4nfV!wpRrDP+fCfkK{~^@g!!VobKkhH5LS(C-b7eb}Nu`~nk<)gF zk;>|T5owDwN!leDCAJhJ((0gw#G=w9)kw16Jcdh%rD;3FfX}vf13kN!)d876k>~X+=3C)|dKUvW=T6F(EHgDEDec?c7 z?Do;#h{p2ySfedW)81Ry#~vNV!tSknFLZ^TPMFcWP50`;h8_wXJz~b@?Ruxh4n0Op zn6cVd_dKS;em|W%^ca@o$b2%>)&6`x471h40lbe9M@*Q}yhHo8aG(=*2Wl@Cj&#E6 zAni41vBv?ccWST35fhq&bzY&x4hI|y6J5WH?}2S$-SPR@V08%hu*Dt&W^CTA{Q)B; z><`s>M01$B$AAgVdvxAmj{zgv!+EE0paVAV19}XY3Nt-f9m(%oj*j_=<^%i=bU0#0bCmWhRv%Q?IAB$)y5FNm`w`6tjF_=HM(43G)8?Z(ZwoCQ zFrztE`;#?o3wwG%haSyby;EU}9wR2~kJG+GkLF{V*Vtf(uF%r~Gu9v1J1trqaGcJM z*WQ2*17@r~p}mCF34Bj%3Ol;T0UZXkC+eM0*bbU^*rP{tlFlb zjMY3o4;$=pDD<>Bjo*u1VNZwUI5JO#nKqx{cVNJXBbw8-S7VP3JyvJ%JF&+B17@s0 ztNj*7tj^@~ut$&MbpAQ+qs0LOMjSDtak}3Z20CK>dF?f5vBLo!dQ4bU^C$OxSqM zEe`0GV_+V!y@1cfgcHJdd)i|Il zjC8{4hulMl39HL=K48L(=5n1|^o4=0e#GZsS2)mt4hW6IMUb`D8-p`{1(7%*aWmELKv!yX3=%h6n|`;!&jqAeU~ zhY=H+pYpq~MT-t2=ECY{{2px4Vu!vk&=E75$nV3tu%TPD*kLX-*YF-%>1{q z#ST3t%xFgKPu6t6>bJayUEx3{Y;M(l{X5y?fDx)hgi9s`br_BP!c3LRbDuJg&7 zZm`8T^2lNdlqwHm3aqSbQlZkf3jaV(iy8q*eh)54tpFh;D`y$ zzjzOasUOw6E$r#Zfwqrnzrz6|CN%%n-hkEP>dA(-*kMGoRQr<^J=xL$M@*Qpc|z|@ zwzMk@bSzACM)RcJtFgfrdvq8vVMg;GejheyvB#m%(-9M9tpCgJE$rw49R?f=6W#nz z?^)~%o2PW%7CL%zq^qa3U*mwT(9;1UW^BwqpZWWVBaUcR(7eV5E%ulTs}*%`vZgH# z*sjFq7LIf(j4NwDW4DTW($O9Rj+oItTlYpxh4rdBFSX2FVWQ1yx>sS19h%j3?$8&y z=jhzyhzaX8bZ)W39zBkj(5|U_0W+HCYF=Yk80j?4*V2Ap=;%pLk62Z@*P_J^dmIWK z9WZ0Jw%+rF_4D`~>~TPk0Y@~?*S(?8(-B8(|BuhXzR=SV>vgo>phbrsBX;X*zb_nU zUuf3Teuo}MjO*)sGSL~$20E|NV#HL~Zm9i9M|&JGq1j0L4O(>QF;C|&(0+$KdJNdU zkUcanQcu?OWJ9-wmhN%DfT^(BnBRjI9R{px_HZb4bSNC@gk}@oL5p2sPuH7jKVe3@ zndV*LKzkgq-JE@N*t}SCivvbXh0ROY#~xi_phv8?(7hHr>~X+=^$hKo2Ii%Sxp}GX zRfR3>F%@RoY{`A}7z;-_VfQlK^B6E<`*NN4I21bCqkV<;2Xq(;Bb~6_O7|=VG_TaW z#U4E-tQ$TT9gbMPO6N^sOFQ(-F)=q=^Eqfw%(K8Q) znKs+&euEtj=rCZ!R9NkxcRC!w)Gxo37`wo3!phrxFeh=Lb%W-60@2Ndo=;??fHnZ5r9s^c;agG7a8#J#9Yr4UJ z8O`3@E3D}jM>KELe)A?7aK!4(nzuM$z!6iS-ADKO!az@s^kk+htNRWEHgDnkV27b_ zq!VT|Z`J(@8?@LLMmiN%Z{vI5fF8TI>%2#^uew2p5l75e?Z@}T8as683j;mb?yq+O zCN#4(Z?P-vX@?P0p*cYB^yo2SDm3rVepA@eU13jq3^*1hda^oDpOdjUi0_FB%{%!% z*kO+WHUc?d2@tRlYF;DYP z=)A$U(9#_`3}{Z!{mF(dZJ9evXiwDr9!E5T=Jj%HnLG3tru~!nUD%>640ObV)hBg- zvZ2k%norhrixzwI7%*bS`cwQ)?65Bk^kkwlcAwTeJvtn*K84R&jsx>j&%8QS_a{4g zGSRuPnx}g$c7>i!n6W)g_c|QVe1L(AC$pKiSf@(9s?Pn)A4iO<|%ln)BJm7HwfqPewWyR$lMb*x-O3 zGgcR9zd>6#&;h#(wLhT4P#Edz>-;`U*nC6tSXg~i^B%+07im5gCOUkJ_b_9%K=T$Y z_Bf!!fDtpc-_|=8J&u?#V|_9E*yD)RcXU44(334apu>pf65Ss$qxr7plMU@KVf{Vr zHHDe3zR&l=7A+2ijt*#o?zh-sz>M_|v^Uw%Eylv;Qoc8K*rUVxhrEjoMjSC0n#**r zD|B?ijP>Pw4z`6IJ)m2TfqBG?_D6gl3{zjB`B3QTfU&S%sCx}|*ki`($Na9shMsiv zh}BQHhaL7Kuk%SucQ_P!+T5Uf9S((_jyO(zqwXbaZ{i)yXclSSFwWTNwOtbU`<$ykki zKWqyv-Qj>EW~_e8_s0$g3}|lQ^Dtp^EBDc2j{{b}<36_7VUGiP3>XVXy81ouVuua` zM$CovZQMs&=;%pL2TXqsEKGE1vsmw1>~Jhhw7G+Q?6AjBnCOh=PTj9C zV4CK4>Ab-K9gc;~-P-T4#{pwup7!q1y#e#ReC{8l!zwYy7CZD9Fk-^`KHe)#bpJ=4 zSAXJju*Zz%&zxiR7j=z2M$A~<&-;az?w6xu9x)d-59r+%2XyE$qFti>9zBj2{>poQ zlO1L>f7iT1iypfNb?z|Yh{HoV9}5#*J*@KvEp~-H?J!_OoB5o=>Yu!iE%rEI^$2^| zqc6;K^)Kz$*kQ!}QQmt@`oc($*#294Bj&>9ah+Rq7_nN)IrcbU#_kF24LBB7Px20S zg*_cGqxp~SO;&V`9mc|uPFVd{@3d%fz=ZC9+N++DJ$ek-Jgsw!1J>q|Xa4@+7A-pT zIAXto_5+TX(X6QR25n)a6J|6k>0X5`4(JQb%G$57#SVK+n9;7HdmWl*sax#PVM6n4 z?OE(`Kvx*(hzaegyn`Kj3}{x)f#-i z!j`t!VZdrl-hHm@3mu&btF^dS=;?@6r9Fou_G@eIF{67Pb4*x2U-KRZOj!M&&LFCLkPH48$ev2LY!bqD}^7+`JD-3kT zs^N1AEgcIpZC|B(p|IXs^A63c)iv6ww_#q`(;g#^uVH`NWp#AM?zK7(n9$7B+!qFV z(r%}{gw5-?ha+Zex7YcI3C#|g*EmkSqvj2ch5b%Cci8Q$9ts1Uv2WSKupG@U+H0}L zfX%Kt@36;+b9_>2OJACJ=wlR z?{w%f7FKW7UXK~g+cdX@o{l(T!i@FXxsL+|j5uP(c3<5a3o~6>@2CA1I}F(Guk%nC z>4fcU?G5NKV4nH_?bYv)76 zf6~$8a;!Su$FVTc^&#wGTR6}W^VILwy^PJFYKJ4%hiTqokA9lJhkF=tM02>#CoMgo z!x6jpYCoWvqps0o_dd;g9I!eFH2d9mVHhiw*-u zOxS!-_a+BAU_{gNJ_a-&(tNU|?KJZce^~brt9Q$d0y!Jc>j8lI?=Nao0 z)DA;ormGXRH(ApbJ&u^L9dvKf(hfc5!tNx#7skSoPFQ_X`yJLNs~hxFe@b(U4g;D` z>pY@4MI8#qQ#CJb=V{*KfF2{d)3oO?V*ZTg&FRZ(X?up|;Y?{hCtK{XbDH;`m(>^8 zLysBb7j-^j|0T7@fc06LTXg6#VfAJ01spM9{}r7N7%*ZpU*{uctj^ZF!5*t~G_SG6 zfVt3qRr@_A9M9Fh9B7XbGY%K( zoyqDO>JEGKIAZvw_A>StsUwb<3hlSF*W-W@M|2Cc7jXEt+F`^IGun%_H=xIW<~urX z&|$>-5}jLg7%)!f-_>6GJz0HU)`bHdaR}`HKnCnCRX0DB9S#^VqrFUf!*U#%+soN2 z40J;OBkkEMq(`$*-D1QM6E;8AUcl-n>bkJGQuESwnqQ@Jiyjl2t95REDmxr7Vf!=Q zDfIMYpfk2n_a-B4uF<^04tpHXVMcSU?)6w*$2%A>Vtc*L>l%e^5JYle$M=80d)hKJ7X#S$T zfaZR6RcPtbj=9J9fbM5(mhe3ayT7uB_HXKd_V4Nr1CCffsPjonn}>L}(EUU6=3!|I z13hA$*++}@KQ(U)J9|sK)7Vl%ggtpSTUyl9SoMZbu_2fXC=QGC&uBRy$0(0OUie86r)?F|?(V!aWcgB?0-UZC@g z=7nm9=0)lnTdX$Lyu&bct@(&%6Lp0(TJ+QTrrNXEZ>IJbF-`N$b>6+0y~0Ghm*{-b z(-GS(ILClvVLL;6J$lSoy;SG* z^(!@R(c*v!t44bb4%og*bBi6Ctu?Q(!4Wg|uhw3~JoPr3kC+SF*XZ2gh<01e1De;e zhY_2ZnztA+6=vFOr~S!>?s33`-Rrd9qr(x~?R8%7AUhl|U`D&6_C`$D?WB3ec4yuz zO!Q>cYOlc&{VtkUyUKcUH{QiqIPR|Vgc*m|Yd&JadJoNe%xLyxj@>NvfO+b@co*#( z)B(-j>WuXpc?SbVtlp$^hXLz1Ywj_i*+=sp9adIz^A;I#ocgVr=fdi3nzwIX){gEm z;)rQF-&glWOoe7Yoe$XWPh-Gtw&os3vAc0}5caVz9B7Xj&AWB4LXQ#6p}dPdW^4}Ad5;Ohdo)j2AI=_D?^V|rF{7KK z^GQz!95G{jpY|sc-M^oAFrYnB^MvLD>b`KGM>I!iufaU^2Q?r1Wes#h^C6vA*kQno z=ELk^Q`pkuwD%G1TO6=GM)OJgQTA}ejMcF^ud$k|c9_r{$2;ggrcP)+u5PeLb3FI3 zN003%bnY->MstGB2lN=QIZ@{w4uzTaCuz@oQnol0Iyy{yCu`rK$AIQjI=mdFr)dN=H~md!Wvu5g;mgg z{R7#eE$r!tBf3j<&;3w#m(dst&E@P}AtRcF>K2Eof2_I19_ycI-drs^bZCC6d4&U} zY5p^v*Vti?Beqd{1BSwpuCCQyd!6hGBWV(Hahm^1dlB1TtE=D0fDsdB>__blzm*Q{E$YdR_876cReKGNXnv=8 zUD(ha#@}l%VZ2S9vAUgiFkr%FvCdnJXztLw!WuI+ck0}t!+`Z&I(KO9=5w&a9tX^Y z_FlfnA7r2KJ{i#dQQiGn#=ppf8LRs>x7cBi33FlhfbIp%OVsUOc@KLW|E75^{k!Jo zK{*zhhcs_6{zF|qECa^EkU3wD=ASxmv3`ViC;z4H&_1djuz5^9InZgE|6At~tH;^L z5!LrXx{&CS?j0NZQ($Vn9x0~{n|YG%wI3BR*((0SgojeixwmLm2^I0SXrG4 z%__{XeirYd#Q_6$&(>bOnslqnfc10KJq~Er(7eG8du-R_Jsh!nuI6Sf8L_U^9on_k zoDr{0n|cGwp>+GE7}<+|UY z-%1^^d8N8XhxMzNW4pE5p)agnt@8#4blYe?yhf&NWybVcbv;uCY_?N(=x{{yI-OVO z(QdDKk8TIG*-6%Dc2*B)TD8MC^)8xw%v0~G`H1yyyoY{w?!8_{G<&FPY_Z>yeN3~| zZZG!VAT360-l+LxpxZaGkLJz1gK_G8G*8%Bb;N`jtGDRfVMg;d%^URCyqBUC7_fae=jbO7)!ZH? z+xM_{xUAkQ9hy1nzA(@c!~57fLJozQHt*+sG)Jl%>@lM`O6L|m4j<%m&|%YS-eFvh z)rYiKeVBL9V{lZR<~b~BX(!21CCgqqq)Tn zGge>a96RhWo~v{BHJQ+yr|xmY;5CofU!We)qrH$hW;EZ>yv7#u)Zf&3kLDtE#{OIC zc7Yr)V*PE+BbtlV_B*oqE{z_|_cX7*FEiSpj_7}&ZZDNRdTf8Fd51lEtS-}ejSWT| zvAvwn`4R73AqUKb?LwW~AIpftPt*ykE7cwLn9y9M^A-a(SMx5KpQ;;dalo)B4d9CYIC3TX#S{nh2~G1w|~L=<%rb- z>VW+cb;j^lb;OM6@0wQ+$`PA~)D8m<|6u=N88Bw=`mvR_>eXx30C^lPf)bLEK5TI^w{*u!RRwa0+z`I_7RlLJ=ksCyjHO|Gl+ zgw=ZLfEnBMHIJClY@oTvgmFXmad?5cdZDbb#SVM)FVbGKvCL>TQMZ^dW4)=)Jw~*f zX>K-`H70Ccta(5A5_Lj1Lme<;`%=x#ma=`h%vimG`BpMw)u=mkuU01V}XL61i!|OE9=(ksQJ22l-Hap246Sg~RK498K-R&wJCLDItyxCp0*t}jnV!bE# zF`}Kt9Q|JEfc6dQc5m5Zextg1lN=`BtWJeyAI)vyKu;R0y$ao1)$QBl5Lvxjro!=1&JU9vW^CT0dBEy$ z`n}T3kqx%kqeGA8ecJEPVf}vIIdWMYov{Ca&OHuCsS}zHa*lP+duTDC`;gAfhh>W$ z4j8dMT6+#hG#}BtMsp1B9V;z1$1$J$m^x$iakaqI9s2!TGsx78->GNd7gzkLKy)+ldgzbfDk74SsYd&K2 zO?8Jo+KV(Fuv(yQzb(^uWc5Aiu>HQe!zQTfA27%IQgw5g957z4uC9$^p$y>KX&uMVdPtF=58;X6@Nu;;&`(8yPX7{jKKX zEwZ^)4%q&V^TOu$nmaVNscUR*SCXR(Y(4>j(?Cb zNpqjfX#S+Oh1H+gzhAcK&_AI0xJ1UkO8Yly9+VY!IO6aS`~Ud=Ha)C){O7VZkEmPh zFbzcGHs}iDbiRT10>%y1b|cxL!}&zY4%20nHxj1_z9jvpC;Nwm6`BgXR(2z12NB44BZrS$p+9azJO*0h_nd=-;L` z`%1T;^cb+(pL=L#t4FL3P)}OA!yX-Gv5dyw=PFk*fudk0I4p;M>Bgq$%d{`zlN2{BUNRRz7?0;0Yn9v=|K8~2No~!fzI2kcx z{V~la`%kF56QslTL>kSYZcmmG!>4HUpH_FLN;gjiOgNmTxyOw0Gu%6abF`mTM~9!6 z^%rG>9oAoB5A9j%?#pt(fZbO#518hw{n^aXp2PjGGDm-|+I)?3bl9AydBWj*wezyR zK!&fghxIqq7VB@ahxQ_MhXJc^X&x8IjPcv*>S9@=#fa^9bly!~LKpUQ!uq@HO@3e9 zV;$5M1Nt9m?k|%@xeCS;#piG(Xn7yHX}>u2Q$yU#%W6VtbwD_Il}W zkRzHKImdXDI-^;{IaWVc*O<`%LUVVsjA(wzK05Shf2H#t&9Bw=H_K{9^`xUS`rqn2 zVS9_Zx>aWEf2R(ZG5uci{x)fD$2(*~d#5^}yG!jc-OYZXxkvLFGp2hrAO0XmG=F3t zt3RtVHV>#BHcQkN1IDTUrt|9Wvc?82_Bi19Aou9Bv1+SD>{BKytQ+gw&J zkqvg}u-!uE0Tad$r#Y_Ue8|=4f|fjv2e1HTT%>!rrdZ>?ZB*%Q{T`dd=%S zJBs7gEbF@`CaUH(qVguI=@@ChsuZ}Cae!*4+FaQaIdgAT=Nml9JR#`du)#2{Udqr z1F}C#x(~{smmV{QqcyLOkqI-Jk8%%l;V@U{BPMK))4V-iMyx-fcG#W3eH>0y+mmI+ z_$l@U7@0gzXucd+a`^9v%B%;QWisu{}#Y zVE<*cJDd4A?0*%{mHpRbMth#xV|~7Qz|pJih0HO3LtTGUdh9P^51VhPs|C`1M>dzp z78CmKX&$f&>d8Q-!r_P78!=y|_Ls|HA&uRS)e+5=>h@~pn6Ueq<|eXtjSLvCRS(z6 zg#C>)HaDpqc8k>Q&*kt7S=}thU&{VhGT``YwH;;mTj_6=sj&W?=JoGoj}Fakns5shoC+`25`+t$m1JYu%gvR!-G$tG# z)VzL3nt#X!yNA_1M)XBw&p&jZPaF4SO->qupEz;<|CZrh{G|OdmN8dXAE=I_G7X` zkNJ4!pOC4rJwfw+@03z&afnu}%s z9lS(lwBJ)#LAK~HU~{R?!w;qTk!;Xk!8uk7>B%3f+n>ncN;zI7tDi~}WqploutR&j z=98X|Sl^)Y_C{IVBu5+;sq3H1<`=TNSq2<`t4_Db;Wk;_F5R8dV<_}@>%6&Fnk0w& zr2C8P?#BmYM)Oy7`!~+9eMsHo_^`Ul?4f&9J!12ix_(@`r7}Gs+y5+UM_2#Te0*BE z6`pwJzu*0eGGf1yI%8OwUPanxN&jpav0hyraCnZoUz7P-(p9ovTbk$57@p63UD=|W zdPB`KRxeaXG#jgXtZTK~guP8=Lch7Xeu?xrY{7g6a~xl$9=77Vk?Ga!Z6oux(!Ex; zGo{^5M$E5Mo1J9XSthJ@QQO^_quoQ@>?ub~d#UTanZH@)eWbUtdW#JE;(oH;Uv{%) ze*ow2pbwPsVA*uCJ5-v(oaBlIrf}1Uy=(tHlGTb0XY;RPjMKYuLg?h552aGp!4~O5VC*7!yn9Jj#E{I|M&oV}&eKOsl#pXB_1vU*wu^eaC3%ztmRE90uttbuFF@LbufEk{gPJx}wz zj;z;{?FP7^9I$<%x_S|FtTt9#Y&TQ)*lezjTgvnbS#2d7?9pM|TIbz1(xZEgy5Ckh zv@_KMHruJ~_A+3^b_dPl+Z;%_4Mi$vAACvBJnQ{D|+B_xG)3RORKhONV9Qe zuOi!L%eWfntIK8$*{vnh+A^$%FOdF)vaV&?M8-{}*-Q@TUaa;nVZH^urR=wo`IWMH zm2_Lnv<>H2zg9hNFPk0N+e!MJrE6usiyYAHt_~PquWt8{9>=}a)f+j-h$A*{(s|s6 z{kO>OZS?*!qd7o594MQ2%7AT0A1d8@eF51*0s8Pa?f&y<~${THOe_DgDi7V|ID^JRB7^YfUWFKaK;MYw?T zZ%cErjF-szdoo?d{Bq`3$c*Mnb#;{-u9kh23Hxi*{`zGdZlJMQq&B~h_GVfCQVze8 z4)gET`8L_!zO4P!_b|U-rU#_`o2>pWJM7Uv$o|7JJhH6yzq$9g^iOb(@9?W*c(HJR3w^>bx|_1fz0`7*8}Gx~Ma0mt>!ZX@PcZK7^BV~+I}>Wp!Q z+PzeETVW%Itz~|-9JiBZd)e+FtDU6ByfgEbJ+!;hdrFJ$jm+P|9GkbP$G6L{uk;7V zgzbUq<{;)64pGN<%W#-<@4+LbIYxHJ%81Wt<@dXVi&?4K+Xnp4!xsmxE4-RUx; zKU3{Lk7vvA9NC^L2TT{L>#xgjk<1HZbFu7xAk9KqVgFM)a(0+K z9c9FDi#psYyW3@b2lG3nzl-@jvbm4>AEo`1j1S26pVDFTh&oR`sy2_Yk3CjTvH!HJ zR`}mDf1C!itErncrNyuo^L1pmt{kynPu*@P)5ciKdQ%xTW4<}QnE4D@zgpUDW!_#^ zJIl6}W>-0&N3$DqY<5@M*GspDG_z#bOAdR}SnZ>ZZScYr>`##XMEoT4lVwJKDt(%?r?ZFMm(=w+?BQ^} zy1GC%7s_t(Vzs+O_Ls{1Bl>FDT_f9TnO`r5oAFmNqyM!!j?(`QZ&MmOQfZ%%&3|QjN)AuUY@T}NkFR<~S*;}9 zYI4N5hPtn$Syv{k)>C(w(5|m}yAf_Kdvsf<{S2A5ljOkG3hsow}*&QJx+9TEO7->H) z+mmJWDf%>NPG|l(na`5x++`iU#yOhv)g1 zx>copj;z;^EmmvN&y{9vnb(nSJzQUo8*si6zEJj?$+WqwUdnt+dK+nXkat-hSn08zs~%63-AOWj zQkqZEr^xnnIZl2~ZNDU&vuJc*QM+?F_p-e}ju*=Qo19-HyNji{jC0IasfTN2xItDo z%JfUw{6>zq$o^K@-6h@KGT$rBUu0S$tB3JlvPQF1J!18Qy8EvT=$=xqx}w>|{J;P3 zxa3*txvR;UtIJu>kqg(539IL-7vuc3)blIZJWsCre7R&D*{v(9^`zfGF2beQY^eFd zjpULS$~l|MgzLXVJsRDULg}4Z3zg+XKk>=GhZ6m{L?u7inWeUGkV~-ITkUZ18`b8`vU#f<-zNRr zWxKDe_G3OG3ty9 zj#aOE9CMueadpHw$E&MB&NxZ>Q)F|hTzs1BKO@J}<&raH_a!;t;sl`F&Ymia(U*GR|?)k2t?drmN+GYh`<#oOivfZr zkYPtTb0^vFEa&Ya9ge#)-&0nz@V#>ayVGJcgfgs{%$$!td5ccF2;Eu(Y!uJW}N>~_4sjV zPLT5kxd3OMM1NAwI!!jG%Qzo_|~v*i5wGU5EQ)r)Y>SJgAl zllDT{ep5CJWPkCp&it-=!S`giOfI}!&im1_I(q((HDCWqx%j7YX_W37yiSIj<>Ft< zc&p66$GhaJcgv-Jl=c0x#W{afFaEon{g7<_AqVUqRxikM;Xh^k7kiK4f8|`W(ldYj zbSub(E6a>?S5uqkNMCV|bJkWbUPpH8%0=t3_d@AkBo}Th*RN&VR5qK-nJ<=0UMWq( z`8KlNUe4WFrdGCZlC$3`2NroxtGlaa@IHG>>tSWe<;o6oG+9MenMX< zm*AYM)N|4NRJ|(Bx>h|Oha1$hZj^Iy_Ak_PZ_zhA3Wq{Dg7QqNvXF0ACZjtuKD--P*QxRqS`N;zvAxnx_}zFy9l zCF9<5G0w5-xmfMXKF&HoUA>Dry2I29-pBcoa#k<2lF$<($vS#>w_8a_-r3(f2sNR1R0je6?Kl8oA^;X>XN_ldSKTvmTOj z{~_0Zm`0P;=25v|aoCvoCUWNHa`DU9dxh+_mUDNKwxw~{ zQ@v;}+3zh^-AB$tXVo+Jm18I8%w>L@ocnR{Q2`jkW4quGZLG+SF`#95y%gRCDAx=F;=c-WQl{dziH@G~4td_cmt_ zAYX3|#ie&x{``J(+5=|JgJ$l-X16isgmGrWXUu97OvyFtOfhG_Y3f{a+7fgAr)IaW z%o0DFr8h5;+Hc#k=E&X5=DV8>tD3ph%}%w<`TLr)>zad)HW!_2jy%K6X=_&NY?kO^ zE*os>P3F>}=E&h@=?BdbFPUTCFgwjQm%d}>d}mJh$!z|o+37E{VUf*KpKsA-W~CBl z^DWI1hnsDio4s3^ole11&5@UxBW}c7%>l#BHV>L3Up0rmW-gm;_I}5#w8$*|wb}ez z^YcHVu-M%nKV(*_V4=BE6>iu;yb8JPk)?Vh&y5{W0=F%4CfY#=; zHfHma&9(s+WZ{#Da%W9%{DKZ(-tuI8TBvBkzbnKmYJQF zn*+AkD)qVMZ)uh&WmenCY`&XWsiv88h&i;Gxu~sKdZgKAqFLv8v-#U*&Ik0Dn7zL+ zEB$Ha)+}ki=fliehnsEBF-M+n=3Gd>pZWO!v*F!l+xyI+@0%mPFh~4pmfv*i)N^u5 zo0WDphaPGUINV&?#4O+3?AF<=)ZJ{TW{G}gn;Xq;BTN}%)+tlU_KOY6S)N_qY<{4b zi?a{1oKxQ%dz4w}SZrn1>0&ng!W>w)bm}?HbIc=;F{d`B-o%`Jta)jBvr-rH)P82e zp=Q~UX79($-6ojba?KJ8%#W9we{Qo)>iL~^Hb?GiHr>n2ZEl{|#@zc<^VBoU4wsu3 zjyCHoG@C9mmwjZ8txzWQoImTCgBzQrTbs48VPDIOZe;EjbLt3l#0+!qIcD>@X8(%Y zrtWW8$^3I4^YdC}?PJZk$D6lYVKyIX4#u$~EDxP+PAE|}b??%v%w@NmI@Fwidavby zc*|_db3Zl@-DA7dJ-w@$UpFzEpJ=?%(9P|yMyOiL&`rJn@_)Q-u0KcboYv>e(5o|-;Sv~ zriXb~A9ME8=7leqoxU)~zPVFszG;b_?Vi)k5&g_D2kernx9wtUsN ztM|-4znXhj+|%k`)=Ig{+IQ=}!KX*&1HOE^1aGK?ArH)KJ|L8qZ>hv0> zHZm`7YM#^~rF1yo{Ol&P*%0dw`^0jE9gj-A*HOEfE%rALZk1A=Kg0awCiB4|*1zCG zbEl2DeN-1*CF{%2J=_zICY_r1>v*@?h?^~u}>T{LdC8hp6$@1y% zS{`4gQL2BS0pJ?BvqBtRa0@L#s2B9=xkG| zo|H(na-{Z_QhRwZo7?oC>WY5RqD4~|l;FN%MZWCPBsHRS>}H!U!!0XnpG4iC7Wtp|Tk%T8r1U@kv*Pp3srSFHxbkxs37@-Gs$EQ~NtwMHr~diLHOw#1 zwoNp@plGUY|7f@UdmkgLv5x;^*jxB=llRuA{{McIfA6hMs$ER>W2|gW_znK=?`B1_ zhzz@9#b1xqyQJ)6$1LvC|DSK<-}@WMJ?jTykEuUIsn58&{ycVmxM^x7>&KtR)xVD~ zKRk9l+DBpj{b|2*?C(c?I-A#>Yp&`~%)kG9jab##I#*kRf37MuOMUuv`_I)n_XYV2 z`38Igz5(BWZ@@R;8}JSI27Cj)0pEacz&GF<@D2C|d;`7#-+*txH{cua4fqCp1HJ*@ zfN#Jz;2ZD__y&9fz5(BWZ@@R;8}JSI27Cj)0pEacz&GF<@D2C|d;`7#-+*txH{cua z4fqCp1HJ*@fN#Jz;2ZD__y&9fz5(BWZ@@R;8}JSI27Cj)0pEacz&GF<@D2C|d;`7# z-+*txH{cua4fqCp1HJ*@fN#Jz;2ZD__y&9fz5(BWZ@@R;8}JSI27Cj)0pEacz&GF< z@D2C|d;`7#-+*txH{cua4fqCp1HJ*@fN#Jz;2ZD__y&9fz5(BWZ@@R;8}JSI27Cj) z0pEacz&GF<@D2C|d;`7#-+*txH{cua4fqCp1HJ*@fN#Jz;2ZD__y&9fz5(BWZ@@R; z8}JSI27Cj)0pEacz&GF<@D2C|d;`7#-+*txH{cua4fqCp1HJ*@fN#Jz;2ZD__y&9f zz5(BWZ@@R;8}JSI27Cj)0pEacz&GF<@D2C|d;`7#-+*txH{cua4fqCp1HJ*@fN#Jz z;2ZD__y&9fz5(BWZ@@R;8}JSI27Cj)0pEacz&GF<@D2C|d;`7#-+*txH{cua4fqCp z1HJ*@fN#Jz;2ZD__y&9fz5(BWZ@@R;8}JSI27Cj)0pEacz&GF<@D2C|d;`7#-+*tx zH{cua4fqCp1HJ*@fN#Jz;2ZD__y&9fz5(BWZ@@R;8}JSI27Cj)0pEacz&GF<@D2C| zd;@D_1KnoUufO6!Z@+Aw`C&?(@u%g5zgn)cMS~S{T4@J!?w%>-l@^wpool(Ksh3=2 zdFmAF_n2vURCmkIZ+%4Sxw+e<)Z=Pc-ll@(1)EzwpsD5lOYHpeTdjU(cdPHZ`;n>l z>QU5eHPrg;YNWDUe3aFjTyO4M*XmtgwS0Gbv*Y7d|FrB;sr$-ZYfdS4bm~3%GW7nT zmf!f@=1aVi%6j*AHowaz$E42R^nuL{ot4T`s$|1d{hrAw<*(|NYj(DL&)`O>=k#jS zI5l_JxlK~;KHRL`H>J${t7)qK(Sa#*ck0l5Mb16FS&LL%#~#1Joa&#nOyyp+TBUM@ zr%zm=e1A(y9eu>fsrr=*Q_AhToRXS<<+amN^*1V>k+Q~vZC6<7hda+)p~#Hqb5g#8 z3vo&0C*&kQrTv-KP1gWr;a-;tMQ@O%2PKTyx)kIcpXM7^;5 znfq3kzi@xH{>pqoUB*3`{EfN1`a9=Vmw#~on)XkgySn|0=d3ON=J{*N$;orp6i;$d9Mw{&G0_vqU9;Cm?89uDI7mCm0dvxu4Tvzu{kMGH~?`eJALk{1gYv1GQ-a~!%?b^QA)*c%0J-GHg z}g}O9iQ8^-$&TP zne4^2z2tWf?b+kTY6m|5#_Cyo{td;m`8;dOjy!)|wG;2Rv3m}C*jVk%=iksgm(R7a zc^;p8L-Tw-*M@EvKIi&kSKfDhvm5Wdp?U$IZ+*2p@4vp-gZJH7ypYekw(QCC*EKKV zz1DYo@j2Evd-LAwsu%Nq>#Kcu|25?$Ja1j~Qr>S}^)lXXZFxD*U)x^6d#o*$=dW%1 z@*eAo{dk`>?Ug)tP1&F4<=3k?Usztvee1f{@SdyN0X%1Qdo9n&*6WyG(_YVWSC<31 zKfey*d|`P5_hsvi%;(j?oGUDE;=Uw@&@U`+=Dz%T3+J=-R_2qujecQ!JNM?*p`6RF zcW^$F!h~siANAn<T?lF8kax6Lc1UdFe>akBz4?ax}jw6r9XA^vmdT=871)LO_OMWr( zC32Ee=m)2gU&U!S9cSQ7oQ1FB87xx1-LMee!zT^AJShO zxrF>FeukgpceoUPz~3T&C;x@ZvB)I*@0VHpOG{IBbT^u_d;`HjyWjPsa|}5zoa7@G86kZ^Dr{ z3PqF{jwe5p-~{T=;&V7L@_F*41SeCE&87ZQ

ryr!p6OnH+qD9DJ3Wt<#uK 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 literal 0 HcmV?d00001 diff --git a/benchmark/.tune.jld b/benchmark/.tune.jld new file mode 100644 index 0000000000000000000000000000000000000000..420d168f9b7f93f0e13060984d773f0f8f02aa97 GIT binary patch literal 35274 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 literal 0 HcmV?d00001 diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl new file mode 100644 index 000000000..2babd4246 --- /dev/null +++ b/benchmark/benchmarks.jl @@ -0,0 +1,35 @@ +using PkgBenchmark +using LightGraphs + + +testdatadir = joinpath(dirname(@__FILE__), "..","test","testdata") +benchdatadir = joinpath(dirname(@__FILE__), "data") +paramsfile = joinpath(benchdatadir, "params.jld") + +println("testdatadir = $testdatadir") +println("paramsfile = $paramsfile") + +dg1fn = joinpath(testdatadir, "graph-5k-50k.jgz") + +DIGRAPHS = Dict{String, DiGraph}( + "complete100" => CompleteDiGraph(100), + "5000-50000" => LightGraphs.load(dg1fn)["graph-5000-50000"], + "path500" => PathDiGraph(500) +) + +GRAPHS = Dict{String, Graph}( + "complete100" => CompleteGraph(100), + "tutte" => smallgraph(:tutte), + "path500" => PathGraph(500), + "5000-49947" => Graph(DIGRAPHS["5000-50000"]) +) + + +@benchgroup "LightGraphsBenchmarks" begin + include("core.jl") + include("edges.jl") + include("centrality.jl") + include("connectivity.jl") + include("max-flow.jl") + include("traversals.jl") +end diff --git a/benchmark/centrality.jl b/benchmark/centrality.jl new file mode 100644 index 000000000..f52403704 --- /dev/null +++ b/benchmark/centrality.jl @@ -0,0 +1,26 @@ + +@benchgroup "centrality" begin + @benchgroup "graphs" begin + for (name, g) in GRAPHS + @bench "$(name): degree" LightGraphs.degree_centrality($g) + @bench "$(name): closeness" LightGraphs.closeness_centrality($g) + if nv(g) < 1000 + @bench "$(name): betweenness" LightGraphs.betweenness_centrality($g) + @bench "$(name): katz" LightGraphs.katz_centrality($g) + end + end + end #graphs + @benchgroup "digraphs" begin + for (name, g) in DIGRAPHS + @bench "$(name): degree" LightGraphs.degree_centrality($g) + @bench "$(name): closeness" LightGraphs.closeness_centrality($g) + if nv(g) < 1000 + @bench "$(name): betweenness" LightGraphs.betweenness_centrality($g) + @bench "$(name): katz" LightGraphs.katz_centrality($g) + end + if nv(g) < 500 + @bench "$(name): pagerank" LightGraphs.pagerank($g) + end + end + end # digraphs +end # centrality diff --git a/benchmark/connectivity.jl b/benchmark/connectivity.jl new file mode 100644 index 000000000..c34730b00 --- /dev/null +++ b/benchmark/connectivity.jl @@ -0,0 +1,13 @@ +@benchgroup "connectivity" begin + @benchgroup "digraphs" begin + for (name, g) in DIGRAPHS + @bench "$(name): connected_components" LightGraphs.connected_components($g) + @bench "$(name): strongly_connected_components" LightGraphs.strongly_connected_components($g) + end + end # digraphs + @benchgroup "graphs" begin + for (name, g) in GRAPHS + @bench "$(name): connected_components" LightGraphs.connected_components($g) + end + end # graphs +end # connectivity diff --git a/benchmarks/core.jl b/benchmark/core.jl similarity index 54% rename from benchmarks/core.jl rename to benchmark/core.jl index 24fa36850..c05646d04 100644 --- a/benchmarks/core.jl +++ b/benchmark/core.jl @@ -1,6 +1,3 @@ -bg = BenchmarkGroup() -SUITE["core"] = bg - function bench_iteredges(g::AbstractGraph) i = 0 for e in edges(g) @@ -29,13 +26,20 @@ EDGEFNS = [ bench_has_edge ] -for fun in EDGEFNS - for (name, g) in GRAPHS - bg["edges","$fun","graph","$name"] = @benchmarkable $fun($g) - end - - for (name, g) in DIGRAPHS - bg["edges","$fun","digraph","$name"] = @benchmarkable $fun($g) - end +@benchgroup "edges" begin -end + for fun in EDGEFNS + @benchgroup "$fun" begin + @benchgroup "graph" begin + for (name, g) in GRAPHS + @bench "$name" $fun($g) + end + end + @benchgroup "digraph" begin + for (name, g) in DIGRAPHS + @bench "$name" $fun($g) + end + end # digraph + end # fun + end +end # edges diff --git a/benchmark/edges.jl b/benchmark/edges.jl new file mode 100644 index 000000000..1bb5bf892 --- /dev/null +++ b/benchmark/edges.jl @@ -0,0 +1,39 @@ +import Base: convert +typealias P Pair{Int, Int} + +convert(::Type{Tuple}, e::Pair) = (e.first, e.second) + +function fille(n) + t = Array{LightGraphs.Edge,1}(n) + for i in 1:n + t[i] = LightGraphs.Edge(i, i+1) + end + return t +end + +function fillp(n) + t = Array{P,1}(n) + for i in 1:n + t[i] = P(i, i+1) + end + return t +end + +function tsum(t) + x = 0 + for i in 1:length(t) + u,v = Tuple(t[i]) + x += u + x += v + end + return x +end + +n = 10000 +@benchgroup "edges" begin + @bench "$(n): fille" fille($n) + @bench "$(n): fillp" fillp($n) + a, b = fille(n), fillp(n) + @bench "$(n): tsume" tsum($a) + @bench "$(n): tsump" tsum($b) +end # edges diff --git a/benchmark/max-flow.jl b/benchmark/max-flow.jl new file mode 100644 index 000000000..bfb5fee58 --- /dev/null +++ b/benchmark/max-flow.jl @@ -0,0 +1,10 @@ +@benchgroup "max-flow" begin + for n in 9:5:29 + srand(1) + p = 8.0 / n + A = sprand(n,n,p) + g = DiGraph(A) + cap = round(A*100) + @bench "n = $n" LightGraphs.maximum_flow($g, 1, $n, $cap) + end +end # max-flow diff --git a/benchmark/traversals.jl b/benchmark/traversals.jl new file mode 100644 index 000000000..cfd510711 --- /dev/null +++ b/benchmark/traversals.jl @@ -0,0 +1,14 @@ +@benchgroup "traversals" begin + @benchgroup "graphs" begin + for (name, g) in GRAPHS + @bench "$(name): bfs_tree" LightGraphs.bfs_tree($g, 1) + @bench "$(name): dfs_tree" LightGraphs.dfs_tree($g, 1) + end + end # graphs + @benchgroup "digraphs" begin + for (name, g) in DIGRAPHS + @bench "$(name): bfs_tree" LightGraphs.bfs_tree($g, 1) + @bench "$(name): dfs_tree" LightGraphs.dfs_tree($g, 1) + end + end # digraphs +end # traversals diff --git a/benchmarks/LightGraphsBenchmarks.jl b/benchmarks/LightGraphsBenchmarks.jl deleted file mode 100644 index 339c6c943..000000000 --- a/benchmarks/LightGraphsBenchmarks.jl +++ /dev/null @@ -1,100 +0,0 @@ -module LightGraphsBenchmarks - -using BenchmarkTools -using JLD -using Compat -using LightGraphs - -import Compat: UTF8String, view - -BenchmarkTools.DEFAULT_PARAMETERS.seconds = 1.0 -BenchmarkTools.DEFAULT_PARAMETERS.samples = 10000 -BenchmarkTools.DEFAULT_PARAMETERS.time_tolerance = 0.15 -BenchmarkTools.DEFAULT_PARAMETERS.memory_tolerance = 0.01 - -const PARAMS_PATH = joinpath(dirname(@__FILE__), "params.jld") -const SUITE = BenchmarkGroup() - -testdatadir = joinpath(dirname(@__FILE__), "..","test","testdata") -benchdatadir = joinpath(dirname(@__FILE__), "data") -paramsfile = joinpath(benchdatadir, "params.jld") - -println("testdatadir = $testdatadir") -println("paramsfile = $paramsfile") - -dg1fn = joinpath(testdatadir, "graph-5k-50k.jgz") - -DIGRAPHS = Dict{String, DiGraph}( - "complete100" => CompleteDiGraph(100), - "5000-50000" => LightGraphs.load(dg1fn)["graph-5000-50000"], - "path500" => PathDiGraph(500) -) - -GRAPHS = Dict{String, Graph}( - "complete100" => CompleteGraph(100), - "tutte" => smallgraph(:tutte), - "path500" => PathGraph(500), - "5000-49947" => Graph(DIGRAPHS["5000-50000"]) -) - -MODULES = [ - "core", - "edgetype", - # "centrality.jl", - "max-flow", - "connectivity", - "traversals" -] - -for m in MODULES - include("$(m).jl") -end - -function save_benchmarks(results, datadir=benchdatadir) - ts = string(now()) - for m in MODULES - d = joinpath(datadir, m) - isdir(d) || mkdir(d) - f = joinpath(d, "$(ts).jld") - JLD.save(f, m, results[m]) - end -end - -function _getlastresults(datadir=benchdatadir) - files = Vector{Tuple{String, String}}() - for m in MODULES - d = joinpath(datadir, m) - f = sort(readdir(d))[end] - push!(files, (m, joinpath(d,f))) - end - return files -end - -function load_benchmarks(files::Vector{Tuple{String, String}}=_getlastresults(), datadir=benchdatadir) - b = BenchmarkGroup() - for (m, f) in files - println("loading module $m from file $f") - b[m] = JLD.load(f, m) - end - return b -end - -function load_benchmarks(dts::String, datadir=benchdatadir) - fs = [joinpath(datadir, m, "$(dts).jld") for m in MODULES] - files = collect(zip(MODULES, fs)) - return load_benchmarks(files, datadir) -end - -function run_benchmarks() - if isfile(paramsfile) - loadparams!(SUITE, BenchmarkTools.load(paramsfile, "suite"), :evals, :samples) - end - run(SUITE, verbose=true) -end - -function tune_and_save!(b::BenchmarkGroup=SUITE) - tune!(b) - BenchmarkTools.save(paramsfile, "suite", params(b)) -end - -end diff --git a/benchmarks/attic/bfstree.jl b/benchmarks/attic/bfstree.jl deleted file mode 100644 index 7199d3fe6..000000000 --- a/benchmarks/attic/bfstree.jl +++ /dev/null @@ -1,69 +0,0 @@ -using LightGraphs -using MatrixDepot -function symmetrize(A) - println("Symmetrizing ") - tic() - if !issymmetric(A) - println(STDERR, "the matrix is not symmetric using A+A'") - A = A + A' - end - if isa(A, Base.LinAlg.Symmetric) - A.data.nzval = abs(A.data.nzval) - else - A.nzval = abs(A.nzval) - end - #= spA = abs(sparse(A)) =# - toc() - return A -end - -function loadmat(matname) - println("Reading MTX for $matname") - tic() - try - info = matrixdepot(matname, :get) - end - A = matrixdepot(matname, :read) - A = symmetrize(A) - g = Graph(A) - toc() - println(STDERR, "size(A) = $(size(A))") - return g -end - -function vec2tree(v::Vector{Int}) - nv = length(v) - I = Vector{Int}() - J = Vector{Int}() - for i in 1:nv - if v[i] > 0 - push!(I, i) - push!(J, v[i]) - end - end - #= @assert length(I) == length(J) =# - ncolumns = max(maximum(J), maximum(I)) - nentries = length(J) - n = ncolumns - return sparse(I,J, fill(1, nentries), n,n) -end - - -names = ["Newman/football" , "Newman/cond-mat-2003", "SNAP/amazon0302", "SNAP/roadNet-CA"] -for matname in names - g = loadmat(matname) - seed = 1 - println("bfs_tree original") - # woah bfs_tree allocates a lot of memory - @time tdg = LightGraphs.bfs_tree(g, seed) - println(tdg) - # preallocating the output tree reduces the number of allocations. - # much faster - println("bfs_tree!(vector)") - visitor = LightGraphs.TreeBFSVisitorVector(zeros(Int, nv(g))) - @time tvec = LightGraphs.bfs_tree!(visitor, g, seed) - println("converting to Sparse") - @time m = vec2tree(visitor.tree) - println("converting to DiGraph") - @time h = DiGraph(m) -end diff --git a/benchmarks/attic/nonbacktracking.jl b/benchmarks/attic/nonbacktracking.jl deleted file mode 100644 index dc8847648..000000000 --- a/benchmarks/attic/nonbacktracking.jl +++ /dev/null @@ -1,48 +0,0 @@ -using LightGraphs - -sizesnbm1 = Int64[@allocated non_backtracking_matrix(CycleGraph(2^i))for i in 4:10] -sizesnbm2 = Int64[@allocated Nonbacktracking(CycleGraph(2^i)) for i in 4:10] - - -println("Relative Sizes:\n $(float(sizesnbm1./sizesnbm2))") - -macro storetime(name, expression) - ex = quote - val = $expression; - timeinfo[$name] = @elapsed $expression; - val - end - return ex -end - -function bench(g) - nbt = @storetime :Construction_Nonbacktracking Nonbacktracking(g) - x = ones(Float64, size(nbt)[1]) - info("Cycle with $n vertices has nbt in R^$(size(nbt))") - - B, nmap = @storetime :Construction_Dense non_backtracking_matrix(g) - y = @storetime :Multiplication_Nonbacktracking nbt*x - z = @storetime :Multiplication_Dense B*x; - S = @storetime :Construction_DS sparse(B) - z = @storetime :Multiplication_Sparse z = S*x; - Sp = @storetime :Construction_Sparse sparse(nbt) - z = @storetime :Multiplication_Sparse Sp*x -end - -function report(timeinfo) - info("Times") - println("Function\t Constructors\t Multiplication") - println("Dense \t $(timeinfo[:Construction_Dense])\t $(timeinfo[:Multiplication_Dense])") - println("Sparse \t $(timeinfo[:Construction_Sparse])\t $(timeinfo[:Multiplication_Sparse])") - println("Implicit\t $(timeinfo[:Construction_Nonbacktracking])\t $(timeinfo[:Multiplication_Nonbacktracking])") - info("Implicit Multiplication is $(timeinfo[:Multiplication_Dense]/timeinfo[:Multiplication_Nonbacktracking]) faster than dense.") - info("Sparse Multiplication is $(timeinfo[:Multiplication_Nonbacktracking]/timeinfo[:Multiplication_Sparse]) faster than implicit.") - info("Direct Sparse Construction took $(timeinfo[:Construction_Sparse]) - Dense to Sparse took: $(timeinfo[:Construction_DS])") -end - -n = 2^13 -C = CycleGraph(n) -timeinfo = Dict{Symbol, Float64}() -bench(C) -report(timeinfo) diff --git a/benchmarks/centrality.jl b/benchmarks/centrality.jl deleted file mode 100644 index 2cddd99fe..000000000 --- a/benchmarks/centrality.jl +++ /dev/null @@ -1,23 +0,0 @@ -bg = BenchmarkGroup() -SUITE["centrality"] = bg - -for (name, g) in GRAPHS - bg["centrality","degree_centrality","graph","$name"] = @benchmarkable LightGraphs.degree_centrality($g) - bg["centrality","closeness_centrality","graph","$name"] = @benchmarkable LightGraphs.closeness_centrality($g) - if nv(g) < 1000 - bg["centrality","betweenness_centrality","graph","$name"] = @benchmarkable LightGraphs.betweenness_centrality($g) - bg["centrality","katz_centrality","graph","$name"] = @benchmarkable LightGraphs.katz_centrality($g) - end -end - -for (name, g) in DIGRAPHS - bg["centrality","degree_centrality","digraph","$name"] = @benchmarkable LightGraphs.degree_centrality($g) - bg["centrality","closeness_centrality","digraph","$name"] = @benchmarkable LightGraphs.closeness_centrality($g) - if nv(g) < 1000 - bg["centrality","betweenness_centrality","digraph","$name"] = @benchmarkable LightGraphs.betweenness_centrality($g) - bg["centrality","katz_centrality","digraph","$name"] = @benchmarkable LightGraphs.katz_centrality($g) - if nv(g) < 500 - bg["centrality","pagerank","digraph","$name"] = @benchmarkable LightGraphs.pagerank($g) - end - end -end diff --git a/benchmarks/connectivity.jl b/benchmarks/connectivity.jl deleted file mode 100644 index fd19e0805..000000000 --- a/benchmarks/connectivity.jl +++ /dev/null @@ -1,13 +0,0 @@ -bg = BenchmarkGroup() -SUITE["connectivity"] = bg - - -for (name, g) in DIGRAPHS - bg["connectivity","connected_components","digraph","$name"] = @benchmarkable LightGraphs.connected_components($g) - bg["connectivity","strongly_connected_components","digraph","$name"] = @benchmarkable LightGraphs.strongly_connected_components($g) - -end - -for (name, g) in GRAPHS - bg["connectivity","connected_components","graph","$name"] = @benchmarkable LightGraphs.connected_components($g) -end diff --git a/benchmarks/edgetype.jl b/benchmarks/edgetype.jl deleted file mode 100644 index bffbb4624..000000000 --- a/benchmarks/edgetype.jl +++ /dev/null @@ -1,46 +0,0 @@ -bg = BenchmarkGroup() -SUITE["edgetype"] = bg -import Base: convert -typealias P Pair{Int, Int} -immutable Edge - src::Int - dst::Int -end - -function fille(n) - t = Array{Edge,1}(n) - for i in 1:n - t[i] = Edge(i, i+1) - end - return t -end - -function fillp(n) - t = Array{P,1}(n) - for i in 1:n - t[i] = P(i, i+1) - end - return t -end - -function tsum(t) - x = 0 - for i in 1:length(t) - u,v = Tuple(t[i]) - x += u - x += v - end - return x -end - -src(e) = e.src -dst(e) = e.dst -convert(::Type{Tuple}, e::Edge) = (src(e), dst(e)) -convert(::Type{Tuple}, e::Pair) = (e.first, e.second) - -n = 10000 -bg["edgetype", "fille", "$n"] = @benchmarkable fille($n) -bg["edgetype", "fillp", "$n"] = @benchmarkable fillp($n) -a, b = fille(n), fillp(n) -bg["edgetype", "tsume", "$n"] = @benchmarkable tsum($a) -bg["edgetype", "tsump", "$n"] = @benchmarkable tsum($b) diff --git a/benchmarks/max-flow.jl b/benchmarks/max-flow.jl deleted file mode 100644 index d8743b6af..000000000 --- a/benchmarks/max-flow.jl +++ /dev/null @@ -1,11 +0,0 @@ -bg = BenchmarkGroup() -SUITE["max-flow"] = bg - -for n in 9:5:29 - srand(1) - p = 8.0 / n - A = sprand(n,n,p) - g = DiGraph(A) - cap = round(A*100) - bg["maximum_flow","digraph","$n"] = @benchmarkable LightGraphs.maximum_flow($g, 1, $n, $cap) -end diff --git a/benchmarks/traversals.jl b/benchmarks/traversals.jl deleted file mode 100644 index ea4c68998..000000000 --- a/benchmarks/traversals.jl +++ /dev/null @@ -1,12 +0,0 @@ -bg = BenchmarkGroup() -SUITE["traversals"] = bg - -for (name, g) in GRAPHS - bg["traversals","bfs_tree","graph","$name"] = @benchmarkable LightGraphs.bfs_tree($g, 1) - bg["traversals","dfs_tree","graph","$name"] = @benchmarkable LightGraphs.dfs_tree($g, 1) -end - -for (name, g) in DIGRAPHS - bg["traversals","bfs_tree","digraph","$name"] = @benchmarkable LightGraphs.bfs_tree($g, 1) - bg["traversals","dfs_tree","digraph","$name"] = @benchmarkable LightGraphs.dfs_tree($g, 1) -end From 748aa61076abc6fa7499946e0476a655d547924b Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Mon, 13 Mar 2017 08:33:47 -0700 Subject: [PATCH 06/56] f --- src/core.jl | 78 +++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/src/core.jl b/src/core.jl index b1175b80c..cb5924a21 100644 --- a/src/core.jl +++ b/src/core.jl @@ -9,10 +9,12 @@ _insert_and_dedup!(v::Vector{Int}, x::Int) = isempty(splice!(v, searchsorted(v,x abstract AbstractEdge """An abstract type representing a graph.""" -abstract AbstractGraph +abstract AbstractLightGraph +abstract AbstractGraph <: AbstractLightGraph +abstract AbstractDiGraph <: AbstractLightGraph """Return the type of a graph's edge""" -edgetype(g::AbstractGraph) = _NI() +edgetype(g::AbstractLightGraph) = _NI() """Return source of an edge.""" src(e::AbstractEdge) = _NI() @@ -29,12 +31,12 @@ is_ordered(e::AbstractEdge) = src(e) <= dst(e) """Return the vertices of a graph.""" -vertices(g::AbstractGraph) = _NI() +vertices(g::AbstractLightGraph) = _NI() """Return an iterator to the edges of a graph. The returned iterator is valid for one pass over the edges, and is invalidated by changes to `g`. """ -edges(g::AbstractGraph) = EdgeIter(g) +edges(g::AbstractLightGraph) = EdgeIter(g) """Returns the forward adjacency list of a graph. @@ -55,26 +57,26 @@ The optional second argument take the `v`th vertex adjacency list, that is: NOTE: returns a reference, not a copy. Do not modify result. """ -fadj(g::AbstractGraph) = _NI() -fadj(g::AbstractGraph, v::Int) = _NI() +fadj(g::AbstractLightGraph) = _NI() +fadj(g::AbstractLightGraph, v::Int) = _NI() -badj(g::AbstractGraph) = _NI() -badj(g::AbstractGraph, v::Int) = _NI() +badj(g::AbstractLightGraph) = _NI() +badj(g::AbstractLightGraph, v::Int) = _NI() -adj(g::AbstractGraph) = _NI() -adj(g::AbstractGraph, v::Int) = _NI() +adj(g::AbstractLightGraph) = _NI() +adj(g::AbstractLightGraph, v::Int) = _NI() """Returns true if all of the vertices and edges of `g` are contained in `h`.""" -issubset{T<:AbstractGraph}(g::T, h::T) = _NI() +issubset{T<:AbstractLightGraph}(g::T, h::T) = _NI() -is_directed(g::AbstractGraph) = _NI() +is_directed(g::AbstractLightGraph) = _NI() -add_vertex!(g::AbstractGraph) = _NI() +add_vertex!(g::AbstractLightGraph) = _NI() """Add `n` new vertices to the graph `g`. Returns true if all vertices were added successfully, false otherwise.""" -add_vertices!(g::AbstractGraph) = _NI() +add_vertices!(g::AbstractLightGraph) = _NI() """Return true if the graph `g` has an edge from `u` to `v`.""" -has_edge(g::AbstractGraph, u::Int, v::Int) = _NI() +has_edge(g::AbstractLightGraph, u::Int, v::Int) = _NI() """ in_edges(g, v) @@ -82,7 +84,7 @@ has_edge(g::AbstractGraph, u::Int, v::Int) = _NI() Returns an Array of the edges in `g` that arrive at vertex `v`. `v=dst(e)` for each returned edge `e`. """ -in_edges(g::AbstractGraph, v::Int) = _NI() +in_edges(g::AbstractLightGraph, v::Int) = _NI() """ out_edges(g, v) @@ -90,24 +92,24 @@ in_edges(g::AbstractGraph, v::Int) = _NI() Returns an Array of the edges in `g` that depart from vertex `v`. `v = src(e)` for each returned edge `e`. """ -out_edges(g::AbstractGraph, v::Int) = _NI() +out_edges(g::AbstractLightGraph, v::Int) = _NI() """Return true if `v` is a vertex of `g`.""" -has_vertex(g::AbstractGraph, v::Int) = _NI() +has_vertex(g::AbstractLightGraph, v::Int) = _NI() """ nv(g) The number of vertices in `g`. """ -nv(g::AbstractGraph) = _NI() +nv(g::AbstractLightGraph) = _NI() """ ne(g) The number of edges in `g`. """ -ne(g::AbstractGraph) = _NI() +ne(g::AbstractLightGraph) = _NI() """ add_edge!(g, u, v) @@ -115,7 +117,7 @@ ne(g::AbstractGraph) = _NI() Add a new edge to `g` from `u` to `v`. Will return false if add fails (e.g., if vertices are not in the graph); true otherwise. """ -add_edge!(g::AbstractGraph, u::Int, v::Int) = _NI() +add_edge!(g::AbstractLightGraph, u::Int, v::Int) = _NI() """ rem_edge!(g, u, v) @@ -124,7 +126,7 @@ Remove the edge from `u` to `v`. Returns false if edge removal fails (e.g., if edge does not exist); true otherwise. """ -rem_edge!(g::AbstractGraph, u::Int, v::Int) = _NI() +rem_edge!(g::AbstractLightGraph, u::Int, v::Int) = _NI() """ rem_vertex!(g, v) @@ -137,20 +139,20 @@ 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. """ -rem_vertex!(g::AbstractGraph, v::Int) = _NI() +rem_vertex!(g::AbstractLightGraph, v::Int) = _NI() """Return the number of edges which end at vertex `v`.""" -indegree(g::AbstractGraph, v::Int) = _NI() +indegree(g::AbstractLightGraph, v::Int) = _NI() """Return the number of edges which start at vertex `v`.""" -outdegree(g::AbstractGraph, v::Int) = _NI() +outdegree(g::AbstractLightGraph, v::Int) = _NI() -indegree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [indegree(g,x) for x in v] -outdegree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [outdegree(g,x) for x in v] +indegree(g::AbstractLightGraph, v::AbstractArray{Int,1} = vertices(g)) = [indegree(g,x) for x in v] +outdegree(g::AbstractLightGraph, v::AbstractArray{Int,1} = vertices(g)) = [outdegree(g,x) for x in v] """Return the number of edges (both ingoing and outgoing) from the vertex `v`.""" -degree(g::AbstractGraph, v::Int) = _NI() +degree(g::AbstractLightGraph, v::Int) = _NI() -degree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [degree(g,x) for x in v] +degree(g::AbstractLightGraph, v::AbstractArray{Int,1} = vertices(g)) = [degree(g,x) for x in v] "Return the maximum `outdegree` of vertices in `g`." Δout(g) = noallocextreme(outdegree,(>), typemin(Int), g) @@ -182,18 +184,18 @@ end Returns a `StatsBase.Histogram` of the degrees of vertices in `g`. """ -degree_histogram(g::AbstractGraph) = fit(Histogram, degree(g)) +degree_histogram(g::AbstractLightGraph) = fit(Histogram, degree(g)) """Returns a list of all neighbors connected to vertex `v` by an incoming edge. NOTE: returns a reference, not a copy. Do not modify result. """ -in_neighbors(g::AbstractGraph, v::Int) = _NI() +in_neighbors(g::AbstractLightGraph, v::Int) = _NI() """Returns a list of all neighbors connected to vertex `v` by an outgoing edge. NOTE: returns a reference, not a copy. Do not modify result. """ -out_neighbors(g::AbstractGraph, v::Int) = _NI() +out_neighbors(g::AbstractLightGraph, v::Int) = _NI() """Returns a list of all neighbors of vertex `v` in `g`. @@ -201,17 +203,17 @@ For DiGraphs, this is equivalent to `out_neighbors(g, v)`. NOTE: returns a reference, not a copy. Do not modify result. """ -neighbors(g::AbstractGraph, v::Int) = _NI() +neighbors(g::AbstractLightGraph, v::Int) = _NI() "Returns the neighbors common to vertices `u` and `v` in `g`." -common_neighbors(g::AbstractGraph, u::Int, v::Int) = _NI() +common_neighbors(g::AbstractLightGraph, u::Int, v::Int) = _NI() -all_neighbors(g::AbstractGraph) = _NI() +all_neighbors(g::AbstractLightGraph) = _NI() "Returns true if `g` has any self loops." -has_self_loops(g::AbstractGraph) = any(v->has_edge(g, v, v), vertices(g)) +has_self_loops(g::AbstractLightGraph) = any(v->has_edge(g, v, v), vertices(g)) "Returns the number of self loops in `g`." -num_self_loops(g::AbstractGraph) = sum(v->has_edge(g, v, v), vertices(g)) +num_self_loops(g::AbstractLightGraph) = sum(v->has_edge(g, v, v), vertices(g)) -density(g::AbstractGraph) = _NI() +density(g::AbstractLightGraph) = _NI() From 3cb3aa330826df938e4c504edc3fa633735b7b46 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Mon, 13 Mar 2017 08:42:12 -0700 Subject: [PATCH 07/56] remove data files from benchmarks --- .gitignore | 3 ++- ...c2f3d620f89970e811af97da4ddf73ebe43933.jld | Bin 5517282 -> 0 bytes benchmark/.tune.jld | Bin 35274 -> 0 bytes 3 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 benchmark/.results/73c2f3d620f89970e811af97da4ddf73ebe43933.jld delete mode 100644 benchmark/.tune.jld diff --git a/.gitignore b/.gitignore index 07eef31a3..5d4a75761 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ *.jl.mem docs/build/ docs/site/ -.results/ +benchmark/.results/* +benchmark/.tune.jld diff --git a/benchmark/.results/73c2f3d620f89970e811af97da4ddf73ebe43933.jld b/benchmark/.results/73c2f3d620f89970e811af97da4ddf73ebe43933.jld deleted file mode 100644 index 4928fe2fe8e05239e86fe85c3c65dc8b4516a7b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5517282 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

Rc9E7-H@X>RXr@#XRP)fHs!V!x*z~)cFWAqm^se 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�P;&sh;Q7tq3a-nPs} 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%0D{i>H* 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%0D8TAfxP5Z3o9?kRYqsNFDL%;Si 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�ni? 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

ryr!p6OnH+qD9DJ3Wt<#uK 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 diff --git a/benchmark/.tune.jld b/benchmark/.tune.jld deleted file mode 100644 index 420d168f9b7f93f0e13060984d773f0f8f02aa97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35274 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 From 8aee9a8dbfd2a969f966f039ebf4fd5176e4bffe Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Mon, 13 Mar 2017 17:08:06 -0700 Subject: [PATCH 08/56] simplegraphs, take 2 --- REQUIRE | 1 + src/LightGraphs.jl | 19 +- src/core.jl | 205 ++++------------ src/deprecations.jl | 2 + src/edgeiter.jl | 76 ------ src/flow/dinic.jl | 4 +- src/flow/edmonds_karp.jl | 4 +- src/flow/push_relabel.jl | 8 +- src/graphtypes/simplegraph/SimpleGraphs.jl | 50 ++-- src/graphtypes/simplegraph/simpledigraph.jl | 6 +- .../simpleedge.jl} | 5 - src/graphtypes/simplegraph/simpleedgeiter.jl | 77 ++++++ src/graphtypes/simplegraph/simplegraph.jl | 12 +- src/interface.jl | 99 ++++++++ src/shortestpaths/astar.jl | 2 +- src/shortestpaths/bellman-ford.jl | 2 +- src/spanningtrees/prim.jl | 2 +- src/traversals/bfs.jl | 3 +- src/traversals/dfs.jl | 4 +- src/traversals/maxadjvisit.jl | 2 +- test/centrality/betweenness.jl | 6 + test/centrality/closeness.jl | 2 + test/centrality/degree.jl | 2 + test/centrality/katz.jl | 2 + test/centrality/pagerank.jl | 2 + test/connectivity.jl | 1 + test/core.jl | 174 -------------- test/distance.jl | 8 + test/edgeiter.jl | 52 ----- test/edit_distance.jl | 5 + test/generators/randgraphs.jl | 4 + .../graphtypes/simplegraphs/simpleedgeiter.jl | 55 +++++ test/graphtypes/simplegraphs/simplegraphs.jl | 219 ++++++++++++++++++ test/interface.jl | 31 +++ test/linalg/spectral.jl | 6 +- test/operators.jl | 83 ++++--- test/persistence/persistence.jl | 5 + test/runtests.jl | 92 ++++---- test/shortestpaths/astar.jl | 3 + test/shortestpaths/bellman-ford.jl | 2 + test/shortestpaths/dijkstra.jl | 1 + test/shortestpaths/floyd-warshall.jl | 1 + test/traversals/bfs.jl | 1 + test/traversals/dfs.jl | 3 + test/traversals/graphvisit.jl | 1 + 45 files changed, 740 insertions(+), 604 deletions(-) create mode 100644 src/deprecations.jl delete mode 100644 src/edgeiter.jl rename src/graphtypes/{simpleedge/SimpleEdges.jl => simplegraph/simpleedge.jl} (91%) create mode 100644 src/graphtypes/simplegraph/simpleedgeiter.jl create mode 100644 src/interface.jl delete mode 100644 test/edgeiter.jl create mode 100644 test/graphtypes/simplegraphs/simpleedgeiter.jl create mode 100644 test/graphtypes/simplegraphs/simplegraphs.jl create mode 100644 test/interface.jl diff --git a/REQUIRE b/REQUIRE index 5662fda19..1dbec1c8b 100644 --- a/REQUIRE +++ b/REQUIRE @@ -6,3 +6,4 @@ JLD 0.6.3 Distributions 0.10.2 StatsBase 0.9.0 DataStructures 0.5.0 +SimpleTraits 0.3.0 diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index d64e13a5e..cff965b8d 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -7,19 +7,22 @@ using DataStructures using EzXML using ParserCombinator: Parsers.DOT, Parsers.GML using StatsBase: fit, Histogram +using SimpleTraits import Base: write, ==, <, *, ≈, convert, isless, issubset, union, intersect, reverse, reverse!, blkdiag, getindex, setindex!, show, print, copy, in, sum, size, sparse, eltype, length, ndims, transpose, - ctranspose, join, start, next, done, eltype, get, issymmetric, A_mul_B! + ctranspose, join, start, next, done, eltype, get, issymmetric, A_mul_B!, + Pair, Tuple # core -export AbstractGraph, AbstractEdge, Edge, Graph, DiGraph, vertices, edges, src, dst, -fadj, badj, in_edges, out_edges, has_vertex, has_edge, is_directed, +export AbstractGraph, AbstractDiGraph, AbstractEdge, AbstractEdgeIter, +Edge, Graph, DiGraph, vertices, edges, src, dst, +adj, fadj, badj, in_edges, out_edges, has_vertex, has_edge, is_directed, nv, ne, add_edge!, rem_edge!, add_vertex!, add_vertices!, indegree, outdegree, degree, degree_histogram, density, Δ, δ, Δout, Δin, δout, δin, neighbors, in_neighbors, out_neighbors, -common_neighbors, all_neighbors, has_self_loops, num_self_loops, +common_neighbors, has_self_loops, num_self_loops, rem_vertex!, is_ordered, # distance @@ -128,17 +131,15 @@ outside of the graph structure itself. Such data lends itself to storage in more traditional and better-optimized mechanisms. """ LightGraphs - +include("interface.jl") +include("deprecations.jl") include("core.jl") - include("graphtypes/simpleedge/SimpleEdges.jl") - typealias Edge SimpleEdges.SimpleEdge - include("graphtypes/simplegraph/SimpleGraphs.jl") typealias Graph SimpleGraphs.SimpleGraph typealias DiGraph SimpleGraphs.SimpleDiGraph + typealias Edge SimpleGraphs.SimpleEdge include("digraph-transitivity.jl") - include("edgeiter.jl") include("traversals/graphvisit.jl") include("traversals/bfs.jl") include("traversals/dfs.jl") diff --git a/src/core.jl b/src/core.jl index cb5924a21..604f6a703 100644 --- a/src/core.jl +++ b/src/core.jl @@ -1,158 +1,36 @@ -_NI(m...) = error("Not implementedxxx") - abstract AbstractPathState # modified from http://stackoverflow.com/questions/25678112/insert-item-into-a-sorted-list-with-julia-with-and-without-duplicates # returns true if insert succeeded, false if it was a duplicate _insert_and_dedup!(v::Vector{Int}, x::Int) = isempty(splice!(v, searchsorted(v,x), x)) -"""A type representing a single edge between two vertices of a graph.""" -abstract AbstractEdge - -"""An abstract type representing a graph.""" -abstract AbstractLightGraph -abstract AbstractGraph <: AbstractLightGraph -abstract AbstractDiGraph <: AbstractLightGraph - -"""Return the type of a graph's edge""" -edgetype(g::AbstractLightGraph) = _NI() - -"""Return source of an edge.""" -src(e::AbstractEdge) = _NI() -"""Return destination of an edge.""" -dst(e::AbstractEdge) = _NI() - -# The following functions should be implemented for each edge type. -Pair(e::AbstractEdge) = _NI() -Tuple(e::AbstractEdge) = _NI() -reverse(e::AbstractEdge) = _NI() -==(e1::AbstractEdge, e2::AbstractEdge) = _NI() - +"""Returns true if the edge is ordered (source vertex <= dest vertex)""" is_ordered(e::AbstractEdge) = src(e) <= dst(e) - -"""Return the vertices of a graph.""" -vertices(g::AbstractLightGraph) = _NI() - -"""Return an iterator to the edges of a graph. -The returned iterator is valid for one pass over the edges, and is invalidated by changes to `g`. -""" -edges(g::AbstractLightGraph) = EdgeIter(g) - -"""Returns the forward adjacency list of a graph. - -The Array, where each vertex the Array of destinations for each of the edges eminating from that vertex. -This is equivalent to: - - fadj = [Vector{Int}() for _ in vertices(g)] - for e in edges(g) - push!(fadj[src(e)], dst(e)) - end - fadj - -For most graphs types this is pre-calculated. - -The optional second argument take the `v`th vertex adjacency list, that is: - - fadj(g, v::Int) == fadj(g)[v] - -NOTE: returns a reference, not a copy. Do not modify result. -""" -fadj(g::AbstractLightGraph) = _NI() -fadj(g::AbstractLightGraph, v::Int) = _NI() - -badj(g::AbstractLightGraph) = _NI() -badj(g::AbstractLightGraph, v::Int) = _NI() - -adj(g::AbstractLightGraph) = _NI() -adj(g::AbstractLightGraph, v::Int) = _NI() -"""Returns true if all of the vertices and edges of `g` are contained in `h`.""" -issubset{T<:AbstractLightGraph}(g::T, h::T) = _NI() - -is_directed(g::AbstractLightGraph) = _NI() - -add_vertex!(g::AbstractLightGraph) = _NI() -"""Add `n` new vertices to the graph `g`. Returns true if all vertices -were added successfully, false otherwise.""" -add_vertices!(g::AbstractLightGraph) = _NI() - -"""Return true if the graph `g` has an edge from `u` to `v`.""" -has_edge(g::AbstractLightGraph, u::Int, v::Int) = _NI() - -""" - in_edges(g, v) - -Returns an Array of the edges in `g` that arrive at vertex `v`. -`v=dst(e)` for each returned edge `e`. -""" -in_edges(g::AbstractLightGraph, v::Int) = _NI() - """ - out_edges(g, v) - -Returns an Array of the edges in `g` that depart from vertex `v`. -`v = src(e)` for each returned edge `e`. +Add `n` new vertices to the graph `g`. +Returns true if all vertices +were added successfully, false otherwise. """ -out_edges(g::AbstractLightGraph, v::Int) = _NI() - - -"""Return true if `v` is a vertex of `g`.""" -has_vertex(g::AbstractLightGraph, v::Int) = _NI() +add_vertices!(g::AbstractGraph, n::Integer) = all([add_vertex!(g) for i=1:n]) -""" - nv(g) - -The number of vertices in `g`. -""" -nv(g::AbstractLightGraph) = _NI() -""" - ne(g) - -The number of edges in `g`. -""" -ne(g::AbstractLightGraph) = _NI() - -""" - add_edge!(g, u, v) +"""Return the number of edges which end at vertex `v`.""" +indegree(g::AbstractGraph, v::Int) = length(in_neighbors(g, v)) +indegree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [indegree(g,x) for x in v] -Add a new edge to `g` from `u` to `v`. -Will return false if add fails (e.g., if vertices are not in the graph); true otherwise. -""" -add_edge!(g::AbstractLightGraph, u::Int, v::Int) = _NI() +"""Return the number of edges which start at vertex `v`.""" +outdegree(g::AbstractGraph, v::Int) = length(out_neighbors(g, v)) +outdegree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [outdegree(g,x) for x in v] """ - rem_edge!(g, u, v) - -Remove the edge from `u` to `v`. - -Returns false if edge removal fails (e.g., if edge does not exist); true otherwise. +Return the number of edges from the vertex `v`. +For directed graphs, this value equals the incoming plus outgoing edges. +For undirected graphs, it equals the connected edges. """ -rem_edge!(g::AbstractLightGraph, u::Int, v::Int) = _NI() +degree(G::AbstractGraph, x...) = _NI("degree") +@traitfn degree{G<:AbstractGraph; IsDirected{G}}(g::G, v::Int) = indegree(g, v) + outdegree(g, v) +@traitfn degree{G<:AbstractGraph; !IsDirected{G}}(g::G, v::Int) = indegree(g, v) -""" - rem_vertex!(g, v) - -Remove the vertex `v` from graph `g`. -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. -""" -rem_vertex!(g::AbstractLightGraph, v::Int) = _NI() - -"""Return the number of edges which end at vertex `v`.""" -indegree(g::AbstractLightGraph, v::Int) = _NI() -"""Return the number of edges which start at vertex `v`.""" -outdegree(g::AbstractLightGraph, v::Int) = _NI() - - -indegree(g::AbstractLightGraph, v::AbstractArray{Int,1} = vertices(g)) = [indegree(g,x) for x in v] -outdegree(g::AbstractLightGraph, v::AbstractArray{Int,1} = vertices(g)) = [outdegree(g,x) for x in v] -"""Return the number of edges (both ingoing and outgoing) from the vertex `v`.""" -degree(g::AbstractLightGraph, v::Int) = _NI() - -degree(g::AbstractLightGraph, v::AbstractArray{Int,1} = vertices(g)) = [degree(g,x) for x in v] +degree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [degree(g, x) for x in v] "Return the maximum `outdegree` of vertices in `g`." Δout(g) = noallocextreme(outdegree,(>), typemin(Int), g) @@ -184,36 +62,43 @@ end Returns a `StatsBase.Histogram` of the degrees of vertices in `g`. """ -degree_histogram(g::AbstractLightGraph) = fit(Histogram, degree(g)) - -"""Returns a list of all neighbors connected to vertex `v` by an incoming edge. +degree_histogram(g::AbstractGraph) = fit(Histogram, degree(g)) -NOTE: returns a reference, not a copy. Do not modify result. """ -in_neighbors(g::AbstractLightGraph, v::Int) = _NI() -"""Returns a list of all neighbors connected to vertex `v` by an outgoing edge. - +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. """ -out_neighbors(g::AbstractLightGraph, v::Int) = _NI() +neighbors(g::AbstractGraph, v::Int) = out_neighbors(g, v) -"""Returns a list of all neighbors of vertex `v` in `g`. - -For DiGraphs, this is equivalent to `out_neighbors(g, v)`. - -NOTE: returns a reference, not a copy. Do not modify result. """ -neighbors(g::AbstractLightGraph, v::Int) = _NI() +Return a list of all inbound and outbount neighbors of `v` in `g`. +For undirected graphs, this is equivalent to `out_neighbors` and +`in_neighbors`. +""" +all_neighbors(g::AbstractGraph, v::Int) = neighbors(g, v) +@traitfn all_neighbors{G<:AbstractGraph; IsDirected{G}}(g::G, v::Int) = + union(out_neighbors(g, v), in_neighbors(g, v)) "Returns the neighbors common to vertices `u` and `v` in `g`." -common_neighbors(g::AbstractLightGraph, u::Int, v::Int) = _NI() - -all_neighbors(g::AbstractLightGraph) = _NI() +common_neighbors(g::AbstractGraph, u::Int, v::Int) = + intersect(neighbors(g, u), neighbors(g, v)) "Returns true if `g` has any self loops." -has_self_loops(g::AbstractLightGraph) = any(v->has_edge(g, v, v), vertices(g)) +has_self_loops(g::AbstractGraph) = any(v->has_edge(g, v, v), vertices(g)) "Returns the number of self loops in `g`." -num_self_loops(g::AbstractLightGraph) = sum(v->has_edge(g, v, v), vertices(g)) +num_self_loops(g::AbstractGraph) = sum(v->has_edge(g, v, v), vertices(g)) -density(g::AbstractLightGraph) = _NI() +""" +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). +""" +density(G::AbstractGraph) = _NI("density") +@traitfn density{G<:AbstractGraph; IsDirected{G}}(g::G) = + ne(g) / (nv(g) * (nv(g)-1)) +@traitfn density{G<:AbstractGraph; !IsDirected{G}}(g::G) = + (2*ne(g)) / (nv(g) * (nv(g)-1)) diff --git a/src/deprecations.jl b/src/deprecations.jl new file mode 100644 index 000000000..709328273 --- /dev/null +++ b/src/deprecations.jl @@ -0,0 +1,2 @@ +@deprecate in_edges in_neighbors +@deprecate out_edges out_neighbors diff --git a/src/edgeiter.jl b/src/edgeiter.jl deleted file mode 100644 index 81334d1ad..000000000 --- a/src/edgeiter.jl +++ /dev/null @@ -1,76 +0,0 @@ -immutable EdgeIterState - s::Int # src vertex - di::Int # index into adj of dest vertex - fin::Bool -end - -type EdgeIter - m::Int - adj::Vector{Vector{Int}} - directed::Bool -end - -eltype(::Type{EdgeIter}) = Edge - -EdgeIter(g::Graph) = EdgeIter(ne(g), g.fadjlist, false) -EdgeIter(g::DiGraph) = EdgeIter(ne(g), g.fadjlist, true) - -function _next(eit::EdgeIter, state::EdgeIterState = EdgeIterState(1,1,false), first::Bool = true) - s = state.s - di = state.di - if !first - di += 1 - end - fin = state.fin - while s <= length(eit.adj) - arr = eit.adj[s] - while di <= length(arr) - if eit.directed || s <= arr[di] - return EdgeIterState(s, di, fin) - end - di += 1 - end - s += 1 - di = 1 - end - fin = true - return EdgeIterState(s, di, fin) -end - -start(eit::EdgeIter) = _next(eit) -done(eit::EdgeIter, state::EdgeIterState) = state.fin -length(eit::EdgeIter) = eit.m - -function next(eit::EdgeIter, state) - edge = Edge(state.s, eit.adj[state.s][state.di]) - return(edge, _next(eit, state, false)) -end - -function _isequal(e1::EdgeIter, e2) - for e in e2 - s, d = Tuple(e) - found = length(searchsorted(e1.adj[s], d)) > 0 - if !e1.directed - found = found || length(searchsorted(e1.adj[d],s)) > 0 - end - !found && return false - end - return true -end -==(e1::EdgeIter, e2::AbstractArray{Edge,1}) = _isequal(e1, e2) -==(e1::AbstractArray{Edge,1}, e2::EdgeIter) = _isequal(e2, e1) -==(e1::EdgeIter, e2::Set{Edge}) = _isequal(e1, e2) -==(e1::Set{Edge}, e2::EdgeIter) = _isequal(e2, e1) - - -function ==(e1::EdgeIter, e2::EdgeIter) - length(e1.adj) == length(e2.adj) || return false - e1.directed == e2.directed || return false - for i in 1:length(e1.adj) - e1.adj[i] == e2.adj[i] || return false - end - return true -end - -show(io::IO, eit::EdgeIter) = write(io, "EdgeIter $(eit.m)") -show(io::IO, s::EdgeIterState) = write(io, "EdgeIterState [$(s.s), $(s.di), $(s.fin)]") diff --git a/src/flow/dinic.jl b/src/flow/dinic.jl index 7101dc00c..972e62113 100644 --- a/src/flow/dinic.jl +++ b/src/flow/dinic.jl @@ -93,7 +93,7 @@ function blocking_flow!{T<:Number}( while length(Q) > 0 # Construct the Level Graph using BFS u = pop!(Q) - for v in fadj(residual_graph, u) + for v in out_neighbors(residual_graph, u) if P[v] == -1 && capacity_matrix[u,v] > flow_matrix[u,v] P[v] = u unshift!(Q, v) @@ -105,7 +105,7 @@ function blocking_flow!{T<:Number}( total_flow = 0 - for bv in badj(residual_graph, target) # Trace all possible routes to source + for bv in in_neighbors(residual_graph, target) # Trace all possible routes to source flow = typemax(T) v = target u = bv diff --git a/src/flow/edmonds_karp.jl b/src/flow/edmonds_karp.jl index 3fb69ce2b..05f521f48 100644 --- a/src/flow/edmonds_karp.jl +++ b/src/flow/edmonds_karp.jl @@ -154,7 +154,7 @@ function fetch_path!{T<:Number}( if length(Q_f) <= length(Q_r) u = pop!(Q_f) - for v in fadj(residual_graph, u) + for v in out_neighbors(residual_graph, u) if capacity_matrix[u,v] - flow_matrix[u,v] > 0 && P[v] == -1 P[v] = u if S[v] == -1 @@ -168,7 +168,7 @@ function fetch_path!{T<:Number}( length(Q_f) == 0 && return 0, P, S, 1 # No paths to target else v = pop!(Q_r) - for u in badj(residual_graph, v) + for u in in_neighbors(residual_graph, v) if capacity_matrix[u,v] - flow_matrix[u,v] > 0 && S[u] == -1 S[u] = v P[u] != -1 && return u, P, S, 0 # 0 indicates success diff --git a/src/flow/push_relabel.jl b/src/flow/push_relabel.jl index 295c47854..87d648a2d 100644 --- a/src/flow/push_relabel.jl +++ b/src/flow/push_relabel.jl @@ -46,7 +46,7 @@ function push_relabel{T<:Number}( sizehint!(Q, n) - for v in fadj(residual_graph, source) + for v in out_neighbors(residual_graph, source) push_flow!(residual_graph, source, v, capacity_matrix, flow_matrix, excess, height, active, Q) end @@ -56,7 +56,7 @@ function push_relabel{T<:Number}( discharge!(residual_graph, v, capacity_matrix, flow_matrix, excess, height, active, count, Q) end - return sum([flow_matrix[v,target] for v in badj(residual_graph, target) ]), flow_matrix + return sum([flow_matrix[v,target] for v in in_neighbors(residual_graph, target) ]), flow_matrix end """ @@ -191,7 +191,7 @@ function relabel!{T<:Number}( n = nv(residual_graph) count[height[v]+1] -= 1 height[v] = 2*n - for to in fadj(residual_graph, v) + for to in out_neighbors(residual_graph, v) if capacity_matrix[v,to] > flow_matrix[v,to] height[v] = min(height[v], height[to]+1) end @@ -229,7 +229,7 @@ function discharge!{T<:Number}( count::AbstractArray{Int,1}, Q::AbstractArray{Int,1} # FIFO queue ) - for to in fadj(residual_graph, v) + for to in out_neighbors(residual_graph, v) excess[v] == 0 && break push_flow!(residual_graph, v, to, capacity_matrix, flow_matrix, excess, height, active, Q) end diff --git a/src/graphtypes/simplegraph/SimpleGraphs.jl b/src/graphtypes/simplegraph/SimpleGraphs.jl index b8f79ccb5..17fa1801f 100644 --- a/src/graphtypes/simplegraph/SimpleGraphs.jl +++ b/src/graphtypes/simplegraph/SimpleGraphs.jl @@ -1,17 +1,20 @@ module SimpleGraphs -import Base:show, ==, Pair, Tuple, copy -import LightGraphs: _insert_and_dedup!, AbstractGraph, edges, vertices, -nv, ne, fadj, badj, adj, degree, indegree, outdegree, in_neighbors, out_neighbors, -all_neighbors, neighbors, common_neighbors, issubset, add_vertex!, add_vertices!, rem_vertex!, -has_edge, in_edges, out_edges, has_vertex, -add_edge!, rem_edge!, is_directed, num_self_loops, has_self_loops, density +import Base: + eltype, show, ==, Pair, Tuple, copy, length, start, next, done, issubset -import LightGraphs.SimpleEdges: AbstractSimpleEdge, SimpleEdge, src, dst, reverse +import LightGraphs: + _insert_and_dedup!, AbstractGraph, AbstractEdge, AbstractEdgeIter, + src, dst, edgetype, nv, ne, vertices, edges, is_directed, + add_vertex!, add_edge!, rem_vertex!, rem_edge!, + has_vertex, has_edge, in_neighbors, out_neighbors, -export AbstractSimpleGraph, AbstractSimpleDiGraph, -SimpleGraph, SimpleGraphEdge, -SimpleDiGraph, SimpleDiGraphEdge + indegree, outdegree, degree, has_self_loops, num_self_loops + +export AbstractSimpleGraph, AbstractSimpleDiGraph, AbstractSimpleEdge, +SimpleEdge, SimpleGraph, SimpleGraphEdge, +SimpleDiGraph, SimpleDiGraphEdge, +fadj, badj, adj """ @@ -21,7 +24,8 @@ AbstractSimpleGraphs must have the following elements: - ne::Integer """ abstract AbstractSimpleGraph <: AbstractGraph -typealias AbstractSimpleGraphEdge AbstractSimpleEdge + + function show(io::IO, g::AbstractSimpleGraph) if is_directed(g) @@ -37,6 +41,7 @@ function show(io::IO, g::AbstractSimpleGraph) end vertices(g::AbstractSimpleGraph) = g.vertices +edges(g::AbstractSimpleGraph) = SimpleEdgeIter(g) nv(g::AbstractSimpleGraph) = length(vertices(g)) fadj(g::AbstractSimpleGraph) = g.fadjlist @@ -46,15 +51,14 @@ fadj(g::AbstractSimpleGraph, v::Int) = g.fadjlist[v] badj(g::AbstractSimpleGraph) = _NI badj(g::AbstractSimpleGraph, v::Int) = _NI -indegree(g::AbstractSimpleGraph, v::Int) = length(badj(g,v)) -outdegree(g::AbstractSimpleGraph, v::Int) = length(fadj(g,v)) -degree(g::AbstractSimpleGraph, v::Int) = _NI() +has_edge(g::AbstractSimpleGraph, u::Int, v::Int) = has_edge(g, SimpleEdge(u,v)) +add_edge!(g::AbstractSimpleGraph, u::Int, v::Int) = add_edge!(g, SimpleEdge(u,v)) in_neighbors(g::AbstractSimpleGraph, v::Int) = badj(g,v) out_neighbors(g::AbstractSimpleGraph, v::Int) = fadj(g,v) neighbors(g::AbstractSimpleGraph, v::Int) = out_neighbors(g, v) -common_neighbors(g::AbstractSimpleGraph, u::Int, v::Int) = intersect(neighbors(g,u), neighbors(g,v)) + function issubset{T<:AbstractSimpleGraph}(g::T, h::T) (gmin, gmax) = extrema(vertices(g)) @@ -72,14 +76,23 @@ function add_vertices!{T<:AbstractSimpleGraph}(g::T, n::Integer) end has_edge{T<:AbstractSimpleGraph}(g::T, u::Int, v::Int) = has_edge(g, edgetype(g)(u, v)) -in_edges{T<:AbstractSimpleGraph}(g::T, v::Int) = [edgetype(g)(x,v) for x in badj(g, v)] -out_edges{T<:AbstractSimpleGraph}(g::T, v::Int) = [edgetype(g)(v,x) for x in fadj(g,v)] +in_edges{T<:AbstractSimpleGraph}(g::T, v::Int) = [edgetype(g)(x,v) for x in in_neighbors(g, v)] +out_edges{T<:AbstractSimpleGraph}(g::T, v::Int) = [edgetype(g)(v,x) for x in out_neighbors(g, v)] has_vertex{T<:AbstractSimpleGraph}(g::T, v::Int) = v in vertices(g) ne{T<:AbstractSimpleGraph}(g::T) = g.ne add_edge!{T<:AbstractSimpleGraph}(g::T, u::Int, v::Int) = add_edge!(g, edgetype(g)(u, v)) rem_edge!{T<:AbstractSimpleGraph}(g::T, u::Int, v::Int) = rem_edge!(g, edgetype(g)(u, v)) +""" +Remove the vertex `v` from graph `g`. +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. +""" function rem_vertex!{T<:AbstractSimpleGraph}(g::T, v::Int) v in vertices(g) || return false n = nv(g) @@ -122,8 +135,11 @@ function rem_vertex!{T<:AbstractSimpleGraph}(g::T, v::Int) return true end +include("simpleedge.jl") include("simpledigraph.jl") include("simplegraph.jl") +include("simpleedgeiter.jl") + diff --git a/src/graphtypes/simplegraph/simpledigraph.jl b/src/graphtypes/simplegraph/simpledigraph.jl index 1f51269c2..681282de3 100644 --- a/src/graphtypes/simplegraph/simpledigraph.jl +++ b/src/graphtypes/simplegraph/simpledigraph.jl @@ -75,6 +75,7 @@ end badj(g) == badj(h) is_directed(g::SimpleDiGraph) = true +is_directed(::Type{SimpleDiGraph}) = true function add_edge!(g::SimpleDiGraph, e::SimpleDiGraphEdge) s, d = Tuple(e) @@ -116,8 +117,3 @@ function has_edge(g::SimpleDiGraph, e::SimpleDiGraphEdge) return length(searchsorted(badj(g,v), u)) > 0 end end - -degree(g::SimpleDiGraph, v::Int) = indegree(g,v) + outdegree(g,v) -"Returns all the vertices which share an edge with `v`." -all_neighbors(g::SimpleDiGraph, v::Int) = union(in_neighbors(g,v), out_neighbors(g,v)) -density(g::SimpleDiGraph) = ne(g) / (nv(g) * (nv(g)-1)) diff --git a/src/graphtypes/simpleedge/SimpleEdges.jl b/src/graphtypes/simplegraph/simpleedge.jl similarity index 91% rename from src/graphtypes/simpleedge/SimpleEdges.jl rename to src/graphtypes/simplegraph/simpleedge.jl index c1b5a80fa..812686ade 100644 --- a/src/graphtypes/simpleedge/SimpleEdges.jl +++ b/src/graphtypes/simplegraph/simpleedge.jl @@ -1,8 +1,5 @@ -module SimpleEdges - import Base: Pair, Tuple, show, == import LightGraphs: AbstractEdge, src, dst, reverse -export AbstractSimpleEdge, SimpleEdge abstract AbstractSimpleEdge <: AbstractEdge @@ -25,5 +22,3 @@ Tuple{T<:AbstractSimpleEdge}(e::T) = (src(e), dst(e)) # Convenience functions reverse{T<:AbstractSimpleEdge}(e::T) = T(dst(e), src(e)) =={T<:AbstractSimpleEdge}(e1::T, e2::T) = (src(e1) == src(e2) && dst(e1) == dst(e2)) - -end diff --git a/src/graphtypes/simplegraph/simpleedgeiter.jl b/src/graphtypes/simplegraph/simpleedgeiter.jl new file mode 100644 index 000000000..b4e2ebec9 --- /dev/null +++ b/src/graphtypes/simplegraph/simpleedgeiter.jl @@ -0,0 +1,77 @@ +type SimpleEdgeIter <: AbstractEdgeIter + m::Int + adj::Vector{Vector{Int}} + directed::Bool +end + +immutable SimpleEdgeIterState + s::Int # src vertex + di::Int # index into adj of dest vertex + fin::Bool +end + + +eltype(::Type{SimpleEdgeIter}) = SimpleEdge + +SimpleEdgeIter(g::SimpleGraph) = SimpleEdgeIter(ne(g), g.fadjlist, false) +SimpleEdgeIter(g::SimpleDiGraph) = SimpleEdgeIter(ne(g), g.fadjlist, true) + +function _next(eit::SimpleEdgeIter, state::SimpleEdgeIterState = SimpleEdgeIterState(1,1,false), first::Bool = true) + s = state.s + di = state.di + if !first + di += 1 + end + fin = state.fin + while s <= length(eit.adj) + arr = eit.adj[s] + while di <= length(arr) + if eit.directed || s <= arr[di] + return SimpleEdgeIterState(s, di, fin) + end + di += 1 + end + s += 1 + di = 1 + end + fin = true + return SimpleEdgeIterState(s, di, fin) +end + +start(eit::SimpleEdgeIter) = _next(eit) +done(eit::SimpleEdgeIter, state::SimpleEdgeIterState) = state.fin +length(eit::SimpleEdgeIter) = eit.m + +function next(eit::SimpleEdgeIter, state::SimpleEdgeIterState) + edge = SimpleEdge(state.s, eit.adj[state.s][state.di]) + return(edge, _next(eit, state, false)) +end + +function _isequal(e1::SimpleEdgeIter, e2) + for e in e2 + s, d = Tuple(e) + found = length(searchsorted(e1.adj[s], d)) > 0 + if !e1.directed + found = found || length(searchsorted(e1.adj[d],s)) > 0 + end + !found && return false + end + return true +end +==(e1::SimpleEdgeIter, e2::AbstractArray{SimpleEdge,1}) = _isequal(e1, e2) +==(e1::AbstractArray{SimpleEdge,1}, e2::SimpleEdgeIter) = _isequal(e2, e1) +==(e1::SimpleEdgeIter, e2::Set{SimpleEdge}) = _isequal(e1, e2) +==(e1::Set{SimpleEdge}, e2::SimpleEdgeIter) = _isequal(e2, e1) + + +function ==(e1::SimpleEdgeIter, e2::SimpleEdgeIter) + length(e1.adj) == length(e2.adj) || return false + e1.directed == e2.directed || return false + for i in 1:length(e1.adj) + e1.adj[i] == e2.adj[i] || return false + end + return true +end + +show(io::IO, eit::SimpleEdgeIter) = write(io, "SimpleEdgeIter $(eit.m)") +show(io::IO, s::SimpleEdgeIterState) = write(io, "SimpleEdgeIterState [$(s.s), $(s.di), $(s.fin)]") diff --git a/src/graphtypes/simplegraph/simplegraph.jl b/src/graphtypes/simplegraph/simplegraph.jl index 3ad95dc7d..d96012aa3 100644 --- a/src/graphtypes/simplegraph/simplegraph.jl +++ b/src/graphtypes/simplegraph/simplegraph.jl @@ -83,7 +83,8 @@ end fadj(g) == fadj(h) -"Returns `true` if `g` is a `SimpleDiSimpleGraph`." +"""Return `true` if `g` is a directed graph.""" +is_directed(::Type{SimpleGraph}) = false is_directed(g::SimpleGraph) = false function has_edge(g::SimpleGraph, e::SimpleGraphEdge) @@ -130,12 +131,3 @@ function add_vertex!(g::SimpleGraph) return true end - - -"""Return the number of edges (both ingoing and outgoing) from the vertex `v`.""" -degree(g::SimpleGraph, v::Int) = indegree(g,v) -doc"""Density is defined as the ratio of the number of actual edges to the -number of possible edges. This is $|v| |v-1|$ for directed graphs and -$(|v| |v-1|) / 2$ for undirected graphs. -""" -density(g::SimpleGraph) = (2*ne(g)) / (nv(g) * (nv(g)-1)) diff --git a/src/interface.jl b/src/interface.jl new file mode 100644 index 000000000..9cc8cffcc --- /dev/null +++ b/src/interface.jl @@ -0,0 +1,99 @@ +# This file contans the common interface for LightGraphs. + +_NI(m...) = error("Not implemented") + +"""A type representing a single edge between two vertices of a graph.""" +abstract AbstractEdge + +"""A type representing an edge iterator""" +abstract AbstractEdgeIter + +"""An abstract type representing a graph.""" +abstract AbstractGraph + + +@traitdef IsDirected{AbstractGraph} +@traitimpl IsDirected{G} <- is_directed(G) + + +# +# Interface for AbstractEdges +# + +"""Return source of an edge.""" +src(e::AbstractEdge) = _NI("src") + +"""Return destination of an edge.""" +dst(e::AbstractEdge) = _NI("dst") + +Pair(e::AbstractEdge) = _NI("Pair") +Tuple(e::AbstractEdge) = _NI("Tuple") + +reverse(e::AbstractEdge) = _NI("reverse") + +==(e1::AbstractEdge, e2::AbstractEdge) = _NI("==") + + +# +# Interface for AbstractGraphs +# +"""Return the type of a graph's edge""" +edgetype(g::AbstractGraph) = _NI("edgetype") + +"""Return the number of vertices in `g`.""" +nv(g::AbstractGraph) = _NI() + +"""Return the number of edges in `g`.""" +ne(g::AbstractGraph) = _NI() + +"""Return the vertices of a graph.""" +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 +is invalidated by changes to `g`. +""" +edges(g::AbstractGraph) = _NI("edges") + +is_directed(g::AbstractGraph) = _NI("is_directed") + +"""Add a new vertex to the graph `g`. +Returns true if the vertex was added successfully, false otherwise. +""" +add_vertex!(g::AbstractGraph) = _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::AbstractGraph, e::AbstractEdge) = _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::AbstractGraph, v::Int) = _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::AbstractGraph, e::AbstractEdge) = _NI("rem_edge!") + +"""Return true if `v` is a vertex of `g`.""" +has_vertex(g::AbstractGraph, v::Int) = _NI("has_vertex") + +"""Return true if the graph `g` has an edge `e`.""" +has_edge(g::AbstractGraph, e::AbstractEdge) = _NI("has_edge") + +""" +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. +""" +in_neighbors(g::AbstractGraph, v::Int) = _NI("in_neighbors") + +""" +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. +""" +out_neighbors(g::AbstractGraph, v::Int) = _NI("out_neighbors") diff --git a/src/shortestpaths/astar.jl b/src/shortestpaths/astar.jl index 1fc29b56e..5c8703981 100644 --- a/src/shortestpaths/astar.jl +++ b/src/shortestpaths/astar.jl @@ -18,7 +18,7 @@ function a_star_impl!{T<:Number}( return path end - for v in LightGraphs.fadj(graph, u) + for v in LightGraphs.out_neighbors(graph, u) if colormap[v] < 2 dist = distmx[u, v] diff --git a/src/shortestpaths/bellman-ford.jl b/src/shortestpaths/bellman-ford.jl index 2a9ede753..8c5bdc3f4 100644 --- a/src/shortestpaths/bellman-ford.jl +++ b/src/shortestpaths/bellman-ford.jl @@ -35,7 +35,7 @@ function bellman_ford_shortest_paths!{R<:Real}( no_changes = true new_active = Set{Int}() for u in active - for v in fadj(graph, u) + for v in out_neighbors(graph, u) edist = distmx[u, v] if state.dists[v] > state.dists[u] + edist state.dists[v] = state.dists[u] + edist diff --git a/src/spanningtrees/prim.jl b/src/spanningtrees/prim.jl index 55bc6da08..97129e785 100644 --- a/src/spanningtrees/prim.jl +++ b/src/spanningtrees/prim.jl @@ -50,7 +50,7 @@ function visit!{T<:Real}( distmx::AbstractArray{T, 2} ) marked[v] = true - for w in fadj(g)[v] + for w in out_neighbors(g, v) if !marked[w] x = min(v, w) y = max(v, w) diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index b32d8e836..3e584c4f3 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -259,7 +259,7 @@ function gdistances!(g::AbstractGraph, source, dists) current = queue[head] distance = dists[current] + 1 head += 1 - for j in fadj(g, current) + for j in out_neighbors(g, current) if dists[j] == -1 dists[j] = distance tail += 1 @@ -279,4 +279,3 @@ If `source` is a collection of vertices they should be unique (not checked). 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 011c1e157..bdd715e58 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -53,7 +53,7 @@ function depth_first_visit_impl!( push!(stack, (u, udsts, tstate)) open_vertex!(visitor, v) - vdsts = fadj(graph, v) + vdsts = out_neighbors(graph, v) push!(stack, (v, vdsts, start(vdsts))) end end @@ -76,7 +76,7 @@ function traverse_graph!( vertexcolormap[s] = -1 discover_vertex!(visitor, s) || return - sdsts = fadj(graph, s) + sdsts = out_neighbors(graph, s) sstate = start(sdsts) stack = [(s, sdsts, sstate)] diff --git a/src/traversals/maxadjvisit.jl b/src/traversals/maxadjvisit.jl index b8660e11b..ece74a13e 100644 --- a/src/traversals/maxadjvisit.jl +++ b/src/traversals/maxadjvisit.jl @@ -24,7 +24,7 @@ function maximum_adjacency_visit_impl!{T}( while !isempty(pq) u = DataStructures.dequeue!(pq) discover_vertex!(visitor, u) - for v in fadj(graph, u) + for v in out_neighbors(graph, u) examine_neighbor!(visitor, u, v, 0, 0, 0) if haskey(pq,v) diff --git a/test/centrality/betweenness.jl b/test/centrality/betweenness.jl index 2a922ab63..1bfecca5a 100644 --- a/test/centrality/betweenness.jl +++ b/test/centrality/betweenness.jl @@ -1,4 +1,10 @@ @testset "Betweenness" begin + # self loops + s2 = DiGraph(3) + add_edge!(s2,1,2); add_edge!(s2,2,3); add_edge!(s2,3,3) + s1 = Graph(s2) + g3 = PathGraph(5) + function readcentrality(f::AbstractString) f = open(f,"r") c = Vector{Float64}() diff --git a/test/centrality/closeness.jl b/test/centrality/closeness.jl index b10e6bb27..25282e08e 100644 --- a/test/centrality/closeness.jl +++ b/test/centrality/closeness.jl @@ -1,4 +1,6 @@ @testset "Closeness" begin + g5 = DiGraph(4) + add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) y = closeness_centrality(g5; normalize=false) z = closeness_centrality(g5) diff --git a/test/centrality/degree.jl b/test/centrality/degree.jl index ce1f5a876..68cbc31d8 100644 --- a/test/centrality/degree.jl +++ b/test/centrality/degree.jl @@ -1,4 +1,6 @@ @testset "Degree" begin + g5 = DiGraph(4) + add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) @test degree_centrality(g5) == [0.6666666666666666, 0.6666666666666666, 1.0, 0.3333333333333333] @test indegree_centrality(g5, normalize=false) == [0.0, 1.0, 2.0, 1.0] @test outdegree_centrality(g5; normalize=false) == [2.0, 1.0, 1.0, 0.0] diff --git a/test/centrality/katz.jl b/test/centrality/katz.jl index b933cd6e9..6e5c0cba8 100644 --- a/test/centrality/katz.jl +++ b/test/centrality/katz.jl @@ -1,4 +1,6 @@ @testset "Katz" begin + g5 = DiGraph(4) + add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) z = katz_centrality(g5, 0.4) @test round(z, 2) == [0.32, 0.44, 0.62, 0.56] end diff --git a/test/centrality/pagerank.jl b/test/centrality/pagerank.jl index ba742cfa2..bd5204fd6 100644 --- a/test/centrality/pagerank.jl +++ b/test/centrality/pagerank.jl @@ -1,4 +1,6 @@ @testset "Pagerank" begin + g5 = DiGraph(4) + add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) @test_approx_eq_eps(pagerank(g5)[3], 0.318, 0.001) @test_throws ErrorException pagerank(g5, 2) @test_throws ErrorException pagerank(g5, 0.85, 2) diff --git a/test/connectivity.jl b/test/connectivity.jl index 8d0e01911..ae128de39 100644 --- a/test/connectivity.jl +++ b/test/connectivity.jl @@ -1,4 +1,5 @@ @testset "Connectivity" begin + g6 = smallgraph(:house) g = PathGraph(4) add_vertices!(g,10) add_edge!(g,5,6) diff --git a/test/core.jl b/test/core.jl index 996578164..e69de29bb 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1,174 +0,0 @@ -@testset "Core" begin - @test e1.src == src(e1) == 1 - @test e1.dst == dst(e1) == 2 - @test reverse(e1) == re1 - @test sprint(show, e1) == "Edge 1 => 2" - @test Pair(e1) == Pair(1,2) - @test Tuple(e1) == (1,2) - - e2 = Edge(1,3) - e3 = Edge(1,4) - e4 = Edge(2,5) - e5 = Edge(3,5) - - @test LightGraphs.is_ordered(e5) - @test !LightGraphs.is_ordered(reverse(e5)) - - g = Graph(5) - @test add_edge!(g, 1, 2) - @test add_edge!(g, e2) - @test add_edge!(g, e3) - @test add_edge!(g, e4) - @test add_edge!(g, e5) - - - h = DiGraph(5) - @test add_edge!(h, 1, 2) - @test add_edge!(h, e2) - @test add_edge!(h, e3) - @test add_edge!(h, e4) - @test add_edge!(h, e5) - - @test vertices(g) == 1:5 - i = 0 - for e in edges(g) - i+=1 - end - @test i == 5 - @test has_edge(g,3,5) - # @test edges(g) == Set([e1, e2, e3, e4, e5]) - # @test Set{Edge}(edges(g)) == Set([e1, e2, e3, e4, e5]) - # fadj, badj, and adj tested in graphdigraph.jl - - @test degree(g) == [3, 2, 2, 1, 2] - @test indegree(g) == [3, 2, 2, 1, 2] - @test indegree(g,1) == 3 - @test outdegree(g) == [3, 2, 2, 1, 2] - @test outdegree(g,1) == 3 - @test degree(h) == [3, 2, 2, 1, 2] - @test indegree(h) == [0, 1, 1, 1, 2] - @test indegree(h,1) == 0 - @test outdegree(h) == [3, 1, 1, 0, 0] - @test outdegree(h,1) == 3 - @test in_neighbors(h,5) == LightGraphs.badj(h)[5] == LightGraphs.badj(h,5) == [2, 3] - @test out_neighbors(h,1) == LightGraphs.fadj(h)[1] == LightGraphs.fadj(h,1) == [2, 3, 4] - - @test p1 == g2 - @test issubset(h2, h1) - - @test has_edge(g, 1, 2) - @test in_edges(g, 2) == [e1, reverse(e4)] - @test out_edges(g, 1) == [e1, e2, e3] - - @test add_vertex!(g) && nv(g) == 6 - @test add_vertices!(g,5) && nv(g) == 11 - @test has_vertex(g, 11) - @test ne(g) == 5 - @test !is_directed(g) - @test is_directed(h) - - @test δ(g) == δin(g) == δout(g) == 0 - @test Δ(g) == Δout(g) == 3 - @test Δin(h) == 2 - @test δ(h) == 1 - @test δin(h) == 0 - @test δout(h) == 0 - @test CompleteGraph(4) == CompleteGraph(4) - @test CompleteGraph(4) != PathGraph(4) - @test CompleteDiGraph(4) != PathDiGraph(4) - @test CompleteDiGraph(4) == CompleteDiGraph(4) - - @test degree_histogram(CompleteDiGraph(10)).weights == [10] - @test degree_histogram(CompleteGraph(10)).weights == [10] - - @test neighbors(g, 1) == [2, 3, 4] - @test common_neighbors(g, 2, 3) == [1, 5] - @test common_neighbors(h, 2, 3) == [5] - - @test add_edge!(g, 1, 1) - @test has_self_loops(g) - @test num_self_loops(g) == 1 - @test !add_edge!(g, 1, 1) - @test rem_edge!(g, 1, 1) - @test !rem_edge!(g, 1, 1) - @test ne(g) == 5 - @test rem_edge!(g, 1, 2) - @test ne(g) == 4 - @test !rem_edge!(g, 2, 1) - add_edge!(g, 1, 2) - @test ne(g) == 5 - - @test has_edge(g,2,1) - @test has_edge(g,1,2) - @test rem_edge!(g, 2, 1) - @test add_edge!(h, 1, 1) - @test rem_edge!(h, 1, 1) - @test rem_edge!(h, 1, 2) - @test !rem_edge!(h, 1, 2) - - function test_rem_edge(g, srcv) - srcv = 2 - for dstv in collect(neighbors(g, srcv)) - rem_edge!(g, srcv, dstv) - end - @test length(neighbors(g,srcv)) == 0 - end - for v in vertices(g) - test_rem_edge(copy(g),v) - end - for v in vertices(h) - test_rem_edge(copy(h),v) - end - - @test g == copy(g) - @test !(g === copy(g)) - g10 = CompleteGraph(5) - @test rem_vertex!(g10, 1) - @test g10 == CompleteGraph(4) - @test rem_vertex!(g10, 4) - @test g10 == CompleteGraph(3) - @test !rem_vertex!(g10, 9) - - g10 = CompleteDiGraph(5) - @test rem_vertex!(g10, 1) - @test g10 == CompleteDiGraph(4) - rem_vertex!(g10, 4) - @test g10 == CompleteDiGraph(3) - @test !rem_vertex!(g10, 9) - g10 = PathGraph(5) - @test rem_vertex!(g10, 5) - @test g10 == PathGraph(4) - @test rem_vertex!(g10, 4) - @test g10 == PathGraph(3) - - g10 = PathDiGraph(5) - @test rem_vertex!(g10, 5) - @test g10 == PathDiGraph(4) - @test rem_vertex!(g10, 4) - @test g10 == PathDiGraph(3) - - g10 = PathDiGraph(5) - @test rem_vertex!(g10, 1) - h10 = PathDiGraph(6) - @test rem_vertex!(h10, 1) - @test rem_vertex!(h10, 1) - @test g10 == h10 - - g10 = CycleGraph(5) - @test rem_vertex!(g10, 5) - @test g10 == PathGraph(4) - - g10 = PathGraph(3) - @test rem_vertex!(g10, 2) - @test g10 == Graph(2) - - g10 = PathGraph(4) - @test rem_vertex!(g10, 3) - h10 =Graph(3) - @test add_edge!(h10,1,2) - @test g10 == h10 - - g10 = CompleteGraph(5) - @test rem_vertex!(g10, 3) - @test g10 == CompleteGraph(4) -end diff --git a/test/distance.jl b/test/distance.jl index a0b4a7c8e..bbda01857 100644 --- a/test/distance.jl +++ b/test/distance.jl @@ -1,4 +1,12 @@ @testset "Distance" begin + g4 = PathDiGraph(5) + adjmx1 = [0 1 0; 1 0 1; 0 1 0] # graph + adjmx2 = [0 1 0; 1 0 1; 1 1 0] # digraph + a1 = Graph(adjmx1) + a2 = DiGraph(adjmx2) + distmx1 = [Inf 2.0 Inf; 2.0 Inf 4.2; Inf 4.2 Inf] + distmx2 = [Inf 2.0 Inf; 3.2 Inf 4.2; 5.5 6.1 Inf] + @test_throws ErrorException eccentricity(g4) z = eccentricity(a1, distmx1) @test z == [6.2, 4.2, 6.2] diff --git a/test/edgeiter.jl b/test/edgeiter.jl deleted file mode 100644 index 57dbfd3df..000000000 --- a/test/edgeiter.jl +++ /dev/null @@ -1,52 +0,0 @@ -@testset "Edgeiter" begin - ga = Graph(10,20; seed=1) - gb = Graph(10,20; seed=1) - @test sprint(show,edges(ga)) == "EdgeIter 20" - @test sprint(show, start(edges(ga))) == "EdgeIterState [1, 1, false]" - - @test length(collect(edges(Graph(0,0)))) == 0 - - @test edges(ga) == edges(gb) - @test edges(ga) == collect(Edge, edges(gb)) - @test collect(Edge, edges(gb)) == edges(ga) - @test Set{Edge}(collect(Edge, edges(gb))) == edges(ga) - @test edges(ga) == Set{Edge}(collect(Edge, edges(gb))) - - @test eltype(edges(ga)) == Edge - - ga = DiGraph(10,20; seed=1) - gb = DiGraph(10,20; seed=1) - @test edges(ga) == edges(gb) - @test edges(ga) == collect(Edge, edges(gb)) - @test collect(Edge, edges(gb)) == edges(ga) - @test Set{Edge}(collect(Edge, edges(gb))) == edges(ga) - @test edges(ga) == Set{Edge}(collect(Edge, edges(gb))) - - ga = Graph(10) - add_edge!(ga, 3, 2) - add_edge!(ga, 3, 10) - add_edge!(ga, 5, 10) - add_edge!(ga, 10, 3) - - eit = edges(ga) - es = start(eit) - - @test es.s == 2 - @test es.di == 1 - - @test [e for e in eit] == [Edge(2, 3), Edge(3, 10), Edge(5,10)] - - ga = DiGraph(10) - add_edge!(ga, 3, 2) - add_edge!(ga, 3, 10) - add_edge!(ga, 5, 10) - add_edge!(ga, 10, 3) - - eit = edges(ga) - es = start(eit) - - @test es.s == 3 - @test es.di == 1 - - @test [e for e in eit] == [Edge(3, 2), Edge(3, 10), Edge(5,10), Edge(10,3)] -end diff --git a/test/edit_distance.jl b/test/edit_distance.jl index 79f1526d8..32f4f5727 100644 --- a/test/edit_distance.jl +++ b/test/edit_distance.jl @@ -1,4 +1,9 @@ @testset "Edit distance" begin + + triangle = random_regular_graph(3,2) + quadrangle = random_regular_graph(4,2) + pentagon = random_regular_graph(5,2) + d, λ = edit_distance(triangle, quadrangle, subst_cost=MinkowskiCost(1:3,1:4)) @test d == 1.0 @test λ == Tuple[(1,1),(2,2),(3,3),(0,4)] diff --git a/test/generators/randgraphs.jl b/test/generators/randgraphs.jl index ad6ecfec6..c544843c1 100644 --- a/test/generators/randgraphs.jl +++ b/test/generators/randgraphs.jl @@ -1,4 +1,8 @@ @testset "Rand graphs" begin + + r1 = Graph(10,20) + r2 = DiGraph(5,10) + @test nv(r1) == 10 @test ne(r1) == 20 @test nv(r2) == 5 diff --git a/test/graphtypes/simplegraphs/simpleedgeiter.jl b/test/graphtypes/simplegraphs/simpleedgeiter.jl new file mode 100644 index 000000000..4c62d59ed --- /dev/null +++ b/test/graphtypes/simplegraphs/simpleedgeiter.jl @@ -0,0 +1,55 @@ +@testset "SimpleEdgeIter" begin + ga = SimpleGraph(10,20; seed=1) + gb = SimpleGraph(10,20; seed=1) + @test sprint(show,edges(ga)) == "SimpleEdgeIter 20" + @test sprint(show, start(edges(ga))) == "SimpleEdgeIterState [1, 1, false]" + + @test length(collect(edges(Graph(0,0)))) == 0 + + @test edges(ga) == edges(gb) + @test edges(ga) == collect(Edge, edges(gb)) + @test collect(Edge, edges(gb)) == edges(ga) + @test Set{Edge}(collect(Edge, edges(gb))) == edges(ga) + @test edges(ga) == Set{Edge}(collect(Edge, edges(gb))) + + @test eltype(edges(ga)) == SimpleEdge + + ga = SimpleDiGraph(10,20; seed=1) + gb = SimpleDiGraph(10,20; seed=1) + @test edges(ga) == edges(gb) + @test edges(ga) == collect(SimpleEdge, edges(gb)) + @test collect(SimpleEdge, edges(gb)) == edges(ga) + @test Set{Edge}(collect(SimpleEdge, edges(gb))) == edges(ga) + @test edges(ga) == Set{SimpleEdge}(collect(SimpleEdge, edges(gb))) + + ga = SimpleGraph(10) + add_edge!(ga, 3, 2) + add_edge!(ga, 3, 10) + add_edge!(ga, 5, 10) + add_edge!(ga, 10, 3) + + eit = edges(ga) + es = start(eit) + + @test es.s == 2 + @test es.di == 1 + + @test [e for e in eit] == [Edge(2, 3), Edge(3, 10), Edge(5,10)] + + ga = SimpleDiGraph(10) + add_edge!(ga, 3, 2) + add_edge!(ga, 3, 10) + add_edge!(ga, 5, 10) + add_edge!(ga, 10, 3) + + eit = edges(ga) + es = start(eit) + + @test es.s == 3 + @test es.di == 1 + + @test [e for e in eit] == [ + SimpleEdge(3, 2), SimpleEdge(3, 10), + SimpleEdge(5,10), SimpleEdge(10,3) + ] +end diff --git a/test/graphtypes/simplegraphs/simplegraphs.jl b/test/graphtypes/simplegraphs/simplegraphs.jl new file mode 100644 index 000000000..f75b52c55 --- /dev/null +++ b/test/graphtypes/simplegraphs/simplegraphs.jl @@ -0,0 +1,219 @@ +@testset "SimpleGraphs" begin + e1 = SimpleEdge(1,2) + e2 = SimpleEdge(1,3) + e3 = SimpleEdge(1,4) + e4 = SimpleEdge(2,5) + e5 = SimpleEdge(3,5) + + re1 = Edge(2, 1) + pdict = load(joinpath(testdir,"testdata","tutte-pathdigraph.jgz")) + p1 = pdict["Tutte"] + g2 = smallgraph(:tutte) + + h1 = Graph(5) + h2 = Graph(3) + + adjmx1 = [0 1 0; 1 0 1; 0 1 0] # graph + adjmx2 = [0 1 0; 1 0 1; 1 1 0] # digraph + + @test e1.src == src(e1) == 1 + @test e1.dst == dst(e1) == 2 + @test reverse(e1) == re1 + @test sprint(show, e1) == "Edge 1 => 2" + @test Pair(e1) == Pair(1,2) + @test Tuple(e1) == (1,2) + + @test LightGraphs.is_ordered(e5) + @test !LightGraphs.is_ordered(reverse(e5)) + + + @test sprint(show, SimpleGraph()) == "empty undirected simple graph" + + g = SimpleGraph(5) + @test sprint(show, g) == "{5, 0} undirected simple graph" + @test add_edge!(g, 1, 2) + @test add_edge!(g, e2) + @test add_edge!(g, e3) + @test add_edge!(g, e4) + @test add_edge!(g, e5) + + @test Graph(DiGraph(g)) == g + + h = SimpleDiGraph(5) + @test add_edge!(h, 1, 2) + @test add_edge!(h, e2) + @test add_edge!(h, e3) + @test add_edge!(h, e4) + @test add_edge!(h, e5) + + @test vertices(g) == 1:5 + i = 0 + for e in edges(g) + i+=1 + end + @test i == 5 + @test has_edge(g, 3, 5) + # @test edges(g) == Set([e1, e2, e3, e4, e5]) + # @test Set{Edge}(edges(g)) == Set([e1, e2, e3, e4, e5]) + # fadj, badj, and adj tested in graphdigraph.jl + + @test degree(g) == [3, 2, 2, 1, 2] + @test degree(g, 1) == 3 + @test indegree(g) == [3, 2, 2, 1, 2] + @test indegree(g, 1) == 3 + @test outdegree(g) == [3, 2, 2, 1, 2] + @test outdegree(g, 1) == 3 + @test in_neighbors(g, 5) == [2, 3] + @test out_neighbors(g, 1) == [2, 3, 4] + @test density(g) == 0.5 + + @test degree(h) == [3, 2, 2, 1, 2] + @test degree(h, 1) == 3 + @test indegree(h) == [0, 1, 1, 1, 2] + @test indegree(h, 1) == 0 + @test outdegree(h) == [3, 1, 1, 0, 0] + @test outdegree(h, 1) == 3 + @test in_neighbors(h, 5) == [2, 3] + @test out_neighbors(h, 1) == [2, 3, 4] + @test density(h) == 0.25 + + @test p1 == g2 + @test issubset(h2, h1) + + @test has_edge(g, 1, 2) + + @test add_vertex!(g) && nv(g) == 6 + @test add_vertices!(g,5) && nv(g) == 11 + @test has_vertex(g, 11) + @test ne(g) == 5 + @test !is_directed(g) + @test is_directed(h) + + @test δ(g) == δin(g) == δout(g) == 0 + @test Δ(g) == Δout(g) == 3 + @test Δin(h) == 2 + @test δ(h) == 1 + @test δin(h) == 0 + @test δout(h) == 0 + @test CompleteGraph(4) == CompleteGraph(4) + @test CompleteGraph(4) != PathGraph(4) + @test CompleteDiGraph(4) != PathDiGraph(4) + @test CompleteDiGraph(4) == CompleteDiGraph(4) + + @test degree_histogram(CompleteDiGraph(10)).weights == [10] + @test degree_histogram(CompleteGraph(10)).weights == [10] + + @test neighbors(g, 1) == [2, 3, 4] + @test common_neighbors(g, 2, 3) == [1, 5] + @test common_neighbors(h, 2, 3) == [5] + + @test add_edge!(g, 1, 1) + @test has_self_loops(g) + @test num_self_loops(g) == 1 + @test !add_edge!(g, 1, 1) + @test rem_edge!(g, 1, 1) + @test !rem_edge!(g, 1, 1) + @test ne(g) == 5 + @test rem_edge!(g, 1, 2) + @test ne(g) == 4 + @test !rem_edge!(g, 2, 1) + add_edge!(g, 1, 2) + @test ne(g) == 5 + + @test has_edge(g,2,1) + @test has_edge(g,1,2) + @test rem_edge!(g, 2, 1) + @test add_edge!(h, 1, 1) + @test rem_edge!(h, 1, 1) + @test rem_edge!(h, 1, 2) + @test !rem_edge!(h, 1, 2) + + function test_rem_edge(g, srcv) + srcv = 2 + for dstv in collect(neighbors(g, srcv)) + rem_edge!(g, srcv, dstv) + end + @test length(neighbors(g,srcv)) == 0 + end + for v in vertices(g) + test_rem_edge(copy(g),v) + end + for v in vertices(h) + test_rem_edge(copy(h),v) + end + + @test g == copy(g) + @test !(g === copy(g)) + + g = CompleteGraph(5) + @test rem_vertex!(g, 1) + @test g == CompleteGraph(4) + @test rem_vertex!(g, 4) + @test g == CompleteGraph(3) + @test !rem_vertex!(g, 9) + + g = CompleteDiGraph(5) + @test rem_vertex!(g, 1) + @test g == CompleteDiGraph(4) + rem_vertex!(g, 4) + @test g == CompleteDiGraph(3) + @test !rem_vertex!(g, 9) + g = PathGraph(5) + @test rem_vertex!(g, 5) + @test g == PathGraph(4) + @test rem_vertex!(g, 4) + @test g == PathGraph(3) + + g = PathDiGraph(5) + @test rem_vertex!(g, 5) + @test g == PathDiGraph(4) + @test rem_vertex!(g, 4) + @test g == PathDiGraph(3) + + g = PathDiGraph(5) + @test rem_vertex!(g, 1) + h10 = PathDiGraph(6) + @test rem_vertex!(h10, 1) + @test rem_vertex!(h10, 1) + @test g == h10 + + g = CycleGraph(5) + @test rem_vertex!(g, 5) + @test g == PathGraph(4) + + g = PathGraph(3) + @test rem_vertex!(g, 2) + @test g == SimpleGraph(2) + + g = PathGraph(4) + @test rem_vertex!(g, 3) + h10 =Graph(3) + @test add_edge!(h10,1,2) + @test g == h10 + + g = CompleteGraph(5) + @test rem_vertex!(g, 3) + @test g == CompleteGraph(4) + + + badadjmx = [ 0 1 0; 1 0 1] + @test_throws ErrorException SimpleGraph(badadjmx) + @test_throws ErrorException SimpleGraph(sparse(badadjmx)) + @test_throws ErrorException SimpleDiGraph(badadjmx) + @test_throws ErrorException SimpleDiGraph(sparse(badadjmx)) + @test_throws ErrorException SimpleGraph([1 0; 1 1]) + + g = CompleteGraph(5) + @test !add_edge!(g, 100, 100) + h = CompleteDiGraph(5) + @test !add_edge!(h, 100, 100) + + @test_throws ErrorException Graph(sparse(adjmx2)) + + g = Graph(sparse(adjmx1)) + h = DiGraph(sparse(adjmx1)) + @test !is_directed(g) + @test is_directed(h) + + +end diff --git a/test/interface.jl b/test/interface.jl new file mode 100644 index 000000000..6cd5b2c48 --- /dev/null +++ b/test/interface.jl @@ -0,0 +1,31 @@ +@testset "Interface" begin + dummygraph = DummyGraph() + dummydigraph = DummyDiGraph() + dummyedge = DummyEdge() + + for edgefun in [src, dst, Pair, Tuple, reverse] + @test_throws ErrorException edgefun(dummyedge) + end + + for edgefun2edges in [ == ] + @test_throws ErrorException edgefun2edges(dummyedge, dummyedge) + end + + for graphfunbasic in [ + nv, ne, vertices, edges, is_directed, add_vertex! + ] + @test_throws ErrorException graphfunbasic(dummygraph) + end + + for graphfun1int in [ + rem_vertex!, has_vertex, in_neighbors, out_neighbors + ] + @test_throws ErrorException graphfun1int(dummygraph, 1) + end + for graphfunedge in [ + has_edge, add_edge!, rem_edge! + ] + @test_throws ErrorException graphfunedge(dummygraph, dummyedge) + end + +end # testset diff --git a/test/linalg/spectral.jl b/test/linalg/spectral.jl index 51b6d289d..5b0b3ff1a 100644 --- a/test/linalg/spectral.jl +++ b/test/linalg/spectral.jl @@ -4,6 +4,10 @@ import Base: full full(nbt::Nonbacktracking) = full(sparse(nbt)) @testset "Spectral" begin + + g3 = PathGraph(5) + g4 = PathDiGraph(5) + g5 = DiGraph(4) @test adjacency_matrix(g3)[3,2] == 1 @test adjacency_matrix(g3)[2,4] == 0 @test laplacian_matrix(g3)[3,2] == -1 @@ -11,7 +15,7 @@ full(nbt::Nonbacktracking) = full(sparse(nbt)) @test laplacian_spectrum(g3)[5] == 3.6180339887498945 @test adjacency_spectrum(g3)[1] == -1.732050807568878 - g5 = DiGraph(4) + add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) @test laplacian_spectrum(g5)[3] == laplacian_spectrum(g5,:both)[3] == 3.0 @test laplacian_spectrum(g5,:in)[3] == 1.0 diff --git a/test/operators.jl b/test/operators.jl index 4cf56116d..4130d0047 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -1,7 +1,12 @@ @testset "Operators" begin + g3 = PathGraph(5) + g4 = PathDiGraph(5) + c3 = complement(g3) c4 = complement(g4) + re1 = Edge(2, 1) + @test nv(c3) == 5 @test ne(c3) == 6 @test nv(c4) == 5 @@ -34,23 +39,23 @@ h = Graph(6) add_edge!(h, 5, 6) - e = Edge(5, 6) + e = SimpleEdge(5, 6) z = union(g3, h) @test has_edge(z, e) @test z == PathGraph(6) h = DiGraph(6) add_edge!(h, 5, 6) - e = Edge(5, 6) + e = SimpleEdge(5, 6) z = union(g4, h) @test has_edge(z, e) @test z == PathDiGraph(6) - g10 = CompleteGraph(2) - h10 = CompleteGraph(2) - z = blkdiag(g10, h10) - @test nv(z) == nv(g10) + nv(h10) - @test ne(z) == ne(g10) + ne(h10) + g = CompleteGraph(2) + h = CompleteGraph(2) + z = blkdiag(g, h) + @test nv(z) == nv(g) + nv(h) + @test ne(z) == ne(g) + ne(h) @test has_edge(z, 1, 2) @test has_edge(z, 3, 4) @test !has_edge(z, 1, 3) @@ -58,10 +63,10 @@ @test !has_edge(z, 2, 3) @test !has_edge(z, 2, 4) - g10 = Graph(2) - h10 = Graph(2) - z = join(g10, h10) - @test nv(z) == nv(g10) + nv(h10) + g = Graph(2) + h = Graph(2) + z = join(g, h) + @test nv(z) == nv(g) + nv(h) @test ne(z) == 4 @test !has_edge(z, 1, 2) @test !has_edge(z, 3, 4) @@ -79,12 +84,12 @@ @test size(p, 3) == 1 - g5 = DiGraph(4) - add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) - @test g5 * ones(nv(g5)) == [2.0, 1.0, 1.0, 0.0] - @test sum(g5, 1) == [0, 1, 2, 1] - @test sum(g5, 2) == [2, 1, 1, 0] - @test sum(g5) == 4 + g = DiGraph(4) + add_edge!(g,1,2); add_edge!(g,2,3); add_edge!(g,1,3); add_edge!(g,3,4) + @test g * ones(nv(g)) == [2.0, 1.0, 1.0, 0.0] + @test sum(g, 1) == [0, 1, 2, 1] + @test sum(g, 2) == [2, 1, 1, 0] + @test sum(g) == 4 @test sum(p,1) == sum(p,2) @test_throws ErrorException sum(p,3) @@ -93,17 +98,7 @@ @test length(p) == 100 @test ndims(p) == 2 @test issymmetric(p) - @test !issymmetric(g5) - - g22 = CompleteGraph(2) - h = cartesian_product(g22, g22) - @test nv(h) == 4 - @test ne(h)== 4 - - g22 = CompleteGraph(2) - h = tensor_product(g22, g22) - @test nv(h) == 4 - @test ne(h) == 1 + @test !issymmetric(g) nx = 20; ny = 21 G = PathGraph(ny); H = PathGraph(nx) @@ -123,7 +118,18 @@ end return g end - @test crosspath_slow(2, g22) == crosspath(2,g22) + + g = CompleteGraph(2) + h = cartesian_product(g, g) + @test nv(h) == 4 + @test ne(h)== 4 + + h = tensor_product(g, g) + @test nv(h) == 4 + @test ne(h) == 1 + + g2 = CompleteGraph(2) + @test crosspath_slow(2, g2) == crosspath(2, g2) ## test subgraphs ## @@ -163,17 +169,20 @@ @test sg2 == sg @test vm[4] == 8 - gg5 = CompleteGraph(10) - elist = [Edge(1,2),Edge(2,3),Edge(3,4),Edge(4,5),Edge(5,1)] - sg, vm = induced_subgraph(gg5, elist) + gg = CompleteGraph(10) + elist = [ + SimpleEdge(1, 2), SimpleEdge(2, 3), SimpleEdge(3, 4), + SimpleEdge(4, 5),SimpleEdge(5, 1) + ] + sg, vm = induced_subgraph(gg, elist) @test sg == CycleGraph(5) @test sort(vm) == [1:5;] - g10 = StarGraph(10) - @test egonet(g10, 1, 0) == Graph(1,0) - @test egonet(g10, 1, 1) == g10 + g = StarGraph(10) + @test egonet(g, 1, 0) == Graph(1,0) + @test egonet(g, 1, 1) == g - @test eltype(g10) == Float64 - @test ndims(g10) == 2 + @test eltype(g) == Float64 + @test ndims(g) == 2 end diff --git a/test/persistence/persistence.jl b/test/persistence/persistence.jl index 7f72b34ee..cd60bc7c0 100644 --- a/test/persistence/persistence.jl +++ b/test/persistence/persistence.jl @@ -1,4 +1,9 @@ @testset "Persistence" begin + pdict = load(joinpath(testdir,"testdata","tutte-pathdigraph.jgz")) + p1 = pdict["Tutte"] + p2 = pdict["pathdigraph"] + g3 = PathGraph(5) + function readback_test(format::Symbol, g::Graph, gname="g", remove=true, fnamefio=mktemp()) fname,fio = fnamefio diff --git a/test/runtests.jl b/test/runtests.jl index 4db9e3e86..31c7c259a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,58 +1,66 @@ using LightGraphs +using LightGraphs.SimpleGraphs using LightGraphs.LinAlg using Base.Test -g1 = smallgraph(:petersen) -g2 = smallgraph(:tutte) -g3 = PathGraph(5) -g4 = PathDiGraph(5) -g5 = DiGraph(4) -add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) -g6 = smallgraph(:house) -h1 = Graph(5) -h2 = Graph(3) -h3 = Graph() -h4 = DiGraph(7) -h5 = DiGraph() +type DummyGraph <: AbstractGraph end +type DummyDiGraph <: AbstractGraph end +type DummyEdge <: AbstractEdge end -# self loops -s2 = DiGraph(3) -add_edge!(s2,1,2); add_edge!(s2,2,3); add_edge!(s2,3,3) -s1 = Graph(s2) -r1 = Graph(10,20) -r2 = DiGraph(5,10) - -e0 = Edge(2, 3) -e1 = Edge(1, 2) -re1 = Edge(2, 1) - -# polygons -triangle = random_regular_graph(3,2) -quadrangle = random_regular_graph(4,2) -pentagon = random_regular_graph(5,2) +# g1 = smallgraph(:petersen) +# g2 = smallgraph(:tutte) +# g3 = PathGraph(5) +# g4 = PathDiGraph(5) +# g5 = DiGraph(4) +# add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) +# g6 = smallgraph(:house) +# +# h1 = Graph(5) +# h2 = Graph(3) +# h3 = Graph() +# h4 = DiGraph(7) +# h5 = DiGraph() +# +# # self loops +# s2 = DiGraph(3) +# add_edge!(s2,1,2); add_edge!(s2,2,3); add_edge!(s2,3,3) +# s1 = Graph(s2) +# +# r1 = Graph(10,20) +# r2 = DiGraph(5,10) +# +# e0 = Edge(2, 3) +# e1 = Edge(1, 2) +# re1 = Edge(2, 1) +# +# # polygons +# triangle = random_regular_graph(3,2) +# quadrangle = random_regular_graph(4,2) +# pentagon = random_regular_graph(5,2) testdir = dirname(@__FILE__) -pdict = load(joinpath(testdir,"testdata","tutte-pathdigraph.jgz")) -p1 = pdict["Tutte"] -p2 = pdict["pathdigraph"] - - -adjmx1 = [0 1 0; 1 0 1; 0 1 0] # graph -adjmx2 = [0 1 0; 1 0 1; 1 1 0] # digraph -distmx1 = [Inf 2.0 Inf; 2.0 Inf 4.2; Inf 4.2 Inf] -distmx2 = [Inf 2.0 Inf; 3.2 Inf 4.2; 5.5 6.1 Inf] - -a1 = Graph(adjmx1) -a2 = DiGraph(adjmx2) +# pdict = load(joinpath(testdir,"testdata","tutte-pathdigraph.jgz")) +# p1 = pdict["Tutte"] +# p2 = pdict["pathdigraph"] +# +# +# adjmx1 = [0 1 0; 1 0 1; 0 1 0] # graph +# adjmx2 = [0 1 0; 1 0 1; 1 1 0] # digraph +# distmx1 = [Inf 2.0 Inf; 2.0 Inf 4.2; Inf 4.2 Inf] +# distmx2 = [Inf 2.0 Inf; 3.2 Inf 4.2; 5.5 6.1 Inf] +# +# a1 = Graph(adjmx1) +# a2 = DiGraph(adjmx2) tests = [ - "core", - "edgeiter", + "interface", + "graphtypes/simplegraphs/simplegraphs", + "graphtypes/simplegraphs/simpleedgeiter", "operators", - "graphdigraph", + # "graphdigraph", "distance", "edit_distance", "linalg/spectral", diff --git a/test/shortestpaths/astar.jl b/test/shortestpaths/astar.jl index 7f7d5e173..b7596879b 100644 --- a/test/shortestpaths/astar.jl +++ b/test/shortestpaths/astar.jl @@ -1,4 +1,7 @@ @testset "A star" begin + g3 = PathGraph(5) + g4 = PathDiGraph(5) + d1 = float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) d2 = sparse(float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) @test a_star(g3, 1, 4, d1) == diff --git a/test/shortestpaths/bellman-ford.jl b/test/shortestpaths/bellman-ford.jl index 77e109567..7e49c9417 100644 --- a/test/shortestpaths/bellman-ford.jl +++ b/test/shortestpaths/bellman-ford.jl @@ -1,4 +1,6 @@ @testset "Bellman Ford" begin + g4 = PathDiGraph(5) + d1 = float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) d2 = sparse(float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) diff --git a/test/shortestpaths/dijkstra.jl b/test/shortestpaths/dijkstra.jl index ed27504fa..b5f0939e0 100644 --- a/test/shortestpaths/dijkstra.jl +++ b/test/shortestpaths/dijkstra.jl @@ -1,4 +1,5 @@ @testset "Dijkstra" begin + g4 = PathDiGraph(5) d1 = float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) d2 = sparse(float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) diff --git a/test/shortestpaths/floyd-warshall.jl b/test/shortestpaths/floyd-warshall.jl index b48f77866..7d4c05f1c 100644 --- a/test/shortestpaths/floyd-warshall.jl +++ b/test/shortestpaths/floyd-warshall.jl @@ -1,4 +1,5 @@ @testset "Floyd Warshall" begin + g3 = PathGraph(5) d = [ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0] z = floyd_warshall_shortest_paths(g3, d) @test z.dists[3,:][:] == [7, 6, 0, 11, 27] diff --git a/test/traversals/bfs.jl b/test/traversals/bfs.jl index 0f681bf8e..127e44ff8 100644 --- a/test/traversals/bfs.jl +++ b/test/traversals/bfs.jl @@ -1,6 +1,7 @@ @testset "BFS" begin g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) + g6 = smallgraph(:house) z = bfs_tree(g5, 1) visitor = LightGraphs.TreeBFSVisitorVector(zeros(Int,nv(g5))) diff --git a/test/traversals/dfs.jl b/test/traversals/dfs.jl index 4b54aa32c..2cc3ecfb7 100644 --- a/test/traversals/dfs.jl +++ b/test/traversals/dfs.jl @@ -1,4 +1,7 @@ @testset "DFS" begin + g5 = DiGraph(4) + add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) + z = dfs_tree(g5,1) @test ne(z) == 3 && nv(z) == 4 diff --git a/test/traversals/graphvisit.jl b/test/traversals/graphvisit.jl index a8f1983c2..abb76d13f 100644 --- a/test/traversals/graphvisit.jl +++ b/test/traversals/graphvisit.jl @@ -1,4 +1,5 @@ @testset "Graph visit" begin + g6 = smallgraph(:house) # stub tests for coverage; disregards output. f = IOBuffer() From 2bbafc5d6386b26c705c7b2cd882cabda4959a07 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Mon, 13 Mar 2017 21:57:32 -0700 Subject: [PATCH 09/56] more changes --- src/LightGraphs.jl | 2 +- src/graphtypes/simplegraph/SimpleGraphs.jl | 34 ++++++-------------- src/interface.jl | 6 ++-- test/generators/randgraphs.jl | 4 +-- test/graphtypes/simplegraphs/simplegraphs.jl | 7 ++++ test/interface.jl | 6 +++- test/runtests.jl | 6 ---- 7 files changed, 28 insertions(+), 37 deletions(-) diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index cff965b8d..92db37c1e 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -17,7 +17,7 @@ import Base: write, ==, <, *, ≈, convert, isless, issubset, union, intersect, # core export AbstractGraph, AbstractDiGraph, AbstractEdge, AbstractEdgeIter, -Edge, Graph, DiGraph, vertices, edges, src, dst, +Edge, Graph, DiGraph, vertices, edges, edgetype, src, dst, adj, fadj, badj, in_edges, out_edges, has_vertex, has_edge, is_directed, nv, ne, add_edge!, rem_edge!, add_vertex!, add_vertices!, indegree, outdegree, degree, degree_histogram, density, Δ, δ, diff --git a/src/graphtypes/simplegraph/SimpleGraphs.jl b/src/graphtypes/simplegraph/SimpleGraphs.jl index 17fa1801f..b1758be39 100644 --- a/src/graphtypes/simplegraph/SimpleGraphs.jl +++ b/src/graphtypes/simplegraph/SimpleGraphs.jl @@ -4,7 +4,7 @@ import Base: eltype, show, ==, Pair, Tuple, copy, length, start, next, done, issubset import LightGraphs: - _insert_and_dedup!, AbstractGraph, AbstractEdge, AbstractEdgeIter, + _NI, _insert_and_dedup!, AbstractGraph, AbstractEdge, AbstractEdgeIter, src, dst, edgetype, nv, ne, vertices, edges, is_directed, add_vertex!, add_edge!, rem_vertex!, rem_edge!, has_vertex, has_edge, in_neighbors, out_neighbors, @@ -48,17 +48,14 @@ fadj(g::AbstractSimpleGraph) = g.fadjlist fadj(g::AbstractSimpleGraph, v::Int) = g.fadjlist[v] -badj(g::AbstractSimpleGraph) = _NI -badj(g::AbstractSimpleGraph, v::Int) = _NI +badj(x...) = _NI("badj") -has_edge(g::AbstractSimpleGraph, u::Int, v::Int) = has_edge(g, SimpleEdge(u,v)) -add_edge!(g::AbstractSimpleGraph, u::Int, v::Int) = add_edge!(g, SimpleEdge(u,v)) +has_edge(g::AbstractSimpleGraph, u::Int, v::Int) = has_edge(g, edgetype(g)(u,v)) +add_edge!(g::AbstractSimpleGraph, u::Int, v::Int) = add_edge!(g, edgetype(g)(u,v)) in_neighbors(g::AbstractSimpleGraph, v::Int) = badj(g,v) out_neighbors(g::AbstractSimpleGraph, v::Int) = fadj(g,v) -neighbors(g::AbstractSimpleGraph, v::Int) = out_neighbors(g, v) - function issubset{T<:AbstractSimpleGraph}(g::T, h::T) (gmin, gmax) = extrema(vertices(g)) @@ -66,23 +63,12 @@ function issubset{T<:AbstractSimpleGraph}(g::T, h::T) return (hmin <= gmin <= gmax <= hmax) && issubset(edges(g), edges(h)) end +in_edges(g::AbstractSimpleGraph, v::Int) = [edgetype(g)(x,v) for x in in_neighbors(g, v)] +out_edges(g::AbstractSimpleGraph, v::Int) = [edgetype(g)(v,x) for x in out_neighbors(g, v)] +has_vertex(g::AbstractSimpleGraph, v::Int) = v in vertices(g) -function add_vertices!{T<:AbstractSimpleGraph}(g::T, n::Integer) - added = true - for i = 1:n - added &= add_vertex!(g) - end - return added -end - -has_edge{T<:AbstractSimpleGraph}(g::T, u::Int, v::Int) = has_edge(g, edgetype(g)(u, v)) -in_edges{T<:AbstractSimpleGraph}(g::T, v::Int) = [edgetype(g)(x,v) for x in in_neighbors(g, v)] -out_edges{T<:AbstractSimpleGraph}(g::T, v::Int) = [edgetype(g)(v,x) for x in out_neighbors(g, v)] -has_vertex{T<:AbstractSimpleGraph}(g::T, v::Int) = v in vertices(g) - -ne{T<:AbstractSimpleGraph}(g::T) = g.ne -add_edge!{T<:AbstractSimpleGraph}(g::T, u::Int, v::Int) = add_edge!(g, edgetype(g)(u, v)) -rem_edge!{T<:AbstractSimpleGraph}(g::T, u::Int, v::Int) = rem_edge!(g, edgetype(g)(u, v)) +ne(g::AbstractSimpleGraph) = g.ne +rem_edge!(g::AbstractSimpleGraph, u::Int, v::Int) = rem_edge!(g, edgetype(g)(u, v)) """ Remove the vertex `v` from graph `g`. @@ -93,7 +79,7 @@ and removing the vertex `n` from the graph. After removal the vertices in the ` 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. """ -function rem_vertex!{T<:AbstractSimpleGraph}(g::T, v::Int) +function rem_vertex!(g::AbstractSimpleGraph, v::Int) v in vertices(g) || return false n = nv(g) diff --git a/src/interface.jl b/src/interface.jl index 9cc8cffcc..59d220282 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -1,6 +1,6 @@ # This file contans the common interface for LightGraphs. -_NI(m...) = error("Not implemented") +_NI(m) = error("Not implemented: $m") """A type representing a single edge between two vertices of a graph.""" abstract AbstractEdge @@ -41,10 +41,10 @@ reverse(e::AbstractEdge) = _NI("reverse") edgetype(g::AbstractGraph) = _NI("edgetype") """Return the number of vertices in `g`.""" -nv(g::AbstractGraph) = _NI() +nv(g::AbstractGraph) = _NI("nv") """Return the number of edges in `g`.""" -ne(g::AbstractGraph) = _NI() +ne(g::AbstractGraph) = _NI("ne") """Return the vertices of a graph.""" vertices(g::AbstractGraph) = _NI("vertices") diff --git a/test/generators/randgraphs.jl b/test/generators/randgraphs.jl index c544843c1..f6c125c69 100644 --- a/test/generators/randgraphs.jl +++ b/test/generators/randgraphs.jl @@ -1,8 +1,8 @@ -@testset "Rand graphs" begin +@testset "Randgraphs" begin r1 = Graph(10,20) r2 = DiGraph(5,10) - + @test nv(r1) == 10 @test ne(r1) == 20 @test nv(r2) == 5 diff --git a/test/graphtypes/simplegraphs/simplegraphs.jl b/test/graphtypes/simplegraphs/simplegraphs.jl index f75b52c55..bea66bf78 100644 --- a/test/graphtypes/simplegraphs/simplegraphs.jl +++ b/test/graphtypes/simplegraphs/simplegraphs.jl @@ -1,4 +1,9 @@ +type DummySimpleGraph <: AbstractSimpleGraph end @testset "SimpleGraphs" begin + dsg = DummySimpleGraph() + + @test_throws ErrorException badj(dsg) + e1 = SimpleEdge(1,2) e2 = SimpleEdge(1,3) e3 = SimpleEdge(1,4) @@ -26,6 +31,8 @@ @test LightGraphs.is_ordered(e5) @test !LightGraphs.is_ordered(reverse(e5)) + @test !is_directed(SimpleGraph) + @test is_directed(SimpleDiGraph) @test sprint(show, SimpleGraph()) == "empty undirected simple graph" diff --git a/test/interface.jl b/test/interface.jl index 6cd5b2c48..8de0ece69 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -1,3 +1,7 @@ +type DummyGraph <: AbstractGraph end +type DummyDiGraph <: AbstractGraph end +type DummyEdge <: AbstractEdge end + @testset "Interface" begin dummygraph = DummyGraph() dummydigraph = DummyDiGraph() @@ -12,7 +16,7 @@ end for graphfunbasic in [ - nv, ne, vertices, edges, is_directed, add_vertex! + nv, ne, vertices, edges, is_directed, add_vertex!, edgetype ] @test_throws ErrorException graphfunbasic(dummygraph) end diff --git a/test/runtests.jl b/test/runtests.jl index 31c7c259a..d46fd34c2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,12 +3,6 @@ using LightGraphs.SimpleGraphs using LightGraphs.LinAlg using Base.Test - -type DummyGraph <: AbstractGraph end -type DummyDiGraph <: AbstractGraph end -type DummyEdge <: AbstractEdge end - - # g1 = smallgraph(:petersen) # g2 = smallgraph(:tutte) # g3 = PathGraph(5) From 8f1c2afab675d43afe6a23d0bb462c2fad005aef Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Mon, 13 Mar 2017 22:08:55 -0700 Subject: [PATCH 10/56] reshuffle --- src/LightGraphs.jl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 92db37c1e..ff69f8ba1 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -14,16 +14,18 @@ import Base: write, ==, <, *, ≈, convert, isless, issubset, union, intersect, sum, size, sparse, eltype, length, ndims, transpose, ctranspose, join, start, next, done, eltype, get, issymmetric, A_mul_B!, Pair, Tuple +export +# Interface +AbstractGraph, AbstractDiGraph, AbstractEdge, AbstractEdgeInter, +Edge, Graph, DiGraph, vertices, edges, edgetype, nv, ne, src, dst, +is_directed, add_vertex!, add_edge!, rem_vertex!, rem_edge!, +has_vertex, has_edge, in_neighbors, out_neighbors, # core -export AbstractGraph, AbstractDiGraph, AbstractEdge, AbstractEdgeIter, -Edge, Graph, DiGraph, vertices, edges, edgetype, src, dst, -adj, fadj, badj, in_edges, out_edges, has_vertex, has_edge, is_directed, -nv, ne, add_edge!, rem_edge!, add_vertex!, add_vertices!, -indegree, outdegree, degree, degree_histogram, density, Δ, δ, -Δout, Δin, δout, δin, neighbors, in_neighbors, out_neighbors, -common_neighbors, has_self_loops, num_self_loops, -rem_vertex!, is_ordered, +is_ordered, add_vertices!, indegree, outdegree, degree, +Δout, Δin, δout, δin, Δ, δ, degree_histogram, +neighbors, all_neighbors, common_neighbors, +has_self_loops, num_self_loops, density, # distance eccentricity, diameter, periphery, radius, center, From ced3ee7ed3293cf6134de2072f8b047df8d54143 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Mon, 13 Mar 2017 23:16:09 -0700 Subject: [PATCH 11/56] fix tests --- src/core.jl | 5 ++++- src/interface.jl | 2 +- test/core.jl | 6 ++++++ test/graphtypes/simplegraphs/simplegraphs.jl | 4 ++-- test/runtests.jl | 1 + 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/core.jl b/src/core.jl index 604f6a703..6b370c898 100644 --- a/src/core.jl +++ b/src/core.jl @@ -77,9 +77,12 @@ Return a list of all inbound and outbount neighbors of `v` in `g`. For undirected graphs, this is equivalent to `out_neighbors` and `in_neighbors`. """ -all_neighbors(g::AbstractGraph, v::Int) = neighbors(g, v) +all_neighbors(x...) = _NI("all_neighbors") @traitfn all_neighbors{G<:AbstractGraph; IsDirected{G}}(g::G, v::Int) = union(out_neighbors(g, v), in_neighbors(g, v)) +@traitfn all_neighbors{G<:AbstractGraph; !IsDirected{G}}(g::G, v::Int) = + neighbors(g, v) + "Returns the neighbors common to vertices `u` and `v` in `g`." common_neighbors(g::AbstractGraph, u::Int, v::Int) = diff --git a/src/interface.jl b/src/interface.jl index 59d220282..90169b013 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -56,7 +56,7 @@ is invalidated by changes to `g`. edges(g::AbstractGraph) = _NI("edges") is_directed(g::AbstractGraph) = _NI("is_directed") - +is_directed{T<:AbstractGraph}(::Type{T}) = _NI("is_directed") """Add a new vertex to the graph `g`. Returns true if the vertex was added successfully, false otherwise. """ diff --git a/test/core.jl b/test/core.jl index e69de29bb..563d5e8cd 100644 --- a/test/core.jl +++ b/test/core.jl @@ -0,0 +1,6 @@ +@testset "Core" begin + d = DummyGraph() + for fn in [ degree, density, all_neighbors ] + @test_throws ErrorException fn(d) + end +end diff --git a/test/graphtypes/simplegraphs/simplegraphs.jl b/test/graphtypes/simplegraphs/simplegraphs.jl index bea66bf78..d58f7c504 100644 --- a/test/graphtypes/simplegraphs/simplegraphs.jl +++ b/test/graphtypes/simplegraphs/simplegraphs.jl @@ -72,6 +72,7 @@ type DummySimpleGraph <: AbstractSimpleGraph end @test outdegree(g, 1) == 3 @test in_neighbors(g, 5) == [2, 3] @test out_neighbors(g, 1) == [2, 3, 4] + @test density(g) == 0.5 @test degree(h) == [3, 2, 2, 1, 2] @@ -82,6 +83,7 @@ type DummySimpleGraph <: AbstractSimpleGraph end @test outdegree(h, 1) == 3 @test in_neighbors(h, 5) == [2, 3] @test out_neighbors(h, 1) == [2, 3, 4] + @test all_neighbors(h, 1) == union(in_neighbors(h, 1), out_neighbors(h, 1)) @test density(h) == 0.25 @test p1 == g2 @@ -221,6 +223,4 @@ type DummySimpleGraph <: AbstractSimpleGraph end h = DiGraph(sparse(adjmx1)) @test !is_directed(g) @test is_directed(h) - - end diff --git a/test/runtests.jl b/test/runtests.jl index d46fd34c2..b9f25c5e6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -51,6 +51,7 @@ testdir = dirname(@__FILE__) tests = [ "interface", + "core", "graphtypes/simplegraphs/simplegraphs", "graphtypes/simplegraphs/simpleedgeiter", "operators", From 1f6b89172d7450dbc29a5934b9c17bd5ba09f32a Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Tue, 14 Mar 2017 09:24:11 -0700 Subject: [PATCH 12/56] more tests --- test/edit_distance.jl | 10 +++++++++- test/graphtypes/simplegraphs/simpleedgeiter.jl | 2 +- test/graphtypes/simplegraphs/simplegraphs.jl | 16 ++++++++++++++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/test/edit_distance.jl b/test/edit_distance.jl index 32f4f5727..747a80525 100644 --- a/test/edit_distance.jl +++ b/test/edit_distance.jl @@ -3,7 +3,7 @@ triangle = random_regular_graph(3,2) quadrangle = random_regular_graph(4,2) pentagon = random_regular_graph(5,2) - + d, λ = edit_distance(triangle, quadrangle, subst_cost=MinkowskiCost(1:3,1:4)) @test d == 1.0 @test λ == Tuple[(1,1),(2,2),(3,3),(0,4)] @@ -26,4 +26,12 @@ @test cost(i,i) == 0. @test bcost(i,i) == 2/3 end + + g1 = CompleteGraph(4) + g2 = CompleteGraph(4) + rem_edge!(g2, 1, 2) + d, λ = edit_distance(g1, g2) + @test d == 2.0 + @test λ == Tuple[(1,1),(2,2),(3,3),(4,4)] + end diff --git a/test/graphtypes/simplegraphs/simpleedgeiter.jl b/test/graphtypes/simplegraphs/simpleedgeiter.jl index 4c62d59ed..a0d146728 100644 --- a/test/graphtypes/simplegraphs/simpleedgeiter.jl +++ b/test/graphtypes/simplegraphs/simpleedgeiter.jl @@ -12,7 +12,7 @@ @test Set{Edge}(collect(Edge, edges(gb))) == edges(ga) @test edges(ga) == Set{Edge}(collect(Edge, edges(gb))) - @test eltype(edges(ga)) == SimpleEdge + @test eltype(edges(ga)) == eltype(typeof(edges(ga))) == SimpleEdge ga = SimpleDiGraph(10,20; seed=1) gb = SimpleDiGraph(10,20; seed=1) diff --git a/test/graphtypes/simplegraphs/simplegraphs.jl b/test/graphtypes/simplegraphs/simplegraphs.jl index d58f7c504..aebdb5895 100644 --- a/test/graphtypes/simplegraphs/simplegraphs.jl +++ b/test/graphtypes/simplegraphs/simplegraphs.jl @@ -34,8 +34,12 @@ type DummySimpleGraph <: AbstractSimpleGraph end @test !is_directed(SimpleGraph) @test is_directed(SimpleDiGraph) - @test sprint(show, SimpleGraph()) == "empty undirected simple graph" + @test edgetype(SimpleGraph()) == SimpleEdge + @test edgetype(SimpleDiGraph()) == SimpleEdge + @test sprint(show, SimpleGraph()) == "empty undirected simple graph" + @test sprint(show, SimpleDiGraph()) == "empty directed simple graph" + @test sprint(show, SimpleDiGraph(5)) == "{5, 0} directed simple graph" g = SimpleGraph(5) @test sprint(show, g) == "{5, 0} undirected simple graph" @test add_edge!(g, 1, 2) @@ -62,7 +66,13 @@ type DummySimpleGraph <: AbstractSimpleGraph end @test has_edge(g, 3, 5) # @test edges(g) == Set([e1, e2, e3, e4, e5]) # @test Set{Edge}(edges(g)) == Set([e1, e2, e3, e4, e5]) - # fadj, badj, and adj tested in graphdigraph.jl + @test fadj(g, 1) == adj(g, 1) == badj(g, 1) == out_neighbors(g, 1) + @test fadj(g) == badj(g) == adj(g) == g.fadjlist + + @test fadj(h, 1) == out_neighbors(h, 1) + @test badj(h, 1) == in_neighbors(h, 1) + @test fadj(h) == h.fadjlist + @test badj(h) == h.badjlist @test degree(g) == [3, 2, 2, 1, 2] @test degree(g, 1) == 3 @@ -96,7 +106,9 @@ type DummySimpleGraph <: AbstractSimpleGraph end @test has_vertex(g, 11) @test ne(g) == 5 @test !is_directed(g) + @test !is_directed(typeof(g)) @test is_directed(h) + @test is_directed(typeof(h)) @test δ(g) == δin(g) == δout(g) == 0 @test Δ(g) == Δout(g) == 3 From 8922fed4def223a1799e0e4c8ecf4f252a0e3e48 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Tue, 14 Mar 2017 16:46:02 -0700 Subject: [PATCH 13/56] abstractions --- src/LightGraphs.jl | 2 +- src/biconnectivity/articulation.jl | 19 ++-- src/biconnectivity/biconnect.jl | 5 +- src/centrality/betweenness.jl | 6 +- src/centrality/degree.jl | 2 +- src/community/cliques.jl | 10 +-- src/community/clustering.jl | 12 ++- src/community/label_propagation.jl | 17 ++-- src/community/modularity.jl | 3 +- src/core.jl | 30 +++---- src/distance.jl | 10 +-- src/edit_distance.jl | 1 + src/flow/boykov_kolmogorov.jl | 93 +++++++++++--------- src/flow/dinic.jl | 38 ++++---- src/flow/edmonds_karp.jl | 10 +-- src/generators/randgraphs.jl | 6 +- src/graphtypes/simplegraph/SimpleGraphs.jl | 28 +++--- src/graphtypes/simplegraph/simpledigraph.jl | 86 ++++++++++++------ src/graphtypes/simplegraph/simpleedge.jl | 6 +- src/graphtypes/simplegraph/simpleedgeiter.jl | 12 +-- src/graphtypes/simplegraph/simplegraph.jl | 72 +++++++++------ src/interface.jl | 27 +++--- src/operators.jl | 23 ++--- src/shortestpaths/dijkstra.jl | 32 +++---- test/biconnectivity/articulation.jl | 3 +- test/graphtypes/simplegraphs/simplegraphs.jl | 5 +- test/operators.jl | 4 +- 27 files changed, 314 insertions(+), 248 deletions(-) diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index ff69f8ba1..2fa61318f 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -19,7 +19,7 @@ export AbstractGraph, AbstractDiGraph, AbstractEdge, AbstractEdgeInter, Edge, Graph, DiGraph, vertices, edges, edgetype, nv, ne, src, dst, is_directed, add_vertex!, add_edge!, rem_vertex!, rem_edge!, -has_vertex, has_edge, in_neighbors, out_neighbors, +has_vertex, has_edge, in_neighbors, out_neighbors, empty, # core is_ordered, add_vertices!, indegree, outdegree, degree, diff --git a/src/biconnectivity/articulation.jl b/src/biconnectivity/articulation.jl index 211bfb7e1..33359a891 100644 --- a/src/biconnectivity/articulation.jl +++ b/src/biconnectivity/articulation.jl @@ -2,36 +2,37 @@ 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) :: AbstractArray{Int} +function articulation(g::AbstractGraph) :: AbstractArray state = Articulations(g) for u in vertices(g) if state.depth[u] == 0 visit!(state, g, u, u) end end - return [x for (x, y) in enumerate(state.articulation_points) if y == true] + return find(state.articulation_points) end """ Articulations: a state type for the Depth first search that finds the articulation points in a graph. """ -type Articulations - low::Vector{Int} - depth::Vector{Int} - articulation_points::Vector{Bool} - id::Int +type Articulations{T<:Integer} + low::Vector{T} + depth::Vector{T} + articulation_points::BitArray + id::T end function Articulations(g::AbstractGraph) n = nv(g) - return Articulations(zeros(Int, n), zeros(Int, n),zeros(Int, n), 0) + T = typeof(n) + return Articulations(zeros(T, n), zeros(T, n), falses(n), zero(T)) 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. """ -function visit!(state::Articulations, g::AbstractGraph, u::Int, v::Int) +function visit!{T<:Integer}(state::Articulations, g::AbstractGraph, u::T, v::T) children = 0 state.id += 1 state.depth[v] = state.id diff --git a/src/biconnectivity/biconnect.jl b/src/biconnectivity/biconnect.jl index dbe5515f7..3aa85b484 100644 --- a/src/biconnectivity/biconnect.jl +++ b/src/biconnectivity/biconnect.jl @@ -31,14 +31,13 @@ function biconnected_components(g::Graph) empty!(state.stack) end end - return state.biconnected_comps end """ Does a DFS visit and stores the depth and low-points of each vertex """ -function visit!(g::Graph, state::Biconnections, u::Int, v::Int) +function visit!{T<:Integer}(g::Graph, state::Biconnections, u::T, v::T) children = 0 state.id += 1 state.depth[v] = state.id @@ -53,7 +52,7 @@ function visit!(g::Graph, state::Biconnections, u::Int, v::Int) #Checking the root, and then the non-roots if they are articulation points if (u == v && children > 1) || (u != v && state.low[w] >= state.depth[v]) - e = Edge(0, 0) #Invalid Edge, used for comparison only + e = Edge(zero(T), zero(T)) #Invalid Edge, used for comparison only st = Vector{Edge}() while e != Edge(min(v, w),max(v, w)) e = pop!(state.stack) diff --git a/src/centrality/betweenness.jl b/src/centrality/betweenness.jl index 6d07569ae..30f51ca6f 100644 --- a/src/centrality/betweenness.jl +++ b/src/centrality/betweenness.jl @@ -46,7 +46,7 @@ betweenness: Array{Float64} """ function betweenness_centrality( g::AbstractGraph, - nodes::AbstractArray{Int, 1} = vertices(g); + nodes::AbstractVector = vertices(g); normalize=true, endpoints=false) @@ -75,7 +75,7 @@ function betweenness_centrality( return betweenness end -betweenness_centrality(g::AbstractGraph, k::Int; normalize=true, endpoints=false) = +betweenness_centrality(g::AbstractGraph, k::Integer; normalize=true, endpoints=false) = betweenness_centrality(g, sample(vertices(g), k); normalize=normalize, endpoints=endpoints) @@ -139,7 +139,7 @@ function _accumulate_endpoints!( end end -function _rescale!(betweenness::Vector{Float64}, n::Int, normalize::Bool, directed::Bool, k::Int) +function _rescale!(betweenness::Vector{Float64}, n::Integer, normalize::Bool, directed::Bool, k::Int) if normalize if n <= 2 do_scale = false diff --git a/src/centrality/degree.jl b/src/centrality/degree.jl index fe069134f..16b751420 100644 --- a/src/centrality/degree.jl +++ b/src/centrality/degree.jl @@ -3,7 +3,7 @@ function _degree_centrality(g::AbstractGraph, gtype::Integer; normalize=true) c = zeros(n_v) for v in 1:n_v if gtype == 0 # count both in and out degree if appropriate - deg = outdegree(g, v) + (typeof(g) == DiGraph? indegree(g, v) : 0.0) + deg = outdegree(g, v) + (is_directed(g)? indegree(g, v) : 0.0) elseif gtype == 1 # count only in degree deg = indegree(g, v) else # count only out degree diff --git a/src/community/cliques.jl b/src/community/cliques.jl index d198eee16..334e3750e 100644 --- a/src/community/cliques.jl +++ b/src/community/cliques.jl @@ -20,16 +20,16 @@ julia> maximal_cliques(g) ``` """ function maximal_cliques(g::Graph) - + T = eltype(g) # Cache nbrs and find first pivot (highest degree) maxconn = -1 - nnbrs = Vector{Set{Int}}() + nnbrs = Vector{Set{T}}() for n in vertices(g) - push!(nnbrs, Set{Int}()) + push!(nnbrs, Set{T}()) end - pivotnbrs = Set{Int}() # handle empty graph - pivotdonenbrs = Set{Int}() # initialize + pivotnbrs = Set{T}() # handle empty graph + pivotdonenbrs = Set{T}() # initialize for n in vertices(g) nbrs = Set{Int}() diff --git a/src/community/clustering.jl b/src/community/clustering.jl index 51131cb66..aa64f8abb 100644 --- a/src/community/clustering.jl +++ b/src/community/clustering.jl @@ -80,14 +80,12 @@ Computes the [global clustering coefficient](https://en.wikipedia.org/wiki/Clust function global_clustering_coefficient(g::AbstractGraph) c = 0 ntriangles = 0 - for v in 1:nv(g) + for v in vertices(g) neighs = neighbors(g, v) - for i=1:length(neighs) - for j=1:length(neighs) - i == j && continue - if has_edge(g, neighs[i], neighs[j]) - c += 1 - end + for i=1:length(neighs), j=1:length(neighs) + i == j && continue + if has_edge(g, neighs[i], neighs[j]) + c += 1 end end k = degree(g, v) diff --git a/src/community/label_propagation.jl b/src/community/label_propagation.jl index e343190b7..653fc7b98 100644 --- a/src/community/label_propagation.jl +++ b/src/community/label_propagation.jl @@ -5,12 +5,13 @@ Community detection using the label propagation algorithm (see [Raghavan et al.] return : vertex assignments and the convergence history """ function label_propagation(g::AbstractGraph; maxiter=1000) + T = eltype(g) n = nv(g) - label = collect(1:n) + label = collect(one(T):n) active_nodes = IntSet(vertices(g)) - c = NeighComm(collect(1:n), fill(-1,n), 1) + c = NeighComm(collect(one(T):n), fill(-1,n), T(1)) convergence_hist = Vector{Int}() - random_order = Array(Int, n) + random_order = Array(T, n) i = 0 while !isempty(active_nodes) && i < maxiter num_active = length(active_nodes) @@ -41,10 +42,10 @@ function label_propagation(g::AbstractGraph; maxiter=1000) end """Type to record neighbor labels and their counts.""" -type NeighComm - neigh_pos::Vector{Int} +type NeighComm{T<:Integer} + neigh_pos::Vector{T} neigh_cnt::Vector{Int} - neigh_last::Int + neigh_last::T end """Fast shuffle Array `a` in UnitRange `r` inplace.""" @@ -59,7 +60,7 @@ function range_shuffle!(r::UnitRange, a::AbstractVector) end """Return the most frequency label.""" -function vote!(g::AbstractGraph, m::Vector{Int}, c::NeighComm, u::Int) +function vote!{T<:Integer}(g::AbstractGraph, m::Vector{T}, c::NeighComm, u::T) @inbounds for i=1:c.neigh_last-1 c.neigh_cnt[c.neigh_pos[i]] = -1 end @@ -89,7 +90,7 @@ function vote!(g::AbstractGraph, m::Vector{Int}, c::NeighComm, u::Int) end end -function renumber_labels!(membership::Vector{Int}, label_counters::Vector{Int}) +function renumber_labels!{T<:Integer}(membership::Vector{T}, label_counters::Vector{Int}) N = length(membership) (maximum(membership) > N || minimum(membership) < 1) && error("Label must between 1 and |V|") j = 1 diff --git a/src/community/modularity.jl b/src/community/modularity.jl index 1c6a9ed86..26ea82281 100644 --- a/src/community/modularity.jl +++ b/src/community/modularity.jl @@ -4,8 +4,7 @@ Computes Newman's modularity `Q` for graph `g` given the partitioning `c`. """ -function modularity(g::Graph, c) - n = nv(g) +function modularity(g::Graph, c::Vector) m = 2*ne(g) m == 0 && return 0. nc = maximum(c) diff --git a/src/core.jl b/src/core.jl index 6b370c898..bdf741851 100644 --- a/src/core.jl +++ b/src/core.jl @@ -1,7 +1,7 @@ abstract AbstractPathState # modified from http://stackoverflow.com/questions/25678112/insert-item-into-a-sorted-list-with-julia-with-and-without-duplicates # returns true if insert succeeded, false if it was a duplicate -_insert_and_dedup!(v::Vector{Int}, x::Int) = isempty(splice!(v, searchsorted(v,x), x)) +_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::AbstractEdge) = src(e) <= dst(e) @@ -14,12 +14,12 @@ 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::AbstractGraph, v::Int) = length(in_neighbors(g, v)) -indegree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [indegree(g,x) for x in v] +indegree(g::AbstractGraph, v::Integer) = length(in_neighbors(g, v)) +indegree{T<:Integer}(g::AbstractGraph, v::AbstractArray{T,1} = vertices(g)) = [indegree(g,x) for x in v] """Return the number of edges which start at vertex `v`.""" -outdegree(g::AbstractGraph, v::Int) = length(out_neighbors(g, v)) -outdegree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [outdegree(g,x) for x in v] +outdegree(g::AbstractGraph, v::Integer) = length(out_neighbors(g, v)) +outdegree{T<:Integer}(g::AbstractGraph, v::AbstractArray{T,1} = vertices(g)) = [outdegree(g,x) for x in v] """ Return the number of edges from the vertex `v`. @@ -27,10 +27,10 @@ For directed graphs, this value equals the incoming plus outgoing edges. For undirected graphs, it equals the connected edges. """ degree(G::AbstractGraph, x...) = _NI("degree") -@traitfn degree{G<:AbstractGraph; IsDirected{G}}(g::G, v::Int) = indegree(g, v) + outdegree(g, v) -@traitfn degree{G<:AbstractGraph; !IsDirected{G}}(g::G, v::Int) = indegree(g, v) +@traitfn degree{G<:AbstractGraph; IsDirected{G}}(g::G, v::Integer) = indegree(g, v) + outdegree(g, v) +@traitfn degree{G<:AbstractGraph; !IsDirected{G}}(g::G, v::Integer) = indegree(g, v) -degree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [degree(g, x) for x in v] +degree{T<:Integer}(g::AbstractGraph, v::AbstractArray{T,1} = vertices(g)) = [degree(g, x) for x in v] "Return the maximum `outdegree` of vertices in `g`." Δout(g) = noallocextreme(outdegree,(>), typemin(Int), g) @@ -48,7 +48,7 @@ degree(g::AbstractGraph, v::AbstractArray{Int,1} = vertices(g)) = [degree(g, x) "Computes 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 1:nv(g) + for i in vertices(g) funci = f(g, i) if comparison(funci, value) value = funci @@ -70,7 +70,7 @@ 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. """ -neighbors(g::AbstractGraph, v::Int) = out_neighbors(g, v) +neighbors(g::AbstractGraph, v::Integer) = out_neighbors(g, v) """ Return a list of all inbound and outbount neighbors of `v` in `g`. @@ -78,21 +78,21 @@ For undirected graphs, this is equivalent to `out_neighbors` and `in_neighbors`. """ all_neighbors(x...) = _NI("all_neighbors") -@traitfn all_neighbors{G<:AbstractGraph; IsDirected{G}}(g::G, v::Int) = +@traitfn all_neighbors{G<:AbstractGraph; IsDirected{G}}(g::G, v::Integer) = union(out_neighbors(g, v), in_neighbors(g, v)) -@traitfn all_neighbors{G<:AbstractGraph; !IsDirected{G}}(g::G, v::Int) = +@traitfn all_neighbors{G<:AbstractGraph; !IsDirected{G}}(g::G, v::Integer) = neighbors(g, v) "Returns the neighbors common to vertices `u` and `v` in `g`." -common_neighbors(g::AbstractGraph, u::Int, v::Int) = +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::AbstractGraph) = any(v->has_edge(g, v, v), vertices(g)) +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) = sum(v->has_edge(g, v, v), vertices(g)) +num_self_loops(g::AbstractGraph) = nv(g) == 0 ? 0 : sum(v->has_edge(g, v, v), vertices(g)) """ Return the density of `g`. diff --git a/src/distance.jl b/src/distance.jl index 19c8fb6c2..ef4f418d9 100644 --- a/src/distance.jl +++ b/src/distance.jl @@ -7,7 +7,7 @@ type DefaultDistance<:AbstractArray{Int, 2} DefaultDistance(nv::Int=typemax(Int)) = new(nv) end -getindex(::DefaultDistance, s::Int, d::Int) = 1 +getindex(::DefaultDistance, s::Integer, d::Integer) = 1 getindex(::DefaultDistance, s::UnitRange, d::UnitRange) = DefaultDistance(length(s)) size(d::DefaultDistance) = (d.nv, d.nv) transpose(d::DefaultDistance) = d @@ -33,9 +33,9 @@ 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, store, and pass the eccentricities if multiple distance measures are desired. """ -function eccentricity{T}( +function eccentricity{T, U<:Integer}( g::AbstractGraph, - v::Int, + v::U, distmx::AbstractArray{T, 2} = DefaultDistance() ) e = maximum(dijkstra_shortest_paths(g,v,distmx).dists) @@ -44,9 +44,9 @@ function eccentricity{T}( return e end -eccentricity{T}( +eccentricity{T, U<:Integer}( g::AbstractGraph, - vs::AbstractArray{Int, 1}=vertices(g), + vs::AbstractArray{U, 1}=vertices(g), distmx::AbstractArray{T, 2} = DefaultDistance() ) = [eccentricity(g,v,distmx) for v in vs] diff --git a/src/edit_distance.jl b/src/edit_distance.jl index 102abdffd..b7af02101 100644 --- a/src/edit_distance.jl +++ b/src/edit_distance.jl @@ -37,6 +37,7 @@ Distance: Approximation Algorithms and Applications. (Chapter 2) Author: Júlio Hoffimann Mendes (juliohm@stanford.edu) """ + function edit_distance(G₁::AbstractGraph, G₂::AbstractGraph; insert_cost::Function=v->1.0, delete_cost::Function=u->1.0, diff --git a/src/flow/boykov_kolmogorov.jl b/src/flow/boykov_kolmogorov.jl index 69e2fea3a..0fe9f6cf2 100644 --- a/src/flow/boykov_kolmogorov.jl +++ b/src/flow/boykov_kolmogorov.jl @@ -16,17 +16,17 @@ Uses a default capacity of 1 when the capacity matrix isn\'t specified. Requires arguments: residual_graph::DiGraph # the input graph -source::Int # the source vertex -target::Int # the target vertex +source::Integer # the source vertex +target::Integer # the target vertex capacity_matrix::AbstractArray{T,2} # edge flow capacities Author: Júlio Hoffimann Mendes (juliohm@stanford.edu) """ -function boykov_kolmogorov_impl{T<:Number}( +function boykov_kolmogorov_impl{T<:Number, U<:Integer}( residual_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::U, # the source vertex + target::U, # the target vertex capacity_matrix::AbstractArray{T,2} # edge flow capacities ) @@ -35,14 +35,14 @@ function boykov_kolmogorov_impl{T<:Number}( flow = 0 flow_matrix = zeros(T, n, n) - TREE = zeros(Int, n) - TREE[source] = 1 - TREE[target] = 2 + TREE = zeros(U, n) + TREE[source] = U(1) + TREE[target] = U(2) - PARENT = zeros(Int, n) + PARENT = zeros(U, n) A = [source,target] - O = Vector{Int}() + O = Vector{U}() while true # growth stage @@ -60,15 +60,15 @@ function boykov_kolmogorov_impl{T<:Number}( return flow, flow_matrix, TREE end -function find_path!{T<:Number}( - residual_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex +function find_path!{T<:Number, U<:Integer}( + residual_graph::DiGraph{U}, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex flow_matrix::AbstractArray{T,2}, # the current flow matrix capacity_matrix::AbstractArray{T,2}, # edge flow capacities - PARENT::Vector{Int}, # parent table - TREE::Vector{Int}, # tree table - A::Vector{Int} # active set + PARENT::Vector{U}, # parent table + TREE::Vector{U}, # tree table + A::Vector{U} # active set ) tree_cap(p,q) = TREE[p] == 1 ? capacity_matrix[p,q] - flow_matrix[p,q] : @@ -77,22 +77,22 @@ function find_path!{T<:Number}( p = last(A) for q in neighbors(residual_graph, p) if tree_cap(p,q) > 0 - if TREE[q] == 0 + if TREE[q] == zero(U) TREE[q] = TREE[p] PARENT[q] = p unshift!(A, q) end - if TREE[q] ≠ 0 && TREE[q] ≠ TREE[p] + if TREE[q] ≠ zero(U) && TREE[q] ≠ TREE[p] # p -> source path_to_source = [p] - while PARENT[p] ≠ 0 + while PARENT[p] ≠ zero(U) p = PARENT[p] push!(path_to_source, p) end # q -> target path_to_target = [q] - while PARENT[q] ≠ 0 + while PARENT[q] ≠ zero(U) q = PARENT[q] push!(path_to_target, q) end @@ -111,16 +111,29 @@ function find_path!{T<:Number}( pop!(A) end - return Vector{Int}() + return Vector{U}() end -function augment!{T<:Number}( +find_path!{T<:Number, U<:Integer}( + residual_graph::DiGraph{U}, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex + flow_matrix::AbstractArray{T,2}, # the current flow matrix + capacity_matrix::AbstractArray{T,2}, # edge flow capacities + PARENT::Vector, # parent table + TREE::Vector, # tree table + A::Vector # active set + ) = find_path!(residual_graph, U(source), U(target), + flow_matrix, capacity_matrix, U.(PARENT), + U.(TREE), U.(A)) + +function augment!{T<:Number, U<:Integer}( path::AbstractVector, # path from source to target flow_matrix::AbstractArray{T,2}, # the current flow matrix capacity_matrix::AbstractArray{T,2}, # edge flow capacities - PARENT::Vector{Int}, # parent table - TREE::Vector{Int}, # tree table - O::Vector{Int} # orphan set + PARENT::Vector{U}, # parent table + TREE::Vector{U}, # tree table + O::Vector{U} # orphan set ) # bottleneck capacity @@ -139,12 +152,12 @@ function augment!{T<:Number}( # create orphans if flow_matrix[p,q] == capacity_matrix[p,q] - if TREE[p] == TREE[q] == 1 - PARENT[q] = 0 + if TREE[p] == TREE[q] == one(U) + PARENT[q] = zero(U) unshift!(O, q) end if TREE[p] == TREE[q] == 2 - PARENT[p] = 0 + PARENT[p] = zero(U) unshift!(O, p) end end @@ -153,19 +166,19 @@ function augment!{T<:Number}( return Δ end -function adopt!{T<:Number}( +function adopt!{T<:Number, U<:Integer}( residual_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::U, # the source vertex + target::U, # the target vertex flow_matrix::AbstractArray{T,2}, # the current flow matrix capacity_matrix::AbstractArray{T,2}, # edge flow capacities - PARENT::Vector{Int}, # parent table - TREE::Vector{Int}, # tree table - A::Vector{Int}, # active set - O::Vector{Int} # orphan set + PARENT::Vector{U}, # parent table + TREE::Vector{U}, # tree table + A::Vector{U}, # active set + O::Vector{U} # orphan set ) - tree_cap(p,q) = TREE[p] == 1 ? capacity_matrix[p,q] - flow_matrix[p,q] : + tree_cap(p,q) = TREE[p] == one(U) ? capacity_matrix[p,q] - flow_matrix[p,q] : capacity_matrix[q,p] - flow_matrix[q,p] while !isempty(O) p = pop!(O) @@ -175,7 +188,7 @@ function adopt!{T<:Number}( if TREE[q] == TREE[p] && tree_cap(q,p) > 0 # check if "origin" is either source or target o = q - while PARENT[o] ≠ 0 + while PARENT[o] ≠ zero(U) o = PARENT[o] end if o == source || o == target @@ -194,13 +207,13 @@ function adopt!{T<:Number}( unshift!(A, q) end if PARENT[q] == p - PARENT[q] = 0 + PARENT[q] = zero(U) unshift!(O, q) end end end - TREE[p] = 0 + TREE[p] = zero(U) B = setdiff(A, p) resize!(A, length(B))[:] = B end diff --git a/src/flow/dinic.jl b/src/flow/dinic.jl index 972e62113..89c75e161 100644 --- a/src/flow/dinic.jl +++ b/src/flow/dinic.jl @@ -7,21 +7,21 @@ Use a default capacity of 1 when the capacity matrix isn\'t specified. Requires arguments: residual_graph::DiGraph # the input graph -source::Int # the source vertex -target::Int # the target vertex +source::Integer # the source vertex +target::Integer # the target vertex capacity_matrix::AbstractArray{T,2} # edge flow capacities """ -function dinic_impl{T<:Number}( - residual_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex +function dinic_impl{T<:Number, U<:Integer}( + residual_graph::DiGraph{U}, # the input graph + source::U, # the source vertex + target::U, # the target vertex capacity_matrix::AbstractArray{T,2} # edge flow capacities ) n = nv(residual_graph) # number of vertexes flow_matrix = zeros(T, n, n) # initialize flow matrix - P = zeros(Int, n) # Sharable parent vector + P = zeros(U, n) # Sharable parent vector flow = 0 @@ -40,15 +40,15 @@ along all possible paths. Requires arguments: residual_graph::DiGraph # the input graph -source::Int # the source vertex -target::Int # the target vertex +source::Integer # the source vertex +target::Integer # the target vertex capacity_matrix::AbstractArray{T,2} # edge flow capacities flow_matrix::AbstractArray{T,2} # the current flow matrix """ -function blocking_flow!{T<:Number}( - residual_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex +function blocking_flow!{T<:Number, U<:Integer}( + residual_graph::DiGraph{U}, # the input graph + source::U, # the source vertex + target::U, # the target vertex capacity_matrix::AbstractArray{T,2}, # edge flow capacities flow_matrix::AbstractArray{T,2}, # the current flow matrix ) @@ -68,17 +68,17 @@ along all possible paths. Requires arguments: residual_graph::DiGraph # the input graph -source::Int # the source vertex -target::Int # the target vertex +source::Integer # the source vertex +target::Integer # the target vertex capacity_matrix::AbstractArray{T,2} # edge flow capacities flow_matrix::AbstractArray{T,2} # the current flow matrix P::AbstractArray{Int, 1} # Parent vector to store Level Graph """ -function blocking_flow!{T<:Number}( - residual_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex +function blocking_flow!{T<:Number, U<:Integer}( + residual_graph::DiGraph{U}, # the input graph + source::U, # the source vertex + target::U, # the target vertex capacity_matrix::AbstractArray{T,2}, # edge flow capacities flow_matrix::AbstractArray{T,2}, # the current flow matrix P::AbstractArray{Int, 1} # Parent vector to store Level Graph diff --git a/src/flow/edmonds_karp.jl b/src/flow/edmonds_karp.jl index 05f521f48..3c4664086 100644 --- a/src/flow/edmonds_karp.jl +++ b/src/flow/edmonds_karp.jl @@ -5,21 +5,21 @@ 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::Int # the source vertex -- target::Int # the target vertex +- source::Integer # the source vertex +- target::Integer # the target vertex - capacity_matrix::AbstractArray{T,2} # edge flow capacities """ function edmonds_karp_impl{T<:Number}( residual_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T,2} # edge flow capacities ) n = nv(residual_graph) # number of vertexes flow = 0 flow_matrix = zeros(T, n, n) # initialize flow matrix - +##============== STOPPED HERE 14 MAR P = zeros(Int, n) S = zeros(Int, n) while true diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 558d55693..4e86c7790 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -1,4 +1,4 @@ -function Graph(nv::Integer, ne::Integer; seed::Int = -1) +function Graph{T<:Integer}(nv::T, ne::Integer; seed::Int = -1) maxe = div(nv * (nv-1), 2) @assert(ne <= maxe, "Maximum number of edges for this graph is $maxe") ne > 2/3 * maxe && return complement(Graph(nv, maxe-ne)) @@ -6,8 +6,8 @@ function Graph(nv::Integer, ne::Integer; seed::Int = -1) rng = getRNG(seed) g = Graph(nv) while g.ne < ne - source = rand(rng, 1:nv) - dest = rand(rng, 1:nv) + source = rand(rng, one(T):nv) + dest = rand(rng, one(T):nv) source != dest && add_edge!(g,source,dest) end return g diff --git a/src/graphtypes/simplegraph/SimpleGraphs.jl b/src/graphtypes/simplegraph/SimpleGraphs.jl index b1758be39..af9238fb4 100644 --- a/src/graphtypes/simplegraph/SimpleGraphs.jl +++ b/src/graphtypes/simplegraph/SimpleGraphs.jl @@ -9,7 +9,7 @@ import LightGraphs: add_vertex!, add_edge!, rem_vertex!, rem_edge!, has_vertex, has_edge, in_neighbors, out_neighbors, - indegree, outdegree, degree, has_self_loops, num_self_loops + indegree, outdegree, degree, has_self_loops, num_self_loops, empty export AbstractSimpleGraph, AbstractSimpleDiGraph, AbstractSimpleEdge, SimpleEdge, SimpleGraph, SimpleGraphEdge, @@ -42,19 +42,19 @@ end vertices(g::AbstractSimpleGraph) = g.vertices edges(g::AbstractSimpleGraph) = SimpleEdgeIter(g) -nv(g::AbstractSimpleGraph) = length(vertices(g)) +nv(g::AbstractSimpleGraph) = last(vertices(g)) fadj(g::AbstractSimpleGraph) = g.fadjlist -fadj(g::AbstractSimpleGraph, v::Int) = g.fadjlist[v] +fadj(g::AbstractSimpleGraph, v::Integer) = g.fadjlist[v] badj(x...) = _NI("badj") -has_edge(g::AbstractSimpleGraph, u::Int, v::Int) = has_edge(g, edgetype(g)(u,v)) -add_edge!(g::AbstractSimpleGraph, u::Int, v::Int) = add_edge!(g, edgetype(g)(u,v)) +has_edge(g::AbstractSimpleGraph, u::Integer, v::Integer) = has_edge(g, edgetype(g)(u,v)) +add_edge!{T<:Integer}(g::AbstractSimpleGraph, u::T, v::T) = add_edge!(g, edgetype(g)(u,v)) -in_neighbors(g::AbstractSimpleGraph, v::Int) = badj(g,v) -out_neighbors(g::AbstractSimpleGraph, v::Int) = fadj(g,v) +in_neighbors(g::AbstractSimpleGraph, v::Integer) = badj(g,v) +out_neighbors(g::AbstractSimpleGraph, v::Integer) = fadj(g,v) function issubset{T<:AbstractSimpleGraph}(g::T, h::T) @@ -63,12 +63,12 @@ function issubset{T<:AbstractSimpleGraph}(g::T, h::T) return (hmin <= gmin <= gmax <= hmax) && issubset(edges(g), edges(h)) end -in_edges(g::AbstractSimpleGraph, v::Int) = [edgetype(g)(x,v) for x in in_neighbors(g, v)] -out_edges(g::AbstractSimpleGraph, v::Int) = [edgetype(g)(v,x) for x in out_neighbors(g, v)] -has_vertex(g::AbstractSimpleGraph, v::Int) = v in vertices(g) +in_edges(g::AbstractSimpleGraph, v::Integer) = [edgetype(g)(x,v) for x in in_neighbors(g, v)] +out_edges(g::AbstractSimpleGraph, v::Integer) = [edgetype(g)(v,x) for x in out_neighbors(g, v)] +has_vertex(g::AbstractSimpleGraph, v::Integer) = v in vertices(g) ne(g::AbstractSimpleGraph) = g.ne -rem_edge!(g::AbstractSimpleGraph, u::Int, v::Int) = rem_edge!(g, edgetype(g)(u, v)) +rem_edge!{T<:Integer}(g::AbstractSimpleGraph, u::T, v::T) = rem_edge!(g, edgetype(g)(u, v)) """ Remove the vertex `v` from graph `g`. @@ -79,7 +79,7 @@ and removing the vertex `n` from the graph. After removal the vertices in the ` 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. """ -function rem_vertex!(g::AbstractSimpleGraph, v::Int) +function rem_vertex!(g::AbstractSimpleGraph, v::Integer) v in vertices(g) || return false n = nv(g) @@ -126,8 +126,4 @@ include("simpledigraph.jl") include("simplegraph.jl") include("simpleedgeiter.jl") - - - - end # module diff --git a/src/graphtypes/simplegraph/simpledigraph.jl b/src/graphtypes/simplegraph/simpledigraph.jl index 681282de3..89003ae9d 100644 --- a/src/graphtypes/simplegraph/simpledigraph.jl +++ b/src/graphtypes/simplegraph/simpledigraph.jl @@ -1,36 +1,46 @@ typealias SimpleDiGraphEdge SimpleEdge """A type representing a directed graph.""" -type SimpleDiGraph <: AbstractSimpleGraph - vertices::UnitRange{Int} +type SimpleDiGraph{T<:Integer} <: AbstractSimpleGraph + vertices::UnitRange{T} ne::Int - fadjlist::Vector{Vector{Int}} # [src]: (dst, dst, dst) - badjlist::Vector{Vector{Int}} # [dst]: (src, src, src) + fadjlist::Vector{Vector{T}} # [src]: (dst, dst, dst) + badjlist::Vector{Vector{T}} # [dst]: (src, src, src) end -edgetype(::SimpleDiGraph) = SimpleDiGraphEdge - -function SimpleDiGraph(n::Int) - fadjlist = Vector{Vector{Int}}() - badjlist = Vector{Vector{Int}}() - for i = 1:n - push!(badjlist, Vector{Int}()) - push!(fadjlist, Vector{Int}()) - end - return SimpleDiGraph(1:n, 0, badjlist, fadjlist) +eltype{T<:Integer}(x::SimpleDiGraph{T}) = T + +# DiGraph{UInt8}(6), DiGraph{Int16}(7), DiGraph{Int8}() +function (::Type{SimpleDiGraph{T}}){T<:Integer}(n::Integer = 0) + fadjlist = Vector{Vector{T}}() + badjlist = Vector{Vector{T}}() + for i = one(T):n + push!(badjlist, Vector{T}()) + push!(fadjlist, Vector{T}()) + end + vertices = one(T):n + return SimpleDiGraph(vertices, 0, badjlist, fadjlist) end -SimpleDiGraph() = SimpleDiGraph(0) +# DiGraph() +SimpleDiGraph() = SimpleDiGraph{Int}() + +# DiGraph(6), DiGraph(0x5) +SimpleDiGraph{T<:Integer}(n::T) = SimpleDiGraph{T}(n) + +# SimpleDiGraph(UInt8) +SimpleDiGraph{T<:Integer}(::Type{T}) = SimpleDiGraph{T}(zero(T)) -function SimpleDiGraph{T<:Real}(adjmx::SparseMatrixCSC{T}) +# sparse adjacency matrix constructor: DiGraph(adjmx) +function (::Type{SimpleDiGraph{T}}){T<:Integer, U}(adjmx::SparseMatrixCSC{U}) dima, dimb = size(adjmx) isequal(dima,dimb) || error("Adjacency / distance matrices must be square") - g = SimpleDiGraph(dima) + g = SimpleDiGraph(T(dima)) maxc = length(adjmx.colptr) for c = 1:(maxc-1) for rind = adjmx.colptr[c]:adjmx.colptr[c+1]-1 - isnz = (adjmx.nzval[rind] != zero(T)) + isnz = (adjmx.nzval[rind] != zero(U)) if isnz r = adjmx.rowval[rind] add_edge!(g,r,c) @@ -40,11 +50,12 @@ function SimpleDiGraph{T<:Real}(adjmx::SparseMatrixCSC{T}) return g end -function SimpleDiGraph{T<:Real}(adjmx::AbstractMatrix{T}) +# dense adjacency matrix constructor: DiGraph{UInt8}(adjmx) +function (::Type{SimpleDiGraph{T}}){T<:Integer}(adjmx::AbstractMatrix) dima,dimb = size(adjmx) isequal(dima,dimb) || error("Adjacency / distance matrices must be square") - g = SimpleDiGraph(dima) + g = SimpleDiGraph(T(dima)) for i in find(adjmx) ind = ind2sub((dima,dimb),i) add_edge!(g,ind...) @@ -52,6 +63,19 @@ function SimpleDiGraph{T<:Real}(adjmx::AbstractMatrix{T}) return g end +# DiGraph(adjmx) +SimpleDiGraph(adjmx::AbstractMatrix) = SimpleDiGraph{Int}(adjmx) + +# converts DiGraph{Int} to DiGraph{Int32} +function (::Type{SimpleDiGraph{T}}){T<:Integer}(g::SimpleDiGraph) + h_vertices = one(T):T(nv(g)) + h_fadj = [Vector{T}(x) for x in fadj(g)] + h_badj = [Vector{T}(x) for x in badj(g)] + return SimpleDiGraph(h_vertices, ne(g), h_fadj, h_badj) +end + + +# constructor from abstract graph: DiGraph(graph) function SimpleDiGraph(g::AbstractSimpleGraph) h = SimpleDiGraph(nv(g)) h.ne = ne(g) * 2 - num_self_loops(g) @@ -60,13 +84,16 @@ function SimpleDiGraph(g::AbstractSimpleGraph) return h end +edgetype{T<:Integer}(::SimpleDiGraph{T}) = SimpleGraphEdge{T} + + badj(g::SimpleDiGraph) = g.badjlist -badj(g::SimpleDiGraph, v::Int) = badj(g)[v] +badj(g::SimpleDiGraph, v::Integer) = badj(g)[v] -function copy(g::SimpleDiGraph) - return SimpleDiGraph(g.vertices, g.ne, deepcopy(g.fadjlist), deepcopy(g.badjlist)) -end +copy{T<:Integer}(g::SimpleDiGraph{T}) = + SimpleDiGraph{T}(g.vertices, g.ne, deepcopy(g.fadjlist), deepcopy(g.badjlist)) + ==(g::SimpleDiGraph, h::SimpleDiGraph) = vertices(g) == vertices(h) && @@ -76,8 +103,9 @@ end is_directed(g::SimpleDiGraph) = true is_directed(::Type{SimpleDiGraph}) = true +is_directed{T}(::Type{SimpleDiGraph{T}}) = true -function add_edge!(g::SimpleDiGraph, e::SimpleDiGraphEdge) +function add_edge!{T<:Integer}(g::SimpleDiGraph{T}, e::SimpleDiGraphEdge) s, d = Tuple(e) (s in vertices(g) && d in vertices(g)) || return false inserted = _insert_and_dedup!(g.fadjlist[s], d) @@ -99,10 +127,10 @@ function rem_edge!(g::SimpleDiGraph, e::SimpleDiGraphEdge) end -function add_vertex!(g::SimpleDiGraph) +function add_vertex!{T<:Integer}(g::SimpleDiGraph{T}) g.vertices = 1:nv(g)+1 - push!(g.badjlist, Vector{Int}()) - push!(g.fadjlist, Vector{Int}()) + push!(g.badjlist, Vector{T}()) + push!(g.fadjlist, Vector{T}()) return true end @@ -117,3 +145,5 @@ function has_edge(g::SimpleDiGraph, e::SimpleDiGraphEdge) return length(searchsorted(badj(g,v), u)) > 0 end end + +empty{T<:Integer}(g::SimpleDiGraph{T}) = SimpleDiGraph{T}() diff --git a/src/graphtypes/simplegraph/simpleedge.jl b/src/graphtypes/simplegraph/simpleedge.jl index 812686ade..95904b49f 100644 --- a/src/graphtypes/simplegraph/simpleedge.jl +++ b/src/graphtypes/simplegraph/simpleedge.jl @@ -3,9 +3,9 @@ import LightGraphs: AbstractEdge, src, dst, reverse abstract AbstractSimpleEdge <: AbstractEdge -immutable SimpleEdge <: AbstractSimpleEdge - src::Int - dst::Int +immutable SimpleEdge{T<:Integer} <: AbstractSimpleEdge + src::T + dst::T end # Accessors diff --git a/src/graphtypes/simplegraph/simpleedgeiter.jl b/src/graphtypes/simplegraph/simpleedgeiter.jl index b4e2ebec9..a1332ce17 100644 --- a/src/graphtypes/simplegraph/simpleedgeiter.jl +++ b/src/graphtypes/simplegraph/simpleedgeiter.jl @@ -1,17 +1,17 @@ -type SimpleEdgeIter <: AbstractEdgeIter +type SimpleEdgeIter{T<:Integer} <: AbstractEdgeIter m::Int - adj::Vector{Vector{Int}} + adj::Vector{Vector{T}} directed::Bool end -immutable SimpleEdgeIterState - s::Int # src vertex - di::Int # index into adj of dest vertex +immutable SimpleEdgeIterState{T<:Integer} + s::T # src vertex + di::T # index into adj of dest vertex fin::Bool end -eltype(::Type{SimpleEdgeIter}) = SimpleEdge +eltype{T}(::Type{SimpleEdgeIter{T}}) = SimpleEdge SimpleEdgeIter(g::SimpleGraph) = SimpleEdgeIter(ne(g), g.fadjlist, false) SimpleEdgeIter(g::SimpleDiGraph) = SimpleEdgeIter(ne(g), g.fadjlist, true) diff --git a/src/graphtypes/simplegraph/simplegraph.jl b/src/graphtypes/simplegraph/simplegraph.jl index d96012aa3..0a7da0f89 100644 --- a/src/graphtypes/simplegraph/simplegraph.jl +++ b/src/graphtypes/simplegraph/simplegraph.jl @@ -1,33 +1,41 @@ typealias SimpleGraphEdge SimpleEdge """A type representing an undirected graph.""" -type SimpleGraph <: AbstractSimpleGraph - vertices::UnitRange{Int} +type SimpleGraph{T<:Integer} <: AbstractSimpleGraph + vertices::UnitRange{T} ne::Int - fadjlist::Vector{Vector{Int}} # [src]: (dst, dst, dst) + fadjlist::Vector{Vector{T}} # [src]: (dst, dst, dst) end -edgetype(::SimpleGraph) = SimpleGraphEdge +eltype{T<:Integer}(x::SimpleGraph{T}) = T -function SimpleGraph(n::Int) - fadjlist = Vector{Vector{Int}}() - sizehint!(fadjlist,n) - for i = 1:n - # sizehint!(i_s, n/4) - # sizehint!(o_s, n/4) - push!(fadjlist, Vector{Int}()) +# Graph{UInt8}(6), Graph{Int16}(7) +function (::Type{SimpleGraph{T}}){T<:Integer}(n::Integer = 0) + fadjlist = Vector{Vector{T}}() + sizehint!(fadjlist, n) + for _ = one(T):n + push!(fadjlist, Vector{T}()) end - return SimpleGraph(1:n, 0, fadjlist) + vertices = one(T):T(n) + return SimpleGraph{T}(vertices, 0, fadjlist) end -SimpleGraph() = SimpleGraph(0) +# Graph() +SimpleGraph() = SimpleGraph{Int}() -function SimpleGraph{T<:Real}(adjmx::AbstractMatrix{T}) +# Graph(6), Graph(0x5) +SimpleGraph{T<:Integer}(n::T) = SimpleGraph{T}(n) + +# SimpleGraph(UInt8) +SimpleGraph{T<:Integer}(::Type{T}) = SimpleGraph{T}(zero(T)) + +# Graph{UInt8}(adjmx) +function (::Type{SimpleGraph{T}}){T<:Integer}(adjmx::AbstractMatrix) dima,dimb = size(adjmx) isequal(dima,dimb) || error("Adjacency / distance matrices must be square") issymmetric(adjmx) || error("Adjacency / distance matrices must be symmetric") - g = SimpleGraph(dima) + g = SimpleGraph(T(dima)) for i in find(triu(adjmx)) ind = ind2sub((dima,dimb),i) add_edge!(g,ind...) @@ -35,12 +43,23 @@ function SimpleGraph{T<:Real}(adjmx::AbstractMatrix{T}) return g end +# converts Graph{Int} to Graph{Int32} +function (::Type{SimpleGraph{T}}){T<:Integer}(g::SimpleGraph) + h_vertices = one(T):T(nv(g)) + h_fadj = [Vector{T}(x) for x in fadj(g)] + return SimpleGraph(h_vertices, ne(g), h_fadj) +end + + +# Graph(adjmx) +SimpleGraph(adjmx::AbstractMatrix) = SimpleGraph{Int}(adjmx) + +# Graph(digraph) function SimpleGraph(g::SimpleDiGraph) gnv = nv(g) - edgect = 0 newfadj = deepcopy(g.fadjlist) - for i in 1:gnv + for i in vertices(g) for j in badj(g,i) if (_insert_and_dedup!(newfadj[i], j)) edgect += 2 # this is a new edge only in badjlist @@ -56,13 +75,15 @@ function SimpleGraph(g::SimpleDiGraph) return SimpleGraph(vertices(g), edgect ÷ 2, newfadj) end +edgetype{T<:Integer}(::SimpleGraph{T}) = SimpleGraphEdge{T} + """Returns the backwards adjacency list of a graph. For each vertex the Array of `dst` for each edge eminating from that vertex. NOTE: returns a reference, not a copy. Do not modify result. """ badj(g::SimpleGraph) = fadj(g) -badj(g::SimpleGraph, v::Int) = fadj(g, v) +badj(g::SimpleGraph, v::Integer) = fadj(g, v) """Returns the adjacency list of a graph. @@ -71,11 +92,9 @@ For each vertex the Array of `dst` for each edge eminating from that vertex. NOTE: returns a reference, not a copy. Do not modify result. """ adj(g::SimpleGraph) = fadj(g) -adj(g::SimpleGraph, v::Int) = fadj(g, v) +adj(g::SimpleGraph, v::Integer) = fadj(g, v) -function copy(g::SimpleGraph) - return SimpleGraph(g.vertices, g.ne, deepcopy(g.fadjlist)) -end +copy(g::SimpleGraph) = SimpleGraph(g.vertices, g.ne, deepcopy(g.fadjlist)) ==(g::SimpleGraph, h::SimpleGraph) = vertices(g) == vertices(h) && @@ -85,6 +104,7 @@ end """Return `true` if `g` is a directed graph.""" is_directed(::Type{SimpleGraph}) = false +is_directed{T}(::Type{SimpleGraph{T}}) = false is_directed(g::SimpleGraph) = false function has_edge(g::SimpleGraph, e::SimpleGraphEdge) @@ -125,9 +145,11 @@ end """Add a new vertex to the graph `g`.""" -function add_vertex!(g::SimpleGraph) - g.vertices = 1:nv(g)+1 - push!(g.fadjlist, Vector{Int}()) +function add_vertex!{T<:Integer}(g::SimpleGraph{T}) + g.vertices = one(T):nv(g)+one(T) + push!(g.fadjlist, Vector{T}()) return true end + +empty{T<:Integer}(g::SimpleGraph{T}) = SimpleGraph{T}() diff --git a/src/interface.jl b/src/interface.jl index 90169b013..3494c799b 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -53,47 +53,52 @@ vertices(g::AbstractGraph) = _NI("vertices") The returned iterator is valid for one pass over the edges, and is invalidated by changes to `g`. """ -edges(g::AbstractGraph) = _NI("edges") +edges(x...) = _NI("edges") -is_directed(g::AbstractGraph) = _NI("is_directed") -is_directed{T<:AbstractGraph}(::Type{T}) = _NI("is_directed") +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::AbstractGraph) = _NI("add_vertex!") +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::AbstractGraph, e::AbstractEdge) = _NI("add_edge!") +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::AbstractGraph, v::Int) = _NI("rem_vertex!") +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::AbstractGraph, e::AbstractEdge) = _NI("rem_edge!") +rem_edge!(x...) = _NI("rem_edge!") """Return true if `v` is a vertex of `g`.""" -has_vertex(g::AbstractGraph, v::Int) = _NI("has_vertex") +has_vertex(x...) = _NI("has_vertex") """Return true if the graph `g` has an edge `e`.""" -has_edge(g::AbstractGraph, e::AbstractEdge) = _NI("has_edge") +has_edge(x...) = _NI("has_edge") """ 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. """ -in_neighbors(g::AbstractGraph, v::Int) = _NI("in_neighbors") +in_neighbors(x...) = _NI("in_neighbors") """ 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. """ -out_neighbors(g::AbstractGraph, v::Int) = _NI("out_neighbors") +out_neighbors(x...) = _NI("out_neighbors") + +""" +Return an empty version of the same type of graph. +""" +empty(x...) = _NI("empty") diff --git a/src/operators.jl b/src/operators.jl index 10cda7e73..d70ad1a0b 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -231,7 +231,7 @@ sum(g::AbstractGraph) = ne(g) sparse(g::AbstractGraph) = adjacency_matrix(g) #arrayfunctions = (:eltype, :length, :ndims, :size, :strides, :issymmetric) -eltype(g::AbstractGraph) = Float64 +# eltype(g::AbstractGraph) = Float64 length(g::AbstractGraph) = nv(g)*nv(g) ndims(g::AbstractGraph) = 2 issymmetric(g::AbstractGraph) = !is_directed(g) @@ -310,20 +310,20 @@ sg, vmap = subgraph(g, 5:8) @assert vm[4] == 8 sg, vmap = subgraph(g, [2,8,3,4]) -@asssert sg == g[[2,8,3,4]] +@assert sg == g[[2,8,3,4]] elist = [Edge(1,2), Edge(3,4), Edge(4,8)] sg, vmap = subgraph(g, elist) -@asssert sg == g[elist] +@assert sg == g[elist] ``` """ -function induced_subgraph{T<:AbstractGraph}(g::T, vlist::AbstractVector{Int}) +function induced_subgraph{T<:AbstractGraph, U<:Integer}(g::T, vlist::AbstractVector{U}) allunique(vlist) || error("Vertices in subgraph list must be unique") h = T(length(vlist)) - newvid = Dict{Int, Int}() - vmap =Vector{Int}(length(vlist)) + newvid = Dict{U, U}() + vmap =Vector{U}(length(vlist)) for (i,v) in enumerate(vlist) - newvid[v] = i + newvid[v] = U(i) vmap[i] = v end @@ -341,10 +341,11 @@ function induced_subgraph{T<:AbstractGraph}(g::T, vlist::AbstractVector{Int}) end -function induced_subgraph{T<:AbstractGraph}(g::T, elist::AbstractVector{Edge}) - h = T() - newvid = Dict{Int, Int}() - vmap = Vector{Int}() +function induced_subgraph{T<:AbstractGraph, U<:AbstractEdge}(g::T, elist::AbstractVector{U}) + h = empty(g) + et = eltype(h) + newvid = Dict{et, et}() + vmap = Vector{et}() for e in elist u, v = Tuple(e) diff --git a/src/shortestpaths/dijkstra.jl b/src/shortestpaths/dijkstra.jl index 8fb9c9057..9f40c6524 100644 --- a/src/shortestpaths/dijkstra.jl +++ b/src/shortestpaths/dijkstra.jl @@ -1,17 +1,17 @@ abstract AbstractDijkstraState<:AbstractPathState -immutable DijkstraHeapEntry{T} - vertex::Int +immutable DijkstraHeapEntry{T, U<:Integer} + vertex::U dist::T end isless(e1::DijkstraHeapEntry, e2::DijkstraHeapEntry) = e1.dist < e2.dist -type DijkstraState{T}<: AbstractDijkstraState - parents::Vector{Int} +type DijkstraState{T, U<:Integer}<: AbstractDijkstraState + parents::Vector{U} dists::Vector{T} - predecessors::Vector{Vector{Int}} - pathcounts::Vector{Int} + predecessors::Vector{Vector{U}} + pathcounts::Vector{U} end """Performs [Dijkstra's algorithm](http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) @@ -22,26 +22,26 @@ information (see below). With `allpaths=true`, returns a `DijkstraState` that keeps track of all predecessors of a given vertex (see below). """ -function dijkstra_shortest_paths{T}( +function dijkstra_shortest_paths{T, U<:Integer}( g::AbstractGraph, - srcs::Vector{Int}, + srcs::Vector{U}, distmx::AbstractArray{T, 2}=DefaultDistance(); allpaths=false ) nvg = nv(g) dists = fill(typemax(T), nvg) - parents = zeros(Int, nvg) - preds = fill(Vector{Int}(),nvg) + parents = zeros(U, nvg) + preds = fill(Vector{U}(),nvg) visited = zeros(Bool, nvg) pathcounts = zeros(Int, nvg) - H = Vector{DijkstraHeapEntry{T}}() # this should be Vector{T}() in 0.4, I think. + H = Vector{DijkstraHeapEntry{T, U}}() # this should be Vector{T}() in 0.4, I think. dists[srcs] = zero(T) pathcounts[srcs] = 1 sizehint!(H, nvg) for v in srcs - heappush!(H, DijkstraHeapEntry{T}(v, dists[v])) + heappush!(H, DijkstraHeapEntry{T, U}(v, dists[v])) visited[v] = true end @@ -60,13 +60,13 @@ function dijkstra_shortest_paths{T}( if allpaths preds[v] = [u;] end - heappush!(H, DijkstraHeapEntry{T}(v, alt)) + heappush!(H, DijkstraHeapEntry{T, U}(v, alt)) # info("Pushed $v") else if alt < dists[v] dists[v] = alt parents[v] = u - heappush!(H, DijkstraHeapEntry{T}(v, alt)) + heappush!(H, DijkstraHeapEntry{T, U}(v, alt)) end if alt == dists[v] pathcounts[v] += pathcounts[u] @@ -84,8 +84,8 @@ function dijkstra_shortest_paths{T}( preds[src] = [] end - return DijkstraState{T}(parents, dists, preds, pathcounts) + return DijkstraState{T, U}(parents, dists, preds, pathcounts) end -dijkstra_shortest_paths{T}(g::AbstractGraph, src::Int, distmx::AbstractArray{T,2}=DefaultDistance(); allpaths=false) = +dijkstra_shortest_paths{T, U}(g::AbstractGraph, src::U, distmx::AbstractArray{T,2}=DefaultDistance(); allpaths=false) = dijkstra_shortest_paths(g, [src;], distmx; allpaths=allpaths) diff --git a/test/biconnectivity/articulation.jl b/test/biconnectivity/articulation.jl index 079ab151d..d17fb13c2 100644 --- a/test/biconnectivity/articulation.jl +++ b/test/biconnectivity/articulation.jl @@ -1,5 +1,5 @@ @testset "Articulation" begin - g = Graph(13) + g = Graph{UInt8}(13) add_edge!(g, 1, 7) add_edge!(g, 1, 2) add_edge!(g, 1, 3) @@ -29,6 +29,7 @@ end h = LightGraphs.blkdiag(WheelGraph(5), WheelGraph(5)) + h = Graph{UInt16}(h) add_edge!(h, 5,6) @test articulation(h) == [5,6] end diff --git a/test/graphtypes/simplegraphs/simplegraphs.jl b/test/graphtypes/simplegraphs/simplegraphs.jl index aebdb5895..a1e1856c0 100644 --- a/test/graphtypes/simplegraphs/simplegraphs.jl +++ b/test/graphtypes/simplegraphs/simplegraphs.jl @@ -34,8 +34,8 @@ type DummySimpleGraph <: AbstractSimpleGraph end @test !is_directed(SimpleGraph) @test is_directed(SimpleDiGraph) - @test edgetype(SimpleGraph()) == SimpleEdge - @test edgetype(SimpleDiGraph()) == SimpleEdge + @test edgetype(SimpleGraph()) == SimpleEdge{Int} + @test edgetype(SimpleDiGraph()) == SimpleEdge{Int} @test sprint(show, SimpleGraph()) == "empty undirected simple graph" @test sprint(show, SimpleDiGraph()) == "empty directed simple graph" @@ -216,7 +216,6 @@ type DummySimpleGraph <: AbstractSimpleGraph end @test rem_vertex!(g, 3) @test g == CompleteGraph(4) - badadjmx = [ 0 1 0; 1 0 1] @test_throws ErrorException SimpleGraph(badadjmx) @test_throws ErrorException SimpleGraph(sparse(badadjmx)) diff --git a/test/operators.jl b/test/operators.jl index 4130d0047..9ae394012 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -94,7 +94,7 @@ @test_throws ErrorException sum(p,3) @test sparse(p) == adjacency_matrix(p) - @test eltype(p) == Float64 + @test eltype(p) == Int @test length(p) == 100 @test ndims(p) == 2 @test issymmetric(p) @@ -183,6 +183,6 @@ @test egonet(g, 1, 0) == Graph(1,0) @test egonet(g, 1, 1) == g - @test eltype(g) == Float64 + @test eltype(g) == Int @test ndims(g) == 2 end From 3099034b5796ba9550bd7a6f555bc1eba1a3f705 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Wed, 15 Mar 2017 10:12:05 -0700 Subject: [PATCH 14/56] more tests --- src/LightGraphs.jl | 4 +- src/centrality/pagerank.jl | 2 +- src/flow/boykov_kolmogorov.jl | 98 ++++++++++---------- src/flow/dinic.jl | 20 ++-- src/flow/edmonds_karp.jl | 17 ++-- src/flow/ext_multiroute_flow.jl | 24 ++--- src/flow/kishimoto.jl | 12 +-- src/flow/maximum_flow.jl | 36 +++---- src/flow/multiroute_flow.jl | 26 +++--- src/flow/push_relabel.jl | 56 +++++------ src/generators/randgraphs.jl | 22 ++--- src/graphtypes/simplegraph/SimpleGraphs.jl | 10 +- src/graphtypes/simplegraph/simpleedge.jl | 17 ++-- src/graphtypes/simplegraph/simpleedgeiter.jl | 6 +- test/biconnectivity/articulation.jl | 62 +++++++------ test/biconnectivity/biconnect.jl | 48 +++++----- test/centrality/betweenness.jl | 19 ++-- test/centrality/closeness.jl | 23 +++-- test/centrality/degree.jl | 8 +- test/centrality/katz.jl | 6 +- test/centrality/pagerank.jl | 8 +- test/community/clustering.jl | 12 ++- test/community/core-periphery.jl | 30 +++--- test/community/label_propagation.jl | 24 ++--- test/community/modularity.jl | 14 ++- test/flow/boykov_kolmogorov.jl | 33 +++---- test/flow/dinic.jl | 78 ++++++++-------- test/flow/edmonds_karp.jl | 91 +++++++++--------- test/flow/maximum_flow.jl | 39 ++++---- test/flow/multiroute_flow.jl | 42 +++++---- 30 files changed, 469 insertions(+), 418 deletions(-) diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 2fa61318f..82fc7d8f5 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -10,8 +10,8 @@ using StatsBase: fit, Histogram using SimpleTraits import Base: write, ==, <, *, ≈, convert, isless, issubset, union, intersect, - reverse, reverse!, blkdiag, getindex, setindex!, show, print, copy, in, - sum, size, sparse, eltype, length, ndims, transpose, + reverse, reverse!, blkdiag, isassigned, getindex, setindex!, show, + print, copy, in, sum, size, sparse, eltype, length, ndims, transpose, ctranspose, join, start, next, done, eltype, get, issymmetric, A_mul_B!, Pair, Tuple export diff --git a/src/centrality/pagerank.jl b/src/centrality/pagerank.jl index 4ef1c1494..f43026e7a 100644 --- a/src/centrality/pagerank.jl +++ b/src/centrality/pagerank.jl @@ -13,7 +13,7 @@ function pagerank(g::DiGraph, α=0.85, n=100, ϵ = 1.0e-6) S[find(S .== Inf)]=0.0 M = A' # need a separate line due to bug #17456 in julia M = (Diagonal(S) * M)' - N = nv(g) + N = Int(nv(g)) x = repmat([1.0/N], N) p = repmat([1.0/N], N) dangling_weights = p diff --git a/src/flow/boykov_kolmogorov.jl b/src/flow/boykov_kolmogorov.jl index 0fe9f6cf2..f1cbf2e87 100644 --- a/src/flow/boykov_kolmogorov.jl +++ b/src/flow/boykov_kolmogorov.jl @@ -24,10 +24,10 @@ Author: Júlio Hoffimann Mendes (juliohm@stanford.edu) """ function boykov_kolmogorov_impl{T<:Number, U<:Integer}( - residual_graph::DiGraph, # the input graph - source::U, # the source vertex - target::U, # the target vertex - capacity_matrix::AbstractArray{T,2} # edge flow capacities + residual_graph::DiGraph{U}, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex + capacity_matrix::AbstractMatrix{T} # edge flow capacities ) n = nv(residual_graph) @@ -60,39 +60,39 @@ function boykov_kolmogorov_impl{T<:Number, U<:Integer}( return flow, flow_matrix, TREE end -function find_path!{T<:Number, U<:Integer}( - residual_graph::DiGraph{U}, # the input graph +function find_path!{T<:Integer}( + residual_graph::DiGraph{T}, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - flow_matrix::AbstractArray{T,2}, # the current flow matrix - capacity_matrix::AbstractArray{T,2}, # edge flow capacities - PARENT::Vector{U}, # parent table - TREE::Vector{U}, # tree table - A::Vector{U} # active set + flow_matrix::AbstractMatrix, # the current flow matrix + capacity_matrix::AbstractMatrix, # edge flow capacities + PARENT::Vector{T}, # parent table + TREE::Vector{T}, # tree table + A::Vector{T} # active set ) - tree_cap(p,q) = TREE[p] == 1 ? capacity_matrix[p,q] - flow_matrix[p,q] : + tree_cap(p,q) = TREE[p] == one(T) ? capacity_matrix[p,q] - flow_matrix[p,q] : capacity_matrix[q,p] - flow_matrix[q,p] while !isempty(A) p = last(A) for q in neighbors(residual_graph, p) if tree_cap(p,q) > 0 - if TREE[q] == zero(U) + if TREE[q] == zero(T) TREE[q] = TREE[p] PARENT[q] = p unshift!(A, q) end - if TREE[q] ≠ zero(U) && TREE[q] ≠ TREE[p] + if TREE[q] ≠ zero(T) && TREE[q] ≠ TREE[p] # p -> source path_to_source = [p] - while PARENT[p] ≠ zero(U) + while PARENT[p] ≠ zero(T) p = PARENT[p] push!(path_to_source, p) end # q -> target path_to_target = [q] - while PARENT[q] ≠ zero(U) + while PARENT[q] ≠ zero(T) q = PARENT[q] push!(path_to_target, q) end @@ -111,29 +111,29 @@ function find_path!{T<:Number, U<:Integer}( pop!(A) end - return Vector{U}() + return Vector{T}() end -find_path!{T<:Number, U<:Integer}( - residual_graph::DiGraph{U}, # the input graph +find_path!{T<:Integer}( + residual_graph::DiGraph{T}, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - flow_matrix::AbstractArray{T,2}, # the current flow matrix - capacity_matrix::AbstractArray{T,2}, # edge flow capacities + flow_matrix::AbstractMatrix, # the current flow matrix + capacity_matrix::AbstractMatrix, # edge flow capacities PARENT::Vector, # parent table TREE::Vector, # tree table A::Vector # active set - ) = find_path!(residual_graph, U(source), U(target), - flow_matrix, capacity_matrix, U.(PARENT), - U.(TREE), U.(A)) - -function augment!{T<:Number, U<:Integer}( - path::AbstractVector, # path from source to target - flow_matrix::AbstractArray{T,2}, # the current flow matrix - capacity_matrix::AbstractArray{T,2}, # edge flow capacities - PARENT::Vector{U}, # parent table - TREE::Vector{U}, # tree table - O::Vector{U} # orphan set + ) = find_path!(residual_graph, T(source), T(target), + flow_matrix, capacity_matrix, T.(PARENT), + T.(TREE), T.(A)) + +function augment!{T<:Integer}( + path::AbstractVector{T}, # path from source to target + flow_matrix::AbstractMatrix, # the current flow matrix + capacity_matrix::AbstractMatrix, # edge flow capacities + PARENT::Vector{T}, # parent table + TREE::Vector{T}, # tree table + O::Vector{T} # orphan set ) # bottleneck capacity @@ -152,12 +152,12 @@ function augment!{T<:Number, U<:Integer}( # create orphans if flow_matrix[p,q] == capacity_matrix[p,q] - if TREE[p] == TREE[q] == one(U) - PARENT[q] = zero(U) + if TREE[p] == TREE[q] == one(T) + PARENT[q] = zero(T) unshift!(O, q) end if TREE[p] == TREE[q] == 2 - PARENT[p] = zero(U) + PARENT[p] = zero(T) unshift!(O, p) end end @@ -166,19 +166,19 @@ function augment!{T<:Number, U<:Integer}( return Δ end -function adopt!{T<:Number, U<:Integer}( - residual_graph::DiGraph, # the input graph - source::U, # the source vertex - target::U, # the target vertex - flow_matrix::AbstractArray{T,2}, # the current flow matrix - capacity_matrix::AbstractArray{T,2}, # edge flow capacities - PARENT::Vector{U}, # parent table - TREE::Vector{U}, # tree table - A::Vector{U}, # active set - O::Vector{U} # orphan set +function adopt!{T<:Integer}( + residual_graph::DiGraph{T}, # 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 + PARENT::Vector, # parent table + TREE::Vector, # tree table + A::Vector, # active set + O::Vector # orphan set ) - tree_cap(p,q) = TREE[p] == one(U) ? capacity_matrix[p,q] - flow_matrix[p,q] : + tree_cap(p,q) = TREE[p] == one(T) ? capacity_matrix[p,q] - flow_matrix[p,q] : capacity_matrix[q,p] - flow_matrix[q,p] while !isempty(O) p = pop!(O) @@ -188,7 +188,7 @@ function adopt!{T<:Number, U<:Integer}( if TREE[q] == TREE[p] && tree_cap(q,p) > 0 # check if "origin" is either source or target o = q - while PARENT[o] ≠ zero(U) + while PARENT[o] ≠ zero(T) o = PARENT[o] end if o == source || o == target @@ -207,13 +207,13 @@ function adopt!{T<:Number, U<:Integer}( unshift!(A, q) end if PARENT[q] == p - PARENT[q] = zero(U) + PARENT[q] = zero(T) unshift!(O, q) end end end - TREE[p] = zero(U) + TREE[p] = zero(T) B = setdiff(A, p) resize!(A, length(B))[:] = B end diff --git a/src/flow/dinic.jl b/src/flow/dinic.jl index 89c75e161..ab6e73b46 100644 --- a/src/flow/dinic.jl +++ b/src/flow/dinic.jl @@ -14,14 +14,14 @@ capacity_matrix::AbstractArray{T,2} # edge flow capacities function dinic_impl{T<:Number, U<:Integer}( residual_graph::DiGraph{U}, # the input graph - source::U, # the source vertex - target::U, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T,2} # edge flow capacities ) n = nv(residual_graph) # number of vertexes flow_matrix = zeros(T, n, n) # initialize flow matrix - P = zeros(U, n) # Sharable parent vector + P = zeros(Int, n) # Sharable parent vector flow = 0 @@ -47,12 +47,12 @@ flow_matrix::AbstractArray{T,2} # the current flow matrix """ function blocking_flow!{T<:Number, U<:Integer}( residual_graph::DiGraph{U}, # the input graph - source::U, # the source vertex - target::U, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T,2}, # edge flow capacities flow_matrix::AbstractArray{T,2}, # the current flow matrix ) - P = zeros(T, nv(residual_graph)) + P = zeros(Int, nv(residual_graph)) return blocking_flow!(residual_graph, source, target, @@ -72,16 +72,16 @@ source::Integer # the source vertex target::Integer # the target vertex capacity_matrix::AbstractArray{T,2} # edge flow capacities flow_matrix::AbstractArray{T,2} # the current flow matrix -P::AbstractArray{Int, 1} # Parent vector to store Level Graph +P::AbstractVector{Int} # Parent vector to store Level Graph """ function blocking_flow!{T<:Number, U<:Integer}( residual_graph::DiGraph{U}, # the input graph - source::U, # the source vertex - target::U, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T,2}, # edge flow capacities flow_matrix::AbstractArray{T,2}, # the current flow matrix - P::AbstractArray{Int, 1} # Parent vector to store Level Graph + P::AbstractVector{Int} # Parent vector to store Level Graph ) n = nv(residual_graph) # number of vertexes diff --git a/src/flow/edmonds_karp.jl b/src/flow/edmonds_karp.jl index 3c4664086..785f3b92e 100644 --- a/src/flow/edmonds_karp.jl +++ b/src/flow/edmonds_karp.jl @@ -19,7 +19,6 @@ function edmonds_karp_impl{T<:Number}( n = nv(residual_graph) # number of vertexes flow = 0 flow_matrix = zeros(T, n, n) # initialize flow matrix -##============== STOPPED HERE 14 MAR P = zeros(Int, n) S = zeros(Int, n) while true @@ -30,7 +29,7 @@ function edmonds_karp_impl{T<:Number}( if flag != 0 # no more valid paths break else - path = [v] # initialize path + path = [Int(v)] # initialize path sizehint!(path, n) u = v @@ -43,7 +42,7 @@ function edmonds_karp_impl{T<:Number}( u = v # trace path from v to target while u!=target u = S[u] - push!(path, u) + push!(path, Int(u)) end # augment flow along path flow += augment_path!(path, flow_matrix, capacity_matrix) @@ -93,10 +92,10 @@ Flag Values: 1 => No Path to target 2 => No Path to source """ -function fetch_path{T<:Number}( - residual_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex +function fetch_path{T<:Number, U<:Integer}( + residual_graph::DiGraph{U}, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex flow_matrix::AbstractArray{T,2}, # the current flow matrix capacity_matrix::AbstractArray{T,2} # edge flow capacities ) @@ -132,8 +131,8 @@ Requires arguments: """ function fetch_path!{T<:Number}( residual_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex flow_matrix::AbstractArray{T,2}, # the current flow matrix capacity_matrix::AbstractArray{T,2}, # edge flow capacities P::Vector{Int}, # parent table of path init to -1s diff --git a/src/flow/ext_multiroute_flow.jl b/src/flow/ext_multiroute_flow.jl index 3ae67341d..231971a65 100644 --- a/src/flow/ext_multiroute_flow.jl +++ b/src/flow/ext_multiroute_flow.jl @@ -20,8 +20,8 @@ Requires arguments: # EMRF (Extended Multiroute Flow) algorithms function emrf{T<:AbstractFloat, R<:Real}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T, 2}, # edge flow capacities flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm routes::R = 0 @@ -47,8 +47,8 @@ Requires arguments: function auxiliaryPoints{T<:AbstractFloat}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T, 2} # edge flow capacities ) # Problem descriptors @@ -112,8 +112,8 @@ Requires arguments: function breakingPoints{T<:AbstractFloat}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T, 2} # edge flow capacities ) auxpoints = auxiliaryPoints(flow_graph, source, target, capacity_matrix) @@ -173,10 +173,10 @@ Requires argument: restriction::T # value of the restriction """ # Function to get the slope of the restricted flow -function slope{T<:AbstractFloat}( - flow_graph::DiGraph, # the input graph +function slope{T<:AbstractFloat, U<:Integer}( + flow_graph::DiGraph{U}, # the input graph capacity_matrix::AbstractArray{T, 2}, # edge flow capacities - cut::Vector{Int}, # cut information for vertices + cut::Vector, # cut information for vertices restriction::T # value of the restriction ) slope = 0 @@ -209,7 +209,7 @@ Requires argument: function intersection{T<:AbstractFloat, R<:Real}( x1::T, # x coordinate of point 1 y1::T, # y coordinate of point 1 - a1::Int, # slope passing by point 1 + a1::Integer, # slope passing by point 1 x2::T, # x coordinate of point 2 y2::T, # y coordinate of point 2 a2::R # slope passing by point 2 @@ -223,8 +223,8 @@ function intersection{T<:AbstractFloat, R<:Real}( return x, y end # Compute the intersection between a set of segment and a line of slope k passing by the origin -function intersection{T<:AbstractFloat, R<:Real}( - points::Vector{Tuple{T, T, Int}}, # vector of breaking points +function intersection{T<:AbstractFloat, I<:Integer, R<:Real}( + points::Vector{Tuple{T, T, I}}, # vector of breaking points k::R # number of routes (slope of the line) ) λ = points[1][1] # Connectivity diff --git a/src/flow/kishimoto.jl b/src/flow/kishimoto.jl index d2a371e39..7885703e7 100644 --- a/src/flow/kishimoto.jl +++ b/src/flow/kishimoto.jl @@ -2,8 +2,8 @@ # Kishimoto algorithm function kishimoto{T<:AbstractFloat}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T, 2}, # edge flow capacities flow_algorithm::BoykovKolmogorovAlgorithm, # keyword argument for algorithm routes::Int # keyword argument for routes @@ -38,8 +38,8 @@ 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::Int # the source vertex -- target::Int # the target vertex +- source::Integer # the source vertex +- target::Integer # the target vertex - capacity_matrix::AbstractArray{T, 2} # edge flow capacities - flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm - routes::Int # keyword argument for routes @@ -47,8 +47,8 @@ Requires arguments: function kishimoto{T<:AbstractFloat}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T, 2}, # edge flow capacities flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm routes::Int # keyword argument for routes diff --git a/src/flow/maximum_flow.jl b/src/flow/maximum_flow.jl index dbda84c80..0c45ecd5c 100644 --- a/src/flow/maximum_flow.jl +++ b/src/flow/maximum_flow.jl @@ -31,13 +31,15 @@ end Type that returns 1 if a forward edge exists, and 0 otherwise """ -type DefaultCapacity <: AbstractArray{Int, 2} - flow_graph::DiGraph - nv::Int - DefaultCapacity(flow_graph::DiGraph) = new(flow_graph, nv(flow_graph)) +type DefaultCapacity{T<:Integer} <: AbstractArray{T, 2} + flow_graph::DiGraph{T} + nv::T end -getindex(d::DefaultCapacity, s::Int, t::Int) = if has_edge(d.flow_graph, s , t) 1 else 0 end +DefaultCapacity{T<:Integer}(flow_graph::DiGraph{T}) = DefaultCapacity(flow_graph, nv(flow_graph)) + +getindex{T<:Integer}(d::DefaultCapacity{T}, s::Integer, t::Integer) = if has_edge(d.flow_graph, s , t) one(T) else zero(T) end +isassigned{T<:Integer}(d::DefaultCapacity{T}, u::T, v::T) = (u in one(T):d.nv) && (v in one(T):d.nv) size(d::DefaultCapacity) = (d.nv, d.nv) transpose(d::DefaultCapacity) = DefaultCapacity(reverse(d.flow_graph)) ctranspose(d::DefaultCapacity) = DefaultCapacity(reverse(d.flow_graph)) @@ -66,8 +68,8 @@ residual(flow_graph::DiGraph) = DiGraph(Graph(flow_graph)) function maximum_flow{T<:Number}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T,2}, # edge flow capacities algorithm::EdmondsKarpAlgorithm # keyword argument for algorithm ) @@ -79,8 +81,8 @@ end function maximum_flow{T<:Number}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T,2}, # edge flow capacities algorithm::DinicAlgorithm # keyword argument for algorithm ) @@ -92,8 +94,8 @@ end function maximum_flow{T<:Number}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T,2}, # edge flow capacities algorithm::BoykovKolmogorovAlgorithm # keyword argument for algorithm ) @@ -105,8 +107,8 @@ end function maximum_flow{T<:Number}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T,2}, # edge flow capacities algorithm::PushRelabelAlgorithm # keyword argument for algorithm ) @@ -118,8 +120,8 @@ end Generic maximum_flow function. Requires arguments: - flow_graph::DiGraph # the input graph -- source::Int # the source vertex -- target::Int # the target vertex +- source::Integer # the source vertex +- target::Integer # the target vertex - capacity_matrix::AbstractArray{T,2} # edge flow capacities - algorithm::AbstractFlowAlgorithm # keyword argument for algorithm - restriction::T # keyword argument for a restriction @@ -170,8 +172,8 @@ f, F, labels = maximum_flow(flow_graph,1,8,capacity_matrix,algorithm=BoykovKolmo function maximum_flow{T<:Number}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T,2} = # edge flow capacities DefaultCapacity(flow_graph); algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm diff --git a/src/flow/multiroute_flow.jl b/src/flow/multiroute_flow.jl index 7ff052b70..5c95fb10c 100644 --- a/src/flow/multiroute_flow.jl +++ b/src/flow/multiroute_flow.jl @@ -37,8 +37,8 @@ end # Method for Kishimoto algorithm function multiroute_flow{T<:Real}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T, 2}, # edge flow capacities flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm mrf_algorithm::KishimotoAlgorithm, # keyword argument for algorithm @@ -51,8 +51,8 @@ end #1 When the breaking points are not already known function multiroute_flow{T<:Real, R<:Real}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T, 2}, # edge flow capacities flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm mrf_algorithm::ExtendedMultirouteFlowAlgorithm, # keyword argument for algorithm @@ -73,9 +73,9 @@ function multiroute_flow{T1<:Real, T2<:Real, R<:Real}( breakingpoints::Vector{Tuple{T1, T1, Int}}, # vector of breaking points routes::R, # keyword argument for routes flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex - capacity_matrix::AbstractArray{T2, 2} = # edge flow capacities + source::Integer, # the source vertex + target::Integer, # the target vertex + capacity_matrix::AbstractArray{T2, 2} = # edge flow capacities DefaultCapacity(flow_graph); flow_algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm PushRelabelAlgorithm() @@ -105,8 +105,8 @@ returned as a third output. When the input is a network, it requires the following arguments: - flow_graph::DiGraph # the input graph -- source::Int # the source vertex -- target::Int # the target vertex +- source::Integer # the source vertex +- target::Integer # the target vertex - capacity_matrix::AbstractArray{T, 2} # edge flow capacities with T<:Real - flow_algorithm::AbstractFlowAlgorithm # keyword argument for flow algorithm - mrf_algorithm::AbstractFlowAlgorithm # keyword argument for multiroute flow algorithm @@ -124,8 +124,8 @@ 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::Int # the source vertex -- target::Int # the target vertex +- source::Integer # the source vertex +- target::Integer # the target vertex - capacity_matrix::AbstractArray{T2, 2} # optional edge flow capacities (T2<:Real) - flow_algorithm::AbstractFlowAlgorithm # keyword argument for algorithm @@ -181,8 +181,8 @@ f, F, labels = multiroute_flow(flow_graph, 1, 8, capacity_matrix, function multiroute_flow{T<:Real, R<:Real}( flow_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T, 2} = # edge flow capacities DefaultCapacity(flow_graph); flow_algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm diff --git a/src/flow/push_relabel.jl b/src/flow/push_relabel.jl index 87d648a2d..f1f71b3d2 100644 --- a/src/flow/push_relabel.jl +++ b/src/flow/push_relabel.jl @@ -12,15 +12,15 @@ Maintains the following auxillary arrays: Requires arguments: - residual_graph::DiGraph # the input graph -- source::Int # the source vertex -- target::Int # the target vertex +- source::Integer # the source vertex +- target::Integer # the target vertex - capacity_matrix::AbstractArray{T,2} # edge flow capacities """ function push_relabel{T<:Number}( residual_graph::DiGraph, # the input graph - source::Int, # the source vertex - target::Int, # the target vertex + source::Integer, # the source vertex + target::Integer, # the target vertex capacity_matrix::AbstractArray{T,2} # edge flow capacities ) @@ -65,14 +65,14 @@ Pushes inactive nodes into the queue and activates them. Requires arguments: - Q::AbstractArray{Int,1} -- v::Int +- v::Integer - active::AbstractArray{Bool,1} - excess::AbstractArray{T,1} """ -function enqueue_vertex!{T<:Number}( - Q::AbstractArray{Int,1}, - v::Int, # input vertex +function enqueue_vertex!{T<:Number, U<:Integer}( + Q::AbstractArray{U,1}, + v::Integer, # input vertex active::AbstractArray{Bool,1}, excess::AbstractArray{T,1} ) @@ -89,26 +89,26 @@ Pushes as much flow as possible through the given edge. Requires arguements: - residual_graph::DiGraph # the input graph -- u::Int # input from-vertex -- v::Int # input to-vetex +- u::Integer # input from-vertex +- v::Integer # input to-vetex - capacity_matrix::AbstractArray{T,2} - flow_matrix::AbstractArray{T,2} - excess::AbstractArray{T,1} - height::AbstractArray{Int,1} - active::AbstractArray{Bool,1} -- Q::AbstractArray{Int,1} +- Q::AbstractArray{Integer,1} """ -function push_flow!{T<:Number}( +function push_flow!{T<:Number, U<:Integer}( residual_graph::DiGraph, # the input graph - u::Int, # input from-vertex - v::Int, # input to-vetex + u::Integer, # input from-vertex + v::Integer, # input to-vetex capacity_matrix::AbstractArray{T,2}, flow_matrix::AbstractArray{T,2}, excess::AbstractArray{T,1}, height::AbstractArray{Int,1}, active::AbstractArray{Bool,1}, - Q::AbstractArray{Int,1} + Q::AbstractArray{U,1} ) flow = min(excess[u], capacity_matrix[u,v] - flow_matrix[u,v]) @@ -137,17 +137,17 @@ Requires arguments: - height::AbstractArray{Int,1} - active::AbstractArray{Bool,1} - count::AbstractArray{Int,1} -- Q::AbstractArray{Int,1} +- Q::AbstractArray{Integer,1} """ -function gap!{T<:Number}( +function gap!{T<:Number, U<:Integer}( residual_graph::DiGraph, # the input graph h::Int, # cutoff height excess::AbstractArray{T,1}, height::AbstractArray{Int,1}, active::AbstractArray{Bool,1}, count::AbstractArray{Int,1}, - Q::AbstractArray{Int,1} # FIFO queue + Q::AbstractArray{U,1} # FIFO queue ) n = nv(residual_graph) for v in vertices(residual_graph) @@ -167,26 +167,26 @@ edge. Requires arguments: - residual_graph::DiGraph # the input graph -- v::Int # input vertex to be relabeled +- v::Integer # input vertex to be relabeled - capacity_matrix::AbstractArray{T,2} - flow_matrix::AbstractArray{T,2} - excess::AbstractArray{T,1} - height::AbstractArray{Int,1} - active::AbstractArray{Bool,1} - count::AbstractArray{Int,1} -- Q::AbstractArray{Int,1} +- Q::AbstractArray{Integer,1} """ -function relabel!{T<:Number}( +function relabel!{T<:Number, U<:Integer}( residual_graph::DiGraph, # the input graph - v::Int, # input vertex to be relabeled + v::U, # input vertex to be relabeled capacity_matrix::AbstractArray{T,2}, flow_matrix::AbstractArray{T,2}, excess::AbstractArray{T,1}, height::AbstractArray{Int,1}, active::AbstractArray{Bool,1}, count::AbstractArray{Int,1}, - Q::AbstractArray{Int,1} + Q::AbstractArray{U,1} ) n = nv(residual_graph) count[height[v]+1] -= 1 @@ -209,25 +209,25 @@ vertex if the excess remains non-zero. Requires arguments: - residual_graph::DiGraph # the input graph -- v::Int # vertex to be discharged +- v::Integer # vertex to be discharged - capacity_matrix::AbstractArray{T,2} - flow_matrix::AbstractArray{T,2} - excess::AbstractArray{T,1} - height::AbstractArray{Int,1} - active::AbstractArray{Bool,1} - count::AbstractArray{Int,1} -- Q::AbstractArray{Int,1} +- Q::AbstractArray{Integer,1} """ -function discharge!{T<:Number}( +function discharge!{T<:Number, U<:Integer}( residual_graph::DiGraph, # the input graph - v::Int, # vertex to be discharged + v::U, # vertex to be discharged capacity_matrix::AbstractArray{T,2}, flow_matrix::AbstractArray{T,2}, excess::AbstractArray{T,1}, height::AbstractArray{Int,1}, active::AbstractArray{Bool,1}, count::AbstractArray{Int,1}, - Q::AbstractArray{Int,1} # FIFO queue + Q::AbstractArray{U,1} # FIFO queue ) for to in out_neighbors(residual_graph, v) excess[v] == 0 && break diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 4e86c7790..2e0869378 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -13,7 +13,7 @@ function Graph{T<:Integer}(nv::T, ne::Integer; seed::Int = -1) return g end -function DiGraph(nv::Integer, ne::Integer; seed::Int = -1) +function DiGraph{T<:Integer}(nv::T, ne::Integer; seed::Int = -1) maxe = nv * (nv-1) @assert(ne <= maxe, "Maximum number of edges for this graph is $maxe") ne > 2/3 * maxe && return complement(DiGraph(nv, maxe-ne)) @@ -21,8 +21,8 @@ function DiGraph(nv::Integer, ne::Integer; seed::Int = -1) rng = getRNG(seed) g = DiGraph(nv) while g.ne < ne - source = rand(rng, 1:nv) - dest = rand(rng, 1:nv) + source = rand(rng, one(T):nv) + dest = rand(rng, one(T):nv) source != dest && add_edge!(g,source,dest) end return g @@ -93,7 +93,7 @@ function watts_strogatz(n::Integer, k::Integer, β::Real; is_directed=false, see return g end -function _suitable(edges::Set{Edge}, potential_edges::Dict{Int, Int}) +function _suitable{T<:Integer}(edges::Set{Edge}, potential_edges::Dict{T, T}) isempty(potential_edges) && return true list = keys(potential_edges) for s1 in list, s2 in list @@ -103,14 +103,14 @@ function _suitable(edges::Set{Edge}, potential_edges::Dict{Int, Int}) return false end -_try_creation(n::Int, k::Int, rng::AbstractRNG) = _try_creation(n, fill(k,n), rng) +_try_creation(n::Integer, k::Integer, rng::AbstractRNG) = _try_creation(n, fill(k,n), rng) -function _try_creation(n::Int, k::Vector{Int}, rng::AbstractRNG) +function _try_creation{T<:Integer}(n::T, k::Vector{T}, rng::AbstractRNG) edges = Set{Edge}() m = 0 - stubs = zeros(Int, sum(k)) - for i=1:n - for j = 1:k[i] + stubs = zeros(T, sum(k)) + for i=one(T):n + for j = one(T):k[i] m += 1 stubs[m] = i end @@ -118,7 +118,7 @@ function _try_creation(n::Int, k::Vector{Int}, rng::AbstractRNG) # stubs = vcat([fill(i, k[i]) for i=1:n]...) # slower while !isempty(stubs) - potential_edges = Dict{Int,Int}() + potential_edges = Dict{T,T}() shuffle!(rng, stubs) for i in 1:2:length(stubs) s1,s2 = stubs[i:i+1] @@ -145,7 +145,7 @@ function _try_creation(n::Int, k::Vector{Int}, rng::AbstractRNG) end return edges end - +# ================= STOPPED HERE SAB """ barabasi_albert(n::Integer, k::Integer; is_directed::Bool = false, complete::Bool = false, seed::Int = -1) diff --git a/src/graphtypes/simplegraph/SimpleGraphs.jl b/src/graphtypes/simplegraph/SimpleGraphs.jl index af9238fb4..be18e9bc8 100644 --- a/src/graphtypes/simplegraph/SimpleGraphs.jl +++ b/src/graphtypes/simplegraph/SimpleGraphs.jl @@ -51,7 +51,10 @@ fadj(g::AbstractSimpleGraph, v::Integer) = g.fadjlist[v] badj(x...) = _NI("badj") has_edge(g::AbstractSimpleGraph, u::Integer, v::Integer) = has_edge(g, edgetype(g)(u,v)) -add_edge!{T<:Integer}(g::AbstractSimpleGraph, u::T, v::T) = add_edge!(g, edgetype(g)(u,v)) +function add_edge!(g::AbstractSimpleGraph, u::Integer, v::Integer) + T = eltype(g) + add_edge!(g, edgetype(g)(T(u),T(v))) +end in_neighbors(g::AbstractSimpleGraph, v::Integer) = badj(g,v) out_neighbors(g::AbstractSimpleGraph, v::Integer) = fadj(g,v) @@ -68,7 +71,10 @@ out_edges(g::AbstractSimpleGraph, v::Integer) = [edgetype(g)(v,x) for x in out_n has_vertex(g::AbstractSimpleGraph, v::Integer) = v in vertices(g) ne(g::AbstractSimpleGraph) = g.ne -rem_edge!{T<:Integer}(g::AbstractSimpleGraph, u::T, v::T) = rem_edge!(g, edgetype(g)(u, v)) +function rem_edge!(g::AbstractSimpleGraph, u::Integer, v::Integer) + T = eltype(g) + rem_edge!(g, edgetype(g)(T(u), T(v))) +end """ Remove the vertex `v` from graph `g`. diff --git a/src/graphtypes/simplegraph/simpleedge.jl b/src/graphtypes/simplegraph/simpleedge.jl index 95904b49f..20bbe1853 100644 --- a/src/graphtypes/simplegraph/simpleedge.jl +++ b/src/graphtypes/simplegraph/simpleedge.jl @@ -8,17 +8,22 @@ immutable SimpleEdge{T<:Integer} <: AbstractSimpleEdge dst::T end +SimpleEdge(t::Tuple) = SimpleEdge(t[1], t[2]) +SimpleEdge(p::Pair) = SimpleEdge(p.first, p.second) + +eltype{T<:AbstractSimpleEdge}(e::T) = T + # Accessors -src{T<:AbstractSimpleEdge}(e::T) = e.src -dst{T<:AbstractSimpleEdge}(e::T) = e.dst +src(e::AbstractSimpleEdge) = e.src +dst(e::AbstractSimpleEdge) = e.dst # I/O -show{T<:AbstractSimpleEdge}(io::IO, e::T) = print(io, "Edge $(e.src) => $(e.dst)") +show(io::IO, e::AbstractSimpleEdge) = print(io, "Edge $(e.src) => $(e.dst)") # Conversions -Pair{T<:AbstractSimpleEdge}(e::T) = Pair(src(e), dst(e)) -Tuple{T<:AbstractSimpleEdge}(e::T) = (src(e), dst(e)) +Pair(e::AbstractSimpleEdge) = Pair(src(e), dst(e)) +Tuple(e::AbstractSimpleEdge) = (src(e), dst(e)) # Convenience functions reverse{T<:AbstractSimpleEdge}(e::T) = T(dst(e), src(e)) -=={T<:AbstractSimpleEdge}(e1::T, e2::T) = (src(e1) == src(e2) && dst(e1) == dst(e2)) +==(e1::AbstractSimpleEdge, e2::AbstractSimpleEdge) = (src(e1) == src(e2) && dst(e1) == dst(e2)) diff --git a/src/graphtypes/simplegraph/simpleedgeiter.jl b/src/graphtypes/simplegraph/simpleedgeiter.jl index a1332ce17..d8051ac37 100644 --- a/src/graphtypes/simplegraph/simpleedgeiter.jl +++ b/src/graphtypes/simplegraph/simpleedgeiter.jl @@ -6,7 +6,7 @@ end immutable SimpleEdgeIterState{T<:Integer} s::T # src vertex - di::T # index into adj of dest vertex + di::Int # index into adj of dest vertex fin::Bool end @@ -16,7 +16,7 @@ eltype{T}(::Type{SimpleEdgeIter{T}}) = SimpleEdge SimpleEdgeIter(g::SimpleGraph) = SimpleEdgeIter(ne(g), g.fadjlist, false) SimpleEdgeIter(g::SimpleDiGraph) = SimpleEdgeIter(ne(g), g.fadjlist, true) -function _next(eit::SimpleEdgeIter, state::SimpleEdgeIterState = SimpleEdgeIterState(1,1,false), first::Bool = true) +function _next{T<:Integer}(eit::SimpleEdgeIter{T}, state::SimpleEdgeIterState{T} = SimpleEdgeIterState(one(T),1,false), first::Bool = true) s = state.s di = state.di if !first @@ -31,7 +31,7 @@ function _next(eit::SimpleEdgeIter, state::SimpleEdgeIterState = SimpleEdgeIterS end di += 1 end - s += 1 + s += one(T) di = 1 end fin = true diff --git a/test/biconnectivity/articulation.jl b/test/biconnectivity/articulation.jl index d17fb13c2..23d8b152b 100644 --- a/test/biconnectivity/articulation.jl +++ b/test/biconnectivity/articulation.jl @@ -1,35 +1,39 @@ @testset "Articulation" begin - g = Graph{UInt8}(13) - add_edge!(g, 1, 7) - add_edge!(g, 1, 2) - add_edge!(g, 1, 3) - add_edge!(g, 12, 13) - add_edge!(g, 10, 13) - add_edge!(g, 10, 12) - add_edge!(g, 12, 11) - add_edge!(g, 5, 4) - add_edge!(g, 6, 4) - add_edge!(g, 8, 9) - add_edge!(g, 6, 5) - add_edge!(g, 1, 6) - add_edge!(g, 7, 5) - add_edge!(g, 7, 3) - add_edge!(g, 7, 8) - add_edge!(g, 7, 10) - add_edge!(g, 7, 12) - - art = articulation(g) - ans = [1, 7, 8, 12] - @test art == ans + gint = Graph(13) + add_edge!(gint, 1, 7) + add_edge!(gint, 1, 2) + add_edge!(gint, 1, 3) + add_edge!(gint, 12, 13) + add_edge!(gint, 10, 13) + add_edge!(gint, 10, 12) + add_edge!(gint, 12, 11) + add_edge!(gint, 5, 4) + add_edge!(gint, 6, 4) + add_edge!(gint, 8, 9) + add_edge!(gint, 6, 5) + add_edge!(gint, 1, 6) + add_edge!(gint, 7, 5) + add_edge!(gint, 7, 3) + add_edge!(gint, 7, 8) + add_edge!(gint, 7, 10) + add_edge!(gint, 7, 12) + for g in (gint, Graph{UInt8}(gint), Graph{Int16}(gint)) + art = articulation(g) + ans = [1, 7, 8, 12] + @test art == ans + end for level in 1:6 - tree = LightGraphs.BinaryTree(level) - artpts = articulation(tree) - @test artpts == collect(1:(2^(level-1)-1)) + btree = LightGraphs.BinaryTree(level) + for tree in [btree, Graph{UInt8}(btree), Graph{Int16}(btree)] + artpts = articulation(tree) + @test artpts == collect(1:(2^(level-1)-1)) + end end - h = LightGraphs.blkdiag(WheelGraph(5), WheelGraph(5)) - h = Graph{UInt16}(h) - add_edge!(h, 5,6) - @test articulation(h) == [5,6] + hint = LightGraphs.blkdiag(WheelGraph(5), WheelGraph(5)) + add_edge!(hint, 5, 6) + for h in (hint, Graph{UInt8}(hint), Graph{Int16}(hint)) + @test articulation(h) == [5, 6] + end end diff --git a/test/biconnectivity/biconnect.jl b/test/biconnectivity/biconnect.jl index b4b7bc754..81e2c388e 100644 --- a/test/biconnectivity/biconnect.jl +++ b/test/biconnectivity/biconnect.jl @@ -1,27 +1,30 @@ @testset "Biconnect" begin - g = Graph(12) - add_edge!(g, 1, 2) - add_edge!(g, 2, 3) - add_edge!(g, 2, 4) - add_edge!(g, 3, 4) - add_edge!(g, 3, 5) - add_edge!(g, 4, 5) - add_edge!(g, 2, 6) - add_edge!(g, 1, 7) - add_edge!(g, 6, 7) - add_edge!(g, 6, 8) - add_edge!(g, 6, 9) - add_edge!(g, 8, 9) - add_edge!(g, 9, 10) - add_edge!(g, 11, 12) + gint = Graph(12) + add_edge!(gint, 1, 2) + add_edge!(gint, 2, 3) + add_edge!(gint, 2, 4) + add_edge!(gint, 3, 4) + add_edge!(gint, 3, 5) + add_edge!(gint, 4, 5) + add_edge!(gint, 2, 6) + add_edge!(gint, 1, 7) + add_edge!(gint, 6, 7) + add_edge!(gint, 6, 8) + add_edge!(gint, 6, 9) + add_edge!(gint, 8, 9) + add_edge!(gint, 9, 10) + add_edge!(gint, 11, 12) - bcc = biconnected_components(g) a = [[Edge(3, 5), Edge(4, 5), Edge(2, 4), Edge(3, 4), Edge(2, 3)], [Edge(9, 10)], [Edge(6, 9), Edge(8, 9), Edge(6, 8)], [Edge(1, 7), Edge(6, 7), Edge(2, 6), Edge(1, 2)], [Edge(11, 12)]] - @test bcc == a + + for g in (gint, Graph{UInt8}(gint), Graph{Int16}(gint)) + bcc = biconnected_components(g) + @test bcc == a + end g = Graph(4) add_edge!(g, 1, 2) @@ -35,10 +38,13 @@ add_edge!(h, 3, 4) add_edge!(h, 1, 4) - G = blkdiag(g, h) - add_edge!(G, 4, 5) + gint = blkdiag(g, h) + add_edge!(gint, 4, 5) - bcc = biconnected_components(G) a = [[Edge(5, 8),Edge(7, 8),Edge(6, 7),Edge(5, 6)], [Edge(4, 5)], [Edge(1, 4),Edge(3, 4),Edge(2, 3),Edge(1, 2)]] - @test bcc == a + + for g in (gint, Graph{UInt8}(gint), Graph{Int16}(gint)) + bcc = biconnected_components(g) + @test bcc == a + end end diff --git a/test/centrality/betweenness.jl b/test/centrality/betweenness.jl index 1bfecca5a..455dccb0d 100644 --- a/test/centrality/betweenness.jl +++ b/test/centrality/betweenness.jl @@ -16,19 +16,20 @@ end - g = load(joinpath(testdir,"testdata","graph-50-500.jgz"), "graph-50-500") + gint = load(joinpath(testdir,"testdata","graph-50-500.jgz"), "graph-50-500") c = readcentrality(joinpath(testdir,"testdata","graph-50-500-bc.txt")) - z = betweenness_centrality(g) + for g in (gint, DiGraph{UInt8}(gint), DiGraph{Int16}(gint)) + z = betweenness_centrality(g) + @test map(Float32, z) == map(Float32, c) - @test map(Float32, z) == map(Float32, c) + y = betweenness_centrality(g, endpoints=true, normalize=false) + @test round(y[1:3],4) == + round([122.10760591498584, 159.0072453120582, 176.39547945994505], 4) - y = betweenness_centrality(g, endpoints=true, normalize=false) - @test round(y[1:3],4) == - round([122.10760591498584, 159.0072453120582, 176.39547945994505], 4) - - x = betweenness_centrality(g,3) - @test length(x) == 50 + x = betweenness_centrality(g,3) + @test length(x) == 50 + end @test betweenness_centrality(s1) == [0, 1, 0] @test betweenness_centrality(s2) == [0, 0.5, 0] diff --git a/test/centrality/closeness.jl b/test/centrality/closeness.jl index 25282e08e..2a1b1a9a5 100644 --- a/test/centrality/closeness.jl +++ b/test/centrality/closeness.jl @@ -1,15 +1,18 @@ @testset "Closeness" begin g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) - y = closeness_centrality(g5; normalize=false) - z = closeness_centrality(g5) + for g in (g5, DiGraph{UInt8}(g5), DiGraph{Int16}(g5)) + y = closeness_centrality(g; normalize=false) + z = closeness_centrality(g) + @test y == [0.75, 0.6666666666666666, 1.0, 0.0] + @test z == [0.75, 0.4444444444444444, 0.3333333333333333, 0.0] + end - @test y == [0.75, 0.6666666666666666, 1.0, 0.0] - @test z == [0.75, 0.4444444444444444, 0.3333333333333333, 0.0] - - g = Graph(5) - add_edge!(g,1,2) - z = closeness_centrality(g) - @test z[1] == z[2] == 0.25 - @test z[3] == z[4] == z[5] == 0.0 + g5 = Graph(5) + add_edge!(g5,1,2) + for g in (g5, Graph{UInt8}(g5), Graph{Int16}(g5)) + z = closeness_centrality(g) + @test z[1] == z[2] == 0.25 + @test z[3] == z[4] == z[5] == 0.0 + end end diff --git a/test/centrality/degree.jl b/test/centrality/degree.jl index 68cbc31d8..294fe25c2 100644 --- a/test/centrality/degree.jl +++ b/test/centrality/degree.jl @@ -1,7 +1,9 @@ @testset "Degree" begin g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) - @test degree_centrality(g5) == [0.6666666666666666, 0.6666666666666666, 1.0, 0.3333333333333333] - @test indegree_centrality(g5, normalize=false) == [0.0, 1.0, 2.0, 1.0] - @test outdegree_centrality(g5; normalize=false) == [2.0, 1.0, 1.0, 0.0] + for g in (g5, DiGraph{UInt8}(g5), DiGraph{Int16}(g5)) + @test degree_centrality(g) == [0.6666666666666666, 0.6666666666666666, 1.0, 0.3333333333333333] + @test indegree_centrality(g, normalize=false) == [0.0, 1.0, 2.0, 1.0] + @test outdegree_centrality(g; normalize=false) == [2.0, 1.0, 1.0, 0.0] + end end diff --git a/test/centrality/katz.jl b/test/centrality/katz.jl index 6e5c0cba8..675bc882c 100644 --- a/test/centrality/katz.jl +++ b/test/centrality/katz.jl @@ -1,6 +1,8 @@ @testset "Katz" begin g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) - z = katz_centrality(g5, 0.4) - @test round(z, 2) == [0.32, 0.44, 0.62, 0.56] + for g in (g5, DiGraph{UInt8}(g5), DiGraph{Int16}(g5)) + z = katz_centrality(g, 0.4) + @test round(z, 2) == [0.32, 0.44, 0.62, 0.56] + end end diff --git a/test/centrality/pagerank.jl b/test/centrality/pagerank.jl index bd5204fd6..74c2e298b 100644 --- a/test/centrality/pagerank.jl +++ b/test/centrality/pagerank.jl @@ -1,7 +1,9 @@ @testset "Pagerank" begin g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) - @test_approx_eq_eps(pagerank(g5)[3], 0.318, 0.001) - @test_throws ErrorException pagerank(g5, 2) - @test_throws ErrorException pagerank(g5, 0.85, 2) + for g in (g5, DiGraph{UInt8}(g5), DiGraph{Int16}(g5)) + @test_approx_eq_eps(pagerank(g)[3], 0.318, 0.001) + @test_throws ErrorException pagerank(g, 2) + @test_throws ErrorException pagerank(g, 0.85, 2) + end end diff --git a/test/community/clustering.jl b/test/community/clustering.jl index 8fac09c8e..6a9f7a119 100644 --- a/test/community/clustering.jl +++ b/test/community/clustering.jl @@ -1,8 +1,10 @@ @testset "Clustering" begin g10 = CompleteGraph(10) - @test local_clustering_coefficient(g10) == ones(10) - @test global_clustering_coefficient(g10) == 1 - @test local_clustering(g10) == (fill(36, 10), fill(36, 10)) - @test triangles(g10) == fill(36, 10) - @test triangles(g10, 1) == 36 + for g in (g10, Graph{UInt8}(g10), Graph{Int16}(g10)) + @test local_clustering_coefficient(g) == ones(10) + @test global_clustering_coefficient(g) == 1 + @test local_clustering(g) == (fill(36, 10), fill(36, 10)) + @test triangles(g) == fill(36, 10) + @test triangles(g, 1) == 36 + end end diff --git a/test/community/core-periphery.jl b/test/community/core-periphery.jl index 8f09d7b48..bd0bacb31 100644 --- a/test/community/core-periphery.jl +++ b/test/community/core-periphery.jl @@ -1,22 +1,26 @@ @testset "Core periphery" begin g10 = StarGraph(10) - c = core_periphery_deg(g10) - @test degree(g10,1) == 9 - @test c[1] == 1 - for i=2:10 - @test c[i] == 2 + for g in (g10, Graph{UInt8}(g10), Graph{Int16}(g10)) + c = core_periphery_deg(g) + @test degree(g, 1) == 9 + @test c[1] == 1 + for i=2:10 + @test c[i] == 2 + end end g10 = StarGraph(10) g10 = blkdiag(g10,g10) add_edge!(g10, 1, 11) - c = core_periphery_deg(g10) - @test c[1] == 1 - @test c[11] == 1 - for i=2:10 - @test c[i] == 2 - end - for i=12:20 - @test c[i] == 2 + for g in (g10, Graph{UInt8}(g10), Graph{Int16}(g10)) + c = core_periphery_deg(g) + @test c[1] == 1 + @test c[11] == 1 + for i=2:10 + @test c[i] == 2 + end + for i=12:20 + @test c[i] == 2 + end end end diff --git a/test/community/label_propagation.jl b/test/community/label_propagation.jl index 368a68836..482fd9349 100644 --- a/test/community/label_propagation.jl +++ b/test/community/label_propagation.jl @@ -1,16 +1,18 @@ @testset "Label propagation" begin n=10 g10 = CompleteGraph(n) - z = copy(g10) - for k=2:5 - z = blkdiag(z, g10) - add_edge!(z, (k-1)*n, k*n) - c, ch = label_propagation(z) - a = collect(n:n:k*n) - a = Int[div(i-1,n)+1 for i=1:k*n] - # check the number of community - @test length(unique(a)) == length(unique(c)) - # check the partition - @test a == c + for g in (g10, Graph{UInt16}(g10), Graph{Int32}(g10)) + z = copy(g) + for k=2:5 + z = blkdiag(z, g) + add_edge!(z, (k-1)*n, k*n) + c, ch = label_propagation(z) + a = collect(n:n:k*n) + a = Int[div(i-1,n)+1 for i=1:k*n] + # check the number of community + @test length(unique(a)) == length(unique(c)) + # check the partition + @test a == c + end end end diff --git a/test/community/modularity.jl b/test/community/modularity.jl index dc9581969..7d58eba57 100644 --- a/test/community/modularity.jl +++ b/test/community/modularity.jl @@ -2,9 +2,13 @@ n = 10 m = n*(n-1)/2 c = ones(Int, n) - g = CompleteGraph(n) - @test modularity(g, c) == 0 - # - g = Graph(n) - @test modularity(g, c) == 0 + gint = CompleteGraph(n) + for g in (gint, Graph{UInt8}(gint), Graph{Int16}(gint)) + @test modularity(g, c) == 0 + end + + gint = Graph(n) + for g in (gint, Graph{UInt8}(gint), Graph{Int16}(gint)) + @test modularity(g, c) == 0 + end end diff --git a/test/flow/boykov_kolmogorov.jl b/test/flow/boykov_kolmogorov.jl index 44fd8da5d..aa602f35b 100644 --- a/test/flow/boykov_kolmogorov.jl +++ b/test/flow/boykov_kolmogorov.jl @@ -7,23 +7,20 @@ # source and sink terminals source, target = 1, 3 - # default capacity - capacity_matrix = LightGraphs.DefaultCapacity(G) - - # state variables - flow_matrix = zeros(Int, 3, 3) - - TREE = zeros(Int, 3) - TREE[source] = 1 - TREE[target] = 2 - - PARENT = zeros(Int, 3) - A = [source,target] - - residual_graph = LightGraphs.residual(G) - - path = LightGraphs.find_path!(residual_graph, source, target, flow_matrix, capacity_matrix, PARENT, TREE, A) - - @test path == [1,2,3] + for g in (G, DiGraph{UInt8}(G), DiGraph{Int16}(G)) + # default capacity + capacity_matrix = LightGraphs.DefaultCapacity(g) + residual_graph = LightGraphs.residual(g) + T = eltype(g) + flow_matrix = zeros(T, 3, 3) + TREE = zeros(T, 3) + TREE[source] = T(1) + TREE[target] = T(2) + PARENT = zeros(T, 3) + A = [T(source),T(target)] + path = LightGraphs.find_path!(residual_graph, source, target, flow_matrix, capacity_matrix, PARENT, TREE, A) + + @test path == [1,2,3] + end end diff --git a/test/flow/dinic.jl b/test/flow/dinic.jl index 6629e33e7..1db63b7d1 100644 --- a/test/flow/dinic.jl +++ b/test/flow/dinic.jl @@ -18,43 +18,45 @@ end # Construct the residual graph - residual_graph = LightGraphs.residual(flow_graph) - - # Test with default distances - @test LightGraphs.dinic_impl(residual_graph, 1, 8, LightGraphs.DefaultCapacity(residual_graph))[1] == 3 - - # Test with capacity matrix - @test LightGraphs.dinic_impl(residual_graph, 1, 8, capacity_matrix)[1] == 28 - - # Test on disconnected graphs - function test_blocking_flow(residual_graph, source, target, capacity_matrix, flow_matrix) - #disconnect source - h = copy(residual_graph) - for dst in collect(neighbors(residual_graph, source)) - rem_edge!(h, source, dst) - end - @test 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 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) - add_vertex!(h) - add_edge!(h, nv(residual_graph)+1, target) - 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 LightGraphs.blocking_flow!(h, source, target, capacity_matrix_, flow_graph_ ) > 0 - - #test with connected graph - @test LightGraphs.blocking_flow!(residual_graph, source, target, capacity_matrix, flow_matrix) > 0 + for fg in (flow_graph, DiGraph{UInt8}(flow_graph), DiGraph{Int16}(flow_graph)) + residual_graph = LightGraphs.residual(fg) + + # Test with default distances + @test LightGraphs.dinic_impl(residual_graph, 1, 8, LightGraphs.DefaultCapacity(residual_graph))[1] == 3 + + # Test with capacity matrix + @test LightGraphs.dinic_impl(residual_graph, 1, 8, capacity_matrix)[1] == 28 + + # Test on disconnected graphs + function test_blocking_flow(residual_graph, source, target, capacity_matrix, flow_matrix) + #disconnect source + h = copy(residual_graph) + for dst in collect(neighbors(residual_graph, source)) + rem_edge!(h, source, dst) + end + @test 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 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) + add_vertex!(h) + add_edge!(h, nv(residual_graph)+1, target) + 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 LightGraphs.blocking_flow!(h, source, target, capacity_matrix_, flow_graph_ ) > 0 + + #test with connected graph + @test LightGraphs.blocking_flow!(residual_graph, source, target, capacity_matrix, flow_matrix) > 0 + end + + flow_matrix = zeros(Int, nv(residual_graph), nv(residual_graph)) + test_blocking_flow(residual_graph, 1, 8, capacity_matrix, flow_matrix) end - - flow_matrix = zeros(Int, nv(residual_graph), nv(residual_graph)) - test_blocking_flow(residual_graph, 1, 8, capacity_matrix, flow_matrix) end diff --git a/test/flow/edmonds_karp.jl b/test/flow/edmonds_karp.jl index 6f97a86e9..c9db0e795 100644 --- a/test/flow/edmonds_karp.jl +++ b/test/flow/edmonds_karp.jl @@ -9,52 +9,55 @@ (5,8,10),(6,7,15),(6,8,10),(7,3,6),(7,8,10) ] - capacity_matrix = zeros(Int,8,8) + for fg in (flow_graph, DiGraph{UInt8}(flow_graph), DiGraph{Int16}(flow_graph)) - for e in flow_edges - u,v,f = e - add_edge!(flow_graph,u,v) - capacity_matrix[u,v] = f - end - - residual_graph = LightGraphs.residual(flow_graph) - - # Test with default distances - @test LightGraphs.edmonds_karp_impl(residual_graph, 1, 8, LightGraphs.DefaultCapacity(residual_graph))[1] == 3 - - # Test with capacity matrix - @test LightGraphs.edmonds_karp_impl(residual_graph,1,8,capacity_matrix)[1] == 28 - - # Test the types of the values returned by fetch_path - function test_find_path_types(residual_graph, s, t, flow_matrix, capacity_matrix) - v, P, S, flag = LightGraphs.fetch_path(residual_graph, s, t, flow_matrix, capacity_matrix) - @test typeof(P) == Vector{Int} - @test typeof(S) == Vector{Int} - @test typeof(flag) == Int - @test typeof(v) == Int - end + capacity_matrix = zeros(Int,8,8) - # Test the value of the flags returned. - function test_find_path_disconnected(residual_graph, s, t, flow_matrix, capacity_matrix) - h = copy(residual_graph) - for dst in collect(neighbors(residual_graph, s)) - rem_edge!(residual_graph, s, dst) - end - v, P, S, flag = LightGraphs.fetch_path(residual_graph, s, t, flow_matrix, capacity_matrix) - @test flag == 1 - for dst in collect(neighbors(h, t)) - rem_edge!(h, t, dst) - end - v, P, S, flag = LightGraphs.fetch_path(h, s, t, flow_matrix, capacity_matrix) - @test flag == 0 - for i in collect(in_neighbors(h, t)) - rem_edge!(h, i, t) + for e in flow_edges + u,v,f = e + add_edge!(fg,u,v) + capacity_matrix[u,v] = f end - v, P, S, flag = LightGraphs.fetch_path(h, s, t, flow_matrix, capacity_matrix) - @test flag == 2 - end - flow_matrix = zeros(Int, nv(residual_graph), nv(residual_graph)) - test_find_path_types(residual_graph, 1,8, flow_matrix, capacity_matrix) - test_find_path_disconnected(residual_graph, 1, 8, flow_matrix, capacity_matrix) + residual_graph = LightGraphs.residual(fg) + + # Test with default distances + @test LightGraphs.edmonds_karp_impl(residual_graph, 1, 8, LightGraphs.DefaultCapacity(residual_graph))[1] == 3 + + # Test with capacity matrix + @test LightGraphs.edmonds_karp_impl(residual_graph,1,8,capacity_matrix)[1] == 28 + + # Test the types of the values returned by fetch_path + function test_find_path_types(residual_graph, s, t, flow_matrix, capacity_matrix) + v, P, S, flag = LightGraphs.fetch_path(residual_graph, s, t, flow_matrix, capacity_matrix) + @test typeof(P) == Vector{Int} + @test typeof(S) == Vector{Int} + @test typeof(flag) == Int + @test typeof(v) == eltype(residual_graph) + end + + # Test the value of the flags returned. + function test_find_path_disconnected(residual_graph, s, t, flow_matrix, capacity_matrix) + h = copy(residual_graph) + for dst in collect(neighbors(residual_graph, s)) + rem_edge!(residual_graph, s, dst) + end + v, P, S, flag = LightGraphs.fetch_path(residual_graph, s, t, flow_matrix, capacity_matrix) + @test flag == 1 + for dst in collect(neighbors(h, t)) + rem_edge!(h, t, dst) + end + v, P, S, flag = LightGraphs.fetch_path(h, s, t, flow_matrix, capacity_matrix) + @test flag == 0 + for i in collect(in_neighbors(h, t)) + rem_edge!(h, i, t) + end + v, P, S, flag = LightGraphs.fetch_path(h, s, t, flow_matrix, capacity_matrix) + @test flag == 2 + end + + flow_matrix = zeros(Int, nv(residual_graph), nv(residual_graph)) + test_find_path_types(residual_graph, 1,8, flow_matrix, capacity_matrix) + test_find_path_disconnected(residual_graph, 1, 8, flow_matrix, capacity_matrix) + end end diff --git a/test/flow/maximum_flow.jl b/test/flow/maximum_flow.jl index 0dfe13503..b4fd06932 100644 --- a/test/flow/maximum_flow.jl +++ b/test/flow/maximum_flow.jl @@ -29,26 +29,29 @@ for (nvertices,flow_edges,s,t,fdefault,fcustom,frestrict,caprestrict) in graphs flow_graph = DiGraph(nvertices) - capacity_matrix = zeros(Int,nvertices,nvertices) - for e in flow_edges - u,v,f = e - add_edge!(flow_graph,u,v) - capacity_matrix[u,v] = f - end + for g in (flow_graph, DiGraph{UInt8}(flow_graph), DiGraph{Int16}(flow_graph)) + capacity_matrix = zeros(Int,nvertices,nvertices) + for e in flow_edges + u,v,f = e + add_edge!(g,u,v) + capacity_matrix[u,v] = f + end - # Test DefaultCapacity - d = LightGraphs.DefaultCapacity(flow_graph) - @test typeof(d) <: AbstractArray{Int, 2} - @test d[s,t] == 0 - @test size(d) == (nvertices,nvertices) - @test typeof(transpose(d)) == LightGraphs.DefaultCapacity - @test typeof(ctranspose(d)) == LightGraphs.DefaultCapacity + # Test DefaultCapacity + d = LightGraphs.DefaultCapacity(g) + T = eltype(d) + @test typeof(d) <: AbstractArray{T, 2} + @test d[s,t] == 0 + @test size(d) == (nvertices,nvertices) + @test typeof(transpose(d)) <: LightGraphs.DefaultCapacity + @test typeof(ctranspose(d)) <: LightGraphs.DefaultCapacity - # Test all algorithms - for ALGO in [EdmondsKarpAlgorithm, DinicAlgorithm, BoykovKolmogorovAlgorithm, PushRelabelAlgorithm] - @test maximum_flow(flow_graph,s,t,algorithm=ALGO())[1] == fdefault - @test maximum_flow(flow_graph,s,t,capacity_matrix,algorithm=ALGO())[1] == fcustom - @test maximum_flow(flow_graph,s,t,capacity_matrix,algorithm=ALGO(),restriction=caprestrict)[1] == frestrict + # Test all algorithms + for ALGO in [EdmondsKarpAlgorithm, DinicAlgorithm, BoykovKolmogorovAlgorithm, PushRelabelAlgorithm] + @test maximum_flow(g,s,t,algorithm=ALGO())[1] == fdefault + @test maximum_flow(g,s,t,capacity_matrix,algorithm=ALGO())[1] == fcustom + @test maximum_flow(g,s,t,capacity_matrix,algorithm=ALGO(),restriction=caprestrict)[1] == frestrict + end end end end diff --git a/test/flow/multiroute_flow.jl b/test/flow/multiroute_flow.jl index b9171faea..1370efd07 100644 --- a/test/flow/multiroute_flow.jl +++ b/test/flow/multiroute_flow.jl @@ -56,29 +56,31 @@ for (nvertices, flow_edges, s, t, froutes, breakpts, ffloat) in graphs flow_graph = DiGraph(nvertices) - capacity_matrix = zeros(Int, nvertices, nvertices) - for e in flow_edges - u, v, f = e - add_edge!(flow_graph, u, v) - capacity_matrix[u, v] = f - end + for g in (flow_graph, DiGraph{UInt8}(flow_graph)) #, DiGraph{Int16}(flow_graph)) + capacity_matrix = zeros(Int, nvertices, nvertices) + for e in flow_edges + u, v, f = e + add_edge!(g, u, v) + capacity_matrix[u, v] = f + end - # Test ExtendedMultirouteFlowAlgorithm when the number of routes is either - # Noninteger or 0 (the algorithm returns the breaking points) - @test multiroute_flow(flow_graph, s, t, capacity_matrix) == breakpts - @test multiroute_flow(flow_graph, s, t, capacity_matrix, routes=1.5)[1] ≈ ffloat - @test multiroute_flow(breakpts, 1.5)[2] ≈ ffloat + # Test ExtendedMultirouteFlowAlgorithm when the number of routes is either + # Noninteger or 0 (the algorithm returns the breaking points) + @test multiroute_flow(g, s, t, capacity_matrix) == breakpts + @test multiroute_flow(g, s, t, capacity_matrix, routes=1.5)[1] ≈ ffloat + @test multiroute_flow(breakpts, 1.5)[2] ≈ ffloat - # Test all other algorithms - for AlgoFlow in [EdmondsKarpAlgorithm, DinicAlgorithm, BoykovKolmogorovAlgorithm, PushRelabelAlgorithm] - # When the input are breaking points and routes number - @test multiroute_flow(breakpts, 1.5, flow_graph, s, t, capacity_matrix)[1] ≈ ffloat - for AlgoMrf in [ExtendedMultirouteFlowAlgorithm, KishimotoAlgorithm] - for (k, val) in enumerate(froutes) - @test multiroute_flow(flow_graph, s, t, capacity_matrix, - flow_algorithm = AlgoFlow(), mrf_algorithm = AlgoMrf(), - routes = k)[1] ≈ val + # Test all other algorithms + for AlgoFlow in [EdmondsKarpAlgorithm, DinicAlgorithm, BoykovKolmogorovAlgorithm, PushRelabelAlgorithm] + # When the input are breaking points and routes number + @test multiroute_flow(breakpts, 1.5, g, s, t, capacity_matrix)[1] ≈ ffloat + for AlgoMrf in [ExtendedMultirouteFlowAlgorithm, KishimotoAlgorithm] + for (k, val) in enumerate(froutes) + @test multiroute_flow(g, s, t, capacity_matrix, + flow_algorithm = AlgoFlow(), mrf_algorithm = AlgoMrf(), + routes = k)[1] ≈ val + end end end end From c1c2e4e31cd31a12eceb0ca4a4c326f3c951dd8b Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Wed, 15 Mar 2017 15:58:41 -0700 Subject: [PATCH 15/56] tests and fixes --- src/biconnectivity/articulation.jl | 2 +- src/biconnectivity/biconnect.jl | 4 +- src/community/cliques.jl | 3 +- src/community/core-periphery.jl | 3 +- src/community/label_propagation.jl | 4 +- src/community/modularity.jl | 3 +- src/flow/boykov_kolmogorov.jl | 54 ++++++++--------- src/flow/dinic.jl | 66 +++++++++------------ src/flow/edmonds_karp.jl | 34 ++++++----- src/flow/ext_multiroute_flow.jl | 42 ++++++------- src/flow/kishimoto.jl | 15 ++--- src/flow/maximum_flow.jl | 51 ++++++++-------- src/flow/multiroute_flow.jl | 46 +++++++-------- src/flow/push_relabel.jl | 95 +++++++++++++++--------------- test/flow/multiroute_flow.jl | 2 +- 15 files changed, 207 insertions(+), 217 deletions(-) diff --git a/src/biconnectivity/articulation.jl b/src/biconnectivity/articulation.jl index 33359a891..2962a2e89 100644 --- a/src/biconnectivity/articulation.jl +++ b/src/biconnectivity/articulation.jl @@ -32,7 +32,7 @@ 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. """ -function visit!{T<:Integer}(state::Articulations, g::AbstractGraph, u::T, v::T) +function visit!(state::Articulations, g::AbstractGraph, u::Integer, v::Integer) children = 0 state.id += 1 state.depth[v] = state.id diff --git a/src/biconnectivity/biconnect.jl b/src/biconnectivity/biconnect.jl index 3aa85b484..1c39efb00 100644 --- a/src/biconnectivity/biconnect.jl +++ b/src/biconnectivity/biconnect.jl @@ -37,7 +37,7 @@ end """ Does a DFS visit and stores the depth and low-points of each vertex """ -function visit!{T<:Integer}(g::Graph, state::Biconnections, u::T, v::T) +function visit!(g::Graph, state::Biconnections, u::Integer, v::Integer) children = 0 state.id += 1 state.depth[v] = state.id @@ -52,7 +52,7 @@ function visit!{T<:Integer}(g::Graph, state::Biconnections, u::T, v::T) #Checking the root, and then the non-roots if they are articulation points if (u == v && children > 1) || (u != v && state.low[w] >= state.depth[v]) - e = Edge(zero(T), zero(T)) #Invalid Edge, used for comparison only + e = Edge(0, 0) #Invalid Edge, used for comparison only st = Vector{Edge}() while e != Edge(min(v, w),max(v, w)) e = pop!(state.stack) diff --git a/src/community/cliques.jl b/src/community/cliques.jl index 334e3750e..25b9b9fed 100644 --- a/src/community/cliques.jl +++ b/src/community/cliques.jl @@ -19,7 +19,8 @@ julia> maximal_cliques(g) [2,1] ``` """ -function maximal_cliques(g::Graph) +function maximal_cliques end +@traitfn function maximal_cliques{G<:AbstractGraph; !IsDirected{G}}(g::G) T = eltype(g) # Cache nbrs and find first pivot (highest degree) diff --git a/src/community/core-periphery.jl b/src/community/core-periphery.jl index d3ad65119..3021630a8 100644 --- a/src/community/core-periphery.jl +++ b/src/community/core-periphery.jl @@ -4,7 +4,8 @@ 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). """ -function core_periphery_deg(g::Graph) +function core_periphery_deg end +@traitfn function core_periphery_deg{G<:AbstractGraph; !IsDirected{G}}(g::G) degs = degree(g) p = sortperm(degs, rev=true) s = sum(degs) / 2. diff --git a/src/community/label_propagation.jl b/src/community/label_propagation.jl index 653fc7b98..3f85d7913 100644 --- a/src/community/label_propagation.jl +++ b/src/community/label_propagation.jl @@ -60,7 +60,7 @@ function range_shuffle!(r::UnitRange, a::AbstractVector) end """Return the most frequency label.""" -function vote!{T<:Integer}(g::AbstractGraph, m::Vector{T}, c::NeighComm, u::T) +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 end @@ -90,7 +90,7 @@ function vote!{T<:Integer}(g::AbstractGraph, m::Vector{T}, c::NeighComm, u::T) end end -function renumber_labels!{T<:Integer}(membership::Vector{T}, label_counters::Vector{Int}) +function renumber_labels!(membership::Vector, label_counters::Vector{Int}) N = length(membership) (maximum(membership) > N || minimum(membership) < 1) && error("Label must between 1 and |V|") j = 1 diff --git a/src/community/modularity.jl b/src/community/modularity.jl index 26ea82281..1f89af553 100644 --- a/src/community/modularity.jl +++ b/src/community/modularity.jl @@ -4,7 +4,8 @@ Computes Newman's modularity `Q` for graph `g` given the partitioning `c`. """ -function modularity(g::Graph, c::Vector) +function modularity end +@traitfn function modularity{G<:AbstractGraph; !IsDirected{G}}(g::G, c::Vector) m = 2*ne(g) m == 0 && return 0. nc = maximum(c) diff --git a/src/flow/boykov_kolmogorov.jl b/src/flow/boykov_kolmogorov.jl index f1cbf2e87..5ac47ff73 100644 --- a/src/flow/boykov_kolmogorov.jl +++ b/src/flow/boykov_kolmogorov.jl @@ -18,18 +18,21 @@ Requires arguments: residual_graph::DiGraph # the input graph source::Integer # the source vertex target::Integer # the target vertex -capacity_matrix::AbstractArray{T,2} # edge flow capacities +capacity_matrix::AbstractMatrix # edge flow capacities Author: Júlio Hoffimann Mendes (juliohm@stanford.edu) """ - -function boykov_kolmogorov_impl{T<:Number, U<:Integer}( - residual_graph::DiGraph{U}, # the input graph +function boykov_kolmogorov_impl end +@traitfn function boykov_kolmogorov_impl{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractMatrix{T} # edge flow capacities + capacity_matrix::AbstractMatrix # edge flow capacities ) + T = eltype(capacity_matrix) + U = eltype(residual_graph) + n = nv(residual_graph) flow = 0 @@ -60,17 +63,17 @@ function boykov_kolmogorov_impl{T<:Number, U<:Integer}( return flow, flow_matrix, TREE end -function find_path!{T<:Integer}( - residual_graph::DiGraph{T}, # the input graph +@traitfn function find_path!{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # 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 - PARENT::Vector{T}, # parent table - TREE::Vector{T}, # tree table - A::Vector{T} # active set + PARENT::Vector, # parent table + TREE::Vector, # tree table + A::Vector # active set ) - + T = eltype(residual_graph) tree_cap(p,q) = TREE[p] == one(T) ? capacity_matrix[p,q] - flow_matrix[p,q] : capacity_matrix[q,p] - flow_matrix[q,p] while !isempty(A) @@ -114,28 +117,16 @@ function find_path!{T<:Integer}( return Vector{T}() end -find_path!{T<:Integer}( - residual_graph::DiGraph{T}, # the input graph - source::Integer, # the source vertex - target::Integer, # the target vertex +function augment!( + path::AbstractVector, # path from source to target flow_matrix::AbstractMatrix, # the current flow matrix capacity_matrix::AbstractMatrix, # edge flow capacities PARENT::Vector, # parent table TREE::Vector, # tree table - A::Vector # active set - ) = find_path!(residual_graph, T(source), T(target), - flow_matrix, capacity_matrix, T.(PARENT), - T.(TREE), T.(A)) - -function augment!{T<:Integer}( - path::AbstractVector{T}, # path from source to target - flow_matrix::AbstractMatrix, # the current flow matrix - capacity_matrix::AbstractMatrix, # edge flow capacities - PARENT::Vector{T}, # parent table - TREE::Vector{T}, # tree table - O::Vector{T} # orphan set + O::Vector # orphan set ) + T = eltype(path) # bottleneck capacity Δ = Inf for i=1:length(path)-1 @@ -166,8 +157,8 @@ function augment!{T<:Integer}( return Δ end -function adopt!{T<:Integer}( - residual_graph::DiGraph{T}, # the input graph +@traitfn function adopt!{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex flow_matrix::AbstractMatrix, # the current flow matrix @@ -178,7 +169,8 @@ function adopt!{T<:Integer}( O::Vector # orphan set ) - tree_cap(p,q) = TREE[p] == one(T) ? capacity_matrix[p,q] - flow_matrix[p,q] : + T = eltype(residual_graph) + tree_cap(p,q) = TREE[p] == 1 ? capacity_matrix[p,q] - flow_matrix[p,q] : capacity_matrix[q,p] - flow_matrix[q,p] while !isempty(O) p = pop!(O) @@ -188,7 +180,7 @@ function adopt!{T<:Integer}( if TREE[q] == TREE[p] && tree_cap(q,p) > 0 # check if "origin" is either source or target o = q - while PARENT[o] ≠ zero(T) + while PARENT[o] ≠ 0 o = PARENT[o] end if o == source || o == target diff --git a/src/flow/dinic.jl b/src/flow/dinic.jl index ab6e73b46..668d39971 100644 --- a/src/flow/dinic.jl +++ b/src/flow/dinic.jl @@ -11,15 +11,15 @@ source::Integer # the source vertex target::Integer # the target vertex capacity_matrix::AbstractArray{T,2} # edge flow capacities """ - -function dinic_impl{T<:Number, U<:Integer}( - residual_graph::DiGraph{U}, # the input graph +function dinic_impl end +@traitfn function dinic_impl{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T,2} # edge flow capacities + capacity_matrix::AbstractMatrix # edge flow capacities ) n = nv(residual_graph) # number of vertexes - + T = eltype(capacity_matrix) flow_matrix = zeros(T, n, n) # initialize flow matrix P = zeros(Int, n) # Sharable parent vector @@ -33,36 +33,10 @@ function dinic_impl{T<:Number, U<:Integer}( return flow, flow_matrix end -""" -Uses BFS to identify a blocking flow in -the input graph and then backtracks from the targetto 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::AbstractArray{T,2} # edge flow capacities -flow_matrix::AbstractArray{T,2} # the current flow matrix -""" -function blocking_flow!{T<:Number, U<:Integer}( - residual_graph::DiGraph{U}, # the input graph - source::Integer, # the source vertex - target::Integer, # the target vertex - capacity_matrix::AbstractArray{T,2}, # edge flow capacities - flow_matrix::AbstractArray{T,2}, # the current flow matrix - ) - P = zeros(Int, nv(residual_graph)) - return blocking_flow!(residual_graph, - source, - target, - capacity_matrix, - flow_matrix, - P) -end -"""blocking_flow! -Preallocated version of blocking_flow.Uses BFS to identify a blocking flow in +""" +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. @@ -74,17 +48,17 @@ capacity_matrix::AbstractArray{T,2} # edge flow capacities flow_matrix::AbstractArray{T,2} # the current flow matrix P::AbstractVector{Int} # Parent vector to store Level Graph """ - -function blocking_flow!{T<:Number, U<:Integer}( - residual_graph::DiGraph{U}, # the input graph +function blocking_flow! end +@traitfn function blocking_flow!{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T,2}, # edge flow capacities - flow_matrix::AbstractArray{T,2}, # the current flow matrix + capacity_matrix::AbstractMatrix, # edge flow capacities + flow_matrix::AbstractMatrix, # the current flow matrix P::AbstractVector{Int} # Parent vector to store Level Graph ) n = nv(residual_graph) # number of vertexes - + T = eltype(capacity_matrix) fill!(P, -1) P[source] = -2 @@ -135,3 +109,17 @@ function blocking_flow!{T<:Number, U<:Integer}( end return total_flow end + +blocking_flow!( + residual_graph::AbstractGraph, # 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 + ) = + blocking_flow!(residual_graph, + source, + target, + capacity_matrix, + flow_matrix, + zeros(Int, nv(residual_graph))) diff --git a/src/flow/edmonds_karp.jl b/src/flow/edmonds_karp.jl index 785f3b92e..31216986e 100644 --- a/src/flow/edmonds_karp.jl +++ b/src/flow/edmonds_karp.jl @@ -9,13 +9,14 @@ Requires arguments: - target::Integer # the target vertex - capacity_matrix::AbstractArray{T,2} # edge flow capacities """ - -function edmonds_karp_impl{T<:Number}( - residual_graph::DiGraph, # the input graph +function edmonds_karp_impl end +@traitfn function edmonds_karp_impl{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T,2} # edge flow capacities + capacity_matrix::AbstractMatrix # edge flow capacities ) + T = eltype(capacity_matrix) n = nv(residual_graph) # number of vertexes flow = 0 flow_matrix = zeros(T, n, n) # initialize flow matrix @@ -61,11 +62,12 @@ Requires arguments: - capacity_matrix::AbstractArray{T,2} # edge flow capacities """ -function augment_path!{T<:Number}( +function augment_path!( path::Vector{Int}, # input path - flow_matrix::AbstractArray{T,2}, # the current flow matrix - capacity_matrix::AbstractArray{T,2} # edge flow capacities + flow_matrix::AbstractMatrix, # the current flow matrix + capacity_matrix::AbstractMatrix # edge flow capacities ) + T = eltype(flow_matrix) augment = typemax(T) # initialize augment for i in 1:length(path)-1 # calculate min capacity along path u = path[i] @@ -92,12 +94,13 @@ Flag Values: 1 => No Path to target 2 => No Path to source """ -function fetch_path{T<:Number, U<:Integer}( - residual_graph::DiGraph{U}, # the input graph +function fetch_path end +@traitfn function fetch_path{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - flow_matrix::AbstractArray{T,2}, # the current flow matrix - capacity_matrix::AbstractArray{T,2} # edge flow capacities + flow_matrix::AbstractMatrix, # the current flow matrix + capacity_matrix::AbstractMatrix # edge flow capacities ) n = nv(residual_graph) P = -1 * ones(Int, n) @@ -129,12 +132,13 @@ Requires arguments: P::Vector{Int} # parent table of path init to -1s S::Vector{Int} # successor table of path init to -1s """ -function fetch_path!{T<:Number}( - residual_graph::DiGraph, # the input graph +function fetch_path! end +@traitfn function fetch_path!{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - flow_matrix::AbstractArray{T,2}, # the current flow matrix - capacity_matrix::AbstractArray{T,2}, # edge flow capacities + 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 ) diff --git a/src/flow/ext_multiroute_flow.jl b/src/flow/ext_multiroute_flow.jl index 231971a65..3f4782b5a 100644 --- a/src/flow/ext_multiroute_flow.jl +++ b/src/flow/ext_multiroute_flow.jl @@ -16,18 +16,17 @@ Requires arguments: - flow_algorithm::AbstractFlowAlgorithm # keyword argument for algorithm - routes::Int # keyword argument for routes """ - # EMRF (Extended Multiroute Flow) algorithms -function emrf{T<:AbstractFloat, R<:Real}( - flow_graph::DiGraph, # the input graph +function emrf( + flow_graph::AbstractGraph, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T, 2}, # edge flow capacities + capacity_matrix::AbstractMatrix, # edge flow capacities flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm - routes::R = 0 + routes::Real = 0 ) breakingpoints = breakingPoints(flow_graph, source, target, capacity_matrix) - if routes > zero(R) + if routes > 0 x, f = intersection(breakingpoints, routes) return maximum_flow(flow_graph, source, target, capacity_matrix, algorithm = flow_algorithm, restriction = x) @@ -44,12 +43,12 @@ Requires arguments: - target::Int # the target vertex - capacity_matrix::AbstractArray{T, 2} # edge flow capacities """ - -function auxiliaryPoints{T<:AbstractFloat}( - flow_graph::DiGraph, # the input graph +function auxiliaryPoints end +@traitfn function auxiliaryPoints{G<:AbstractGraph; IsDirected{G}}( + flow_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T, 2} # edge flow capacities + capacity_matrix::AbstractMatrix # edge flow capacities ) # Problem descriptors λ = maximum_flow(flow_graph, source, target)[1] # Connectivity @@ -109,16 +108,17 @@ Requires arguments: - target::Int # the target vertex - capacity_matrix::AbstractArray{T, 2} # edge flow capacities """ - -function breakingPoints{T<:AbstractFloat}( - flow_graph::DiGraph, # the input graph +function breakingPoints end +@traitfn function breakingPoints{G<:AbstractGraph; IsDirected{G}}( + flow_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T, 2} # edge flow capacities + capacity_matrix::AbstractMatrix # edge flow capacities ) auxpoints = auxiliaryPoints(flow_graph, source, target, capacity_matrix) λ = length(auxpoints) - 1 left_index = 1 + T = eltype(capacity_matrix) breakingpoints = Vector{Tuple{T, T, Int}}() for (id, point) in enumerate(auxpoints) @@ -150,9 +150,10 @@ Requires argument: # Function to get the nonzero min and max function of a Matrix # note: this is more efficient than maximum() / minimum() / extrema() # since we have to ignore zero values. -function minmaxCapacity{T<:AbstractFloat}( - capacity_matrix::AbstractArray{T, 2} # edge flow capacities +function minmaxCapacity( + capacity_matrix::AbstractMatrix # edge flow capacities ) + T = eltype(capacity_matrix) cmin, cmax = typemax(T), typemin(T) for c in capacity_matrix if c > zero(T) @@ -172,12 +173,13 @@ Requires argument: cut::Vector{Int}, # cut information for vertices restriction::T # value of the restriction """ +function slope end # Function to get the slope of the restricted flow -function slope{T<:AbstractFloat, U<:Integer}( - flow_graph::DiGraph{U}, # the input graph - capacity_matrix::AbstractArray{T, 2}, # edge flow capacities +@traitfn function slope{G<:AbstractGraph; IsDirected{G}}( + flow_graph::G, # the input graph + capacity_matrix::AbstractMatrix, # edge flow capacities cut::Vector, # cut information for vertices - restriction::T # value of the restriction + restriction::Number # value of the restriction ) slope = 0 for e in edges(flow_graph) diff --git a/src/flow/kishimoto.jl b/src/flow/kishimoto.jl index 7885703e7..18c7c2fad 100644 --- a/src/flow/kishimoto.jl +++ b/src/flow/kishimoto.jl @@ -1,10 +1,11 @@ # Method when using Boykov-Kolmogorov as a subroutine # Kishimoto algorithm -function kishimoto{T<:AbstractFloat}( - flow_graph::DiGraph, # the input graph + +@traitfn function kishimoto{G<:AbstractGraph; IsDirected{G}}( + flow_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T, 2}, # edge flow capacities + capacity_matrix::AbstractMatrix, # edge flow capacities flow_algorithm::BoykovKolmogorovAlgorithm, # keyword argument for algorithm routes::Int # keyword argument for routes ) @@ -44,12 +45,12 @@ Requires arguments: - flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm - routes::Int # keyword argument for routes """ - -function kishimoto{T<:AbstractFloat}( - flow_graph::DiGraph, # the input graph +function kishimoto end +@traitfn function kishimoto{G<:AbstractGraph; IsDirected{G}}( + flow_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T, 2}, # edge flow capacities + capacity_matrix::AbstractMatrix, # edge flow capacities flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm routes::Int # keyword argument for routes ) diff --git a/src/flow/maximum_flow.jl b/src/flow/maximum_flow.jl index 0c45ecd5c..bcc6d2f00 100644 --- a/src/flow/maximum_flow.jl +++ b/src/flow/maximum_flow.jl @@ -1,3 +1,4 @@ +import LightGraphs.SimpleGraphs.SimpleDiGraph """ Abstract type that allows users to pass in their preferred Algorithm """ @@ -30,17 +31,16 @@ end """ Type that returns 1 if a forward edge exists, and 0 otherwise """ - -type DefaultCapacity{T<:Integer} <: AbstractArray{T, 2} - flow_graph::DiGraph{T} +type DefaultCapacity{T<:Integer} <: AbstractMatrix{T} + flow_graph::SimpleDiGraph nv::T end -DefaultCapacity{T<:Integer}(flow_graph::DiGraph{T}) = DefaultCapacity(flow_graph, nv(flow_graph)) +@traitfn DefaultCapacity{G<:AbstractGraph; IsDirected{G}}(flow_graph::G) = DefaultCapacity(SimpleDiGraph(flow_graph), nv(flow_graph)) getindex{T<:Integer}(d::DefaultCapacity{T}, s::Integer, t::Integer) = if has_edge(d.flow_graph, s , t) one(T) else zero(T) end -isassigned{T<:Integer}(d::DefaultCapacity{T}, u::T, v::T) = (u in one(T):d.nv) && (v in one(T):d.nv) -size(d::DefaultCapacity) = (d.nv, d.nv) +# isassigned{T<:Integer}(d::DefaultCapacity{T}, u::T, v::T) = (u in 1:d.nv) && (v in 1:d.nv) +size(d::DefaultCapacity) = (Int(d.nv), Int(d.nv)) transpose(d::DefaultCapacity) = DefaultCapacity(reverse(d.flow_graph)) ctranspose(d::DefaultCapacity) = DefaultCapacity(reverse(d.flow_graph)) @@ -61,16 +61,16 @@ Requires arguments: - flow_graph::DiGraph, # the input graph - capacity_matrix::AbstractArray{T,2} # input capacity matrix """ - -residual(flow_graph::DiGraph) = DiGraph(Graph(flow_graph)) +function residual end +@traitfn residual{G<:AbstractGraph; IsDirected{G}}(flow_graph::G) = DiGraph(Graph(flow_graph)) # Method for Edmonds–Karp algorithm -function maximum_flow{T<:Number}( - flow_graph::DiGraph, # the input graph +@traitfn function maximum_flow{G<:AbstractGraph; IsDirected{G}}( + flow_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T,2}, # edge flow capacities + capacity_matrix::AbstractMatrix, # edge flow capacities algorithm::EdmondsKarpAlgorithm # keyword argument for algorithm ) residual_graph = residual(flow_graph) @@ -79,11 +79,11 @@ end # Method for Dinic's algorithm -function maximum_flow{T<:Number}( - flow_graph::DiGraph, # the input graph +@traitfn function maximum_flow{G<:AbstractGraph; IsDirected{G}}( + flow_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T,2}, # edge flow capacities + capacity_matrix::AbstractMatrix, # edge flow capacities algorithm::DinicAlgorithm # keyword argument for algorithm ) residual_graph = residual(flow_graph) @@ -92,11 +92,11 @@ end # Method for Boykov-Kolmogorov algorithm -function maximum_flow{T<:Number}( - flow_graph::DiGraph, # the input graph +@traitfn function maximum_flow{G<:AbstractGraph; IsDirected{G}}( + flow_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T,2}, # edge flow capacities + capacity_matrix::AbstractMatrix, # edge flow capacities algorithm::BoykovKolmogorovAlgorithm # keyword argument for algorithm ) residual_graph = residual(flow_graph) @@ -105,11 +105,11 @@ end # Method for Push-relabel algorithm -function maximum_flow{T<:Number}( - flow_graph::DiGraph, # the input graph +@traitfn function maximum_flow{G<:AbstractGraph; IsDirected{G}}( + flow_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T,2}, # edge flow capacities + capacity_matrix::AbstractMatrix, # edge flow capacities algorithm::PushRelabelAlgorithm # keyword argument for algorithm ) residual_graph = residual(flow_graph) @@ -169,18 +169,17 @@ f, F, labels = maximum_flow(flow_graph,1,8,capacity_matrix,algorithm=BoykovKolmo ``` """ - -function maximum_flow{T<:Number}( - flow_graph::DiGraph, # the input graph +function maximum_flow( + flow_graph::AbstractGraph, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T,2} = # edge flow capacities + capacity_matrix::AbstractMatrix = # edge flow capacities DefaultCapacity(flow_graph); algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm PushRelabelAlgorithm(), - restriction::T = zero(T) # keyword argument for restriction max-flow + restriction::Real = 0 # keyword argument for restriction max-flow ) - if restriction > zero(T) + if restriction > 0 return maximum_flow(flow_graph, source, target, min(restriction, capacity_matrix), algorithm) end return maximum_flow(flow_graph, source, target, capacity_matrix, algorithm) diff --git a/src/flow/multiroute_flow.jl b/src/flow/multiroute_flow.jl index 5c95fb10c..11aae8137 100644 --- a/src/flow/multiroute_flow.jl +++ b/src/flow/multiroute_flow.jl @@ -19,7 +19,7 @@ end # 1) When using Boykov-Kolmogorov as a flow subroutine # 2) Other flow algorithm function empty_flow{T<:Real}( - capacity_matrix::AbstractArray{T, 2}, # edge flow capacities + capacity_matrix::AbstractMatrix{T}, # edge flow capacities flow_algorithm::BoykovKolmogorovAlgorithm # keyword argument for algorithm ) n = size(capacity_matrix, 1) @@ -27,7 +27,7 @@ function empty_flow{T<:Real}( end # 2) Other flow algorithm function empty_flow{T<:Real}( - capacity_matrix::AbstractArray{T, 2}, # edge flow capacities + capacity_matrix::AbstractMatrix{T}, # edge flow capacities flow_algorithm::AbstractFlowAlgorithm # keyword argument for algorithm ) n = size(capacity_matrix, 1) @@ -35,11 +35,11 @@ function empty_flow{T<:Real}( end # Method for Kishimoto algorithm -function multiroute_flow{T<:Real}( - flow_graph::DiGraph, # the input graph +@traitfn function multiroute_flow{G<:AbstractGraph; IsDirected{G}}( + flow_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T, 2}, # edge flow capacities + capacity_matrix::AbstractMatrix, # edge flow capacities flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm mrf_algorithm::KishimotoAlgorithm, # keyword argument for algorithm routes::Int # keyword argument for routes @@ -49,14 +49,14 @@ end ## Methods for Extended Multiroute Flow Algorithm #1 When the breaking points are not already known -function multiroute_flow{T<:Real, R<:Real}( - flow_graph::DiGraph, # the input graph +@traitfn function multiroute_flow{G<:AbstractGraph; IsDirected{G}}( + flow_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T, 2}, # edge flow capacities + capacity_matrix::AbstractMatrix, # edge flow capacities flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm mrf_algorithm::ExtendedMultirouteFlowAlgorithm, # keyword argument for algorithm - routes::R # keyword argument for routes + routes::Real # keyword argument for routes ) return emrf(flow_graph, source, target, capacity_matrix, flow_algorithm, routes) end @@ -69,19 +69,19 @@ function multiroute_flow{T<:Real, R<:Real}( return intersection(breakingpoints, routes) end #2-b Output: flow value, flows(, labels) -function multiroute_flow{T1<:Real, T2<:Real, R<:Real}( - breakingpoints::Vector{Tuple{T1, T1, Int}}, # vector of breaking points +function multiroute_flow{T1<:Real, R<:Real}( + breakingpoints::AbstractVector{Tuple{T1, T1, Int}}, # vector of breaking points routes::R, # keyword argument for routes - flow_graph::DiGraph, # the input graph + flow_graph::AbstractGraph, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T2, 2} = # edge flow capacities + capacity_matrix::AbstractMatrix = # edge flow capacities DefaultCapacity(flow_graph); flow_algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm PushRelabelAlgorithm() ) x, f = intersection(breakingpoints, routes) - + T2 = eltype(capacity_matrix) # For other cases, capacities need to be Floats if !(T2<:AbstractFloat) capacity_matrix = convert(AbstractArray{Float64, 2}, capacity_matrix) @@ -178,12 +178,11 @@ f, F, labels = multiroute_flow(flow_graph, 1, 8, capacity_matrix, ``` """ - -function multiroute_flow{T<:Real, R<:Real}( - flow_graph::DiGraph, # the input graph +function multiroute_flow{R<:Real}( + flow_graph::AbstractGraph, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T, 2} = # edge flow capacities + capacity_matrix::AbstractMatrix = # edge flow capacities DefaultCapacity(flow_graph); flow_algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm PushRelabelAlgorithm(), @@ -191,6 +190,7 @@ function multiroute_flow{T<:Real, R<:Real}( KishimotoAlgorithm(), routes::R = 0 # keyword argument for number of routes (0 = all values) ) + # a flow with a set of 1-disjoint pathes is a classical max-flow (routes == 1) && return maximum_flow(flow_graph, source, target, capacity_matrix, flow_algorithm) @@ -201,18 +201,18 @@ function multiroute_flow{T<:Real, R<:Real}( (routes > λ) && return empty_flow(capacity_matrix, flow_algorithm) # For other cases, capacities need to be Floats + T = eltype(capacity_matrix) if !(T<:AbstractFloat) - capacity_matrix = convert(AbstractArray{Float64, 2}, capacity_matrix) + capacity_matrix = convert(AbstractMatrix{Float64}, capacity_matrix) end # Ask for all possible values (breaking points) (routes == 0) && - return emrf(flow_graph, source, target, capacity_matrix, flow_algorithm) - + return emrf(flow_graph, source, target, capacity_matrix, flow_algorithm) # The number of routes is a float → EMRF (R <: AbstractFloat) && - return emrf(flow_graph, source, target, capacity_matrix, flow_algorithm, routes) - + return emrf(flow_graph, source, target, capacity_matrix, flow_algorithm, routes) + # Other calls return multiroute_flow(flow_graph, source, target, capacity_matrix, flow_algorithm, mrf_algorithm, routes) diff --git a/src/flow/push_relabel.jl b/src/flow/push_relabel.jl index f1f71b3d2..4d4eb211d 100644 --- a/src/flow/push_relabel.jl +++ b/src/flow/push_relabel.jl @@ -16,16 +16,16 @@ Requires arguments: - target::Integer # the target vertex - capacity_matrix::AbstractArray{T,2} # edge flow capacities """ - -function push_relabel{T<:Number}( - residual_graph::DiGraph, # the input graph +function push_relabel end +@traitfn function push_relabel{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex - capacity_matrix::AbstractArray{T,2} # edge flow capacities + capacity_matrix::AbstractMatrix # edge flow capacities ) n = nv(residual_graph) - + T = eltype(capacity_matrix) flow_matrix = zeros(T, n, n) height = zeros(Int, n) @@ -70,11 +70,11 @@ Requires arguments: - excess::AbstractArray{T,1} """ -function enqueue_vertex!{T<:Number, U<:Integer}( - Q::AbstractArray{U,1}, +function enqueue_vertex!( + Q::AbstractVector, v::Integer, # input vertex - active::AbstractArray{Bool,1}, - excess::AbstractArray{T,1} + active::AbstractVector{Bool}, + excess::AbstractVector ) if !active[v] && excess[v] > 0 active[v] = true @@ -98,17 +98,17 @@ Requires arguements: - active::AbstractArray{Bool,1} - Q::AbstractArray{Integer,1} """ - -function push_flow!{T<:Number, U<:Integer}( - residual_graph::DiGraph, # the input graph +function push_flow! end +@traitfn function push_flow!{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # the input graph u::Integer, # input from-vertex v::Integer, # input to-vetex - capacity_matrix::AbstractArray{T,2}, - flow_matrix::AbstractArray{T,2}, - excess::AbstractArray{T,1}, - height::AbstractArray{Int,1}, - active::AbstractArray{Bool,1}, - Q::AbstractArray{U,1} + capacity_matrix::AbstractMatrix, + flow_matrix::AbstractMatrix, + excess::AbstractVector, + height::AbstractVector{Int}, + active::AbstractVector{Bool}, + Q::AbstractVector ) flow = min(excess[u], capacity_matrix[u,v] - flow_matrix[u,v]) @@ -139,15 +139,15 @@ Requires arguments: - count::AbstractArray{Int,1} - Q::AbstractArray{Integer,1} """ - -function gap!{T<:Number, U<:Integer}( - residual_graph::DiGraph, # the input graph +function gap! end +@traitfn function gap!{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # the input graph h::Int, # cutoff height - excess::AbstractArray{T,1}, - height::AbstractArray{Int,1}, - active::AbstractArray{Bool,1}, - count::AbstractArray{Int,1}, - Q::AbstractArray{U,1} # FIFO queue + excess::AbstractVector, + height::AbstractVector{Int}, + active::AbstractVector{Bool}, + count::AbstractVector{Int}, + Q::AbstractVector # FIFO queue ) n = nv(residual_graph) for v in vertices(residual_graph) @@ -176,17 +176,17 @@ Requires arguments: - count::AbstractArray{Int,1} - Q::AbstractArray{Integer,1} """ - -function relabel!{T<:Number, U<:Integer}( - residual_graph::DiGraph, # the input graph - v::U, # input vertex to be relabeled - capacity_matrix::AbstractArray{T,2}, - flow_matrix::AbstractArray{T,2}, - excess::AbstractArray{T,1}, - height::AbstractArray{Int,1}, - active::AbstractArray{Bool,1}, - count::AbstractArray{Int,1}, - Q::AbstractArray{U,1} +function relabel! end +@traitfn function relabel!{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # 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 ) n = nv(residual_graph) count[height[v]+1] -= 1 @@ -218,16 +218,17 @@ Requires arguments: - count::AbstractArray{Int,1} - Q::AbstractArray{Integer,1} """ -function discharge!{T<:Number, U<:Integer}( - residual_graph::DiGraph, # the input graph - v::U, # vertex to be discharged - capacity_matrix::AbstractArray{T,2}, - flow_matrix::AbstractArray{T,2}, - excess::AbstractArray{T,1}, - height::AbstractArray{Int,1}, - active::AbstractArray{Bool,1}, - count::AbstractArray{Int,1}, - Q::AbstractArray{U,1} # FIFO queue +function discharge! end +@traitfn function discharge!{G<:AbstractGraph; IsDirected{G}}( + residual_graph::G, # 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::AbstractArray # FIFO queue ) for to in out_neighbors(residual_graph, v) excess[v] == 0 && break diff --git a/test/flow/multiroute_flow.jl b/test/flow/multiroute_flow.jl index 1370efd07..7231b6482 100644 --- a/test/flow/multiroute_flow.jl +++ b/test/flow/multiroute_flow.jl @@ -56,7 +56,7 @@ for (nvertices, flow_edges, s, t, froutes, breakpts, ffloat) in graphs flow_graph = DiGraph(nvertices) - for g in (flow_graph, DiGraph{UInt8}(flow_graph)) #, DiGraph{Int16}(flow_graph)) + for g in (flow_graph, DiGraph{UInt8}(flow_graph), DiGraph{Int16}(flow_graph)) capacity_matrix = zeros(Int, nvertices, nvertices) for e in flow_edges u, v, f = e From a8a015eb179beadeca9cb5e68ea68ff45612f8e6 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Thu, 16 Mar 2017 15:47:58 -0700 Subject: [PATCH 16/56] trait fixes and tests - unrolling --- src/centrality/pagerank.jl | 10 +++- src/linalg/spectral.jl | 47 ++++++++++------ test/flow/push_relabel.jl | 112 ++++++++++++++++++------------------- 3 files changed, 94 insertions(+), 75 deletions(-) diff --git a/src/centrality/pagerank.jl b/src/centrality/pagerank.jl index f43026e7a..6412d5373 100644 --- a/src/centrality/pagerank.jl +++ b/src/centrality/pagerank.jl @@ -6,7 +6,10 @@ iterations (`n`), and convergence threshold (`ϵ`). If convergence is not reached within `n` iterations, an error will be returned. """ -function pagerank(g::DiGraph, α=0.85, n=100, ϵ = 1.0e-6) +function pagerank end + +#TODO: re-roll this to @traitfn function pagerank{G<:AbstractGraph; IsDirected{G}}(g::G, α=0.85, n=100, ϵ = 1.0e-6) +@traitfn function pagerank{G<:AbstractGraph; IsDirected{G}}(g::G, α, n, ϵ) A = adjacency_matrix(g,:in,Float64) S = vec(sum(A,1)) S = 1./S @@ -29,3 +32,8 @@ function pagerank(g::DiGraph, α=0.85, n=100, ϵ = 1.0e-6) end error("Pagerank did not converge after $n iterations.") end + +### TODO: remove these when re-rolled. +@traitfn pagerank{G<:AbstractGraph; IsDirected{G}}(g::G, α, n) = pagerank(g, α, n, 1e-6) +@traitfn pagerank{G<:AbstractGraph; IsDirected{G}}(g::G, α) = pagerank(g, α, 100, 1e-6) +@traitfn pagerank{G<:AbstractGraph; IsDirected{G}}(g::G) = pagerank(g, 0.85, 100, 1e-6) diff --git a/src/linalg/spectral.jl b/src/linalg/spectral.jl index 5679450c4..9c64189b7 100644 --- a/src/linalg/spectral.jl +++ b/src/linalg/spectral.jl @@ -59,23 +59,25 @@ function adjacency_matrix(g::AbstractGraph, dir::Symbol=:out, T::DataType=Int) return spmx end -adjacency_matrix(g::Graph, T::DataType=Int) = adjacency_matrix(g, :out, T) +adjacency_matrix(g::AbstractGraph, T::DataType) = adjacency_matrix(g, :out, T) + +### TODO: re-roll this to be +# function adjacency_matrix(g::AbstractGraph, dir::Symbol=:out, T::DataType=Int) +# @traitfn adjacency_matrix{G<:AbstractGraph; !IsDirected{G}}(g::G, T::DataType) = adjacency_matrix(g, :out, T) +# @traitfn adjacency_matrix{G<:AbstractGraph; !IsDirected{G}}(g::G) = adjacency_matrix(g, :out, Int) """Returns 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. """ -function laplacian_matrix(g::Graph, dir::Symbol=:out, T::DataType=Int) - A = adjacency_matrix(g, dir, T) - D = spdiagm(sum(A,2)[:]) - return D - A -end - -function laplacian_matrix(g::DiGraph, dir::Symbol=:both, T::DataType=Int) - A = adjacency_matrix(g, dir, T) - D = spdiagm(sum(A,2)[:]) - return D - A +function laplacian_matrix(g::AbstractGraph, dir::Symbol=:unspec, T::DataType=Int) + if dir == :unspec + dir = is_directed(g)? :both : :out + end + A = adjacency_matrix(g, dir, T) + D = spdiagm(sum(A,2)[:]) + return D - A end doc"""Returns the eigenvalues of the Laplacian matrix for a graph `g`, indexed @@ -84,8 +86,7 @@ by vertex. Warning: Converts the matrix to dense with $nv^2$ memory usage. Use eigenvalues/eigenvectors. Default values for `dir` and `T` are the same as `laplacian_matrix`. """ -laplacian_spectrum(g::Graph, dir::Symbol=:out, T::DataType=Int) = eigvals(full(laplacian_matrix(g, dir, T))) -laplacian_spectrum(g::DiGraph, dir::Symbol=:both, T::DataType=Int) = eigvals(full(laplacian_matrix(g, dir, T))) +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 @@ -93,15 +94,23 @@ by vertex. Warning: Converts the matrix to dense with $nv^2$ memory usage. Use eigenvalues/eigenvectors. Default values for `dir` and `T` are the same as `adjacency_matrix`. """ -adjacency_spectrum(g::Graph, dir::Symbol=:out, T::DataType=Int) = eigvals(full(adjacency_matrix(g, dir, T))) -adjacency_spectrum(g::DiGraph, dir::Symbol=:both, T::DataType=Int) = eigvals(full(adjacency_matrix(g, dir, T))) +function adjacency_spectrum end +#TODO: Re-roll this to adjacency_spectrum(g::Graph, dir::Symbol=:out, T::DataType=Int) = eigvals(full(adjacency_matrix(g, dir, T))) +@traitfn adjacency_spectrum{G<:AbstractGraph; !IsDirected{G}}(g::G, dir::Symbol, T::DataType) = eigvals(full(adjacency_matrix(g, dir, T))) +@traitfn adjacency_spectrum{G<:AbstractGraph; !IsDirected{G}}(g::G, dir::Symbol) = adjacency_spectrum(g, dir, Int) +@traitfn adjacency_spectrum{G<:AbstractGraph; !IsDirected{G}}(g::G) = adjacency_spectrum(g, :out, Int) + +#TODO: Re-roll this to adjacency_spectrum(g::DiGraph, dir::Symbol=:both, T::DataType=Int) = eigvals(full(adjacency_matrix(g, dir, T))) +@traitfn adjacency_spectrum{G<:AbstractGraph; IsDirected{G}}(g::G, dir::Symbol, T::DataType) = eigvals(full(adjacency_matrix(g, dir, T))) +@traitfn adjacency_spectrum{G<:AbstractGraph; IsDirected{G}}(g::G, dir::Symbol) = adjacency_spectrum(g, dir, Int) +@traitfn adjacency_spectrum{G<:AbstractGraph; IsDirected{G}}(g::G,) = adjacency_spectrum(g, :both, Int) """Returns 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`, +`0`. For undirected graphs, if the optional keyword `oriented` is `false`, both entries are `1`, otherwise, an arbitrary orientation is chosen. """ function incidence_matrix(g::AbstractGraph, T::DataType=Int; oriented=false) @@ -141,7 +150,9 @@ For further details, please refer to: JOVANOVIC, I.; STANIC, Z., 2014. Spectral Distances of Graphs Based on their Different Matrix Representations """ -function spectral_distance(G₁::Graph, G₂::Graph, k::Integer) +function spectral_distance end + +@traitfn function spectral_distance{G<:AbstractGraph; !IsDirected{G}}(G₁::G, G₂::G, k::Integer) A₁ = adjacency_matrix(G₁) A₂ = adjacency_matrix(G₂) @@ -151,7 +162,7 @@ function spectral_distance(G₁::Graph, G₂::Graph, k::Integer) sumabs(λ₁ - λ₂) end -function spectral_distance(G₁::Graph, G₂::Graph) +@traitfn function spectral_distance{G<:AbstractGraph; !IsDirected{G}}(G₁::G, G₂::G) @assert nv(G₁) == nv(G₂) "spectral distance not defined for |G₁| != |G₂|" spectral_distance(G₁, G₂, nv(G₁)) end diff --git a/test/flow/push_relabel.jl b/test/flow/push_relabel.jl index 1c77bd959..75c9381b4 100644 --- a/test/flow/push_relabel.jl +++ b/test/flow/push_relabel.jl @@ -16,72 +16,72 @@ add_edge!(flow_graph,u,v) capacity_matrix[u,v] = f end + for g in (flow_graph, DiGraph{UInt8}(flow_graph), DiGraph{Int16}(flow_graph)) + residual_graph = LightGraphs.residual(g) - residual_graph = LightGraphs.residual(flow_graph) + # Test enqueue_vertex + Q = Array{Int,1}() + excess = [0, 1, 0, 1] + active = [false, false, true, true] + @test LightGraphs.enqueue_vertex!(Q, 1, active, excess) == nothing + @test LightGraphs.enqueue_vertex!(Q, 3, active, excess) == nothing + @test LightGraphs.enqueue_vertex!(Q, 4, active, excess) == nothing + @test length(Q) == 0 + @test LightGraphs.enqueue_vertex!(Q, 2, active, excess) == nothing + @test length(Q) == 1 - # Test enqueue_vertex - Q = Array{Int,1}() - excess = [0, 1, 0, 1] - active = [false, false, true, true] - @test LightGraphs.enqueue_vertex!(Q, 1, active, excess) == nothing - @test LightGraphs.enqueue_vertex!(Q, 3, active, excess) == nothing - @test LightGraphs.enqueue_vertex!(Q, 4, active, excess) == nothing - @test length(Q) == 0 - @test LightGraphs.enqueue_vertex!(Q, 2, active, excess) == nothing - @test length(Q) == 1 + # Test push_flow + Q = Array{Int,1}() + excess = [15, 1, 1, 0, 0, 0, 0, 0] + height = [8, 0, 0, 0, 0, 0, 0, 0] + active = [true, false, false, false, false, false, false, true] + flow_matrix = zeros(Int, 8, 8) + @test LightGraphs.push_flow!(residual_graph, 1, 2, capacity_matrix, flow_matrix, excess, height, active, Q) == nothing + @test length(Q) == 1 + @test flow_matrix[1,2] == 10 + @test LightGraphs.push_flow!(residual_graph, 2, 3, capacity_matrix, flow_matrix, excess, height, active, Q) == nothing + @test length(Q) == 1 + @test flow_matrix[2,3] == 0 - # Test push_flow - Q = Array{Int,1}() - excess = [15, 1, 1, 0, 0, 0, 0, 0] - height = [8, 0, 0, 0, 0, 0, 0, 0] - active = [true, false, false, false, false, false, false, true] - flow_matrix = zeros(Int, 8, 8) - @test LightGraphs.push_flow!(residual_graph, 1, 2, capacity_matrix, flow_matrix, excess, height, active, Q) == nothing - @test length(Q) == 1 - @test flow_matrix[1,2] == 10 - @test LightGraphs.push_flow!(residual_graph, 2, 3, capacity_matrix, flow_matrix, excess, height, active, Q) == nothing - @test length(Q) == 1 - @test flow_matrix[2,3] == 0 + # Test gap + Q = Array{Int,1}() + excess = [15, 1, 1, 0, 0, 0, 0, 0] + height = [8, 2, 2, 1, 3, 3, 4, 5] + active = [true, false, false, false, false, false, false, true] + count = [0, 1, 2, 2, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0] + flow_matrix = zeros(Int, 8, 8) - # Test gap - Q = Array{Int,1}() - excess = [15, 1, 1, 0, 0, 0, 0, 0] - height = [8, 2, 2, 1, 3, 3, 4, 5] - active = [true, false, false, false, false, false, false, true] - count = [0, 1, 2, 2, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0] - flow_matrix = zeros(Int, 8, 8) + @test LightGraphs.gap!(residual_graph, 1, excess, height, active, count, Q) == nothing + @test length(Q) == 2 - @test LightGraphs.gap!(residual_graph, 1, excess, height, active, count, Q) == nothing - @test length(Q) == 2 + # Test relabel + Q = Array{Int,1}() + excess = [15, 1, 1, 0, 0, 0, 0, 0] + height = [8, 1, 1, 1, 1, 1, 1, 0] + active = [true, false, false, false, false, false, false, true] + count = [1, 6, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0] + flow_matrix = zeros(Int, 8, 8) - # Test relabel - Q = Array{Int,1}() - excess = [15, 1, 1, 0, 0, 0, 0, 0] - height = [8, 1, 1, 1, 1, 1, 1, 0] - active = [true, false, false, false, false, false, false, true] - count = [1, 6, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0] - flow_matrix = zeros(Int, 8, 8) + @test LightGraphs.relabel!(residual_graph, 2, capacity_matrix, flow_matrix, excess, height, active, count, Q) == nothing + @test length(Q) == 1 - @test LightGraphs.relabel!(residual_graph, 2, capacity_matrix, flow_matrix, excess, height, active, count, Q) == nothing - @test length(Q) == 1 + # Test discharge + Q = Array{Int,1}() + excess = [50, 1, 1, 0, 0, 0, 0, 0] + height = [8, 0, 0, 0, 0, 0, 0, 0] + active = [true, false, false, false, false, false, false, true] + count = [7, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] + flow_matrix = zeros(Int, 8, 8) - # Test discharge - Q = Array{Int,1}() - excess = [50, 1, 1, 0, 0, 0, 0, 0] - height = [8, 0, 0, 0, 0, 0, 0, 0] - active = [true, false, false, false, false, false, false, true] - count = [7, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] - flow_matrix = zeros(Int, 8, 8) + @test LightGraphs.discharge!(residual_graph, 1, capacity_matrix, flow_matrix, excess, height, active, count, Q) == nothing + @test length(Q) == 3 - @test LightGraphs.discharge!(residual_graph, 1, capacity_matrix, flow_matrix, excess, height, active, count, Q) == nothing - @test length(Q) == 3 - - # Test with default distances - @test LightGraphs.push_relabel(residual_graph, 1, 8, LightGraphs.DefaultCapacity(residual_graph))[1] == 3 - - # Test with capacity matrix - @test LightGraphs.push_relabel(residual_graph, 1, 8, capacity_matrix)[1] == 28 + # Test with default distances + @test LightGraphs.push_relabel(residual_graph, 1, 8, LightGraphs.DefaultCapacity(residual_graph))[1] == 3 + # Test with capacity matrix + @test LightGraphs.push_relabel(residual_graph, 1, 8, capacity_matrix)[1] == 28 + end # Non regression test added for #448 M448 =[0 1 0 0 1 1 1 0 0 0 1 0 From 914c47d676ee1ce2e82a38fdbe9761af21b2c9df Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Thu, 16 Mar 2017 16:24:09 -0700 Subject: [PATCH 17/56] persistence and floyd-warshall --- src/persistence/common.jl | 6 ++---- src/shortestpaths/astar.jl | 2 +- src/shortestpaths/floyd-warshall.jl | 25 +++++++++---------------- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/persistence/common.jl b/src/persistence/common.jl index 407c53be6..0ac86f8b0 100644 --- a/src/persistence/common.jl +++ b/src/persistence/common.jl @@ -1,7 +1,5 @@ NI(x...) = error("This function is not implemented.") -@deprecate readgraph load - # filemap is filled in the format specific source files const filemap = Dict{Symbol, Tuple{Function, Function, Function, Function}}() # :gml => (loadgml, loadgml_mult, savegml, savegml_mult) @@ -89,8 +87,8 @@ function save{T<:AbstractGraph}(io::IO, g::T, gname::String, t::Symbol=:lg) end # save a single graph without name -save(io::IO, g::Graph, t::Symbol=:lg) = save(io, g, "graph", t) -save(io::IO, g::DiGraph, t::Symbol=:lg) = save(io, g, "digraph", t) +save(io::IO, g::AbstractGraph, t::Symbol=:lg) = + save(io, g, is_directed(g)? "digraph" : "graph", t) # save a dictionary of graphs {"name" => graph} function save{T<:String, U<:AbstractGraph}(io::IO, d::Dict{T, U}, t::Symbol=:lg) diff --git a/src/shortestpaths/astar.jl b/src/shortestpaths/astar.jl index 5c8703981..21406ca41 100644 --- a/src/shortestpaths/astar.jl +++ b/src/shortestpaths/astar.jl @@ -5,7 +5,7 @@ function a_star_impl!{T<:Number}( graph::AbstractGraph,# the graph - t::Int, # the end vertex + 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 distmx::AbstractArray{T, 2}, diff --git a/src/shortestpaths/floyd-warshall.jl b/src/shortestpaths/floyd-warshall.jl index 582f68807..f2dbe7c87 100644 --- a/src/shortestpaths/floyd-warshall.jl +++ b/src/shortestpaths/floyd-warshall.jl @@ -2,9 +2,9 @@ # licensing details. -type FloydWarshallState{T}<:AbstractPathState +type FloydWarshallState{T, U<:Integer}<:AbstractPathState dists::Matrix{T} - parents::Matrix{Int} + parents::Matrix{U} end doc"""Uses the [Floyd-Warshall algorithm](http://en.wikipedia.org/wiki/Floyd–Warshall_algorithm) @@ -18,12 +18,12 @@ on the order of $\mathcal{O}(nv^2)$). """ function floyd_warshall_shortest_paths{T}( g::AbstractGraph, - distmx::AbstractArray{T, 2} = DefaultDistance() + distmx::AbstractMatrix{T} = DefaultDistance() ) - + U = eltype(g) n_v = nv(g) dists = fill(typemax(T), (n_v,n_v)) - parents = zeros(Int, (n_v,n_v)) + parents = zeros(U, (n_v,n_v)) # fws = FloydWarshallState(Matrix{T}(), Matrix{Int}()) for v in 1:n_v @@ -55,24 +55,17 @@ function floyd_warshall_shortest_paths{T}( end end fws = FloydWarshallState(dists, parents) - # for r in 1:size(parents,1) # row by row - # push!(fws.parents, vec(parents[r,:])) - # end - # for r in 1:size(dists,1) - # push!(fws.dists, vec(dists[r,:])) - # end - return fws end -function enumerate_paths(s::FloydWarshallState, v::Integer) +function enumerate_paths{T, U<:Integer}(s::FloydWarshallState{T, U}, v::Integer) pathinfo = s.parents[v,:] - paths = Vector{Int}[] + paths = Vector{Vector{U}}() for i in 1:length(pathinfo) if i == v - push!(paths, Vector{Int}()) + push!(paths, Vector{U}()) else - path = Vector{Int}() + path = Vector{U}() currpathindex = i while currpathindex != 0 push!(path,currpathindex) From 1f7a2690ce12434b61a2c4edeb4dfc8b4cf838b4 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Thu, 16 Mar 2017 17:32:32 -0700 Subject: [PATCH 18/56] make(di)graphs, through spanningtrees --- src/linalg/nonbacktracking.jl | 9 +- src/shortestpaths/astar.jl | 11 +- src/shortestpaths/bellman-ford.jl | 34 ++--- src/shortestpaths/floyd-warshall.jl | 4 +- src/spanningtrees/kruskal.jl | 5 +- src/spanningtrees/prim.jl | 6 +- test/biconnectivity/articulation.jl | 2 +- test/biconnectivity/biconnect.jl | 4 +- test/centrality/betweenness.jl | 2 +- test/centrality/closeness.jl | 4 +- test/centrality/degree.jl | 2 +- test/centrality/katz.jl | 2 +- test/centrality/pagerank.jl | 2 +- test/community/clustering.jl | 2 +- test/community/core-periphery.jl | 4 +- test/community/label_propagation.jl | 2 +- test/community/modularity.jl | 4 +- test/flow/boykov_kolmogorov.jl | 2 +- test/flow/maximum_flow.jl | 2 +- test/flow/multiroute_flow.jl | 2 +- test/flow/push_relabel.jl | 2 +- test/linalg/spectral.jl | 213 +++++++++++++++------------ test/runtests.jl | 3 + test/shortestpaths/astar.jl | 10 +- test/shortestpaths/bellman-ford.jl | 19 +-- test/shortestpaths/dijkstra.jl | 64 ++++---- test/shortestpaths/floyd-warshall.jl | 12 +- 27 files changed, 232 insertions(+), 196 deletions(-) diff --git a/src/linalg/nonbacktracking.jl b/src/linalg/nonbacktracking.jl index 298880c5f..0b8eaee9e 100644 --- a/src/linalg/nonbacktracking.jl +++ b/src/linalg/nonbacktracking.jl @@ -140,11 +140,10 @@ end contract(nbt, edgespace). modifies first argument """ function contract!(vertexspace::Vector, nbt::Nonbacktracking, edgespace::Vector) - for i=1:nv(nbt.g) - for j in neighbors(nbt.g, i) - u = nbt.edgeidmap[i > j ? Edge(j,i) : Edge(i,j)] - vertexspace[i] += edgespace[u] - end + T = eltype(nbt.g) + for i = one(T):nv(nbt.g), j in neighbors(nbt.g, i) + u = nbt.edgeidmap[i > j ? Edge(j,i) : Edge(i,j)] + vertexspace[i] += edgespace[u] end end diff --git a/src/shortestpaths/astar.jl b/src/shortestpaths/astar.jl index 21406ca41..6789f5e4b 100644 --- a/src/shortestpaths/astar.jl +++ b/src/shortestpaths/astar.jl @@ -8,7 +8,7 @@ function a_star_impl!{T<:Number}( 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 - distmx::AbstractArray{T, 2}, + distmx::AbstractMatrix{T}, heuristic::Function # heuristic fn (under)estimating distance to target ) @@ -43,13 +43,14 @@ optional heuristic function and edge distance matrix may be supplied. function a_star{T<:Number}( graph::AbstractGraph, # the graph - s::Int, # the start vertex - t::Int, # the end vertex - distmx::AbstractArray{T, 2} = LightGraphs.DefaultDistance(), + s::Integer, # the start vertex + t::Integer, # the end vertex + distmx::AbstractMatrix{T} = LightGraphs.DefaultDistance(), heuristic::Function = n -> 0 ) # heuristic (under)estimating distance to target - frontier = PriorityQueue(Tuple{T,Array{Edge,1},Int},T) + U = eltype(graph) + frontier = PriorityQueue(Tuple{T,Vector{Edge},U},T) frontier[(zero(T), Vector{Edge}(), s)] = zero(T) colormap = zeros(Int, nv(graph)) colormap[s] = 1 diff --git a/src/shortestpaths/bellman-ford.jl b/src/shortestpaths/bellman-ford.jl index 8c5bdc3f4..31f8bab01 100644 --- a/src/shortestpaths/bellman-ford.jl +++ b/src/shortestpaths/bellman-ford.jl @@ -12,28 +12,28 @@ type NegativeCycleError <: Exception end # AbstractPathState is defined in core -type BellmanFordState{T<:Number}<:AbstractPathState - parents::Vector{Int} +type BellmanFordState{T<:Number, U<:Integer}<:AbstractPathState + parents::Vector{U} dists::Vector{T} end -function bellman_ford_shortest_paths!{R<:Real}( +function bellman_ford_shortest_paths!{R<:Real, T<:Integer}( graph::AbstractGraph, - sources::AbstractVector{Int}, - distmx::AbstractArray{R, 2}, + sources::AbstractVector{T}, + distmx::AbstractMatrix{R}, state::BellmanFordState ) - active = Set{Int}() + active = Set{T}() for v in sources state.dists[v] = 0 state.parents[v] = 0 push!(active, v) end no_changes = false - for i in 1:nv(graph) + for i in one(T):nv(graph) no_changes = true - new_active = Set{Int}() + new_active = Set{T}() for u in active for v in out_neighbors(graph, u) edist = distmx[u, v] @@ -59,21 +59,21 @@ 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). """ -function bellman_ford_shortest_paths{T}( +function bellman_ford_shortest_paths{T, U<:Integer}( graph::AbstractGraph, - sources::AbstractVector{Int}, - distmx::AbstractArray{T, 2} = DefaultDistance() + sources::AbstractVector{U}, + distmx::AbstractMatrix{T} = DefaultDistance() ) nvg = nv(graph) - state = BellmanFordState(zeros(Int,nvg), fill(typemax(T), nvg)) + state = BellmanFordState(zeros(U,nvg), fill(typemax(T), nvg)) bellman_ford_shortest_paths!(graph, sources, distmx, state) end bellman_ford_shortest_paths{T}( graph::AbstractGraph, - v::Int, - distmx::AbstractArray{T, 2} = DefaultDistance() + v::Integer, + distmx::AbstractMatrix{T} = DefaultDistance() ) = bellman_ford_shortest_paths(graph, [v], distmx) function has_negative_edge_cycle(graph::AbstractGraph) @@ -85,13 +85,13 @@ function has_negative_edge_cycle(graph::AbstractGraph) return false end -function enumerate_paths(state::AbstractPathState, dest::Vector{Int}) +function enumerate_paths{T<:Integer}(state::AbstractPathState, dest::Vector{T}) parents = state.parents num_dest = length(dest) - all_paths = Array(Vector{Int},num_dest) + all_paths = Array(Vector{T},num_dest) for i=1:num_dest - all_paths[i] = Vector{Int}() + all_paths[i] = Vector{T}() index = dest[i] if parents[index] != 0 || parents[index] == index while parents[index] != 0 diff --git a/src/shortestpaths/floyd-warshall.jl b/src/shortestpaths/floyd-warshall.jl index f2dbe7c87..47c6d1066 100644 --- a/src/shortestpaths/floyd-warshall.jl +++ b/src/shortestpaths/floyd-warshall.jl @@ -22,8 +22,8 @@ function floyd_warshall_shortest_paths{T}( ) U = eltype(g) n_v = nv(g) - dists = fill(typemax(T), (n_v,n_v)) - parents = zeros(U, (n_v,n_v)) + dists = fill(typemax(T), (Int(n_v),Int(n_v))) + parents = zeros(U, (Int(n_v),Int(n_v))) # fws = FloydWarshallState(Matrix{T}(), Matrix{Int}()) for v in 1:n_v diff --git a/src/spanningtrees/kruskal.jl b/src/spanningtrees/kruskal.jl index 61668968f..3b5001014 100644 --- a/src/spanningtrees/kruskal.jl +++ b/src/spanningtrees/kruskal.jl @@ -26,12 +26,13 @@ that contains the containing edges and its weights. """ function kruskal_mst{T<:Real}( g::AbstractGraph, - distmx::AbstractArray{T, 2} = DefaultDistance() + distmx::AbstractMatrix{T} = DefaultDistance() ) + U = eltype(g) edge_list = Vector{KruskalHeapEntry{T}}() mst = Vector{Edge}() - connected_nodes = Vector{Int}(1:nv(g)) + connected_nodes = collect(one(U):nv(g)) sizehint!(edge_list, ne(g)) sizehint!(mst, ne(g)) diff --git a/src/spanningtrees/prim.jl b/src/spanningtrees/prim.jl index 97129e785..f95f0815b 100644 --- a/src/spanningtrees/prim.jl +++ b/src/spanningtrees/prim.jl @@ -12,7 +12,7 @@ that contains the edges. """ function prim_mst{T<:Real}( g::AbstractGraph, - distmx::AbstractArray{T, 2} = DefaultDistance() + distmx::AbstractMatrix{T} = DefaultDistance() ) :: Vector{Edge} pq = Vector{PrimHeapEntry{T}}() @@ -44,10 +44,10 @@ and enters all its edges into priority queue `pq` with its `distmx` values as a """ function visit!{T<:Real}( g::AbstractGraph, - v::Int, + v::Integer, marked::AbstractArray{Bool, 1}, pq::Vector{PrimHeapEntry{T}}, - distmx::AbstractArray{T, 2} + distmx::AbstractMatrix{T} ) marked[v] = true for w in out_neighbors(g, v) diff --git a/test/biconnectivity/articulation.jl b/test/biconnectivity/articulation.jl index 23d8b152b..002ced521 100644 --- a/test/biconnectivity/articulation.jl +++ b/test/biconnectivity/articulation.jl @@ -18,7 +18,7 @@ add_edge!(gint, 7, 10) add_edge!(gint, 7, 12) - for g in (gint, Graph{UInt8}(gint), Graph{Int16}(gint)) + for g in testgraphs(gint) art = articulation(g) ans = [1, 7, 8, 12] @test art == ans diff --git a/test/biconnectivity/biconnect.jl b/test/biconnectivity/biconnect.jl index 81e2c388e..178f7fcf6 100644 --- a/test/biconnectivity/biconnect.jl +++ b/test/biconnectivity/biconnect.jl @@ -21,7 +21,7 @@ [Edge(1, 7), Edge(6, 7), Edge(2, 6), Edge(1, 2)], [Edge(11, 12)]] - for g in (gint, Graph{UInt8}(gint), Graph{Int16}(gint)) + for g in testgraphs(gint) bcc = biconnected_components(g) @test bcc == a end @@ -43,7 +43,7 @@ a = [[Edge(5, 8),Edge(7, 8),Edge(6, 7),Edge(5, 6)], [Edge(4, 5)], [Edge(1, 4),Edge(3, 4),Edge(2, 3),Edge(1, 2)]] - for g in (gint, Graph{UInt8}(gint), Graph{Int16}(gint)) + for g in testgraphs(gint) bcc = biconnected_components(g) @test bcc == a end diff --git a/test/centrality/betweenness.jl b/test/centrality/betweenness.jl index 455dccb0d..30b582284 100644 --- a/test/centrality/betweenness.jl +++ b/test/centrality/betweenness.jl @@ -19,7 +19,7 @@ gint = load(joinpath(testdir,"testdata","graph-50-500.jgz"), "graph-50-500") c = readcentrality(joinpath(testdir,"testdata","graph-50-500-bc.txt")) - for g in (gint, DiGraph{UInt8}(gint), DiGraph{Int16}(gint)) + for g in testdigraphs(gint) z = betweenness_centrality(g) @test map(Float32, z) == map(Float32, c) diff --git a/test/centrality/closeness.jl b/test/centrality/closeness.jl index 2a1b1a9a5..99b37f627 100644 --- a/test/centrality/closeness.jl +++ b/test/centrality/closeness.jl @@ -1,7 +1,7 @@ @testset "Closeness" begin g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) - for g in (g5, DiGraph{UInt8}(g5), DiGraph{Int16}(g5)) + for g in testdigraphs(g5) y = closeness_centrality(g; normalize=false) z = closeness_centrality(g) @test y == [0.75, 0.6666666666666666, 1.0, 0.0] @@ -10,7 +10,7 @@ g5 = Graph(5) add_edge!(g5,1,2) - for g in (g5, Graph{UInt8}(g5), Graph{Int16}(g5)) + for g in testgraphs(g5) z = closeness_centrality(g) @test z[1] == z[2] == 0.25 @test z[3] == z[4] == z[5] == 0.0 diff --git a/test/centrality/degree.jl b/test/centrality/degree.jl index 294fe25c2..548262cf1 100644 --- a/test/centrality/degree.jl +++ b/test/centrality/degree.jl @@ -1,7 +1,7 @@ @testset "Degree" begin g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) - for g in (g5, DiGraph{UInt8}(g5), DiGraph{Int16}(g5)) + for g in testdigraphs(g5) @test degree_centrality(g) == [0.6666666666666666, 0.6666666666666666, 1.0, 0.3333333333333333] @test indegree_centrality(g, normalize=false) == [0.0, 1.0, 2.0, 1.0] @test outdegree_centrality(g; normalize=false) == [2.0, 1.0, 1.0, 0.0] diff --git a/test/centrality/katz.jl b/test/centrality/katz.jl index 675bc882c..f9532a22e 100644 --- a/test/centrality/katz.jl +++ b/test/centrality/katz.jl @@ -1,7 +1,7 @@ @testset "Katz" begin g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) - for g in (g5, DiGraph{UInt8}(g5), DiGraph{Int16}(g5)) + for g in testdigraphs(g5) z = katz_centrality(g, 0.4) @test round(z, 2) == [0.32, 0.44, 0.62, 0.56] end diff --git a/test/centrality/pagerank.jl b/test/centrality/pagerank.jl index 74c2e298b..d603f8960 100644 --- a/test/centrality/pagerank.jl +++ b/test/centrality/pagerank.jl @@ -1,7 +1,7 @@ @testset "Pagerank" begin g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) - for g in (g5, DiGraph{UInt8}(g5), DiGraph{Int16}(g5)) + for g in testdigraphs(g5) @test_approx_eq_eps(pagerank(g)[3], 0.318, 0.001) @test_throws ErrorException pagerank(g, 2) @test_throws ErrorException pagerank(g, 0.85, 2) diff --git a/test/community/clustering.jl b/test/community/clustering.jl index 6a9f7a119..a65e2439e 100644 --- a/test/community/clustering.jl +++ b/test/community/clustering.jl @@ -1,6 +1,6 @@ @testset "Clustering" begin g10 = CompleteGraph(10) - for g in (g10, Graph{UInt8}(g10), Graph{Int16}(g10)) + for g in testgraphs(g10) @test local_clustering_coefficient(g) == ones(10) @test global_clustering_coefficient(g) == 1 @test local_clustering(g) == (fill(36, 10), fill(36, 10)) diff --git a/test/community/core-periphery.jl b/test/community/core-periphery.jl index bd0bacb31..c516cc661 100644 --- a/test/community/core-periphery.jl +++ b/test/community/core-periphery.jl @@ -1,6 +1,6 @@ @testset "Core periphery" begin g10 = StarGraph(10) - for g in (g10, Graph{UInt8}(g10), Graph{Int16}(g10)) + for g in testgraphs(g10) c = core_periphery_deg(g) @test degree(g, 1) == 9 @test c[1] == 1 @@ -12,7 +12,7 @@ g10 = StarGraph(10) g10 = blkdiag(g10,g10) add_edge!(g10, 1, 11) - for g in (g10, Graph{UInt8}(g10), Graph{Int16}(g10)) + for g in testgraphs(g10) c = core_periphery_deg(g) @test c[1] == 1 @test c[11] == 1 diff --git a/test/community/label_propagation.jl b/test/community/label_propagation.jl index 482fd9349..f05f6b409 100644 --- a/test/community/label_propagation.jl +++ b/test/community/label_propagation.jl @@ -1,7 +1,7 @@ @testset "Label propagation" begin n=10 g10 = CompleteGraph(n) - for g in (g10, Graph{UInt16}(g10), Graph{Int32}(g10)) + for g in testgraphs(g10) z = copy(g) for k=2:5 z = blkdiag(z, g) diff --git a/test/community/modularity.jl b/test/community/modularity.jl index 7d58eba57..c084e2028 100644 --- a/test/community/modularity.jl +++ b/test/community/modularity.jl @@ -3,12 +3,12 @@ m = n*(n-1)/2 c = ones(Int, n) gint = CompleteGraph(n) - for g in (gint, Graph{UInt8}(gint), Graph{Int16}(gint)) + for g in testgraphs(gint) @test modularity(g, c) == 0 end gint = Graph(n) - for g in (gint, Graph{UInt8}(gint), Graph{Int16}(gint)) + for g in testgraphs(gint) @test modularity(g, c) == 0 end end diff --git a/test/flow/boykov_kolmogorov.jl b/test/flow/boykov_kolmogorov.jl index aa602f35b..78a49fe1b 100644 --- a/test/flow/boykov_kolmogorov.jl +++ b/test/flow/boykov_kolmogorov.jl @@ -8,7 +8,7 @@ source, target = 1, 3 - for g in (G, DiGraph{UInt8}(G), DiGraph{Int16}(G)) + for g in testdigraphs(G) # default capacity capacity_matrix = LightGraphs.DefaultCapacity(g) residual_graph = LightGraphs.residual(g) diff --git a/test/flow/maximum_flow.jl b/test/flow/maximum_flow.jl index b4fd06932..1f3111466 100644 --- a/test/flow/maximum_flow.jl +++ b/test/flow/maximum_flow.jl @@ -29,7 +29,7 @@ for (nvertices,flow_edges,s,t,fdefault,fcustom,frestrict,caprestrict) in graphs flow_graph = DiGraph(nvertices) - for g in (flow_graph, DiGraph{UInt8}(flow_graph), DiGraph{Int16}(flow_graph)) + for g in testdigraphs(flow_graph) capacity_matrix = zeros(Int,nvertices,nvertices) for e in flow_edges u,v,f = e diff --git a/test/flow/multiroute_flow.jl b/test/flow/multiroute_flow.jl index 7231b6482..e9d68a53a 100644 --- a/test/flow/multiroute_flow.jl +++ b/test/flow/multiroute_flow.jl @@ -56,7 +56,7 @@ for (nvertices, flow_edges, s, t, froutes, breakpts, ffloat) in graphs flow_graph = DiGraph(nvertices) - for g in (flow_graph, DiGraph{UInt8}(flow_graph), DiGraph{Int16}(flow_graph)) + for g in testdigraphs(flow_graph) capacity_matrix = zeros(Int, nvertices, nvertices) for e in flow_edges u, v, f = e diff --git a/test/flow/push_relabel.jl b/test/flow/push_relabel.jl index 75c9381b4..54ec82635 100644 --- a/test/flow/push_relabel.jl +++ b/test/flow/push_relabel.jl @@ -16,7 +16,7 @@ add_edge!(flow_graph,u,v) capacity_matrix[u,v] = f end - for g in (flow_graph, DiGraph{UInt8}(flow_graph), DiGraph{Int16}(flow_graph)) + for g in testdigraphs(flow_graph) residual_graph = LightGraphs.residual(g) # Test enqueue_vertex diff --git a/test/linalg/spectral.jl b/test/linalg/spectral.jl index 5b0b3ff1a..48461bcae 100644 --- a/test/linalg/spectral.jl +++ b/test/linalg/spectral.jl @@ -8,136 +8,157 @@ full(nbt::Nonbacktracking) = full(sparse(nbt)) g3 = PathGraph(5) g4 = PathDiGraph(5) g5 = DiGraph(4) - @test adjacency_matrix(g3)[3,2] == 1 - @test adjacency_matrix(g3)[2,4] == 0 - @test laplacian_matrix(g3)[3,2] == -1 - @test laplacian_matrix(g3)[1,3] == 0 - @test laplacian_spectrum(g3)[5] == 3.6180339887498945 - @test adjacency_spectrum(g3)[1] == -1.732050807568878 - - + for g in testgraphs(g3) + @test adjacency_matrix(g)[3,2] == 1 + @test adjacency_matrix(g)[2,4] == 0 + @test laplacian_matrix(g)[3,2] == -1 + @test laplacian_matrix(g)[1,3] == 0 + @test laplacian_spectrum(g)[5] == 3.6180339887498945 + @test adjacency_spectrum(g)[1] == -1.732050807568878 + end + + add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) - @test laplacian_spectrum(g5)[3] == laplacian_spectrum(g5,:both)[3] == 3.0 - @test laplacian_spectrum(g5,:in)[3] == 1.0 - @test laplacian_spectrum(g5,:out)[3] == 1.0 + for g in testdigraphs(g5) + @test laplacian_spectrum(g)[3] == laplacian_spectrum(g,:both)[3] == 3.0 + @test laplacian_spectrum(g,:in)[3] == 1.0 + @test laplacian_spectrum(g,:out)[3] == 1.0 + end # check adjacency matrices with self loops - g = copy(g3) - add_edge!(g,1,1) - @test adjacency_matrix(g)[1,1] == 2 + gx = copy(g3) + add_edge!(gx,1,1) + for g in testgraphs(gx) + @test adjacency_matrix(g)[1,1] == 2 + end g10 = CompleteGraph(10) - B, em = non_backtracking_matrix(g10) - @test length(em) == 2*ne(g10) - @test size(B) == (2*ne(g10),2*ne(g10)) - for i=1:10 - @test sum(B[:,i]) == 8 - @test sum(B[i,:]) == 8 + for g in testgraphs(g10) + B, em = non_backtracking_matrix(g) + @test length(em) == 2*ne(g) + @test size(B) == (2*ne(g),2*ne(g)) + for i=1:10 + @test sum(B[:,i]) == 8 + @test sum(B[i,:]) == 8 + end + @test !issymmetric(B) + + v = ones(Float64, ne(g)) + z = zeros(Float64, nv(g)) + n10 = Nonbacktracking(g) + @test size(n10) == (2*ne(g), 2*ne(g)) + @test eltype(n10) == Float64 + @test !issymmetric(n10) + + LightGraphs.contract!(z, n10, v) + + zprime = contract(n10, v) + @test z == zprime + @test z == 9*ones(Float64, nv(g)) end - @test !issymmetric(B) - - v = ones(Float64, ne(g10)) - z = zeros(Float64, nv(g10)) - n10 = Nonbacktracking(g10) - @test size(n10) == (2*ne(g10), 2*ne(g10)) - @test eltype(n10) == Float64 - @test !issymmetric(n10) - LightGraphs.contract!(z, n10, v) - - zprime = contract(n10, v) - @test z == zprime - @test z == 9*ones(Float64, nv(g10)) - - @test_approx_eq_eps(adjacency_spectrum(g5)[3],0.311, 0.001) + for g in testdigraphs(g5) + @test_approx_eq_eps(adjacency_spectrum(g)[3],0.311, 0.001) + end - @test adjacency_matrix(g3) == - adjacency_matrix(g3, :out) == - adjacency_matrix(g3, :in) == - adjacency_matrix(g3, :both) + for g in testgraphs(g3) + @test adjacency_matrix(g) == + adjacency_matrix(g, :out) == + adjacency_matrix(g, :in) == + adjacency_matrix(g, :both) - @test_throws ErrorException adjacency_matrix(g3, :purple) + @test_throws ErrorException adjacency_matrix(g, :purple) + end #that call signature works - inmat = adjacency_matrix(g5, :in, Int) - outmat = adjacency_matrix(g5, :out, Int) - bothmat = adjacency_matrix(g5, :both, Int) + for g in testdigraphs(g5) + inmat = adjacency_matrix(g, :in, Int) + outmat = adjacency_matrix(g, :out, Int) + bothmat = adjacency_matrix(g, :both, Int) #relations that should be true - @test inmat' == outmat - @test all((bothmat - outmat) .>= 0) - @test all((bothmat - inmat) .>= 0) - - #check properties of the undirected laplacian carry over. - for dir in [:in, :out, :both] - amat = adjacency_matrix(g5, dir, Float64) - lmat = laplacian_matrix(g5, dir, Float64) + @test inmat' == outmat + @test all((bothmat - outmat) .>= 0) + @test all((bothmat - inmat) .>= 0) + + #check properties of the undirected laplacian carry over. + for dir in [:in, :out, :both] + amat = adjacency_matrix(g, dir, Float64) + lmat = laplacian_matrix(g, dir, Float64) @test isa(amat, SparseMatrixCSC{Float64, Int64}) @test isa(lmat, SparseMatrixCSC{Float64, Int64}) evals = eigvals(full(lmat)) @test all(evals .>= -1e-15) # positive semidefinite @test_approx_eq_eps minimum(evals) 0 1e-13 + end end + for g in testdigraphs(g4) # testing incidence_matrix, first directed graph - @test size(incidence_matrix(g4)) == (5,4) - @test incidence_matrix(g4)[1,1] == -1 - @test incidence_matrix(g4)[2,1] == 1 - @test incidence_matrix(g4)[3,1] == 0 + @test size(incidence_matrix(g)) == (5,4) + @test incidence_matrix(g)[1,1] == -1 + @test incidence_matrix(g)[2,1] == 1 + @test incidence_matrix(g)[3,1] == 0 + end + + for g in testgraphs(g3) # now undirected graph - @test size(incidence_matrix(g3)) == (5,4) - @test incidence_matrix(g3)[1,1] == 1 - @test incidence_matrix(g3)[2,1] == 1 - @test incidence_matrix(g3)[3,1] == 0 + @test size(incidence_matrix(g)) == (5,4) + @test incidence_matrix(g)[1,1] == 1 + @test incidence_matrix(g)[2,1] == 1 + @test incidence_matrix(g)[3,1] == 0 # undirected graph with orientation - @test size(incidence_matrix(g3; oriented=true)) == (5,4) - @test incidence_matrix(g3; oriented=true)[1,1] == -1 - @test incidence_matrix(g3; oriented=true)[2,1] == 1 - @test incidence_matrix(g3; oriented=true)[3,1] == 0 - + @test size(incidence_matrix(g; oriented=true)) == (5,4) + @test incidence_matrix(g; oriented=true)[1,1] == -1 + @test incidence_matrix(g; oriented=true)[2,1] == 1 + @test incidence_matrix(g; oriented=true)[3,1] == 0 + end # TESTS FOR Nonbacktracking operator. n = 10; k = 5 pg = CompleteGraph(n) # ϕ1 = nonbacktrack_embedding(pg, k)' - - nbt = Nonbacktracking(pg) - B, emap = non_backtracking_matrix(pg) - Bs = sparse(nbt) - @test sparse(B) == Bs - @test_approx_eq_eps(eigs(nbt, nev=1)[1], eigs(B, nev=1)[1], 1e-5) - - # check that matvec works - x = ones(Float64, nbt.m) - y = nbt * x - z = B * x - @test norm(y-z) < 1e-8 - - #check that matmat works and full(nbt) == B - @test norm(nbt*eye(nbt.m) - B) < 1e-8 - - #check that matmat works and full(nbt) == B - @test norm(nbt*eye(nbt.m) - B) < 1e-8 - - #check that we can use the implicit matvec in nonbacktrack_embedding - @test size(y) == size(x) - - B₁ = Nonbacktracking(g10) - - @test full(B₁) == full(B) - @test B₁ * ones(size(B₁)[2]) == B*ones(size(B)[2]) - @test size(B₁) == size(B) - # @test_approx_eq_eps norm(eigs(B₁)[1] - eigs(B)[1]) 0.0 1e-8 - @test !issymmetric(B₁) - @test eltype(B₁) == Float64 + for g in testgraphs(pg) + nbt = Nonbacktracking(g) + B, emap = non_backtracking_matrix(g) + Bs = sparse(nbt) + @test sparse(B) == Bs + @test_approx_eq_eps(eigs(nbt, nev=1)[1], eigs(B, nev=1)[1], 1e-5) + + # check that matvec works + x = ones(Float64, nbt.m) + y = nbt * x + z = B * x + @test norm(y-z) < 1e-8 + + #check that matmat works and full(nbt) == B + @test norm(nbt*eye(nbt.m) - B) < 1e-8 + + #check that matmat works and full(nbt) == B + @test norm(nbt*eye(nbt.m) - B) < 1e-8 + + #check that we can use the implicit matvec in nonbacktrack_embedding + @test size(y) == size(x) + + B₁ = Nonbacktracking(g10) + + @test full(B₁) == full(B) + @test B₁ * ones(size(B₁)[2]) == B*ones(size(B)[2]) + @test size(B₁) == size(B) + # @test_approx_eq_eps norm(eigs(B₁)[1] - eigs(B)[1]) 0.0 1e-8 + @test !issymmetric(B₁) + @test eltype(B₁) == Float64 + end # END tests for Nonbacktracking # spectral distance checks for n=3:10 polygon = random_regular_graph(n, 2) - @test isapprox(spectral_distance(polygon, polygon), 0, atol=1e-8) - @test isapprox(spectral_distance(polygon, polygon, 1), 0, atol=1e-8) + for g in testgraphs(polygon) + @test isapprox(spectral_distance(g, g), 0, atol=1e-8) + @test isapprox(spectral_distance(g, g, 1), 0, atol=1e-8) + end end end diff --git a/test/runtests.jl b/test/runtests.jl index b9f25c5e6..25efef57d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -49,6 +49,9 @@ testdir = dirname(@__FILE__) # a1 = Graph(adjmx1) # a2 = DiGraph(adjmx2) +testgraphs(g) = (g, Graph{UInt8}(g), Graph{Int16}(g)) +testdigraphs(g) = (g, DiGraph{UInt8}(g), DiGraph{Int16}(g)) + tests = [ "interface", "core", diff --git a/test/shortestpaths/astar.jl b/test/shortestpaths/astar.jl index b7596879b..c59fab47e 100644 --- a/test/shortestpaths/astar.jl +++ b/test/shortestpaths/astar.jl @@ -4,8 +4,10 @@ d1 = float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) d2 = sparse(float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) - @test a_star(g3, 1, 4, d1) == - a_star(g4, 1, 4, d1) == - a_star(g3, 1, 4, d2) - @test a_star(g4, 4, 1) == nothing + for g in testgraphs(g3), dg in testdigraphs(g4) + @test a_star(g, 1, 4, d1) == + a_star(dg, 1, 4, d1) == + a_star(g, 1, 4, d2) + @test a_star(dg, 4, 1) == nothing + end end diff --git a/test/shortestpaths/bellman-ford.jl b/test/shortestpaths/bellman-ford.jl index 7e49c9417..187dbc2e4 100644 --- a/test/shortestpaths/bellman-ford.jl +++ b/test/shortestpaths/bellman-ford.jl @@ -3,14 +3,15 @@ d1 = float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) d2 = sparse(float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) + for g in testdigraphs(g4) + y = bellman_ford_shortest_paths(g, 2, d1) + z = bellman_ford_shortest_paths(g, 2, d2) + @test y.dists == z.dists == [Inf, 0, 6, 17, 33] + @test enumerate_paths(z)[2] == [] + @test enumerate_paths(z)[4] == enumerate_paths(z,4) == [2,3,4] + @test !has_negative_edge_cycle(g) - y = bellman_ford_shortest_paths(g4, 2, d1) - z = bellman_ford_shortest_paths(g4, 2, d2) - @test y.dists == z.dists == [Inf, 0, 6, 17, 33] - @test enumerate_paths(z)[2] == [] - @test enumerate_paths(z)[4] == enumerate_paths(z,4) == [2,3,4] - @test !has_negative_edge_cycle(g4) - - z = bellman_ford_shortest_paths(g4, 2) - @test z.dists == [typemax(Int), 0, 1, 2, 3] + z = bellman_ford_shortest_paths(g, 2) + @test z.dists == [typemax(Int), 0, 1, 2, 3] + end end diff --git a/test/shortestpaths/dijkstra.jl b/test/shortestpaths/dijkstra.jl index b5f0939e0..27ed3f16f 100644 --- a/test/shortestpaths/dijkstra.jl +++ b/test/shortestpaths/dijkstra.jl @@ -3,29 +3,32 @@ d1 = float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) d2 = sparse(float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) - y = dijkstra_shortest_paths(g4, 2, d1) - z = dijkstra_shortest_paths(g4, 2, d2) + for g in testdigraphs(g4) + y = dijkstra_shortest_paths(g, 2, d1) + z = dijkstra_shortest_paths(g, 2, d2) - @test y.parents == z.parents == [0, 0, 2, 3, 4] - @test y.dists == z.dists == [Inf, 0, 6, 17, 33] + @test y.parents == z.parents == [0, 0, 2, 3, 4] + @test y.dists == z.dists == [Inf, 0, 6, 17, 33] - y = dijkstra_shortest_paths(g4, 2, d1; allpaths=true) - z = dijkstra_shortest_paths(g4, 2, d2; allpaths=true) - @test z.predecessors[3] == y.predecessors[3] == [2] + y = dijkstra_shortest_paths(g, 2, d1; allpaths=true) + z = dijkstra_shortest_paths(g, 2, d2; allpaths=true) + @test z.predecessors[3] == y.predecessors[3] == [2] - @test enumerate_paths(z) == enumerate_paths(y) - @test enumerate_paths(z)[4] == - enumerate_paths(z,4) == - enumerate_paths(y,4) == [2,3,4] + @test enumerate_paths(z) == enumerate_paths(y) + @test enumerate_paths(z)[4] == + enumerate_paths(z,4) == + enumerate_paths(y,4) == [2,3,4] + end - g = PathGraph(5) - add_edge!(g,2,4) + gx = PathGraph(5) + add_edge!(gx,2,4) d = ones(Int, 5,5) d[2,3] = 100 - z = dijkstra_shortest_paths(g,1,d) - @test z.dists == [0, 1, 3, 2, 3] - @test z.parents == [0, 1, 4, 2, 4] - + for g in testgraphs(gx) + z = dijkstra_shortest_paths(g,1,d) + @test z.dists == [0, 1, 3, 2, 3] + @test z.parents == [0, 1, 4, 2, 4] + end # small function to reconstruct the shortest path; I copied it from somewhere, can't find the original source to give the credits # @Beatzekatze on github @@ -50,18 +53,21 @@ 3. 0. 2. 0.; 0. 2. 0. 3.; 1. 0. 3. 0.] - ds = dijkstra_shortest_paths(G,2,w) - # this loop reconstructs the shortest path for nodes 1, 3 and 4 - @test spaths(ds, [1,3,4], 2) == Array[[2 1], - [2 3], - [2 1 4]] + + for g in testgraphs(G) + ds = dijkstra_shortest_paths(g,2,w) + # this loop reconstructs the shortest path for nodes 1, 3 and 4 + @test spaths(ds, [1,3,4], 2) == Array[[2 1], + [2 3], + [2 1 4]] # here a selflink at source is introduced; it should not change the shortest paths - w[2,2] = 10.0 - ds = dijkstra_shortest_paths(G,2,w) - shortest_paths = [] - # this loop reconstructs the shortest path for nodes 1, 3 and 4 - @test spaths(ds, [1,3,4], 2) == Array[[2 1], - [2 3], - [2 1 4]] + w[2,2] = 10.0 + ds = dijkstra_shortest_paths(g,2,w) + shortest_paths = [] + # this loop reconstructs the shortest path for nodes 1, 3 and 4 + @test spaths(ds, [1,3,4], 2) == Array[[2 1], + [2 3], + [2 1 4]] + end end diff --git a/test/shortestpaths/floyd-warshall.jl b/test/shortestpaths/floyd-warshall.jl index 7d4c05f1c..6f09c9d34 100644 --- a/test/shortestpaths/floyd-warshall.jl +++ b/test/shortestpaths/floyd-warshall.jl @@ -1,10 +1,12 @@ @testset "Floyd Warshall" begin g3 = PathGraph(5) d = [ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0] - z = floyd_warshall_shortest_paths(g3, d) - @test z.dists[3,:][:] == [7, 6, 0, 11, 27] - @test z.parents[3,:][:] == [2, 3, 0, 3, 4] + for g in testgraphs(g3) + z = floyd_warshall_shortest_paths(g, d) + @test z.dists[3,:][:] == [7, 6, 0, 11, 27] + @test z.parents[3,:][:] == [2, 3, 0, 3, 4] - @test enumerate_paths(z)[2][2] == [] - @test enumerate_paths(z)[2][4] == enumerate_paths(z,2)[4] == enumerate_paths(z,2,4) == [2,3,4] + @test enumerate_paths(z)[2][2] == [] + @test enumerate_paths(z)[2][4] == enumerate_paths(z,2)[4] == enumerate_paths(z,2,4) == [2,3,4] + end end From ee544664cf617742bde95aa96e22408b7b543bf6 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Thu, 16 Mar 2017 23:45:14 -0700 Subject: [PATCH 19/56] moved cliques, testing through connectivity.jl --- src/community/cliques.jl | 12 +- src/connectivity.jl | 109 ++++++++------ src/graphtypes/simplegraph/SimpleGraphs.jl | 1 + src/graphtypes/simplegraph/simpledigraph.jl | 7 +- src/graphtypes/simplegraph/simplegraph.jl | 4 +- src/interface.jl | 3 + src/operators.jl | 18 +-- src/traversals/bfs.jl | 64 ++++---- src/traversals/dfs.jl | 47 +++--- src/traversals/graphvisit.jl | 36 ++--- src/traversals/maxadjvisit.jl | 71 ++++----- src/traversals/randomwalks.jl | 23 ++- test/{ => community}/cliques.jl | 23 ++- test/connectivity.jl | 155 +++++++++++--------- test/runtests.jl | 2 +- test/spanningtrees/kruskal.jl | 25 ++-- test/spanningtrees/prim.jl | 20 +-- test/traversals/bfs.jl | 100 +++++++------ test/traversals/dfs.jl | 22 +-- test/traversals/graphvisit.jl | 36 ++--- test/traversals/maxadjvisit.jl | 33 +++-- test/traversals/randomwalks.jl | 70 +++++---- 22 files changed, 482 insertions(+), 399 deletions(-) rename test/{ => community}/cliques.jl (70%) diff --git a/src/community/cliques.jl b/src/community/cliques.jl index 25b9b9fed..dfcedbc98 100644 --- a/src/community/cliques.jl +++ b/src/community/cliques.jl @@ -33,7 +33,7 @@ function maximal_cliques end pivotdonenbrs = Set{T}() # initialize for n in vertices(g) - nbrs = Set{Int}() + nbrs = Set{T}() union!(nbrs, out_neighbors(g, n)) delete!(nbrs, n) # ignore edges between n and itself conn = length(nbrs) @@ -47,13 +47,13 @@ function maximal_cliques end end # Initial setup - cand = Set{Int}(vertices(g)) + cand = Set{T}(vertices(g)) # union!(cand, keys(nnbrs)) smallcand = setdiff(cand, pivotnbrs) - done = Set{Int}() - stack = Vector{Tuple{Set{Int}, Set{Int}, Set{Int}}}() - clique_so_far = Vector{Int}() - cliques = Vector{Array{Int}}() + done = Set{T}() + stack = Vector{Tuple{Set{T}, Set{T}, Set{T}}}() + clique_so_far = Vector{T}() + cliques = Vector{Array{T}}() # Start main loop while !isempty(smallcand) || !isempty(stack) diff --git a/src/connectivity.jl b/src/connectivity.jl index 03a27951c..fc4efcc44 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -14,7 +14,7 @@ Output: c = labels[i] => vertex i belongs to component c. c is the smallest vertex id in the component. """ -function connected_components!(label::Vector{Int}, g::AbstractGraph) +function connected_components!{T<:Integer}(label::Vector{T}, g::AbstractGraph) # this version of connected components uses Breadth First Traversal # with custom visitor type in order to improve performance. # one BFS is performed for each component. @@ -23,12 +23,12 @@ function connected_components!(label::Vector{Int}, g::AbstractGraph) # the return type is a vector of labels which can be used directly or # passed to components(a) nvg = nv(g) - visitor = LightGraphs.ComponentVisitorVector(label, 0) + visitor = LightGraphs.ComponentVisitorVector(label, zero(T)) colormap = fill(0, nvg) - queue = Vector{Int}() + queue = Vector{T}() sizehint!(queue, nvg) - for v in 1:nvg - if label[v] == 0 + for v in vertices(g) + if label[v] == zero(T) visitor.labels[v] = v visitor.seed = v traverse_graph!(g, BreadthFirst(), v, visitor; vertexcolormap=colormap, queue=queue) @@ -37,17 +37,17 @@ function connected_components!(label::Vector{Int}, g::AbstractGraph) return label end -"""components_dict(labels) converts an array of labels to a Dict{Int,Vector{Int}} of components +"""components_dict(labels) converts an array of labels to a Dict{Integer,Vector{Int}} of components Arguments: c = labels[i] => vertex i belongs to component c. Output: vs = d[c] => vertices in vs belong to component c. """ -function components_dict(labels::Vector{Int}) - d = Dict{Int,Vector{Int}}() +function components_dict{T<:Integer}(labels::Vector{T}) + d = Dict{T,Vector{T}}() for (v,l) in enumerate(labels) - vec = get(d, l, Vector{Int}()) + vec = get(d, l, Vector{T}()) push!(vec, v) d[l] = vec end @@ -65,10 +65,10 @@ Output: vs = c[i] => vertices in vs belong to component i. a = d[i] => if labels[v]==i then v in c[a] end """ -function components(labels::Vector{Int}) - d = Dict{Int, Int}() - c = Vector{Vector{Int}}() - i = 1 +function components{T<:Integer}(labels::Vector{T}) + d = Dict{T, T}() + c = Vector{Vector{T}}() + i = one(T) for (v,l) in enumerate(labels) index = get!(d, l, i) if length(c) >= index @@ -89,7 +89,8 @@ of `g` as a vector of components, each represented by a vector of vertices belonging to the component. """ function connected_components(g::AbstractGraph) - label = zeros(Int, nv(g)) + T = eltype(g) + label = zeros(T, nv(g)) connected_components!(label, g) c, d = components(label) return c @@ -101,30 +102,33 @@ end Returns `true` if `g` is connected. For DiGraphs, this is equivalent to a test of weak connectivity. """ -is_connected(g::Graph) = length(connected_components(g)) == 1 -is_connected(g::DiGraph) = is_weakly_connected(g) +function is_connected end +@traitfn is_connected{G<:AbstractGraph; !IsDirected{G}}(g::G) = length(connected_components(g)) == 1 +@traitfn is_connected{G<:AbstractGraph; IsDirected{G}}(g::G) = is_weakly_connected(g) """Returns connected components of the undirected graph of `g`.""" -weakly_connected_components(g::DiGraph) = connected_components(Graph(g)) +function weakly_connected_components end +@traitfn weakly_connected_components{G<:AbstractGraph; IsDirected{G}}(g::G) = connected_components(Graph(g)) """Returns `true` if the undirected graph of `g` is connected.""" -is_weakly_connected(g::DiGraph) = length(weakly_connected_components(g)) == 1 +function is_weakly_connected end +@traitfn is_weakly_connected{G<:AbstractGraph; IsDirected{G}}(g::G) = length(weakly_connected_components(g)) == 1 # Adapated from Graphs.jl -type TarjanVisitor <: AbstractGraphVisitor - stack::Vector{Int} +type TarjanVisitor{T<:Integer} <: AbstractGraphVisitor + stack::Vector{T} onstack::BitVector - lowlink::Vector{Int} - index::Vector{Int} - components::Vector{Vector{Int}} + lowlink::Vector{T} + index::Vector{T} + components::Vector{Vector{T}} end -TarjanVisitor(n::Int) = TarjanVisitor( - Vector{Int}(), +TarjanVisitor{T<:Integer}(n::T) = TarjanVisitor( + Vector{T}(), falses(n), - Vector{Int}(), - zeros(Int, n), - Vector{Vector{Int}}() + Vector{T}(), + zeros(T, n), + Vector{Vector{T}}() ) function discover_vertex!(vis::TarjanVisitor, v) @@ -155,10 +159,12 @@ function close_vertex!(vis::TarjanVisitor, v) end """Computes the (strongly) connected components of a directed graph.""" -function strongly_connected_components(g::DiGraph) +function strongly_connected_components end +@traitfn function strongly_connected_components{G<:AbstractGraph; IsDirected{G}}(g::G) + T = eltype(g) nvg = nv(g) cmap = zeros(Int, nvg) - components = Vector{Vector{Int}}() + components = Vector{Vector{T}}() for v in vertices(g) if cmap[v] == 0 # 0 means not visited yet @@ -173,10 +179,13 @@ function strongly_connected_components(g::DiGraph) end """Returns `true` if `g` is (strongly) connected.""" -is_strongly_connected(g::DiGraph) = length(strongly_connected_components(g)) == 1 +function is_strongly_connected end +@traitfn is_strongly_connected{G<:AbstractGraph; IsDirected{G}}(g::G) = length(strongly_connected_components(g)) == 1 """Computes the (common) period for all nodes in a strongly connected graph.""" -function period(g::DiGraph) +function period end +@traitfn function period{G<:AbstractGraph; IsDirected{G}}(g::G) + T = eltype(g) !is_strongly_connected(g) && error("Graph must be strongly connected") # First check if there's a self loop @@ -185,7 +194,7 @@ function period(g::DiGraph) g_bfs_tree = bfs_tree(g,1) levels = gdistances(g_bfs_tree,1) tree_diff = difference(g,g_bfs_tree) - edge_values = Vector{Int}() + edge_values = Vector{T}() divisor = 0 for e in edges(tree_diff) @@ -198,10 +207,11 @@ function period(g::DiGraph) end """Computes the condensation graph of the strongly connected components.""" -function condensation(g::DiGraph, scc::Vector{Vector{Int}}) - h = DiGraph(length(scc)) +function condensation end +@traitfn function condensation{T<:Integer, G<:AbstractGraph; IsDirected{G}}(g::G, scc::Vector{Vector{T}}) + h = DiGraph{T}(length(scc)) - component = Vector{Int}(nv(g)) + component = Vector{T}(nv(g)) for (i,s) in enumerate(scc) @inbounds component[s] = i @@ -222,16 +232,18 @@ 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::DiGraph) = condensation(g,strongly_connected_components(g)) +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.""" -function attracting_components(g::DiGraph) +function attracting_components end +@traitfn function attracting_components{G<:AbstractGraph; IsDirected{G}}(g::G) + T = eltype(g) scc = strongly_connected_components(g) cond = condensation(g,scc) - attracting = Vector{Int}() + attracting = Vector{T}() for v in vertices(cond) if outdegree(cond,v) == 0 @@ -241,14 +253,14 @@ function attracting_components(g::DiGraph) return scc[attracting] end -type NeighborhoodVisitor <: AbstractGraphVisitor - d::Int - neigs::Vector{Int} +type NeighborhoodVisitor{T<:Integer} <: AbstractGraphVisitor + d::T + neigs::Vector{T} end -NeighborhoodVisitor(d::Int) = NeighborhoodVisitor(d, Vector{Int}()) +NeighborhoodVisitor{T<:Integer}(d::T) = NeighborhoodVisitor(d, Vector{T}()) -function examine_neighbor!(visitor::NeighborhoodVisitor, u::Int, v::Int, ucolor::Int, vcolor::Int, ecolor::Int) +function examine_neighbor!(visitor::NeighborhoodVisitor, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) -ucolor > visitor.d && return false # color is negative for not-closed vertices if vcolor == 0 push!(visitor.neigs, v) @@ -258,18 +270,19 @@ end """ - neighborhood(g, v::Int, d::Int; dir=:out) + neighborhood(g, v::Integer, d::Integer; dir=:out) 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. """ -function neighborhood(g::AbstractGraph, v::Int, d::Int; dir=:out) +function neighborhood(g::AbstractGraph, v::Integer, d::Integer; dir=:out) @assert d >= 0 "Distance has to be greater then zero." + T = eltype(g) visitor = NeighborhoodVisitor(d) - push!(visitor.neigs, v) + push!(visitor.neigs, T(v)) traverse_graph!(g, BreadthFirst(), v, visitor, - vertexcolormap=Dict{Int,Int}(), dir=dir) + vertexcolormap=Dict{T,Int}(), dir=dir) return visitor.neigs end diff --git a/src/graphtypes/simplegraph/SimpleGraphs.jl b/src/graphtypes/simplegraph/SimpleGraphs.jl index be18e9bc8..d78e9c262 100644 --- a/src/graphtypes/simplegraph/SimpleGraphs.jl +++ b/src/graphtypes/simplegraph/SimpleGraphs.jl @@ -126,6 +126,7 @@ function rem_vertex!(g::AbstractSimpleGraph, v::Integer) end return true end +empty{T<:AbstractGraph}(g::T) = T() include("simpleedge.jl") include("simpledigraph.jl") diff --git a/src/graphtypes/simplegraph/simpledigraph.jl b/src/graphtypes/simplegraph/simpledigraph.jl index 89003ae9d..d03e4cacb 100644 --- a/src/graphtypes/simplegraph/simpledigraph.jl +++ b/src/graphtypes/simplegraph/simpledigraph.jl @@ -10,16 +10,17 @@ end eltype{T<:Integer}(x::SimpleDiGraph{T}) = T + # DiGraph{UInt8}(6), DiGraph{Int16}(7), DiGraph{Int8}() function (::Type{SimpleDiGraph{T}}){T<:Integer}(n::Integer = 0) fadjlist = Vector{Vector{T}}() badjlist = Vector{Vector{T}}() - for i = one(T):n + for _ = one(T):n push!(badjlist, Vector{T}()) push!(fadjlist, Vector{T}()) end - vertices = one(T):n - return SimpleDiGraph(vertices, 0, badjlist, fadjlist) + vertices = one(T):T(n) + return SimpleDiGraph(vertices, 0, fadjlist, badjlist) end # DiGraph() diff --git a/src/graphtypes/simplegraph/simplegraph.jl b/src/graphtypes/simplegraph/simplegraph.jl index 0a7da0f89..31921c2a1 100644 --- a/src/graphtypes/simplegraph/simplegraph.jl +++ b/src/graphtypes/simplegraph/simplegraph.jl @@ -9,7 +9,7 @@ end eltype{T<:Integer}(x::SimpleGraph{T}) = T -# Graph{UInt8}(6), Graph{Int16}(7) +# Graph{UInt8}(6), Graph{Int16}(7), Graph{UInt8}() function (::Type{SimpleGraph{T}}){T<:Integer}(n::Integer = 0) fadjlist = Vector{Vector{T}}() sizehint!(fadjlist, n) @@ -151,5 +151,3 @@ function add_vertex!{T<:Integer}(g::SimpleGraph{T}) return true end - -empty{T<:Integer}(g::SimpleGraph{T}) = SimpleGraph{T}() diff --git a/src/interface.jl b/src/interface.jl index 3494c799b..1114dda5c 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -40,6 +40,9 @@ reverse(e::AbstractEdge) = _NI("reverse") """Return the type of a graph's edge""" edgetype(g::AbstractGraph) = _NI("edgetype") +"""Return the type of the graph's vertices""" +eltype(g::AbstractGraph) = _NI("eltype") + """Return the number of vertices in `g`.""" nv(g::AbstractGraph) = _NI("nv") diff --git a/src/operators.jl b/src/operators.jl index d70ad1a0b..45ff4d466 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -20,12 +20,10 @@ end function complement(g::DiGraph) gnv = nv(g) h = DiGraph(gnv) - for i=1:gnv - for j=1:gnv - if i != j && !has_edge(g,i,j) - add_edge!(h,i,j) - end - end + for i in vertices(g), j in vertices(g) + if i != j && !has_edge(g,i,j) + add_edge!(h,i,j) + end end return h end @@ -147,7 +145,7 @@ function union{T<:AbstractGraph}(g::T, h::T) r = T(max(gnv, hnv)) r.ne = ne(g) - for i = 1:gnv + for i in vertices(g) r.fadjlist[i] = deepcopy(g.fadjlist[i]) if is_directed(g) r.badjlist[i] = deepcopy(g.badjlist[i]) @@ -168,7 +166,7 @@ Merges graphs `g` and `h` using `blkdiag` and then adds all the edges between """ function join(g::Graph, h::Graph) r = blkdiag(g, h) - for i=1:nv(g) + for i in vertices(g) for j=nv(g)+1:nv(g)+nv(h) add_edge!(r, i, j) end @@ -253,7 +251,7 @@ function cartesian_product{G<:AbstractGraph}(g::G, h::G) for e in edges(h) j1, j2 = Tuple(e) - for i=1:nv(g) + for i in vertices(g) add_edge!(z, id(i,j1), id(i,j2)) end end @@ -378,4 +376,4 @@ Returns the subgraph of `g` induced by the neighbors of `v` up to distance 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].` """ -egonet(g::AbstractGraph, v::Int, d::Int; dir=:out) = g[neighborhood(g, v, d, dir=dir)] +egonet(g::AbstractGraph, v::Integer, d::Integer; dir=:out) = g[neighborhood(g, v, d, dir=dir)] diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 3e584c4f3..8590a678b 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -23,9 +23,9 @@ EdgeColorMap : type BreadthFirst <: AbstractGraphVisitAlgorithm end -function breadth_first_visit_impl!( +function breadth_first_visit_impl!{T<:Integer}( graph::AbstractGraph, # the graph - queue::Vector{Int}, # an (initialized) queue that stores the active vertices + queue::Vector{T}, # an (initialized) queue that stores the active vertices vertexcolormap::AbstractVertexMap, # an (initialized) color-map to indicate status of vertices (-1=unseen, otherwise distance from root) edgecolormap::AbstractEdgeMap, # an (initialized) color-map to indicate status of edges visitor::AbstractGraphVisitor, # the visitor @@ -59,9 +59,9 @@ function traverse_graph!( alg::BreadthFirst, source, visitor::AbstractGraphVisitor; - vertexcolormap::AbstractVertexMap = Dict{Int, Int}(), + vertexcolormap::AbstractVertexMap = Dict{eltype(graph), Int}(), edgecolormap::AbstractEdgeMap = DummyEdgeMap(), - queue = Vector{Int}(), + queue = Vector{eltype(graph)}(), dir = :out) for s in source @@ -89,21 +89,21 @@ end """TreeBFSVisitorVector is a type for representing a BFS traversal of the graph as a parents array. This type allows for a more performant implementation. """ -type TreeBFSVisitorVector <: AbstractGraphVisitor - tree::Vector{Int} +type TreeBFSVisitorVector{T<:Integer} <: AbstractGraphVisitor + tree::Vector{T} end -function TreeBFSVisitorVector(n::Int) - return TreeBFSVisitorVector(fill(0, n)) +function TreeBFSVisitorVector{T<:Integer}(n::T) + return TreeBFSVisitorVector(fill(zero(T), n)) end """tree converts a parents array into a DiGraph""" -function tree(parents::AbstractVector) - n = length(parents) - t = DiGraph(n) - for i in 1:n +function tree{T<:Integer}(parents::AbstractVector{T}) + n = T(length(parents)) + t = DiGraph{T}(n) + for i in one(T):n parent = parents[i] - if parent > 0 && parent != i + if parent > zero(T) && parent != i add_edge!(t, parent, i) end end @@ -112,7 +112,7 @@ end tree(parents::TreeBFSVisitorVector) = tree(parents.tree) -function examine_neighbor!(visitor::TreeBFSVisitorVector, u::Int, v::Int, +function examine_neighbor!(visitor::TreeBFSVisitorVector, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) if u != v && vcolor == 0 visitor.tree[v] = u @@ -120,11 +120,11 @@ function examine_neighbor!(visitor::TreeBFSVisitorVector, u::Int, v::Int, return true end -function bfs_tree!(visitor::TreeBFSVisitorVector, +function bfs_tree!{T<:Integer}(visitor::TreeBFSVisitorVector{T}, g::AbstractGraph, - s::Int; - vertexcolormap = Dict{Int,Int}(), - queue = Vector{Int}()) + s::Integer; + vertexcolormap = Dict{T,Int}(), + queue = Vector{T}()) # this version of bfs_tree! allows one to reuse the memory necessary to compute the tree # the output is stored in the visitor.tree array whose entries are the vertex id of the # parent of the index. This function checks if the scratch space is too small for the graph. @@ -143,7 +143,7 @@ and returns a directed acyclic graph of vertices in the order they were discover This function is a high level wrapper around bfs_tree!, use that function for more performance. """ -function bfs_tree(g::AbstractGraph, s::Int) +function bfs_tree(g::AbstractGraph, s::Integer) nvg = nv(g) visitor = TreeBFSVisitorVector(nvg) bfs_tree!(visitor, g, s) @@ -154,12 +154,12 @@ end # Connected Components with BFS # ############################################ """Performing connected components with BFS starting from seed""" -type ComponentVisitorVector <: AbstractGraphVisitor - labels::Vector{Int} - seed::Int +type ComponentVisitorVector{T<:Integer} <: AbstractGraphVisitor + labels::Vector{T} + seed::T end -function examine_neighbor!(visitor::ComponentVisitorVector, u::Int, v::Int, +function examine_neighbor!(visitor::ComponentVisitorVector, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) if u != v && vcolor == 0 visitor.labels[v] = visitor.seed @@ -175,9 +175,9 @@ type BipartiteVisitor <: AbstractGraphVisitor is_bipartite::Bool end -BipartiteVisitor(n::Int) = BipartiteVisitor(zeros(UInt8,n), true) +BipartiteVisitor(n::Integer) = BipartiteVisitor(zeros(UInt8,n), true) -function examine_neighbor!(visitor::BipartiteVisitor, u::Int, v::Int, +function examine_neighbor!(visitor::BipartiteVisitor, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) if vcolor == 0 visitor.bipartitemap[v] = (visitor.bipartitemap[u] == 1) ? 2 : 1 @@ -197,19 +197,20 @@ Will return `true` if graph `g` is [bipartite](https://en.wikipedia.org/wiki/Bip If a node `v` is specified, only the connected component to which it belongs is considered. """ function is_bipartite(g::AbstractGraph) + T = eltype(g) cc = filter(x->length(x)>2, connected_components(g)) - vmap = Dict{Int,Int}() + vmap = Dict{T,Int}() for c in cc _is_bipartite(g,c[1], vmap=vmap) || return false end return true end -is_bipartite(g::AbstractGraph, v::Int) = _is_bipartite(g, v) +is_bipartite(g::AbstractGraph, v::Integer) = _is_bipartite(g, v) -_is_bipartite(g::AbstractGraph, v::Int; vmap = Dict{Int,Int}()) = _bipartite_visitor(g, v, vmap=vmap).is_bipartite +_is_bipartite(g::AbstractGraph, v::Integer; vmap = Dict{eltype(g),Int}()) = _bipartite_visitor(g, v, vmap=vmap).is_bipartite -function _bipartite_visitor(g::AbstractGraph, s::Int; vmap=Dict{Int,Int}()) +function _bipartite_visitor(g::AbstractGraph, s::Integer; vmap=Dict{eltype(g),Int}()) nvg = nv(g) visitor = BipartiteVisitor(nvg) for v in keys(vmap) #have to reset vmap, otherway problems with digraphs @@ -229,7 +230,7 @@ function bipartite_map(g::AbstractGraph) visitors = [_bipartite_visitor(g, x[1]) for x in cc] !all([v.is_bipartite for v in visitors]) && return zeros(Int, 0) m = zeros(Int, nv(g)) - for i=1:nv(g) + for i in vertices(g) m[i] = any(v->v.bipartitemap[i] == 1, visitors) ? 2 : 1 end m @@ -246,9 +247,10 @@ Fills `dists` with the geodesic distances of vertices in `g` from vertex/vertice `dists` should be a vector of length `nv(g)`. """ function gdistances!(g::AbstractGraph, source, dists) + T = eltype(g) n = nv(g) fill!(dists, -1) - queue = Vector{Int}(n) + queue = Vector{T}(n) for i in 1:length(source) queue[i] = source[i] dists[source[i]] = 0 diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index bdd715e58..d78c45060 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -66,21 +66,22 @@ function depth_first_visit_impl!( end function traverse_graph!( - graph::AbstractGraph, + g::AbstractGraph, alg::DepthFirst, - s::Int, + s::Integer, visitor::AbstractGraphVisitor; - vertexcolormap = Dict{Int, Int}(), + vertexcolormap = Dict{eltype(g), Int}(), edgecolormap = DummyEdgeMap()) + T = eltype(g) vertexcolormap[s] = -1 discover_vertex!(visitor, s) || return - sdsts = out_neighbors(graph, s) + sdsts = out_neighbors(g, s) sstate = start(sdsts) - stack = [(s, sdsts, sstate)] + stack = [(T(s), sdsts, sstate)] - depth_first_visit_impl!(graph, stack, vertexcolormap, edgecolormap, visitor) + depth_first_visit_impl!(g, stack, vertexcolormap, edgecolormap, visitor) end ################################################# @@ -99,8 +100,8 @@ end function examine_neighbor!( vis::DFSCyclicTestVisitor, - u::Int, - v::Int, + u::Integer, + v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) @@ -133,22 +134,22 @@ end # Topological sort using DFS -type TopologicalSortVisitor <: AbstractGraphVisitor - vertices::Vector{Int} +type TopologicalSortVisitor{T} <: AbstractGraphVisitor + vertices::Vector{T} - function TopologicalSortVisitor(n::Int) - vs = Array(Int, 0) - sizehint!(vs, n) - new(vs) - end end +function TopologicalSortVisitor{T<:Integer}(n::T) + vs = Vector{T}() + sizehint!(vs, n) + return TopologicalSortVisitor(vs) +end -function examine_neighbor!(visitor::TopologicalSortVisitor, u::Int, v::Int, ucolor::Int, vcolor::Int, ecolor::Int) +function examine_neighbor!(visitor::TopologicalSortVisitor, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) (vcolor < 0 && ecolor == 0) && error("The input graph contains at least one loop.") end -function close_vertex!(visitor::TopologicalSortVisitor, v::Int) +function close_vertex!(visitor::TopologicalSortVisitor, v::Integer) push!(visitor.vertices, v) end @@ -167,14 +168,14 @@ function topological_sort_by_dfs(graph::AbstractGraph) end -type TreeDFSVisitor <:AbstractGraphVisitor +type TreeDFSVisitor{T} <:AbstractGraphVisitor tree::DiGraph - predecessor::Vector{Int} + predecessor::Vector{T} end -TreeDFSVisitor(n::Int) = TreeDFSVisitor(DiGraph(n), zeros(Int,n)) +TreeDFSVisitor{T<:Integer}(n::T) = TreeDFSVisitor(DiGraph(n), zeros(T,n)) -function examine_neighbor!(visitor::TreeDFSVisitor, u::Int, v::Int, ucolor::Int, vcolor::Int, ecolor::Int) +function examine_neighbor!(visitor::TreeDFSVisitor, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) if (vcolor == 0) visitor.predecessor[v] = u end @@ -182,12 +183,12 @@ function examine_neighbor!(visitor::TreeDFSVisitor, u::Int, v::Int, ucolor::Int, end """ - dfs_tree(g, s::Int) + dfs_tree(g, s::Integer) 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. """ -function dfs_tree(g::AbstractGraph, s::Int) +function dfs_tree(g::AbstractGraph, s::Integer) nvg = nv(g) visitor = TreeDFSVisitor(nvg) traverse_graph!(g, DepthFirst(), s, visitor) diff --git a/src/traversals/graphvisit.jl b/src/traversals/graphvisit.jl index 002f6eb4f..dc538495e 100644 --- a/src/traversals/graphvisit.jl +++ b/src/traversals/graphvisit.jl @@ -29,7 +29,7 @@ end abstract AbstractGraphVisitAlgorithm typealias AbstractEdgeMap{T} Associative{Edge,T} -typealias AbstractVertexMap{T} Union{AbstractVector{T},Associative{Int, T}} +typealias AbstractVertexMap{T<:Integer, U} Union{AbstractVector{T},Associative{T, U}} type DummyEdgeMap <: AbstractEdgeMap{Int} end @@ -47,29 +47,29 @@ get(d::DummyEdgeMap, e::Edge, x::Int) = x # List vertices by the order of being discovered -type VertexListVisitor <: AbstractGraphVisitor - vertices::Vector{Int} +type VertexListVisitor{T<:Integer} <: AbstractGraphVisitor + vertices::Vector{T} +end - function VertexListVisitor(n::Integer=0) - vs = Vector{Int}() - sizehint!(vs, n) - new(vs) - end +function VertexListVisitor{T<:Integer}(n::T=0) + vs = Vector{T}() + sizehint!(vs, n) + return VertexListVisitor(vs) end -function discover_vertex!(visitor::VertexListVisitor, v::Int) +function discover_vertex!(visitor::VertexListVisitor, v::Integer) push!(visitor.vertices, v) return true end function visited_vertices( - graph::AbstractGraph, + g::AbstractGraph, alg::AbstractGraphVisitAlgorithm, sources) - - visitor = VertexListVisitor(nv(graph)) - traverse_graph!(graph, alg, sources, visitor) - visitor.vertices::Vector{Int} + T = eltype(g) + visitor = VertexListVisitor(nv(g)) + traverse_graph!(g, alg, sources, visitor) + visitor.vertices::Vector{T} end @@ -79,22 +79,22 @@ type LogGraphVisitor{S<:IO} <: AbstractGraphVisitor io::S end -function discover_vertex!(vis::LogGraphVisitor, v::Int) +function discover_vertex!(vis::LogGraphVisitor, v::Integer) println(vis.io, "discover vertex: $v") return true end -function open_vertex!(vis::LogGraphVisitor, v::Int) +function open_vertex!(vis::LogGraphVisitor, v::Integer) println(vis.io, "open vertex: $v") return true end -function close_vertex!(vis::LogGraphVisitor, v::Int) +function close_vertex!(vis::LogGraphVisitor, v::Integer) println(vis.io, "close vertex: $v") return true end -function examine_neighbor!(vis::LogGraphVisitor, u::Int, v::Int, ucolor::Int, vcolor::Int, ecolor::Int) +function examine_neighbor!(vis::LogGraphVisitor, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) println(vis.io, "examine neighbor: $u -> $v (ucolor = $ucolor, vcolor = $vcolor, edgecolor= $ecolor)") return true end diff --git a/src/traversals/maxadjvisit.jl b/src/traversals/maxadjvisit.jl index ece74a13e..17ef7e4f0 100644 --- a/src/traversals/maxadjvisit.jl +++ b/src/traversals/maxadjvisit.jl @@ -16,7 +16,7 @@ end abstract AbstractMASVisitor <: AbstractGraphVisitor function maximum_adjacency_visit_impl!{T}( - graph::AbstractGraph, # the graph + g::AbstractGraph, # the graph pq::DataStructures.PriorityQueue{Int, T}, # priority queue visitor::AbstractMASVisitor, # the visitor colormap::Vector{Int}) # traversal status @@ -24,7 +24,7 @@ function maximum_adjacency_visit_impl!{T}( while !isempty(pq) u = DataStructures.dequeue!(pq) discover_vertex!(visitor, u) - for v in out_neighbors(graph, u) + for v in out_neighbors(g, u) examine_neighbor!(visitor, u, v, 0, 0, 0) if haskey(pq,v) @@ -38,10 +38,10 @@ function maximum_adjacency_visit_impl!{T}( end function traverse_graph!( - graph::AbstractGraph, + g::AbstractGraph, T::DataType, alg::MaximumAdjacency, - s::Int, + s::Integer, visitor::AbstractMASVisitor, colormap::Vector{Int}) @@ -49,18 +49,18 @@ function traverse_graph!( pq = DataStructures.PriorityQueue(Int,T,Base.Order.Reverse) # Set number of visited neighbors for all vertices to 0 - for v in vertices(graph) + for v in vertices(g) pq[v] = zero(T) end @assert haskey(pq,s) - @assert nv(graph) >= 2 + @assert nv(g) >= 2 #Give the starting vertex high priority pq[s] = one(T) #start traversing the graph - maximum_adjacency_visit_impl!(graph, pq, visitor, colormap) + maximum_adjacency_visit_impl!(g, pq, visitor, colormap) end @@ -77,40 +77,41 @@ end # ################################################# -type MinCutVisitor{T} <: AbstractMASVisitor +type MinCutVisitor{T, U<:Integer} <: AbstractMASVisitor graph::AbstractGraph - parities::AbstractArray{Bool,1} + parities::BitVector colormap::Vector{Int} bestweight::T cutweight::T - visited::Integer - distmx::AbstractArray{T, 2} - vertices::Vector{Int} + visited::Int + distmx::AbstractMatrix{T} + vertices::Vector{U} end -function MinCutVisitor{T}(graph::AbstractGraph, distmx::AbstractArray{T, 2}) - n = nv(graph) +function MinCutVisitor{T}(g::AbstractGraph, distmx::AbstractMatrix{T}) + U = eltype(g) + n = nv(g) parities = falses(n) return MinCutVisitor( - graph, + g, falses(n), zeros(Int,n), typemax(T), zero(T), zero(Int), distmx, - Vector{Int}() + Vector{U}() ) end -function discover_vertex!(vis::MinCutVisitor, v::Int) +function discover_vertex!(vis::MinCutVisitor, v::Integer) vis.parities[v] = false vis.colormap[v] = 1 push!(vis.vertices,v) return true end -function examine_neighbor!(vis::MinCutVisitor, u::Int, v::Int, ucolor::Int, vcolor::Int, ecolor::Int) +function examine_neighbor!(vis::MinCutVisitor, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) ew = vis.distmx[u, v] # if the target of e is already marked then decrease cutweight @@ -124,7 +125,7 @@ function examine_neighbor!(vis::MinCutVisitor, u::Int, v::Int, ucolor::Int, vcol return true end -function close_vertex!(vis::MinCutVisitor, v::Int) +function close_vertex!(vis::MinCutVisitor, v::Integer) vis.colormap[v] = 2 vis.visited += 1 @@ -143,25 +144,25 @@ end # ################################################# -type MASVisitor{T} <: AbstractMASVisitor +type MASVisitor{T, U<:Integer} <: AbstractMASVisitor io::IO - vertices::Vector{Int} - distmx::AbstractArray{T, 2} + vertices::Vector{U} + distmx::AbstractMatrix{T} log::Bool end -function discover_vertex!{T}(visitor::MASVisitor{T}, v::Int) +function discover_vertex!{T}(visitor::MASVisitor{T}, v::Integer) push!(visitor.vertices,v) visitor.log ? println(visitor.io, "discover vertex: $v") : nothing return true end -function examine_neighbor!(visitor::MASVisitor, u::Int, v::Int, ucolor::Int, vcolor::Int, ecolor::Int) +function examine_neighbor!(visitor::MASVisitor, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) visitor.log ? println(visitor.io, " -- examine neighbor from $u to $v") : nothing return true end -function close_vertex!(visitor::MASVisitor, v::Int) +function close_vertex!(visitor::MASVisitor, v::Integer) visitor.log ? println(visitor.io, "close vertex: $v") : nothing return true end @@ -179,16 +180,16 @@ 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}( - graph::AbstractGraph, - distmx::AbstractArray{T, 2} + g::AbstractGraph, + distmx::AbstractMatrix{T} ) - visitor = MinCutVisitor(graph, distmx) - colormap = zeros(Int, nv(graph)) - traverse_graph!(graph, T, MaximumAdjacency(), 1, visitor, colormap) + visitor = MinCutVisitor(g, distmx) + colormap = zeros(Int, nv(g)) + traverse_graph!(g, T, MaximumAdjacency(), 1, visitor, colormap) return(visitor.parities + 1, visitor.bestweight) end -mincut(graph::AbstractGraph) = mincut(graph,DefaultDistance()) +mincut(g::AbstractGraph) = mincut(g,DefaultDistance()) """Returns the vertices in `g` traversed by maximum adjacency search. An optional `distmx` matrix may be specified; if omitted, edge distances are assumed to @@ -197,18 +198,18 @@ be 1. If `log` (default `false`) is `true`, visitor events will be printed to displayed. """ function maximum_adjacency_visit{T}( - graph::AbstractGraph, + g::AbstractGraph, distmx::AbstractArray{T, 2}, log::Bool, io::IO ) visitor = MASVisitor(io, Vector{Int}(), distmx, log) - traverse_graph!(graph, T, MaximumAdjacency(), 1, visitor, zeros(Int, nv(graph))) + traverse_graph!(g, T, MaximumAdjacency(), 1, visitor, zeros(Int, nv(g))) return visitor.vertices end -maximum_adjacency_visit(graph::AbstractGraph) = maximum_adjacency_visit( - graph, +maximum_adjacency_visit(g::AbstractGraph) = maximum_adjacency_visit( + g, DefaultDistance(), false, STDOUT diff --git a/src/traversals/randomwalks.jl b/src/traversals/randomwalks.jl index 8f4631b89..81bb57c89 100644 --- a/src/traversals/randomwalks.jl +++ b/src/traversals/randomwalks.jl @@ -2,8 +2,9 @@ a maximum of `niter` steps. Returns a vector of vertices visited in order. """ function randomwalk(g::AbstractGraph, s::Integer, niter::Integer) + T = eltype(g) s in vertices(g) || throw(BoundsError()) - visited = Vector{Int}() + visited = Vector{T}() sizehint!(visited, niter) currs = s i = 1 @@ -20,9 +21,13 @@ 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. """ -function non_backtracking_randomwalk(g::Graph, s::Integer, niter::Integer) +function non_backtracking_randomwalk end +@traitfn function non_backtracking_randomwalk{G<:AbstractGraph; !IsDirected{G}}( + g::G, s::Integer, niter::Integer + ) + T = eltype(g) s in vertices(g) || throw(BoundsError()) - visited = Vector{Int}() + visited = Vector{T}() sizehint!(visited, niter) currs = s prev = -1 @@ -51,9 +56,12 @@ function non_backtracking_randomwalk(g::Graph, s::Integer, niter::Integer) return visited[1:i-1] end -function non_backtracking_randomwalk(g::DiGraph, s::Integer, niter::Integer) +@traitfn function non_backtracking_randomwalk{G<:AbstractGraph; IsDirected{G}}( + g::G, s::Integer, niter::Integer + ) + T = eltype(g) s in vertices(g) || throw(BoundsError()) - visited = Vector{Int}() + visited = Vector{T}() sizehint!(visited, niter) currs = s prev = -1 @@ -84,9 +92,10 @@ on graph `g` starting at vertex `s` and continuing for a maximum of `niter` step Returns a vector of vertices visited in order. """ function saw(g::AbstractGraph, s::Integer, niter::Integer) + T = eltype(g) s in vertices(g) || throw(BoundsError()) - visited = Vector{Int}() - svisited = Set{Int}() + visited = Vector{T}() + svisited = Set{T}() sizehint!(visited, niter) sizehint!(svisited, niter) currs = s diff --git a/test/cliques.jl b/test/community/cliques.jl similarity index 70% rename from test/cliques.jl rename to test/community/cliques.jl index 95c9f6d1e..aaf349991 100644 --- a/test/cliques.jl +++ b/test/community/cliques.jl @@ -15,12 +15,15 @@ setofsets(maximal_cliques(graph)) == setofsets(expected) end - g = Graph(3) - add_edge!(g, 1, 2) - @test test_cliques(g, Array[[1,2], [3]]) - add_edge!(g, 2, 3) - @test test_cliques(g, Array[[1,2], [2,3]]) - + gx = Graph(3) + add_edge!(gx, 1, 2) + for g in testgraphs(gx) + @test test_cliques(g, Array[[1,2], [3]]) + end + add_edge!(gx, 2, 3) + for g in testgraphs(gx) + @test test_cliques(g, Array[[1,2], [2,3]]) + end # Test for "pivotdonenbrs not defined" bug h = Graph(6) add_edge!(h, 1, 2) @@ -32,7 +35,9 @@ add_edge!(h, 3, 6) add_edge!(h, 5, 6) - @test maximal_cliques(h) != [] + for g in testgraphs(h) + @test maximal_cliques(g) != [] + end # test for extra cliques bug @@ -44,5 +49,7 @@ add_edge!(h,4,5) add_edge!(h,4,7) add_edge!(h,5,7) - @test test_cliques(h, Array[[7,4,5], [2,6], [3,5], [3,6], [3,1]]) + for g in testgraphs(h) + @test test_cliques(h, Array[[7,4,5], [2,6], [3,5], [3,6], [3,1]]) + end end diff --git a/test/connectivity.jl b/test/connectivity.jl index ae128de39..5ad84d779 100644 --- a/test/connectivity.jl +++ b/test/connectivity.jl @@ -1,45 +1,55 @@ @testset "Connectivity" begin g6 = smallgraph(:house) - g = PathGraph(4) - add_vertices!(g,10) - add_edge!(g,5,6) - add_edge!(g,6,7) - add_edge!(g,8,9) - add_edge!(g,10,9) - - - @test !is_connected(g) - @test is_connected(g6) - - cc = connected_components(g) - label = zeros(Int, nv(g)) - LightGraphs.connected_components!(label, g) - @test label[1:10] == [1,1,1,1,5,5,5,8,8,8] - import LightGraphs: components, components_dict - cclab = components_dict(label) - @test cclab[1] == [1,2,3,4] - @test cclab[5] == [5,6,7] - @test cclab[8] == [8,9,10] - @test length(cc) >= 3 && sort(cc[3]) == [8,9,10] - - g10 =DiGraph(4) + gx = PathGraph(4) + add_vertices!(gx,10) + add_edge!(gx,5,6) + add_edge!(gx,6,7) + add_edge!(gx,8,9) + add_edge!(gx,10,9) + + + for g in testgraphs(gx) + @test !is_connected(g) + cc = connected_components(g) + label = zeros(eltype(g), nv(g)) + LightGraphs.connected_components!(label, g) + @test label[1:10] == [1,1,1,1,5,5,5,8,8,8] + import LightGraphs: components, components_dict + cclab = components_dict(label) + @test cclab[1] == [1,2,3,4] + @test cclab[5] == [5,6,7] + @test cclab[8] == [8,9,10] + @test length(cc) >= 3 && sort(cc[3]) == [8,9,10] + end + for g in testgraphs(g6) + @test is_connected(g) + end + + + g10 = DiGraph(4) add_edge!(g10,1,3) add_edge!(g10,2,4) - @test is_bipartite(g10) == true + for g in testdigraphs(g10) + @test is_bipartite(g) + end add_edge!(g10,1,4) - @test is_bipartite(g10) == true + for g in testdigraphs(g10) + @test is_bipartite(g) + end g10 = DiGraph(20) - for m=1:50 + for g in testdigraphs(g10) + for m=1:50 i = rand(1:10) j = rand(11:20) if rand() < 0.5 i, j = j, i end - if !has_edge(g10, i, j) - add_edge!(g10, i, j) - @test is_bipartite(g10) == true + if !has_edge(g, i, j) + add_edge!(g, i, j) + @test is_bipartite(g) end + end end # graph from https://en.wikipedia.org/wiki/Strongly_connected_component @@ -49,14 +59,16 @@ add_edge!(h,4,3); add_edge!(h,4,8); add_edge!(h,5,1); add_edge!(h,5,6); add_edge!(h,6,7); add_edge!(h,7,6); add_edge!(h,8,4); add_edge!(h,8,7) + for g in testdigraphs(h) + @test is_connected(g) + scc = strongly_connected_components(g) + wcc = weakly_connected_components(g) - @test is_connected(h) + @test length(scc) == 3 && sort(scc[3]) == [1,2,5] + @test length(wcc) == 1 && length(wcc[1]) == nv(g) - scc = strongly_connected_components(h) - wcc = weakly_connected_components(h) + end - @test length(scc) == 3 && sort(scc[3]) == [1,2,5] - @test length(wcc) == 1 && length(wcc[1]) == nv(h) function scc_ok(graph) """Check that all SCC really are strongly connected""" @@ -67,23 +79,29 @@ # the two graphs below are isomorphic (exchange 2 <--> 4) h = DiGraph(4); add_edge!(h, 1, 4); add_edge!(h, 4, 2); add_edge!(h, 2, 3); add_edge!(h, 1, 3); + for g in testdigraphs(h) + @test scc_ok(g) + end + h2 = DiGraph(4); add_edge!(h2, 1, 2); add_edge!(h2, 2, 4); add_edge!(h2, 4, 3); add_edge!(h2, 1, 3); - @test scc_ok(h) - @test scc_ok(h2) + for g in testdigraphs(h2) + @test scc_ok(g) + end h = DiGraph(6) add_edge!(h,1,3); add_edge!(h,3,4); add_edge!(h,4,2); add_edge!(h,2,1) add_edge!(h,3,5); add_edge!(h,5,6); add_edge!(h,6,4) - - scc = strongly_connected_components(h) - - @test length(scc) == 1 && sort(scc[1]) == [1:6;] - + for g in testdigraphs(h) + scc = strongly_connected_components(g) + @test length(scc) == 1 && sort(scc[1]) == [1:6;] + end # tests from Graphs.jl h = DiGraph(4) add_edge!(h,1,2); add_edge!(h,2,3); add_edge!(h,3,1); add_edge!(h,4,1) - scc = strongly_connected_components(h) - @test length(scc) == 2 && sort(scc[1]) == [1:3;] && sort(scc[2]) == [4] + for g in testdigraphs(h) + scc = strongly_connected_components(g) + @test length(scc) == 2 && sort(scc[1]) == [1:3;] && sort(scc[2]) == [4] + end h = DiGraph(12) add_edge!(h,1,2); add_edge!(h,2,3); add_edge!(h,2,4); add_edge!(h,2,5); @@ -92,13 +110,14 @@ add_edge!(h,7,8); add_edge!(h,7,10); add_edge!(h,8,7); add_edge!(h,9,7); add_edge!(h,10,9); add_edge!(h,10,11); add_edge!(h,11,12); add_edge!(h,12,10) - scc = strongly_connected_components(h) - @test length(scc) == 4 - @test sort(scc[1]) == [7,8,9,10,11,12] - @test sort(scc[2]) == [3, 6] - @test sort(scc[3]) == [2, 4, 5] - @test scc[4] == [1] - + for g in testdigraphs(h) + scc = strongly_connected_components(g) + @test length(scc) == 4 + @test sort(scc[1]) == [7,8,9,10,11,12] + @test sort(scc[2]) == [3, 6] + @test sort(scc[3]) == [2, 4, 5] + @test scc[4] == [1] + end # Test examples with self-loops from # Graph-Theoretic Analysis of Finite Markov Chains by J.P. Jarvis & D. R. Shier @@ -148,24 +167,26 @@ @test attracting_components(fig3) == Vector[[3,4],[8]] g10 = StarGraph(10) - @test neighborhood(g10, 1 , 0) == [1] - @test length(neighborhood(g10, 1, 1)) == 10 - @test length(neighborhood(g10, 2, 1)) == 2 - @test length(neighborhood(g10, 1, 2)) == 10 - @test length(neighborhood(g10, 2, 2)) == 10 - + for g in testgraphs(g10) + @test neighborhood(g, 1 , 0) == [1] + @test length(neighborhood(g, 1, 1)) == 10 + @test length(neighborhood(g, 2, 1)) == 2 + @test length(neighborhood(g, 1, 2)) == 10 + @test length(neighborhood(g, 2, 2)) == 10 + end g10 = StarDiGraph(10) - @test neighborhood(g10, 1 , 0, dir=:out) == [1] - @test length(neighborhood(g10, 1, 1, dir=:out)) == 10 - @test length(neighborhood(g10, 2, 1, dir=:out)) == 1 - @test length(neighborhood(g10, 1, 2, dir=:out)) == 10 - @test length(neighborhood(g10, 2, 2, dir=:out)) == 1 - @test neighborhood(g10, 1 , 0, dir=:in) == [1] - @test length(neighborhood(g10, 1, 1, dir=:in)) == 1 - @test length(neighborhood(g10, 2, 1, dir=:in)) == 2 - @test length(neighborhood(g10, 1, 2, dir=:in)) == 1 - @test length(neighborhood(g10, 2, 2, dir=:in)) == 2 - + for g in testdigraphs(g10) + @test neighborhood(g10, 1 , 0, dir=:out) == [1] + @test length(neighborhood(g, 1, 1, dir=:out)) == 10 + @test length(neighborhood(g, 2, 1, dir=:out)) == 1 + @test length(neighborhood(g, 1, 2, dir=:out)) == 10 + @test length(neighborhood(g, 2, 2, dir=:out)) == 1 + @test neighborhood(g, 1 , 0, dir=:in) == [1] + @test length(neighborhood(g, 1, 1, dir=:in)) == 1 + @test length(neighborhood(g, 2, 1, dir=:in)) == 2 + @test length(neighborhood(g, 1, 2, dir=:in)) == 1 + @test length(neighborhood(g, 2, 2, dir=:in)) == 2 + end @test !isgraphical([1,1,1]) @test isgraphical([2,2,2]) @test isgraphical(fill(3,10)) diff --git a/test/runtests.jl b/test/runtests.jl index 25efef57d..e81cb8961 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -63,7 +63,6 @@ tests = [ "edit_distance", "linalg/spectral", "linalg/graphmatrices", - "cliques", "connectivity", "transitivity", "persistence/persistence", @@ -80,6 +79,7 @@ tests = [ "traversals/maxadjvisit", "traversals/graphvisit", "traversals/randomwalks", + "community/cliques", "community/core-periphery", "community/label_propagation", "community/modularity", diff --git a/test/spanningtrees/kruskal.jl b/test/spanningtrees/kruskal.jl index e35be3b1d..5613820f6 100644 --- a/test/spanningtrees/kruskal.jl +++ b/test/spanningtrees/kruskal.jl @@ -1,5 +1,5 @@ @testset "Kruskal" begin - g = CompleteGraph(4) + g4 = CompleteGraph(4) distmx = [ 0 1 5 6 @@ -8,14 +8,14 @@ 6 10 3 0 ] - # Testing Kruskal's algorithm - mst = kruskal_mst(g, distmx) vec_mst = Vector{Edge}([Edge(1, 2), Edge(3, 4), Edge(2, 3)]) - - @test mst == vec_mst - - #second test - distmx_sec = [ + for g in testgraphs(g4) + # Testing Kruskal's algorithm + mst = kruskal_mst(g, distmx) + @test mst == vec_mst + end + #second test + distmx_sec = [ 0 0 0.26 0 0.38 0 0.58 0.16 0 0 0.36 0.29 0 0.32 0 0.19 0.26 0.36 0 0.17 0 0 0.4 0.34 @@ -26,9 +26,10 @@ 0.16 0.19 0.34 0 0.37 0.28 0 0 ] - g = Graph(distmx_sec) - mst2 = kruskal_mst(g, distmx_sec) + gx = Graph(distmx_sec) vec2 = Vector{Edge}([Edge(1, 8),Edge(3, 4),Edge(2, 8),Edge(1, 3),Edge(6, 8),Edge(5, 6),Edge(3, 7)]) - - @test mst2 == vec2 + for g in testgraphs(gx) + mst2 = kruskal_mst(g, distmx_sec) + @test mst2 == vec2 + end end diff --git a/test/spanningtrees/prim.jl b/test/spanningtrees/prim.jl index 00c506c23..2d38a5255 100644 --- a/test/spanningtrees/prim.jl +++ b/test/spanningtrees/prim.jl @@ -1,5 +1,5 @@ @testset "Prim" begin - g = CompleteGraph(4) + g4 = CompleteGraph(4) distmx = [ 0 1 5 6 @@ -8,11 +8,12 @@ 6 10 3 0 ] - # Testing Prim's algorithm - mst = prim_mst(g, distmx) vec_mst = Vector{Edge}([Edge(1, 2), Edge(2, 3), Edge(3, 4)]) - - @test mst == vec_mst + for g in testgraphs(g4) + # Testing Prim's algorithm + mst = prim_mst(g, distmx) + @test mst == vec_mst + end #second test distmx_sec = [ @@ -26,9 +27,10 @@ 0.16 0.19 0.34 0 0.37 0.28 0 0 ] - g = Graph(distmx_sec) - mst2 = prim_mst(g, distmx_sec) vec2 = Vector{Edge}([Edge(1, 8),Edge(2, 8),Edge(1, 3),Edge(3, 4),Edge(6, 8),Edge(5, 6),Edge(3, 7)]) - - @test mst2 == vec2 + gx = Graph(distmx_sec) + for g in testgraphs(gx) + mst2 = prim_mst(g, distmx_sec) + @test mst2 == vec2 + end end diff --git a/test/traversals/bfs.jl b/test/traversals/bfs.jl index 127e44ff8..db1947c7e 100644 --- a/test/traversals/bfs.jl +++ b/test/traversals/bfs.jl @@ -1,34 +1,41 @@ +import LightGraphs: TreeBFSVisitorVector, bfs_tree!, tree @testset "BFS" begin g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) g6 = smallgraph(:house) - z = bfs_tree(g5, 1) - visitor = LightGraphs.TreeBFSVisitorVector(zeros(Int,nv(g5))) - LightGraphs.bfs_tree!(visitor, g5, 1) - t = visitor.tree - @test t == [1,1,1,3] - @test nv(z) == 4 && ne(z) == 3 && !has_edge(z, 2, 3) + for g in testdigraphs(g5) + z = bfs_tree(g, 1) + visitor = LightGraphs.TreeBFSVisitorVector(zeros(eltype(g),nv(g))) + LightGraphs.bfs_tree!(visitor, g, 1) + t = visitor.tree + @test t == [1,1,1,3] + @test nv(z) == 4 && ne(z) == 3 && !has_edge(z, 2, 3) + end + for g in testgraphs(g6) + @test gdistances(g, 2) == [1, 0, 2, 1, 2] + @test gdistances(g, [1,2]) == [0, 0, 1, 1, 2] + @test gdistances(g, []) == [-1, -1, -1, -1, -1] + @test !is_bipartite(g) + @test !is_bipartite(g, 2) + end - @test gdistances(g6, 2) == [1, 0, 2, 1, 2] - @test gdistances(g6, [1,2]) == [0, 0, 1, 1, 2] - @test gdistances(g6, []) == [-1, -1, -1, -1, -1] - @test !is_bipartite(g6) - @test !is_bipartite(g6, 2) + gx = Graph(5) + add_edge!(gx,1,2); add_edge!(gx,1,4) + add_edge!(gx,2,3); add_edge!(gx,2,5) + add_edge!(gx,3,4) - g = Graph(5) - add_edge!(g,1,2); add_edge!(g,1,4) - add_edge!(g,2,3); add_edge!(g,2,5) - add_edge!(g,3,4) - @test is_bipartite(g) - @test is_bipartite(g, 2) + for g in testgraphs(gx) + @test is_bipartite(g) + @test is_bipartite(g, 2) + end - import LightGraphs: TreeBFSVisitorVector, bfs_tree!, tree + # import LightGraphs: TreeBFSVisitorVector, bfs_tree!, tree - function istree(parents::Vector{Int}, maxdepth) + function istree{T<:Integer}(parents::Vector{T}, maxdepth, n::T) flag = true - for i in 1:n + for i in one(T):n s = i depth = 0 while parents[s] > 0 && parents[s] != s @@ -42,35 +49,44 @@ return flag end - n = nv(g6) - visitor = TreeBFSVisitorVector(n) - @test length(visitor.tree) == n - parents = visitor.tree - bfs_tree!(visitor, g6, 1) + for g in testgraphs(g6) + n = nv(g) + visitor = TreeBFSVisitorVector(n) + @test length(visitor.tree) == n + parents = visitor.tree + bfs_tree!(visitor, g, 1) - @test istree(parents, n) == true - t = tree(parents) - @test typeof(t) <: DiGraph - @test ne(t) < nv(t) + @test istree(parents, n, n) + t = tree(parents) + @test is_directed(t) + @test typeof(t) <: AbstractGraph + @test ne(t) < nv(t) # test Dict{Int,Int}() colormap - n = nv(g6) - visitor = TreeBFSVisitorVector(n) - @test length(visitor.tree) == n - parents = visitor.tree - bfs_tree!(visitor, g6, 1, vertexcolormap = Dict{Int,Int}()) - @test istree(parents, n) == true - t = tree(parents) - @test typeof(t) <: DiGraph - @test ne(t) < nv(t) + visitor = TreeBFSVisitorVector(n) + @test length(visitor.tree) == n + parents = visitor.tree + bfs_tree!(visitor, g, 1, vertexcolormap = Dict{Int,Int}()) + + @test istree(parents, n, n) + t = tree(parents) + @test is_directed(t) + @test typeof(t) <: AbstractGraph + @test ne(t) < nv(t) + end g10 = CompleteGraph(10) - @test bipartite_map(g10) == Vector{Int}() + for g in testgraphs(g10) + @test bipartite_map(g) == Vector{eltype(g)}() + end g10 = CompleteBipartiteGraph(10,10) - @test bipartite_map(g10) == Vector{Int}([ones(10); 2*ones(10)]) + for g in testgraphs(g10) + T = eltype(g) + @test bipartite_map(g10) == Vector{T}([ones(T, 10); 2*ones(T, 10)]) - h10 = blkdiag(g10,g10) - @test bipartite_map(h10) == Vector{Int}([ones(10); 2*ones(10); ones(10); 2*ones(10)]) + h = blkdiag(g,g) + @test bipartite_map(h) == Vector{T}([ones(T, 10); 2*ones(T, 10); ones(T, 10); 2*ones(T, 10)]) + end end diff --git a/test/traversals/dfs.jl b/test/traversals/dfs.jl index 2cc3ecfb7..d2dcb2ff8 100644 --- a/test/traversals/dfs.jl +++ b/test/traversals/dfs.jl @@ -1,17 +1,17 @@ @testset "DFS" begin g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) + for g in testdigraphs(g5) + z = dfs_tree(g,1) + @test ne(z) == 3 && nv(z) == 4 + @test !has_edge(z, 1, 3) - z = dfs_tree(g5,1) + @test topological_sort_by_dfs(g) == [1, 2, 3, 4] + @test !is_cyclic(g) + end - @test ne(z) == 3 && nv(z) == 4 - @test !has_edge(z, 1, 3) - - @test topological_sort_by_dfs(g5) == [1, 2, 3, 4] - @test !is_cyclic(g5) - g = DiGraph(3) - - add_edge!(g,1,2); add_edge!(g,2,3); add_edge!(g,3,1) - - @test is_cyclic(g) + gx = CycleDiGraph(3) + for g in testdigraphs(gx) + @test is_cyclic(g) + end end diff --git a/test/traversals/graphvisit.jl b/test/traversals/graphvisit.jl index abb76d13f..eb218212d 100644 --- a/test/traversals/graphvisit.jl +++ b/test/traversals/graphvisit.jl @@ -1,29 +1,29 @@ @testset "Graph visit" begin g6 = smallgraph(:house) - # stub tests for coverage; disregards output. + for g in testgraphs(g6) + # stub tests for coverage; disregards output. - f = IOBuffer() + f = IOBuffer() - @test traverse_graph_withlog(g6, BreadthFirst(), [1;], f) == nothing + @test traverse_graph_withlog(g, BreadthFirst(), [1;], f) == nothing + @test visited_vertices(g, BreadthFirst(), [1;]) == [1, 2, 3, 4, 5] - @test visited_vertices(g6, BreadthFirst(), [1;]) == [1, 2, 3, 4, 5] + function trivialgraphvisit( + g::AbstractGraph, + alg::LightGraphs.AbstractGraphVisitAlgorithm, + sources + ) + visitor = TrivialGraphVisitor() + traverse_graph!(g, alg, sources, visitor) + end - function trivialgraphvisit( - g::AbstractGraph, - alg::LightGraphs.AbstractGraphVisitAlgorithm, - sources - ) - visitor = TrivialGraphVisitor() - traverse_graph!(g, alg, sources, visitor) - end - - @test trivialgraphvisit(g6, BreadthFirst(), 1) == nothing - - # this just exercises some graph visitors - @test traverse_graph!(g6, BreadthFirst(), 1, TrivialGraphVisitor()) == nothing - @test traverse_graph!(g6, BreadthFirst(), 1, LogGraphVisitor(IOBuffer())) == nothing + @test trivialgraphvisit(g, BreadthFirst(), 1) == nothing + # this just exercises some graph visitors + @test traverse_graph!(g, BreadthFirst(), 1, TrivialGraphVisitor()) == nothing + @test traverse_graph!(g, BreadthFirst(), 1, LogGraphVisitor(IOBuffer())) == nothing + end # dummy edge map test d = LightGraphs.DummyEdgeMap() e = Edge(1,2) diff --git a/test/traversals/maxadjvisit.jl b/test/traversals/maxadjvisit.jl index 3084d375f..2755419bf 100644 --- a/test/traversals/maxadjvisit.jl +++ b/test/traversals/maxadjvisit.jl @@ -1,6 +1,6 @@ @testset "Max adj visit" begin - g = Graph(8) + gx = Graph(8) # Test of Min-Cut and maximum adjacency visit # Original example by Stoer @@ -21,30 +21,31 @@ m = length(wedges) - eweights = spzeros(nv(g),nv(g)) + eweights = spzeros(nv(gx),nv(gx)) for (s, d, w) in wedges - add_edge!(g, s, d) + add_edge!(gx, s, d) eweights[s, d] = w eweights[d, s] = w end + for g in testgraphs(gx) + @test nv(g) == 8 + @test ne(g) == m - @test nv(g) == 8 - @test ne(g) == m + parity, bestcut = mincut(g, eweights) - parity, bestcut = mincut(g, eweights) + @test length(parity) == 8 + @test parity == [2, 2, 1, 1, 2, 2, 1, 1] + @test bestcut == 4.0 - @test length(parity) == 8 - @test parity == [2, 2, 1, 1, 2, 2, 1, 1] - @test bestcut == 4.0 + parity, bestcut = mincut(g) - parity, bestcut = mincut(g) + @test length(parity) == 8 + @test parity == [2, 1, 1, 1, 1, 1, 1, 1] + @test bestcut == 2.0 - @test length(parity) == 8 - @test parity == [2, 1, 1, 1, 1, 1, 1, 1] - @test bestcut == 2.0 + v = maximum_adjacency_visit(g) - v = maximum_adjacency_visit(g) - - @test v == Vector{Int64}([1, 2, 5, 6, 3, 7, 4, 8]) + @test v == Vector{Int64}([1, 2, 5, 6, 3, 7, 4, 8]) + end end diff --git a/test/traversals/randomwalks.jl b/test/traversals/randomwalks.jl index cebcb7868..8ef3c1057 100644 --- a/test/traversals/randomwalks.jl +++ b/test/traversals/randomwalks.jl @@ -27,50 +27,58 @@ end return false end - g = PathDiGraph(10) - @test randomwalk(g, 1, 5) == [1:5;] - @test randomwalk(g, 2, 100) == [2:10;] - @test_throws BoundsError randomwalk(g, 20, 20) - - g = PathGraph(10) - @test saw(g, 1, 20) == [1:10;] - @test_throws BoundsError saw(g, 20, 20) + gx = PathDiGraph(10) + for g in testdigraphs(gx) + @test randomwalk(g, 1, 5) == [1:5;] + @test randomwalk(g, 2, 100) == [2:10;] + @test_throws BoundsError randomwalk(g, 20, 20) + @test non_backtracking_randomwalk(g, 10, 20) == [10] + @test non_backtracking_randomwalk(g, 1, 20) == [1:10;] + end - g = PathGraph(10) - @test non_backtracking_randomwalk(g, 1, 20) == [1:10;] - @test_throws BoundsError non_backtracking_randomwalk(g, 20, 20) + gx = PathGraph(10) + for g in testgraphs(gx) + @test saw(g, 1, 20) == [1:10;] + @test_throws BoundsError saw(g, 20, 20) + @test non_backtracking_randomwalk(g, 1, 20) == [1:10;] + @test_throws BoundsError non_backtracking_randomwalk(g, 20, 20) + end g = DiGraph(PathGraph(10)) - @test non_backtracking_randomwalk(g, 1, 20) == [1:10;] - @test_throws BoundsError non_backtracking_randomwalk(g, 20, 20) - - g = PathDiGraph(10) - @test non_backtracking_randomwalk(g, 10, 20) == [10] - - g = PathDiGraph(10) - @test non_backtracking_randomwalk(g, 1, 20) == [1:10;] + for g in testgraphs(gx) + @test non_backtracking_randomwalk(g, 1, 20) == [1:10;] + @test_throws BoundsError non_backtracking_randomwalk(g, 20, 20) + end - g = CycleGraph(10) - visited = non_backtracking_randomwalk(g, 1, 20) - @test visited == [1:10; 1:10;] || visited == [1; 10:-1:1; 10:-1:2;] + gx = CycleGraph(10) + for g in testgraphs(gx) + visited = non_backtracking_randomwalk(g, 1, 20) + @test visited == [1:10; 1:10;] || visited == [1; 10:-1:1; 10:-1:2;] + end - g = CycleDiGraph(10) - @test non_backtracking_randomwalk(g, 1, 20) == [1:10; 1:10;] + gx = CycleDiGraph(10) + for g in testdigraphs(gx) + @test non_backtracking_randomwalk(g, 1, 20) == [1:10; 1:10;] + end n = 10 - g = CycleGraph(n) + gx = CycleGraph(n) for k = 3:n-1 - add_edge!(g, 1, k) + add_edge!(gx, 1, k) end - for len = 1:3*n + for g in testgraphs(gx) + for len = 1:3*n @test test_nbw(g,1,len) @test test_nbw(g,2,len) + end end #test to make sure it works with self loops. - add_edge!(g, 1, 1) - for len = 1:3*n - @test test_nbw(g,1,len) == true - @test test_nbw(g,2,len) == true + add_edge!(gx, 1, 1) + for g in testgraphs(gx) + for len = 1:3*n + @test test_nbw(g,1,len) == true + @test test_nbw(g,2,len) == true + end end end From 38bf2338dd941fd481190a0971e4aeb87faf019c Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Fri, 17 Mar 2017 00:14:09 -0700 Subject: [PATCH 20/56] @jpfairbanks first round of review --- src/centrality/degree.jl | 2 +- src/centrality/pagerank.jl | 4 ++-- src/community/clustering.jl | 21 ++++++++++----------- src/community/label_propagation.jl | 2 +- src/core.jl | 2 +- src/edit_distance.jl | 1 - src/flow/maximum_flow.jl | 5 ++--- 7 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/centrality/degree.jl b/src/centrality/degree.jl index 16b751420..24b184e91 100644 --- a/src/centrality/degree.jl +++ b/src/centrality/degree.jl @@ -3,7 +3,7 @@ function _degree_centrality(g::AbstractGraph, gtype::Integer; normalize=true) c = zeros(n_v) for v in 1:n_v if gtype == 0 # count both in and out degree if appropriate - deg = outdegree(g, v) + (is_directed(g)? indegree(g, v) : 0.0) + deg = is_directed(g)? outdegree(g, v) + indegree(g, v) : outdegree(g, v) elseif gtype == 1 # count only in degree deg = indegree(g, v) else # count only out degree diff --git a/src/centrality/pagerank.jl b/src/centrality/pagerank.jl index 6412d5373..84723de60 100644 --- a/src/centrality/pagerank.jl +++ b/src/centrality/pagerank.jl @@ -17,8 +17,8 @@ function pagerank end M = A' # need a separate line due to bug #17456 in julia M = (Diagonal(S) * M)' N = Int(nv(g)) - x = repmat([1.0/N], N) - p = repmat([1.0/N], N) + x = fill(1.0/N, N) + p = fill(1.0/N, N) dangling_weights = p is_dangling = find(S .== 0) diff --git a/src/community/clustering.jl b/src/community/clustering.jl index aa64f8abb..0cdcba0f3 100644 --- a/src/community/clustering.jl +++ b/src/community/clustering.jl @@ -21,13 +21,12 @@ function local_clustering(g::AbstractGraph, v::Integer) k <= 1 && return (0, 0) neighs = neighbors(g, v) c = 0 - for i=1:length(neighs) - for j=1:length(neighs) - i == j && continue - if has_edge(g, neighs[i], neighs[j]) - c += 1 - end + for i in neighs, j in neighs + i == j && continue + if has_edge(g, i, j) + c += 1 end + end end return is_directed(g) ? (c , k*(k-1)) : (div(c,2) , div(k*(k-1),2)) end @@ -82,11 +81,11 @@ function global_clustering_coefficient(g::AbstractGraph) ntriangles = 0 for v in vertices(g) neighs = neighbors(g, v) - for i=1:length(neighs), j=1:length(neighs) - i == j && continue - if has_edge(g, neighs[i], neighs[j]) - c += 1 - end + for i in neighs, j in neighs + i == j && continue + if has_edge(g, i, j) + c += 1 + end end k = degree(g, v) ntriangles += k*(k-1) diff --git a/src/community/label_propagation.jl b/src/community/label_propagation.jl index 3f85d7913..1b5fd1966 100644 --- a/src/community/label_propagation.jl +++ b/src/community/label_propagation.jl @@ -9,7 +9,7 @@ function label_propagation(g::AbstractGraph; maxiter=1000) n = nv(g) label = collect(one(T):n) active_nodes = IntSet(vertices(g)) - c = NeighComm(collect(one(T):n), fill(-1,n), T(1)) + c = NeighComm(collect(one(T):n), fill(-1,n), one(T)) convergence_hist = Vector{Int}() random_order = Array(T, n) i = 0 diff --git a/src/core.jl b/src/core.jl index bdf741851..49b692e81 100644 --- a/src/core.jl +++ b/src/core.jl @@ -73,7 +73,7 @@ NOTE: returns a reference, not a copy. Do not modify result. neighbors(g::AbstractGraph, v::Integer) = out_neighbors(g, v) """ -Return a list of all inbound and outbount neighbors of `v` in `g`. +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`. """ diff --git a/src/edit_distance.jl b/src/edit_distance.jl index b7af02101..102abdffd 100644 --- a/src/edit_distance.jl +++ b/src/edit_distance.jl @@ -37,7 +37,6 @@ Distance: Approximation Algorithms and Applications. (Chapter 2) Author: Júlio Hoffimann Mendes (juliohm@stanford.edu) """ - function edit_distance(G₁::AbstractGraph, G₂::AbstractGraph; insert_cost::Function=v->1.0, delete_cost::Function=u->1.0, diff --git a/src/flow/maximum_flow.jl b/src/flow/maximum_flow.jl index bcc6d2f00..27c9c8247 100644 --- a/src/flow/maximum_flow.jl +++ b/src/flow/maximum_flow.jl @@ -1,4 +1,3 @@ -import LightGraphs.SimpleGraphs.SimpleDiGraph """ Abstract type that allows users to pass in their preferred Algorithm """ @@ -32,11 +31,11 @@ end Type that returns 1 if a forward edge exists, and 0 otherwise """ type DefaultCapacity{T<:Integer} <: AbstractMatrix{T} - flow_graph::SimpleDiGraph + flow_graph::DiGraph nv::T end -@traitfn DefaultCapacity{G<:AbstractGraph; IsDirected{G}}(flow_graph::G) = DefaultCapacity(SimpleDiGraph(flow_graph), nv(flow_graph)) +@traitfn DefaultCapacity{G<:AbstractGraph; IsDirected{G}}(flow_graph::G) = DefaultCapacity(DiGraph(flow_graph), nv(flow_graph)) getindex{T<:Integer}(d::DefaultCapacity{T}, s::Integer, t::Integer) = if has_edge(d.flow_graph, s , t) one(T) else zero(T) end # isassigned{T<:Integer}(d::DefaultCapacity{T}, u::T, v::T) = (u in 1:d.nv) && (v in 1:d.nv) From 1e22b2c75bbbc097564d7dea2b17d724af1016fe Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Fri, 17 Mar 2017 00:28:57 -0700 Subject: [PATCH 21/56] another fix --- src/community/clustering.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/community/clustering.jl b/src/community/clustering.jl index 0cdcba0f3..b7ca5aa2d 100644 --- a/src/community/clustering.jl +++ b/src/community/clustering.jl @@ -26,7 +26,6 @@ function local_clustering(g::AbstractGraph, v::Integer) if has_edge(g, i, j) c += 1 end - end end return is_directed(g) ? (c , k*(k-1)) : (div(c,2) , div(k*(k-1),2)) end From 893987dcff95176945b765c19ca64bf2741abe8f Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Fri, 17 Mar 2017 14:53:50 -0700 Subject: [PATCH 22/56] all tests --- src/biconnectivity/articulation.jl | 2 +- src/centrality/betweenness.jl | 34 +- src/centrality/closeness.jl | 3 +- src/centrality/degree.jl | 28 +- src/centrality/pagerank.jl | 8 +- src/community/cliques.jl | 3 +- src/community/clustering.jl | 26 +- src/community/core-periphery.jl | 2 +- src/community/label_propagation.jl | 14 +- src/community/modularity.jl | 2 +- src/connectivity.jl | 2 - src/core.jl | 12 +- src/flow/boykov_kolmogorov.jl | 184 +++++----- src/flow/dinic.jl | 14 +- src/flow/edmonds_karp.jl | 29 +- src/flow/ext_multiroute_flow.jl | 313 ++++++++-------- src/flow/kishimoto.jl | 92 ++--- src/flow/maximum_flow.jl | 12 +- src/flow/multiroute_flow.jl | 170 ++++----- src/generators/euclideangraphs.jl | 8 +- src/generators/randgraphs.jl | 71 ++-- src/generators/smallgraphs.jl | 365 +++++++++---------- src/generators/staticgraphs.jl | 11 +- src/graphtypes/simplegraph/SimpleGraphs.jl | 81 ++-- src/graphtypes/simplegraph/simpledigraph.jl | 39 +- src/graphtypes/simplegraph/simpleedge.jl | 4 +- src/graphtypes/simplegraph/simplegraph.jl | 19 +- src/linalg/spectral.jl | 65 ++-- src/operators.jl | 33 +- src/shortestpaths/astar.jl | 9 +- src/shortestpaths/bellman-ford.jl | 11 +- src/shortestpaths/dijkstra.jl | 6 +- src/shortestpaths/floyd-warshall.jl | 3 +- src/spanningtrees/kruskal.jl | 3 +- src/spanningtrees/prim.jl | 3 +- src/traversals/dfs.jl | 8 +- src/traversals/randomwalks.jl | 62 ++-- test/core.jl | 79 +++- test/distance.jl | 33 +- test/edit_distance.jl | 48 +-- test/graphdigraph.jl | 77 ---- test/graphtypes/simplegraphs/simplegraphs.jl | 350 +++++++----------- test/interface.jl | 3 +- test/operators.jl | 351 ++++++++++-------- test/runtests.jl | 18 +- test/transitivity.jl | 44 ++- 46 files changed, 1352 insertions(+), 1402 deletions(-) delete mode 100644 test/graphdigraph.jl diff --git a/src/biconnectivity/articulation.jl b/src/biconnectivity/articulation.jl index 2962a2e89..e445e7dd9 100644 --- a/src/biconnectivity/articulation.jl +++ b/src/biconnectivity/articulation.jl @@ -51,7 +51,7 @@ function visit!(state::Articulations, g::AbstractGraph, u::Integer, v::Integer) elseif w != u state.low[v] = min(state.low[v], state.depth[w]) end - end + end if u == v && children > 1 state.articulation_points[v] = true end diff --git a/src/centrality/betweenness.jl b/src/centrality/betweenness.jl index 30f51ca6f..caf1e882c 100644 --- a/src/centrality/betweenness.jl +++ b/src/centrality/betweenness.jl @@ -3,7 +3,7 @@ doc""" - betweenness_centrality(g, k=0; normalize=true, endpoints=false) +betweenness_centrality(g, k=0; normalize=true, endpoints=false) Calculates the [betweenness centrality](https://en.wikipedia.org/wiki/Centrality#Betweenness_centrality) of @@ -14,30 +14,30 @@ default. Betweenness centrality is defined as: $bc(v) = \frac{1}{\mathcal{N}} \sum_{s \neq t \neq v} - \frac{\sigma_{st}(v)}{\sigma_{st}}$. +\frac{\sigma_{st}(v)}{\sigma_{st}}$. - **Parameters** +**Parameters** g: AbstractGraph - A Graph, directed or undirected. +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. +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. +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. +If true, endpoints are included in the shortest path count. **Returns** betweenness: Array{Float64} - Betweenness centrality value per node id. +Betweenness centrality value per node id. **References** @@ -67,16 +67,16 @@ function betweenness_centrality( end _rescale!(betweenness, - n_v, - normalize, - isdir, - k) + n_v, + normalize, + isdir, + k) return betweenness end betweenness_centrality(g::AbstractGraph, k::Integer; normalize=true, endpoints=false) = - betweenness_centrality(g, sample(vertices(g), k); normalize=normalize, endpoints=endpoints) +betweenness_centrality(g, sample(vertices(g), k); normalize=normalize, endpoints=endpoints) diff --git a/src/centrality/closeness.jl b/src/centrality/closeness.jl index 46125501b..238925bcf 100644 --- a/src/centrality/closeness.jl +++ b/src/centrality/closeness.jl @@ -8,7 +8,7 @@ function closeness_centrality( n_v = nv(g) closeness = zeros(n_v) - for u = 1:n_v + for u in vertices(g) if degree(g, u) == 0 # no need to do Dijkstra here closeness[u] = 0.0 else @@ -18,7 +18,6 @@ function closeness_centrality( l = length(δ) - 1 if σ > 0 closeness[u] = l / σ - if normalize n = l / (n_v-1) closeness[u] *= n diff --git a/src/centrality/degree.jl b/src/centrality/degree.jl index 24b184e91..812106e93 100644 --- a/src/centrality/degree.jl +++ b/src/centrality/degree.jl @@ -1,18 +1,18 @@ function _degree_centrality(g::AbstractGraph, gtype::Integer; normalize=true) - n_v = nv(g) - c = zeros(n_v) - for v in 1:n_v - if gtype == 0 # count both in and out degree if appropriate - deg = is_directed(g)? outdegree(g, v) + indegree(g, v) : outdegree(g, v) - elseif gtype == 1 # count only in degree - deg = indegree(g, v) - else # count only out degree - deg = outdegree(g, v) - end - s = normalize? (1.0 / (n_v - 1.0)) : 1.0 - c[v] = deg*s - end - return c + n_v = nv(g) + c = zeros(n_v) + for v in vertices(g) + if gtype == 0 # count both in and out degree if appropriate + deg = is_directed(g)? outdegree(g, v) + indegree(g, v) : outdegree(g, v) + elseif gtype == 1 # count only in degree + deg = indegree(g, v) + else # count only out degree + deg = outdegree(g, v) + end + s = normalize? (1.0 / (n_v - 1.0)) : 1.0 + c[v] = deg*s + end + return c end # TODO avoid repetition of this docstring diff --git a/src/centrality/pagerank.jl b/src/centrality/pagerank.jl index 84723de60..e9c4a562b 100644 --- a/src/centrality/pagerank.jl +++ b/src/centrality/pagerank.jl @@ -8,8 +8,7 @@ reached within `n` iterations, an error will be returned. """ function pagerank end -#TODO: re-roll this to @traitfn function pagerank{G<:AbstractGraph; IsDirected{G}}(g::G, α=0.85, n=100, ϵ = 1.0e-6) -@traitfn function pagerank{G<:AbstractGraph; IsDirected{G}}(g::G, α, n, ϵ) +@traitfn function pagerank{G<:AbstractGraph; IsDirected{G}}(g::G, α=0.85, n=100, ϵ = 1.0e-6) A = adjacency_matrix(g,:in,Float64) S = vec(sum(A,1)) S = 1./S @@ -32,8 +31,3 @@ function pagerank end end error("Pagerank did not converge after $n iterations.") end - -### TODO: remove these when re-rolled. -@traitfn pagerank{G<:AbstractGraph; IsDirected{G}}(g::G, α, n) = pagerank(g, α, n, 1e-6) -@traitfn pagerank{G<:AbstractGraph; IsDirected{G}}(g::G, α) = pagerank(g, α, 100, 1e-6) -@traitfn pagerank{G<:AbstractGraph; IsDirected{G}}(g::G) = pagerank(g, 0.85, 100, 1e-6) diff --git a/src/community/cliques.jl b/src/community/cliques.jl index dfcedbc98..ef8660e9e 100644 --- a/src/community/cliques.jl +++ b/src/community/cliques.jl @@ -22,7 +22,6 @@ julia> maximal_cliques(g) function maximal_cliques end @traitfn function maximal_cliques{G<:AbstractGraph; !IsDirected{G}}(g::G) T = eltype(g) - # Cache nbrs and find first pivot (highest degree) maxconn = -1 nnbrs = Vector{Set{T}}() @@ -45,7 +44,7 @@ function maximal_cliques end nnbrs[n] = nbrs end end - + # Initial setup cand = Set{T}(vertices(g)) # union!(cand, keys(nnbrs)) diff --git a/src/community/clustering.jl b/src/community/clustering.jl index b7ca5aa2d..83b369d90 100644 --- a/src/community/clustering.jl +++ b/src/community/clustering.jl @@ -1,5 +1,5 @@ """ - local_clustering_coefficient(g, v) +local_clustering_coefficient(g, v) Computes the [local clustering coefficient](https://en.wikipedia.org/wiki/Clustering_coefficient) for node `v`. """ @@ -10,7 +10,7 @@ function local_clustering_coefficient(g::AbstractGraph, v::Integer) end """ - local_clustering(g, v) +local_clustering(g, v) 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. @@ -22,16 +22,16 @@ function local_clustering(g::AbstractGraph, v::Integer) neighs = neighbors(g, v) c = 0 for i in neighs, j in neighs - i == j && continue + i == j && continue if has_edge(g, i, j) - c += 1 + c += 1 end end return is_directed(g) ? (c , k*(k-1)) : (div(c,2) , div(k*(k-1),2)) end """ - triangles(g, v) +triangles(g, v) Returns the number of triangles in the neighborhood for node `v`. """ @@ -39,14 +39,14 @@ triangles(g::AbstractGraph, v::Integer) = local_clustering(g, v)[1] """ - local_clustering_coefficient(g, vlist = vertices(g)) +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)) +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`. @@ -63,7 +63,7 @@ function local_clustering(g::AbstractGraph, vlist = vertices(g)) end """ - triangles(g, vlist = vertices(g)) +triangles(g, vlist = vertices(g)) Returns a vector containing the number of triangles for vertices `vlist`. """ @@ -71,7 +71,7 @@ triangles(g::AbstractGraph, vlist = vertices(g)) = local_clustering(g, vlist)[1] """ - global_clustering_coefficient(g) +global_clustering_coefficient(g) Computes the [global clustering coefficient](https://en.wikipedia.org/wiki/Clustering_coefficient). """ @@ -81,10 +81,10 @@ function global_clustering_coefficient(g::AbstractGraph) for v in vertices(g) neighs = neighbors(g, v) for i in neighs, j in neighs - i == j && continue - if has_edge(g, i, j) - c += 1 - end + i == j && continue + if has_edge(g, i, j) + c += 1 + end end k = degree(g, v) ntriangles += k*(k-1) diff --git a/src/community/core-periphery.jl b/src/community/core-periphery.jl index 3021630a8..96b827bf2 100644 --- a/src/community/core-periphery.jl +++ b/src/community/core-periphery.jl @@ -1,5 +1,5 @@ """ - 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). diff --git a/src/community/label_propagation.jl b/src/community/label_propagation.jl index 1b5fd1966..b4fb65f1b 100644 --- a/src/community/label_propagation.jl +++ b/src/community/label_propagation.jl @@ -43,9 +43,9 @@ end """Type to record neighbor labels and their counts.""" type NeighComm{T<:Integer} - neigh_pos::Vector{T} - neigh_cnt::Vector{Int} - neigh_last::T + neigh_pos::Vector{T} + neigh_cnt::Vector{Int} + neigh_last::T end """Fast shuffle Array `a` in UnitRange `r` inplace.""" @@ -78,15 +78,15 @@ function vote!(g::AbstractGraph, m::Vector, c::NeighComm, u::Integer) end c.neigh_cnt[neigh_comm] += 1 if c.neigh_cnt[neigh_comm] > max_cnt - max_cnt = c.neigh_cnt[neigh_comm] + max_cnt = c.neigh_cnt[neigh_comm] end end # ties breaking randomly range_shuffle!(1:c.neigh_last-1, c.neigh_pos) for lbl in c.neigh_pos - if c.neigh_cnt[lbl] == max_cnt - return lbl - end + if c.neigh_cnt[lbl] == max_cnt + return lbl + end end end diff --git a/src/community/modularity.jl b/src/community/modularity.jl index 1f89af553..799f58cdf 100644 --- a/src/community/modularity.jl +++ b/src/community/modularity.jl @@ -1,5 +1,5 @@ """ - modularity(g, c) +modularity(g, c) Computes Newman's modularity `Q` for graph `g` given the partitioning `c`. diff --git a/src/connectivity.jl b/src/connectivity.jl index fc4efcc44..636591928 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -1,7 +1,5 @@ # Parts of this code were taken / derived from Graphs.jl. See LICENSE for # licensing details. - - """ connected_components!(label::Vector{Int}, g::AbstractGraph) diff --git a/src/core.jl b/src/core.jl index 49b692e81..7ee3b84b9 100644 --- a/src/core.jl +++ b/src/core.jl @@ -58,7 +58,7 @@ function noallocextreme(f, comparison, initial, g) end """ - degree_histogram(g) +degree_histogram(g) Returns a `StatsBase.Histogram` of the degrees of vertices in `g`. """ @@ -79,14 +79,14 @@ For undirected graphs, this is equivalent to `out_neighbors` and """ all_neighbors(x...) = _NI("all_neighbors") @traitfn all_neighbors{G<:AbstractGraph; IsDirected{G}}(g::G, v::Integer) = - union(out_neighbors(g, v), in_neighbors(g, v)) + union(out_neighbors(g, v), in_neighbors(g, v)) @traitfn all_neighbors{G<:AbstractGraph; !IsDirected{G}}(g::G, v::Integer) = - neighbors(g, v) + neighbors(g, v) "Returns the neighbors common to vertices `u` and `v` in `g`." common_neighbors(g::AbstractGraph, u::Integer, v::Integer) = - intersect(neighbors(g, u), neighbors(g, v)) + intersect(neighbors(g, u), neighbors(g, v)) "Returns 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)) @@ -102,6 +102,6 @@ number of possible edges ( |v| |v-1| for directed graphs and """ density(G::AbstractGraph) = _NI("density") @traitfn density{G<:AbstractGraph; IsDirected{G}}(g::G) = - ne(g) / (nv(g) * (nv(g)-1)) +ne(g) / (nv(g) * (nv(g)-1)) @traitfn density{G<:AbstractGraph; !IsDirected{G}}(g::G) = - (2*ne(g)) / (nv(g) * (nv(g)-1)) +(2*ne(g)) / (nv(g) * (nv(g)-1)) diff --git a/src/flow/boykov_kolmogorov.jl b/src/flow/boykov_kolmogorov.jl index 5ac47ff73..fd1d7e40e 100644 --- a/src/flow/boykov_kolmogorov.jl +++ b/src/flow/boykov_kolmogorov.jl @@ -31,7 +31,7 @@ function boykov_kolmogorov_impl end ) T = eltype(capacity_matrix) - U = eltype(residual_graph) +U = eltype(residual_graph) n = nv(residual_graph) @@ -48,16 +48,16 @@ function boykov_kolmogorov_impl end O = Vector{U}() while true - # growth stage - path = find_path!(residual_graph, source, target, flow_matrix, capacity_matrix, PARENT, TREE, A) + # growth stage + path = find_path!(residual_graph, source, target, flow_matrix, capacity_matrix, PARENT, TREE, A) - isempty(path) && break + isempty(path) && break - # augmentation stage - flow += augment!(path, flow_matrix, capacity_matrix, PARENT, TREE, O) + # augmentation stage + flow += augment!(path, flow_matrix, capacity_matrix, PARENT, TREE, O) - # adoption stage - adopt!(residual_graph, source, target, flow_matrix, capacity_matrix, PARENT, TREE, A, O) + # adoption stage + adopt!(residual_graph, source, target, flow_matrix, capacity_matrix, PARENT, TREE, A, O) end return flow, flow_matrix, TREE @@ -75,43 +75,43 @@ end ) T = eltype(residual_graph) tree_cap(p,q) = TREE[p] == one(T) ? capacity_matrix[p,q] - flow_matrix[p,q] : - capacity_matrix[q,p] - flow_matrix[q,p] + capacity_matrix[q,p] - flow_matrix[q,p] while !isempty(A) - p = last(A) - for q in neighbors(residual_graph, p) - if tree_cap(p,q) > 0 - if TREE[q] == zero(T) - TREE[q] = TREE[p] - PARENT[q] = p - unshift!(A, q) - end - if TREE[q] ≠ zero(T) && TREE[q] ≠ TREE[p] - # p -> source - path_to_source = [p] - while PARENT[p] ≠ zero(T) - p = PARENT[p] - push!(path_to_source, p) - end - - # q -> target - path_to_target = [q] - while PARENT[q] ≠ zero(T) - q = PARENT[q] - push!(path_to_target, q) - end - - # source -> target - path = [reverse!(path_to_source); path_to_target] - - if path[1] == source && path[end] == target - return path - elseif path[1] == target && path[end] == source - return reverse!(path) + p = last(A) + for q in neighbors(residual_graph, p) + if tree_cap(p,q) > 0 + if TREE[q] == zero(T) + TREE[q] = TREE[p] + PARENT[q] = p + unshift!(A, q) + end + if TREE[q] ≠ zero(T) && TREE[q] ≠ TREE[p] + # p -> source + path_to_source = [p] + while PARENT[p] ≠ zero(T) + p = PARENT[p] + push!(path_to_source, p) + end + + # q -> target + path_to_target = [q] + while PARENT[q] ≠ zero(T) + q = PARENT[q] + push!(path_to_target, q) + end + + # source -> target + path = [reverse!(path_to_source); path_to_target] + + if path[1] == source && path[end] == target + return path + elseif path[1] == target && path[end] == source + return reverse!(path) + end + end end - end end - end - pop!(A) + pop!(A) end return Vector{T}() @@ -130,28 +130,28 @@ function augment!( # bottleneck capacity Δ = Inf for i=1:length(path)-1 - p, q = path[i:i+1] - cap = capacity_matrix[p,q] - flow_matrix[p,q] - cap < Δ && (Δ = cap) + p, q = path[i:i+1] + cap = capacity_matrix[p,q] - flow_matrix[p,q] + cap < Δ && (Δ = cap) end # update residual graph for i=1:length(path)-1 - p, q = path[i:i+1] - flow_matrix[p,q] += Δ - flow_matrix[q,p] -= Δ - - # create orphans - if flow_matrix[p,q] == capacity_matrix[p,q] - if TREE[p] == TREE[q] == one(T) - PARENT[q] = zero(T) - unshift!(O, q) - end - if TREE[p] == TREE[q] == 2 - PARENT[p] = zero(T) - unshift!(O, p) + p, q = path[i:i+1] + flow_matrix[p,q] += Δ + flow_matrix[q,p] -= Δ + + # create orphans + if flow_matrix[p,q] == capacity_matrix[p,q] + if TREE[p] == TREE[q] == one(T) + PARENT[q] = zero(T) + unshift!(O, q) + end + if TREE[p] == TREE[q] == 2 + PARENT[p] = zero(T) + unshift!(O, p) + end end - end end return Δ @@ -171,43 +171,43 @@ end T = eltype(residual_graph) tree_cap(p,q) = TREE[p] == 1 ? capacity_matrix[p,q] - flow_matrix[p,q] : - capacity_matrix[q,p] - flow_matrix[q,p] + capacity_matrix[q,p] - flow_matrix[q,p] while !isempty(O) - p = pop!(O) - # try to find parent that is not an orphan - parent_found = false - for q in neighbors(residual_graph, p) - if TREE[q] == TREE[p] && tree_cap(q,p) > 0 - # check if "origin" is either source or target - o = q - while PARENT[o] ≠ 0 - o = PARENT[o] - end - if o == source || o == target - parent_found = true - PARENT[p] = q - break - end - end - end - - if !parent_found - # scan all neighbors and make the orphan a free node + p = pop!(O) + # try to find parent that is not an orphan + parent_found = false for q in neighbors(residual_graph, p) - if TREE[q] == TREE[p] - if tree_cap(q,p) > 0 - unshift!(A, q) - end - if PARENT[q] == p - PARENT[q] = zero(T) - unshift!(O, q) + if TREE[q] == TREE[p] && tree_cap(q,p) > 0 + # check if "origin" is either source or target + o = q + while PARENT[o] ≠ 0 + o = PARENT[o] + end + if o == source || o == target + parent_found = true + PARENT[p] = q + break + end end - end end - TREE[p] = zero(T) - B = setdiff(A, p) - resize!(A, length(B))[:] = B - end + if !parent_found + # scan all neighbors and make the orphan a free node + for q in neighbors(residual_graph, p) + if TREE[q] == TREE[p] + if tree_cap(q,p) > 0 + unshift!(A, q) + end + if PARENT[q] == p + PARENT[q] = zero(T) + unshift!(O, q) + end + end + end + + TREE[p] = zero(T) + B = setdiff(A, p) + resize!(A, length(B))[:] = B + end end end diff --git a/src/flow/dinic.jl b/src/flow/dinic.jl index 668d39971..f47920b92 100644 --- a/src/flow/dinic.jl +++ b/src/flow/dinic.jl @@ -116,10 +116,10 @@ blocking_flow!( target::Integer, # the target vertex capacity_matrix::AbstractMatrix, # edge flow capacities flow_matrix::AbstractMatrix, # the current flow matrix - ) = - blocking_flow!(residual_graph, - source, - target, - capacity_matrix, - flow_matrix, - zeros(Int, nv(residual_graph))) + ) = blocking_flow!( + residual_graph, + source, + target, + capacity_matrix, + flow_matrix, + zeros(Int, nv(residual_graph))) diff --git a/src/flow/edmonds_karp.jl b/src/flow/edmonds_karp.jl index 31216986e..20a1b11be 100644 --- a/src/flow/edmonds_karp.jl +++ b/src/flow/edmonds_karp.jl @@ -45,7 +45,7 @@ function edmonds_karp_impl end u = S[u] push!(path, Int(u)) end - # augment flow along path + # augment flow along path flow += augment_path!(path, flow_matrix, capacity_matrix) end end @@ -106,12 +106,12 @@ function fetch_path end P = -1 * ones(Int, n) S = -1 * ones(Int, n) return fetch_path!(residual_graph, - source, - target, - flow_matrix, - capacity_matrix, - P, - S) + source, + target, + flow_matrix, + capacity_matrix, + P, + S) end """ @@ -124,13 +124,13 @@ Flag Values: 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::AbstractArray{T,2} # the current flow matrix - capacity_matrix::AbstractArray{T,2} # edge flow capacities - P::Vector{Int} # parent table of path init to -1s - S::Vector{Int} # successor table of path init to -1s +residual_graph::DiGraph # the input graph +source::Int # the source vertex +target::Int # the target vertex +flow_matrix::AbstractArray{T,2} # the current flow matrix +capacity_matrix::AbstractArray{T,2} # edge flow capacities +P::Vector{Int} # parent table of path init to -1s +S::Vector{Int} # successor table of path init to -1s """ function fetch_path! end @traitfn function fetch_path!{G<:AbstractGraph; IsDirected{G}}( @@ -154,7 +154,6 @@ function fetch_path! end sizehint!(Q_r, n) while true - if length(Q_f) <= length(Q_r) u = pop!(Q_f) for v in out_neighbors(residual_graph, u) diff --git a/src/flow/ext_multiroute_flow.jl b/src/flow/ext_multiroute_flow.jl index 3f4782b5a..4332c1cb9 100644 --- a/src/flow/ext_multiroute_flow.jl +++ b/src/flow/ext_multiroute_flow.jl @@ -18,20 +18,20 @@ Requires arguments: """ # EMRF (Extended Multiroute Flow) algorithms function emrf( - flow_graph::AbstractGraph, # the input graph - source::Integer, # the source vertex - target::Integer, # the target vertex - capacity_matrix::AbstractMatrix, # edge flow capacities - flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm - routes::Real = 0 - ) - breakingpoints = breakingPoints(flow_graph, source, target, capacity_matrix) - if routes > 0 - x, f = intersection(breakingpoints, routes) - return maximum_flow(flow_graph, source, target, capacity_matrix, - algorithm = flow_algorithm, restriction = x) - end - return breakingpoints + flow_graph::AbstractGraph, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex + capacity_matrix::AbstractMatrix, # edge flow capacities + flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm + routes::Real = 0 + ) + breakingpoints = breakingPoints(flow_graph, source, target, capacity_matrix) + if routes > 0 + x, f = intersection(breakingpoints, routes) + return maximum_flow(flow_graph, source, target, capacity_matrix, + algorithm = flow_algorithm, restriction = x) + end + return breakingpoints end """ @@ -45,59 +45,59 @@ Requires arguments: """ function auxiliaryPoints end @traitfn function auxiliaryPoints{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph - source::Integer, # the source vertex - target::Integer, # the target vertex - capacity_matrix::AbstractMatrix # edge flow capacities - ) - # Problem descriptors - λ = maximum_flow(flow_graph, source, target)[1] # Connectivity - n = nv(flow_graph) # number of nodes - r1, r2 = minmaxCapacity(capacity_matrix) # restriction left (1) and right (2) - auxpoints = fill((0., 0.), λ + 1) - - # Initialisation of left side (1) - f1, F1, cut1 = maximum_flow(flow_graph, source, target, capacity_matrix, - algorithm = BoykovKolmogorovAlgorithm(), restriction = r1) - s1 = slope(flow_graph, capacity_matrix, cut1, r1) # left slope - auxpoints[λ + 1 - s1] = (r1, f1) # Add left initial auxiliary point - - # Initialisation of right side (2) - f2, F2, cut2 = maximum_flow(flow_graph, source, target, capacity_matrix, - algorithm = BoykovKolmogorovAlgorithm(), restriction = r2) - s2 = slope(flow_graph, capacity_matrix, cut2, r2) # right slope - auxpoints[λ + 1 - s2] = (r2, f2) # Add right initial auxiliary point - - # Loop if the slopes are distinct by at least 2 - if s1 > s2 + 1 - queue = [((f1, s1, r1), (f2, s2, r2))] - - while !isempty(queue) - # Computes an intersection (middle) with a new associated slope - (f1, s1, r1), (f2, s2, r2) = pop!(queue) - r, expectedflow = intersection(r1, f1, s1, r2, f2, s2) - f, F, cut = maximum_flow(flow_graph, source, target, capacity_matrix, - algorithm = BoykovKolmogorovAlgorithm(), restriction = r) - s = slope(flow_graph, capacity_matrix, max(cut, 1), r) # current slope - auxpoints[λ + 1 - s] = (r, f) - # If the flow at the intersection (middle) is as expected - if expectedflow ≉ f # approximatively not equal (enforced by floating precision) - # if the slope difference between (middle) and left is at least 2 - # push (left),(middle) - if s1 > s + 1 && (r2, f2) ≉ (r, f) - q = (f1, s1, r1), (f, s, r) - push!(queue, q) - end - # if the slope difference between (middle) and right is at least 2 - # push (middle),(right) - if s > s2 + 1 && (r1, f1) ≉ (r, f) - q = (f, s, r), (f2, s2, r2) - push!(queue, q) + flow_graph::G, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex + capacity_matrix::AbstractMatrix # edge flow capacities + ) + # Problem descriptors + λ = maximum_flow(flow_graph, source, target)[1] # Connectivity + n = nv(flow_graph) # number of nodes + r1, r2 = minmaxCapacity(capacity_matrix) # restriction left (1) and right (2) + auxpoints = fill((0., 0.), λ + 1) + + # Initialisation of left side (1) + f1, F1, cut1 = maximum_flow(flow_graph, source, target, capacity_matrix, + algorithm = BoykovKolmogorovAlgorithm(), restriction = r1) + s1 = slope(flow_graph, capacity_matrix, cut1, r1) # left slope + auxpoints[λ + 1 - s1] = (r1, f1) # Add left initial auxiliary point + + # Initialisation of right side (2) + f2, F2, cut2 = maximum_flow(flow_graph, source, target, capacity_matrix, + algorithm = BoykovKolmogorovAlgorithm(), restriction = r2) + s2 = slope(flow_graph, capacity_matrix, cut2, r2) # right slope + auxpoints[λ + 1 - s2] = (r2, f2) # Add right initial auxiliary point + + # Loop if the slopes are distinct by at least 2 + if s1 > s2 + 1 + queue = [((f1, s1, r1), (f2, s2, r2))] + + while !isempty(queue) + # Computes an intersection (middle) with a new associated slope + (f1, s1, r1), (f2, s2, r2) = pop!(queue) + r, expectedflow = intersection(r1, f1, s1, r2, f2, s2) + f, F, cut = maximum_flow(flow_graph, source, target, capacity_matrix, + algorithm = BoykovKolmogorovAlgorithm(), restriction = r) + s = slope(flow_graph, capacity_matrix, max(cut, 1), r) # current slope + auxpoints[λ + 1 - s] = (r, f) + # If the flow at the intersection (middle) is as expected + if expectedflow ≉ f # approximatively not equal (enforced by floating precision) + # if the slope difference between (middle) and left is at least 2 + # push (left),(middle) + if s1 > s + 1 && (r2, f2) ≉ (r, f) + q = (f1, s1, r1), (f, s, r) + push!(queue, q) + end + # if the slope difference between (middle) and right is at least 2 + # push (middle),(right) + if s > s2 + 1 && (r1, f1) ≉ (r, f) + q = (f, s, r), (f2, s2, r2) + push!(queue, q) + end + end end - end end - end - return auxpoints + return auxpoints end """ @@ -110,31 +110,31 @@ Requires arguments: """ function breakingPoints end @traitfn function breakingPoints{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph - source::Integer, # the source vertex - target::Integer, # the target vertex - capacity_matrix::AbstractMatrix # edge flow capacities - ) - auxpoints = auxiliaryPoints(flow_graph, source, target, capacity_matrix) - λ = length(auxpoints) - 1 - left_index = 1 - T = eltype(capacity_matrix) - breakingpoints = Vector{Tuple{T, T, Int}}() - - for (id, point) in enumerate(auxpoints) - if id == 1 - push!(breakingpoints, (0., 0., λ)) - else - pleft = breakingpoints[left_index] - if point[1] != 0 - x, y = intersection(pleft[1], pleft[2], pleft[3], - point[1], point[2], λ + 1 - id) - push!(breakingpoints,(x, y, λ + 1 - id)) - left_index += 1 - end + flow_graph::G, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex + capacity_matrix::AbstractMatrix # edge flow capacities + ) + auxpoints = auxiliaryPoints(flow_graph, source, target, capacity_matrix) + λ = length(auxpoints) - 1 + left_index = 1 + T = eltype(capacity_matrix) + breakingpoints = Vector{Tuple{T, T, Int}}() + + for (id, point) in enumerate(auxpoints) + if id == 1 + push!(breakingpoints, (0., 0., λ)) + else + pleft = breakingpoints[left_index] + if point[1] != 0 + x, y = intersection(pleft[1], pleft[2], pleft[3], + point[1], point[2], λ + 1 - id) + push!(breakingpoints,(x, y, λ + 1 - id)) + left_index += 1 + end + end end - end - return breakingpoints + return breakingpoints end """ @@ -151,50 +151,50 @@ Requires argument: # note: this is more efficient than maximum() / minimum() / extrema() # since we have to ignore zero values. function minmaxCapacity( - capacity_matrix::AbstractMatrix # edge flow capacities - ) - T = eltype(capacity_matrix) - cmin, cmax = typemax(T), typemin(T) - for c in capacity_matrix - if c > zero(T) - cmin = min(cmin, c) + capacity_matrix::AbstractMatrix # edge flow capacities + ) + T = eltype(capacity_matrix) + cmin, cmax = typemax(T), typemin(T) + for c in capacity_matrix + if c > zero(T) + cmin = min(cmin, c) + end + cmax = max(cmax, c) end - cmax = max(cmax, c) - end - return cmin, cmax + return cmin, cmax 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::AbstractArray{T, 2}, # edge flow capacities - cut::Vector{Int}, # cut information for vertices - restriction::T # value of the restriction +flow_graph::DiGraph, # the input graph +capacity_matrix::AbstractArray{T, 2}, # edge flow capacities +cut::Vector{Int}, # cut information for vertices +restriction::T # value of the restriction """ function slope end # Function to get the slope of the restricted flow @traitfn function slope{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph - capacity_matrix::AbstractMatrix, # edge flow capacities - cut::Vector, # cut information for vertices - restriction::Number # value of the restriction - ) - slope = 0 - for e in edges(flow_graph) - ## Chain comparison to wether an edge cross the cut from the source side of - # the cut to the target side of the cut. Then the edge is selected iff the - # capacity of the edge is larger then the restriction argument. - # cut[dst(e)] == 2 > cut[src(e)] is equivalent to - # cut[dst(e)] == 2 && 2 > cut[src(e)] - # Description of chain comparisons can be found at https://goo.gl/IJpCqe - if cut[dst(e)] == 2 > cut[src(e)] && - capacity_matrix[src(e), dst(e)] > restriction - slope += 1 + flow_graph::G, # the input graph + capacity_matrix::AbstractMatrix, # edge flow capacities + cut::Vector, # cut information for vertices + restriction::Number # value of the restriction + ) + slope = 0 + for e in edges(flow_graph) + ## Chain comparison to wether an edge cross the cut from the source side of + # the cut to the target side of the cut. Then the edge is selected iff the + # capacity of the edge is larger then the restriction argument. + # cut[dst(e)] == 2 > cut[src(e)] is equivalent to + # cut[dst(e)] == 2 && 2 > cut[src(e)] + # Description of chain comparisons can be found at https://goo.gl/IJpCqe + if cut[dst(e)] == 2 > cut[src(e)] && + capacity_matrix[src(e), dst(e)] > restriction + slope += 1 + end end - end - return slope + return slope end """ @@ -204,55 +204,50 @@ Computes the intersection between: Requires argument: 1) - x1, y1, a1, x2, y2, a2::T<:AbstractFloat # Coordinates/slopes 2) - points::Vector{Tuple{T, T, Int}} # vector of points with T<:AbstractFloat - - k::R<:Real # number of routes (slope of the line) +- k::R<:Real # number of routes (slope of the line) """ # Compute the (expected) intersection of two lines function intersection{T<:AbstractFloat, R<:Real}( - x1::T, # x coordinate of point 1 - y1::T, # y coordinate of point 1 - a1::Integer, # slope passing by point 1 - x2::T, # x coordinate of point 2 - y2::T, # y coordinate of point 2 - a2::R # slope passing by point 2 - ) - - (a1 == a2) && return -1., -1. # result will be ignored in other intersection method - b1 = y1 - a1 * x1 - b2 = y2 - a2 * x2 - x = (b2 - b1) / (a1 - a2) - y = a1 * x + b1 - return x, y + x1::T, # x coordinate of point 1 + y1::T, # y coordinate of point 1 + a1::Integer, # slope passing by point 1 + x2::T, # x coordinate of point 2 + y2::T, # y coordinate of point 2 + a2::R # slope passing by point 2 + ) + + (a1 == a2) && return -1., -1. # result will be ignored in other intersection method + b1 = y1 - a1 * x1 + b2 = y2 - a2 * x2 + x = (b2 - b1) / (a1 - a2) + y = a1 * x + b1 + return x, y end # Compute the intersection between a set of segment and a line of slope k passing by the origin function intersection{T<:AbstractFloat, I<:Integer, R<:Real}( - points::Vector{Tuple{T, T, I}}, # vector of breaking points - k::R # number of routes (slope of the line) - ) - λ = points[1][1] # Connectivity - - # Loop over the segments (pair of breaking points) - for (id, p) in enumerate(points[1:(end - 1)]) - if id == 1 - (k ≈ λ) && return points[2] - else - x, y = intersection(p[1], p[2], p[3], 0., 0., k) - (p[1] ≤ x ≤ points[id + 1][1]) && return x, y + points::Vector{Tuple{T, T, I}}, # vector of breaking points + k::R # number of routes (slope of the line) + ) + λ = points[1][1] # Connectivity + + # Loop over the segments (pair of breaking points) + for (id, p) in enumerate(points[1:(end - 1)]) + if id == 1 + (k ≈ λ) && return points[2] + else + x, y = intersection(p[1], p[2], p[3], 0., 0., k) + (p[1] ≤ x ≤ points[id + 1][1]) && return x, y + end end - end - p = points[end] - return intersection(p[1], p[2], p[3], 0., 0., k) + p = points[end] + return intersection(p[1], p[2], p[3], 0., 0., k) 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::Tuple{T, T}, # Point A with floating coordinates +b::Tuple{T, T} # Point B with floating coordinates """ -function ≈{T<:AbstractFloat}( - a::Tuple{T, T}, # Point A with floating coordinates - b::Tuple{T, T} # Point B with floating coordinates - ) - return a[1] ≈ b[1] && a[2] ≈ b[2] -end +≈{T<:AbstractFloat}(a::Tuple{T, T}, b::Tuple{T, T}) = a[1] ≈ b[1] && a[2] ≈ b[2] diff --git a/src/flow/kishimoto.jl b/src/flow/kishimoto.jl index 18c7c2fad..6aa462f8a 100644 --- a/src/flow/kishimoto.jl +++ b/src/flow/kishimoto.jl @@ -2,31 +2,31 @@ # Kishimoto algorithm @traitfn function kishimoto{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph - source::Integer, # the source vertex - target::Integer, # the target vertex - capacity_matrix::AbstractMatrix, # edge flow capacities - flow_algorithm::BoykovKolmogorovAlgorithm, # keyword argument for algorithm - routes::Int # keyword argument for routes - ) - # Initialisation - flow, F, labels = maximum_flow(flow_graph, source, target, - capacity_matrix, algorithm = flow_algorithm) - restriction = flow / routes - flow, F, labels = maximum_flow(flow_graph, source, target, capacity_matrix, - algorithm = flow_algorithm, restriction = restriction) - - # Loop condition : approximatively not equal is enforced by floating precision - i = 1 - while flow < routes * restriction && flow ≉ routes * restriction - restriction = (flow - i * restriction) / (routes - i) - i += 1 + flow_graph::G, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex + capacity_matrix::AbstractMatrix, # edge flow capacities + flow_algorithm::BoykovKolmogorovAlgorithm, # keyword argument for algorithm + routes::Int # keyword argument for routes + ) + # Initialisation + flow, F, labels = maximum_flow(flow_graph, source, target, + capacity_matrix, algorithm = flow_algorithm) + restriction = flow / routes flow, F, labels = maximum_flow(flow_graph, source, target, capacity_matrix, - algorithm = flow_algorithm, restriction = restriction) - end + algorithm = flow_algorithm, restriction = restriction) + + # Loop condition : approximatively not equal is enforced by floating precision + i = 1 + while flow < routes * restriction && flow ≉ routes * restriction + restriction = (flow - i * restriction) / (routes - i) + i += 1 + flow, F, labels = maximum_flow(flow_graph, source, target, capacity_matrix, + algorithm = flow_algorithm, restriction = restriction) + end - # End - return flow, F, labels + # End + return flow, F, labels end @@ -47,30 +47,30 @@ Requires arguments: """ function kishimoto end @traitfn function kishimoto{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph - source::Integer, # the source vertex - target::Integer, # the target vertex - capacity_matrix::AbstractMatrix, # edge flow capacities - flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm - routes::Int # keyword argument for routes - ) - # Initialisation - flow, F = maximum_flow(flow_graph, source, target, - capacity_matrix, algorithm = flow_algorithm) - restriction = flow / routes + flow_graph::G, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex + capacity_matrix::AbstractMatrix, # edge flow capacities + flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm + routes::Int # keyword argument for routes + ) + # Initialisation + flow, F = maximum_flow(flow_graph, source, target, + capacity_matrix, algorithm = flow_algorithm) + restriction = flow / routes - flow, F = maximum_flow(flow_graph, source, target, capacity_matrix, - algorithm = flow_algorithm, restriction = restriction) - - # Loop condition : approximatively not equal is enforced by floating precision - i = 1 - while flow < routes * restriction && flow ≉ routes * restriction - restriction = (flow - i * restriction) / (routes - i) - i += 1 flow, F = maximum_flow(flow_graph, source, target, capacity_matrix, - algorithm = flow_algorithm, restriction = restriction) - end + algorithm = flow_algorithm, restriction = restriction) + + # Loop condition : approximatively not equal is enforced by floating precision + i = 1 + while flow < routes * restriction && flow ≉ routes * restriction + restriction = (flow - i * restriction) / (routes - i) + i += 1 + flow, F = maximum_flow(flow_graph, source, target, capacity_matrix, + algorithm = flow_algorithm, restriction = restriction) + end - # End - return flow, F + # End + return flow, F end diff --git a/src/flow/maximum_flow.jl b/src/flow/maximum_flow.jl index 27c9c8247..8d39c5c38 100644 --- a/src/flow/maximum_flow.jl +++ b/src/flow/maximum_flow.jl @@ -140,9 +140,9 @@ For the Boykov-Kolmogorov algorithm, the associated mincut is returned as a thir # Create a flow-graph and a capacity matrix flow_graph = DiGraph(8) 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) +(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 @@ -173,13 +173,13 @@ function maximum_flow( source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix = # edge flow capacities - DefaultCapacity(flow_graph); + DefaultCapacity(flow_graph); algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm - PushRelabelAlgorithm(), + PushRelabelAlgorithm(), restriction::Real = 0 # keyword argument for restriction max-flow ) if restriction > 0 - return maximum_flow(flow_graph, source, target, min(restriction, capacity_matrix), algorithm) + return maximum_flow(flow_graph, source, target, min(restriction, capacity_matrix), algorithm) end return maximum_flow(flow_graph, source, target, capacity_matrix, algorithm) end diff --git a/src/flow/multiroute_flow.jl b/src/flow/multiroute_flow.jl index 11aae8137..c663f12d3 100644 --- a/src/flow/multiroute_flow.jl +++ b/src/flow/multiroute_flow.jl @@ -19,76 +19,76 @@ end # 1) When using Boykov-Kolmogorov as a flow subroutine # 2) Other flow algorithm function empty_flow{T<:Real}( - capacity_matrix::AbstractMatrix{T}, # edge flow capacities - flow_algorithm::BoykovKolmogorovAlgorithm # keyword argument for algorithm - ) - n = size(capacity_matrix, 1) - return zero(T), zeros(T, n, n), zeros(T, n) + capacity_matrix::AbstractMatrix{T}, # edge flow capacities + flow_algorithm::BoykovKolmogorovAlgorithm # keyword argument for algorithm + ) + n = size(capacity_matrix, 1) + return zero(T), zeros(T, n, n), zeros(T, n) end # 2) Other flow algorithm function empty_flow{T<:Real}( - capacity_matrix::AbstractMatrix{T}, # edge flow capacities - flow_algorithm::AbstractFlowAlgorithm # keyword argument for algorithm - ) - n = size(capacity_matrix, 1) - return zero(T), zeros(T, n, n) + capacity_matrix::AbstractMatrix{T}, # edge flow capacities + flow_algorithm::AbstractFlowAlgorithm # keyword argument for algorithm + ) + n = size(capacity_matrix, 1) + return zero(T), zeros(T, n, n) end # Method for Kishimoto algorithm @traitfn function multiroute_flow{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph - source::Integer, # the source vertex - target::Integer, # the target vertex - capacity_matrix::AbstractMatrix, # edge flow capacities - flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm - mrf_algorithm::KishimotoAlgorithm, # keyword argument for algorithm - routes::Int # keyword argument for routes - ) - return kishimoto(flow_graph, source, target, capacity_matrix, flow_algorithm, routes) + flow_graph::G, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex + capacity_matrix::AbstractMatrix, # edge flow capacities + flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm + mrf_algorithm::KishimotoAlgorithm, # keyword argument for algorithm + routes::Int # keyword argument for routes + ) + return kishimoto(flow_graph, source, target, capacity_matrix, flow_algorithm, routes) end ## Methods for Extended Multiroute Flow Algorithm #1 When the breaking points are not already known @traitfn function multiroute_flow{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph - source::Integer, # the source vertex - target::Integer, # the target vertex - capacity_matrix::AbstractMatrix, # edge flow capacities - flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm - mrf_algorithm::ExtendedMultirouteFlowAlgorithm, # keyword argument for algorithm - routes::Real # keyword argument for routes - ) - return emrf(flow_graph, source, target, capacity_matrix, flow_algorithm, routes) + flow_graph::G, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex + capacity_matrix::AbstractMatrix, # edge flow capacities + flow_algorithm::AbstractFlowAlgorithm, # keyword argument for algorithm + mrf_algorithm::ExtendedMultirouteFlowAlgorithm, # keyword argument for algorithm + routes::Real # keyword argument for routes + ) + return emrf(flow_graph, source, target, capacity_matrix, flow_algorithm, routes) end #2 When the breaking points are already known #2-a Output: flow value (paired with the associated restriction) function multiroute_flow{T<:Real, R<:Real}( - breakingpoints::Vector{Tuple{T, T, Int}}, # vector of breaking points - routes::R # keyword argument for routes - ) - return intersection(breakingpoints, routes) + breakingpoints::Vector{Tuple{T, T, Int}}, # vector of breaking points + routes::R # keyword argument for routes + ) + return intersection(breakingpoints, routes) end #2-b Output: flow value, flows(, labels) function multiroute_flow{T1<:Real, R<:Real}( - breakingpoints::AbstractVector{Tuple{T1, T1, Int}}, # vector of breaking points - routes::R, # keyword argument for routes - flow_graph::AbstractGraph, # the input graph - source::Integer, # the source vertex - target::Integer, # the target vertex - capacity_matrix::AbstractMatrix = # edge flow capacities + breakingpoints::AbstractVector{Tuple{T1, T1, Int}}, # vector of breaking points + routes::R, # keyword argument for routes + flow_graph::AbstractGraph, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex + capacity_matrix::AbstractMatrix = # edge flow capacities DefaultCapacity(flow_graph); - flow_algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm + flow_algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm PushRelabelAlgorithm() - ) - x, f = intersection(breakingpoints, routes) - T2 = eltype(capacity_matrix) - # For other cases, capacities need to be Floats - if !(T2<:AbstractFloat) - capacity_matrix = convert(AbstractArray{Float64, 2}, capacity_matrix) - end - - return maximum_flow(flow_graph, source, target, capacity_matrix, - algorithm = flow_algorithm, restriction = x) + ) + x, f = intersection(breakingpoints, routes) + T2 = eltype(capacity_matrix) + # For other cases, capacities need to be Floats + if !(T2<:AbstractFloat) + capacity_matrix = convert(AbstractArray{Float64, 2}, capacity_matrix) + end + + return maximum_flow(flow_graph, source, target, capacity_matrix, + algorithm = flow_algorithm, restriction = x) end """ @@ -149,9 +149,9 @@ and capacity_matrix) # Create a flow-graph and a capacity matrix flow_graph = DiGraph(8) 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) +(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 @@ -174,46 +174,46 @@ 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) +algorithm = BoykovKolmogorovAlgorithm(), routes = 2) ``` """ function multiroute_flow{R<:Real}( - flow_graph::AbstractGraph, # the input graph - source::Integer, # the source vertex - target::Integer, # the target vertex - capacity_matrix::AbstractMatrix = # edge flow capacities + flow_graph::AbstractGraph, # the input graph + source::Integer, # the source vertex + target::Integer, # the target vertex + capacity_matrix::AbstractMatrix = # edge flow capacities DefaultCapacity(flow_graph); - flow_algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm + flow_algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm PushRelabelAlgorithm(), - mrf_algorithm::AbstractMultirouteFlowAlgorithm = # keyword argument for algorithm + mrf_algorithm::AbstractMultirouteFlowAlgorithm = # keyword argument for algorithm KishimotoAlgorithm(), - routes::R = 0 # keyword argument for number of routes (0 = all values) - ) - - # a flow with a set of 1-disjoint pathes is a classical max-flow - (routes == 1) && - return maximum_flow(flow_graph, source, target, capacity_matrix, flow_algorithm) - - # routes > λ (connectivity) → f = 0 - λ = maximum_flow(flow_graph, source, target, DefaultCapacity(flow_graph), - algorithm = flow_algorithm)[1] - (routes > λ) && return empty_flow(capacity_matrix, flow_algorithm) - - # For other cases, capacities need to be Floats - T = eltype(capacity_matrix) - if !(T<:AbstractFloat) - capacity_matrix = convert(AbstractMatrix{Float64}, capacity_matrix) - end - - # Ask for all possible values (breaking points) - (routes == 0) && + routes::R = 0 # keyword argument for number of routes (0 = all values) + ) + + # a flow with a set of 1-disjoint pathes is a classical max-flow + (routes == 1) && + return maximum_flow(flow_graph, source, target, capacity_matrix, flow_algorithm) + + # routes > λ (connectivity) → f = 0 + λ = maximum_flow(flow_graph, source, target, DefaultCapacity(flow_graph), + algorithm = flow_algorithm)[1] + (routes > λ) && return empty_flow(capacity_matrix, flow_algorithm) + + # For other cases, capacities need to be Floats + T = eltype(capacity_matrix) + if !(T<:AbstractFloat) + capacity_matrix = convert(AbstractMatrix{Float64}, capacity_matrix) + end + + # Ask for all possible values (breaking points) + (routes == 0) && return emrf(flow_graph, source, target, capacity_matrix, flow_algorithm) - # The number of routes is a float → EMRF - (R <: AbstractFloat) && + # The number of routes is a float → EMRF + (R <: AbstractFloat) && return emrf(flow_graph, source, target, capacity_matrix, flow_algorithm, routes) - - # Other calls - return multiroute_flow(flow_graph, source, target, capacity_matrix, - flow_algorithm, mrf_algorithm, routes) + + # Other calls + return multiroute_flow(flow_graph, source, target, capacity_matrix, + flow_algorithm, mrf_algorithm, routes) end diff --git a/src/generators/euclideangraphs.jl b/src/generators/euclideangraphs.jl index 30dd98fa0..c8d916a25 100644 --- a/src/generators/euclideangraphs.jl +++ b/src/generators/euclideangraphs.jl @@ -1,5 +1,5 @@ """ - euclidean_graph(points::Matrix, L=1., p=2., cutoff=-1., bc=:open) +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. @@ -13,7 +13,7 @@ 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. - 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 and Euclidean graph. @@ -24,14 +24,14 @@ the points' positions. function euclidean_graph end function euclidean_graph(N::Int, d::Int; - L=1., seed = -1, kws...) + L=1., seed = -1, kws...) rng = LightGraphs.getRNG(seed) points = scale!(rand(rng, d, N), L) return (euclidean_graph(points; L=L, kws...)..., points) end function euclidean_graph(points::Matrix; - L=1., p=2., cutoff=-1., bc=:open) + L=1., p=2., cutoff=-1., bc=:open) d, N = size(points) g = Graph(N) weights = Dict{Edge,Float64}() diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 2e0869378..2f02d4799 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -29,8 +29,8 @@ function DiGraph{T<:Integer}(nv::T, 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::Integer, p::Real; is_directed=false, seed=-1) +erdos_renyi(n::Integer, ne::Integer; is_directed=false, seed=-1) Creates 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 @@ -145,9 +145,9 @@ function _try_creation{T<:Integer}(n::T, k::Vector{T}, rng::AbstractRNG) end return edges end -# ================= STOPPED HERE SAB + """ - barabasi_albert(n::Integer, k::Integer; is_directed::Bool = false, complete::Bool = false, seed::Int = -1) +barabasi_albert(n::Integer, k::Integer; is_directed::Bool = false, complete::Bool = false, seed::Int = -1) Creates 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 @@ -157,10 +157,10 @@ Initial graphs are undirected and consist of isolated vertices by default; use `is_directed=true` and `complete=true` for directed and complete initial graphs. """ barabasi_albert(n::Integer, k::Integer; keyargs...) = - barabasi_albert(n, k, k; 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; is_directed::Bool = false, complete::Bool = false, seed::Int = -1) Creates 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 @@ -181,7 +181,7 @@ 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; seed::Int = -1) Creates 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 @@ -191,8 +191,8 @@ already present in the system by preferential attachment. 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")) + throw(ArgumentError("Barabási-Albert model requires 1 <= k <= n0 <= n" * + "where n0 is the number of nodes in graph g")) n0 == n && return g # seed random number generator @@ -257,7 +257,7 @@ end """ - static_fitness_model{T<:Real}(m::Int, fitness::Vector{T}; seed::Int=-1) +static_fitness_model{T<:Real}(m::Int, fitness::Vector{T}; seed::Int=-1) Generates a random graph with `length(fitness)` nodes and `m` edges, in which the probability of the existence of edge `(i, j)` is proportional @@ -329,7 +329,7 @@ function _create_static_fitness_graph!{T<:Real,S<:Real}(g::AbstractGraph, m::Int end """ - function static_scale_free(n::Int, m::Int, α::Float64; seed::Int=-1, finite_size_correction::Bool=true) +function static_scale_free(n::Int, m::Int, α::Float64; seed::Int=-1, finite_size_correction::Bool=true) Generates a random graph with `n` vertices, `m` edges and expected power-law degree distribution with exponent `α`. `finite_size_correction` determines @@ -381,7 +381,7 @@ function _construct_fitness(n::Int, α::Float64, finite_size_correction::Bool) end doc""" - random_regular_graph(n::Int, k::Int; seed=-1) +random_regular_graph(n::Int, k::Int; seed=-1) Creates a random undirected [regular graph](https://en.wikipedia.org/wiki/Regular_graph) with `n` vertices, @@ -416,9 +416,8 @@ function random_regular_graph(n::Int, k::Int; seed::Int=-1) return g end - doc""" - random_configuration_model(n::Int, k::Array{Int}; seed=-1, check_graphical=false) +random_configuration_model(n::Int, k::Array{Int}; seed=-1, check_graphical=false) Creates a random undirected graph according to the [configuration model] (http://tuvalu.santafe.edu/~aaronc/courses/5352/fall2013/csci5352_2013_L11.pdf). @@ -454,7 +453,7 @@ function random_configuration_model(n::Int, k::Array{Int}; seed::Int=-1, check_g end doc""" - random_regular_digraph(n::Int, k::Int; dir::Symbol=:out, seed=-1) +random_regular_digraph(n::Int, k::Int; dir::Symbol=:out, seed=-1) Creates a random directed [regular graph](https://en.wikipedia.org/wiki/Regular_graph) with `n` vertices, @@ -495,14 +494,14 @@ function random_regular_digraph(n::Int, k::Int; dir::Symbol=:out, seed::Int=-1) end doc""" - stochastic_block_model(c::Matrix{Float64}, n::Vector{Int}; seed::Int = -1) - stochastic_block_model(cin::Float64, coff::Float64, n::Vector{Int}; seed::Int = -1) +stochastic_block_model(c::Matrix{Float64}, n::Vector{Int}; seed::Int = -1) +stochastic_block_model(cin::Float64, coff::Float64, n::Vector{Int}; seed::Int = -1) Returns 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]$. +Only the upper triangular part is considered, since the lower traingular is +determined by $c[b,a] = c[a,b] * 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`. @@ -553,12 +552,12 @@ function stochastic_block_model{T<:Real}(cint::T, cext::T, n::Vector{Int}; seed: end """ - type StochasticBlockModel{T<:Integer,P<:Real} - n::T - nodemap::Array{T} - affinities::Matrix{P} - rng::MersenneTwister - end +type StochasticBlockModel{T<:Integer,P<:Real} +n::T +nodemap::Array{T} +affinities::Matrix{P} +rng::MersenneTwister +end A type capturing the parameters of the SBM. Each vertex is assigned to a block and the probability of edge `(i,j)` @@ -581,7 +580,7 @@ type StochasticBlockModel{T<:Integer,P<:Real} end ==(sbm::StochasticBlockModel, other::StochasticBlockModel) = - (sbm.n == other.n) && (sbm.nodemap == other.nodemap) && (sbm.affinities == other.affinities) +(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 @@ -612,17 +611,17 @@ function sbmaffinity(internalp::Vector{Float64}, externalp::Float64, sizes::Vect end function StochasticBlockModel(internalp::Float64, - externalp::Float64, - size::Int, - numblocks::Int; - seed::Int = -1) + externalp::Float64, + size::Int, + numblocks::Int; + seed::Int = -1) sizes = fill(size, numblocks) B = sbmaffinity(fill(internalp, numblocks), externalp, sizes) StochasticBlockModel(sizes, B, seed=seed) end function StochasticBlockModel(internalp::Vector{Float64}, externalp::Float64 - , sizes::Vector{Int}; seed::Int = -1) + , sizes::Vector{Int}; seed::Int = -1) B = sbmaffinity(internalp, externalp, sizes) return StochasticBlockModel(sizes, B, seed=seed) end @@ -635,8 +634,8 @@ 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` -The blocks are connected with probability `between`. + Each half is connected as a random bipartite graph with probability `intra` + The blocks are connected with probability `between`. """ function nearbipartiteaffinity(sizes::Vector{Int}, between::Float64, intra::Float64) numblocks = div(length(sizes), 2) @@ -664,7 +663,7 @@ end """ - make_edgestream(sbm::StochasticBlockModel) +make_edgestream(sbm::StochasticBlockModel) Take an infinite sample from the sbm. Pass to `Graph(nvg, neg, edgestream)` to get a Graph object. @@ -672,7 +671,7 @@ Pass to `Graph(nvg, neg, edgestream)` to get a Graph object. function make_edgestream(sbm::StochasticBlockModel) pairs = @task random_pair(sbm.rng, sbm.n) for (i,j) in pairs - if i == j + if i == j continue end p = sbm.affinities[sbm.nodemap[i], sbm.nodemap[j]] @@ -695,7 +694,7 @@ function Graph(nvg::Int, neg::Int, edgestream::Task) end Graph(nvg::Int, neg::Int, sbm::StochasticBlockModel) = - Graph(nvg, neg, @task make_edgestream(sbm)) +Graph(nvg, neg, @task make_edgestream(sbm)) """counts the number of edges that go between each block""" function blockcounts(sbm::StochasticBlockModel, A::AbstractMatrix) diff --git a/src/generators/smallgraphs.jl b/src/generators/smallgraphs.jl index 00b74c77b..6d725d651 100644 --- a/src/generators/smallgraphs.jl +++ b/src/generators/smallgraphs.jl @@ -19,8 +19,8 @@ function _make_simple_directed_graph{T<:Integer}(n::T, edgelist::Vector{Tuple{T, end doc""" - smallgraph(s::Symbol) - smallgraph(s::AbstractString) +smallgraph(s::Symbol) +smallgraph(s::AbstractString) Creates a small graph of type `s`. Admissible values for `s` are: @@ -48,7 +48,6 @@ Creates a small graph of type `s`. Admissible values for `s` are: | :truncatedtetrahedron | A skeleton of the [truncated tetrahedron graph](https://en.wikipedia.org/wiki/Truncated_tetrahedron). | | :truncatedtetrahedron_dir | A skeleton of the [truncated tetrahedron digraph](https://en.wikipedia.org/wiki/Truncated_tetrahedron). | | :tutte | A [Tutte graph](https://en.wikipedia.org/wiki/Tutte_graph). | - """ function smallgraph(s::Symbol) graphmap = Dict( @@ -93,25 +92,25 @@ end DiamondGraph() = - _make_simple_undirected_graph(4, [(1,2), (1,3), (2,3), (2,4), (3,4)]) +_make_simple_undirected_graph(4, [(1,2), (1,3), (2,3), (2,4), (3,4)]) BullGraph() = - _make_simple_undirected_graph(5, [(1,2), (1,3), (2,3), (2,4), (3,5)]) +_make_simple_undirected_graph(5, [(1,2), (1,3), (2,3), (2,4), (3,5)]) function ChvatalGraph() e = [ - (1, 2), (1, 5), (1, 7), (1, 10), - (2, 3), (2, 6), (2, 8), - (3, 4), (3, 7), (3, 9), - (4, 5), (4, 8), (4, 10), - (5, 6), (5, 9), - (6, 11), (6, 12), - (7, 11), (7, 12), - (8, 9), (8, 12), - (9, 11), - (10, 11), (10, 12) + (1, 2), (1, 5), (1, 7), (1, 10), + (2, 3), (2, 6), (2, 8), + (3, 4), (3, 7), (3, 9), + (4, 5), (4, 8), (4, 10), + (5, 6), (5, 9), + (6, 11), (6, 12), + (7, 11), (7, 12), + (8, 9), (8, 12), + (9, 11), + (10, 11), (10, 12) ] return _make_simple_undirected_graph(12,e) end @@ -119,12 +118,12 @@ end function CubicalGraph() e = [ - (1, 2), (1, 4), (1, 5), - (2, 3), (2, 8), - (3, 4), (3, 7), - (4, 6), (5, 6), (5, 8), - (6, 7), - (7, 8) + (1, 2), (1, 4), (1, 5), + (2, 3), (2, 8), + (3, 4), (3, 7), + (4, 6), (5, 6), (5, 8), + (6, 7), + (7, 8) ] return _make_simple_undirected_graph(8,e) end @@ -132,25 +131,25 @@ end function DesarguesGraph() e = [ - (1, 2), (1, 6), (1, 20), - (2, 3), (2, 17), - (3, 4), (3, 12), - (4, 5), (4, 15), - (5, 6), (5, 10), - (6, 7), - (7, 8), (7, 16), - (8, 9), (8, 19), - (9, 10), (9, 14), - (10, 11), - (11, 12), (11, 20), - (12, 13), - (13, 14), (13, 18), - (14, 15), - (15, 16), - (16, 17), - (17, 18), - (18, 19), - (19, 20) + (1, 2), (1, 6), (1, 20), + (2, 3), (2, 17), + (3, 4), (3, 12), + (4, 5), (4, 15), + (5, 6), (5, 10), + (6, 7), + (7, 8), (7, 16), + (8, 9), (8, 19), + (9, 10), (9, 14), + (10, 11), + (11, 12), (11, 20), + (12, 13), + (13, 14), (13, 18), + (14, 15), + (15, 16), + (16, 17), + (17, 18), + (18, 19), + (19, 20) ] return _make_simple_undirected_graph(20,e) end @@ -158,25 +157,25 @@ end function DodecahedralGraph() e = [ - (1, 2), (1, 11), (1, 20), - (2, 3), (2, 9), - (3, 4), (3, 7), - (4, 5), (4, 20), - (5, 6), (5, 18), - (6, 7), (6, 16), - (7, 8), - (8, 9), (8, 15), - (9, 10), - (10, 11), (10, 14), - (11, 12), - (12, 13), (12, 19), - (13, 14), (13, 17), - (14, 15), - (15, 16), - (16, 17), - (17, 18), - (18, 19), - (19, 20) + (1, 2), (1, 11), (1, 20), + (2, 3), (2, 9), + (3, 4), (3, 7), + (4, 5), (4, 20), + (5, 6), (5, 18), + (6, 7), (6, 16), + (7, 8), + (8, 9), (8, 15), + (9, 10), + (10, 11), (10, 14), + (11, 12), + (12, 13), (12, 19), + (13, 14), (13, 17), + (14, 15), + (15, 16), + (16, 17), + (17, 18), + (18, 19), + (19, 20) ] return _make_simple_undirected_graph(20,e) end @@ -184,16 +183,16 @@ end function FruchtGraph() e = [ - (1, 2), (1, 7), (1, 8), - (2, 3), (2, 8), - (3, 4), (3, 9), - (4, 5), (4, 10), - (5, 6), (5, 10), - (6, 7), (6, 11), - (7, 11), - (8, 12), - (9, 10), (9, 12), - (11, 12) + (1, 2), (1, 7), (1, 8), + (2, 3), (2, 8), + (3, 4), (3, 9), + (4, 5), (4, 10), + (5, 6), (5, 10), + (6, 7), (6, 11), + (7, 11), + (8, 12), + (9, 10), (9, 12), + (11, 12) ] return _make_simple_undirected_graph(20,e) end @@ -235,15 +234,15 @@ end function IcosahedralGraph() e = [ - (1, 2), (1, 6), (1, 8), (1, 9), (1, 12), - (2, 3), (2, 6), (2, 7), (2, 9), - (3, 4), (3, 7), (3, 9), (3, 10), - (4, 5), (4, 7), (4, 10), (4, 11), - (5, 6), (5, 7), (5, 11), (5, 12), - (6, 7), (6, 12), - (8, 9), (8, 10), (8, 11), (8, 12), - (9, 10), - (10, 11), (11, 12) + (1, 2), (1, 6), (1, 8), (1, 9), (1, 12), + (2, 3), (2, 6), (2, 7), (2, 9), + (3, 4), (3, 7), (3, 9), (3, 10), + (4, 5), (4, 7), (4, 10), (4, 11), + (5, 6), (5, 7), (5, 11), (5, 12), + (6, 7), (6, 12), + (8, 9), (8, 10), (8, 11), (8, 12), + (9, 10), + (10, 11), (11, 12) ] return _make_simple_undirected_graph(12, e) end @@ -251,15 +250,15 @@ end function KrackhardtKiteGraph() e = [ - (1, 2), (1, 3), (1, 4), (1, 6), - (2, 4), (2, 5), (2, 7), - (3, 4), (3, 6), - (4, 5), (4, 6), (4, 7), - (5, 7), - (6, 7), (6, 8), - (7, 8), - (8, 9), - (9, 10) + (1, 2), (1, 3), (1, 4), (1, 6), + (2, 4), (2, 5), (2, 7), + (3, 4), (3, 6), + (4, 5), (4, 6), (4, 7), + (5, 7), + (6, 7), (6, 8), + (7, 8), + (8, 9), + (9, 10) ] return _make_simple_undirected_graph(10,e) end @@ -267,21 +266,21 @@ end function MoebiusKantorGraph() e = [ - (1, 2), (1, 6), (1, 16), - (2, 3), (2, 13), - (3, 4), (3, 8), - (4, 5), (4, 15), - (5, 6), (5, 10), - (6, 7), - (7, 8), (7, 12), - (8, 9), - (9, 10), (9, 14), - (10, 11), - (11, 12), (11, 16), - (12, 13), - (13, 14), - (14, 15), - (15, 16) + (1, 2), (1, 6), (1, 16), + (2, 3), (2, 13), + (3, 4), (3, 8), + (4, 5), (4, 15), + (5, 6), (5, 10), + (6, 7), + (7, 8), (7, 12), + (8, 9), + (9, 10), (9, 14), + (10, 11), + (11, 12), (11, 16), + (12, 13), + (13, 14), + (14, 15), + (15, 16) ] return _make_simple_undirected_graph(16,e) end @@ -289,11 +288,11 @@ end function OctahedralGraph() e = [ - (1, 2), (1, 3), (1, 4), (1, 5), - (2, 3), (2, 4), (2, 6), - (3, 5), (3, 6), - (4, 5), (4, 6), - (5, 6) + (1, 2), (1, 3), (1, 4), (1, 5), + (2, 3), (2, 4), (2, 6), + (3, 5), (3, 6), + (4, 5), (4, 6), + (5, 6) ] return _make_simple_undirected_graph(6,e) end @@ -301,23 +300,23 @@ end function PappusGraph() e = [ - (1, 2), (1, 6), (1, 18), - (2, 3), (2, 9), - (3, 4), (3, 14), - (4, 5), (4, 11), - (5, 6), (5, 16), - (6, 7), - (7, 8), (7, 12), - (8, 9), (8, 15), - (9, 10), - (10, 11), (10, 17), - (11, 12), - (12, 13), - (13, 14), (13, 18), - (14, 15), - (15, 16), - (16, 17), - (17, 18) + (1, 2), (1, 6), (1, 18), + (2, 3), (2, 9), + (3, 4), (3, 14), + (4, 5), (4, 11), + (5, 6), (5, 16), + (6, 7), + (7, 8), (7, 12), + (8, 9), (8, 15), + (9, 10), + (10, 11), (10, 17), + (11, 12), + (12, 13), + (13, 14), (13, 18), + (14, 15), + (15, 16), + (16, 17), + (17, 18) ] return _make_simple_undirected_graph(18,e) end @@ -325,59 +324,59 @@ end function PetersenGraph() e = [ - (1, 2), (1, 5), (1, 6), - (2, 3), (2, 7), - (3, 4), (3, 8), - (4, 5), (4, 9), - (5, 10), - (6, 8), (6, 9), - (7, 9), (7, 10), - (8, 10) + (1, 2), (1, 5), (1, 6), + (2, 3), (2, 7), + (3, 4), (3, 8), + (4, 5), (4, 9), + (5, 10), + (6, 8), (6, 9), + (7, 9), (7, 10), + (8, 10) ] return _make_simple_undirected_graph(10,e) end function SedgewickMazeGraph() e = [ - (1, 3), - (1, 6), (1, 8), - (2, 8), - (3, 7), - (4, 5), (4, 6), - (5, 6), (5, 7), (5, 8) + (1, 3), + (1, 6), (1, 8), + (2, 8), + (3, 7), + (4, 5), (4, 6), + (5, 6), (5, 7), (5, 8) ] return _make_simple_undirected_graph(8,e) end TetrahedralGraph() = - _make_simple_undirected_graph(4, [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]) +_make_simple_undirected_graph(4, [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]) function TruncatedCubeGraph() e = [ - (1, 2), (1, 3), (1, 5), - (2, 12), (2, 15), - (3, 4), (3, 5), - (4, 7), (4, 9), - (5, 6), - (6, 17), (6, 19), - (7, 8), (7, 9), - (8, 11), (8, 13), - (9, 10), - (10, 18), (10, 21), - (11, 12), (11, 13), - (12, 15), (13, 14), - (14, 22), (14, 23), - (15, 16), - (16, 20), (16, 24), - (17, 18), (17, 19), - (18, 21), - (19, 20), - (20, 24), - (21, 22), - (22, 23), - (23, 24) + (1, 2), (1, 3), (1, 5), + (2, 12), (2, 15), + (3, 4), (3, 5), + (4, 7), (4, 9), + (5, 6), + (6, 17), (6, 19), + (7, 8), (7, 9), + (8, 11), (8, 13), + (9, 10), + (10, 18), (10, 21), + (11, 12), (11, 13), + (12, 15), (13, 14), + (14, 22), (14, 23), + (15, 16), + (16, 20), (16, 24), + (17, 18), (17, 19), + (18, 21), + (19, 20), + (20, 24), + (21, 22), + (22, 23), + (23, 24) ] return _make_simple_undirected_graph(24,e) end @@ -385,17 +384,17 @@ end function TruncatedTetrahedronGraph() e = [ - (1, 2),(1, 3),(1, 10), - (2, 3),(2, 7), - (3, 4), - (4, 5),(4, 12), - (5, 6),(5, 12), - (6, 7),(6, 8), - (7, 8), - (8, 9), - (9, 10),(9, 11), - (10, 11), - (11, 12) + (1, 2),(1, 3),(1, 10), + (2, 3),(2, 7), + (3, 4), + (4, 5),(4, 12), + (5, 6),(5, 12), + (6, 7),(6, 8), + (7, 8), + (8, 9), + (9, 10),(9, 11), + (10, 11), + (11, 12) ] return _make_simple_undirected_graph(12,e) end @@ -403,17 +402,17 @@ end function TruncatedTetrahedronDiGraph() e = [ - (1, 2),(1, 3),(1, 10), - (2, 3),(2, 7), - (3, 4), - (4, 5),(4, 12), - (5, 6),(5, 12), - (6, 7),(6, 8), - (7, 8), - (8, 9), - (9, 10),(9, 11), - (10, 11), - (11, 12) + (1, 2),(1, 3),(1, 10), + (2, 3),(2, 7), + (3, 4), + (4, 5),(4, 12), + (5, 6),(5, 12), + (6, 7),(6, 8), + (7, 8), + (8, 9), + (9, 10),(9, 11), + (10, 11), + (11, 12) ] return _make_simple_directed_graph(12,e) end diff --git a/src/generators/staticgraphs.jl b/src/generators/staticgraphs.jl index 5bb241a59..260de7889 100644 --- a/src/generators/staticgraphs.jl +++ b/src/generators/staticgraphs.jl @@ -1,7 +1,8 @@ # Parts of this code were taken / derived from NetworkX. See LICENSE for # licensing details. -"""Creates a complete graph with `n` vertices. A complete graph has edges +""" +Creates a complete graph with `n` vertices. A complete graph has edges connecting each pair of vertices. """ function CompleteGraph(n::Integer) @@ -133,7 +134,7 @@ function WheelDiGraph(n::Integer) end """ - Grid{T<:Integer}(dims::AbstractVector{T}; periodic=false) +Grid{T<:Integer}(dims::AbstractVector{T}; periodic=false) 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. @@ -159,8 +160,10 @@ function BinaryTree(levels::Int) return g end -"""create a double complete binary tree with k-levels -used as an example for spectral clustering by Guattery and Miller 1998.""" +""" +Create a double complete binary tree with k-levels +used as an example for spectral clustering by Guattery and Miller 1998. +""" function DoubleBinaryTree(levels::Int) gl = BinaryTree(levels) gr = BinaryTree(levels) diff --git a/src/graphtypes/simplegraph/SimpleGraphs.jl b/src/graphtypes/simplegraph/SimpleGraphs.jl index d78e9c262..67ce45c95 100644 --- a/src/graphtypes/simplegraph/SimpleGraphs.jl +++ b/src/graphtypes/simplegraph/SimpleGraphs.jl @@ -1,20 +1,19 @@ module SimpleGraphs import Base: - eltype, show, ==, Pair, Tuple, copy, length, start, next, done, issubset + eltype, show, ==, Pair, Tuple, copy, length, start, next, done, issubset import LightGraphs: - _NI, _insert_and_dedup!, AbstractGraph, AbstractEdge, AbstractEdgeIter, - src, dst, edgetype, nv, ne, vertices, edges, is_directed, - add_vertex!, add_edge!, rem_vertex!, rem_edge!, - has_vertex, has_edge, in_neighbors, out_neighbors, + _NI, _insert_and_dedup!, AbstractGraph, AbstractEdge, AbstractEdgeIter, + src, dst, edgetype, nv, ne, vertices, edges, is_directed, + add_vertex!, add_edge!, rem_vertex!, rem_edge!, + has_vertex, has_edge, in_neighbors, out_neighbors, - indegree, outdegree, degree, has_self_loops, num_self_loops, empty + indegree, outdegree, degree, has_self_loops, num_self_loops, empty export AbstractSimpleGraph, AbstractSimpleDiGraph, AbstractSimpleEdge, -SimpleEdge, SimpleGraph, SimpleGraphEdge, -SimpleDiGraph, SimpleDiGraphEdge, -fadj, badj, adj + SimpleEdge, SimpleGraph, SimpleGraphEdge, + SimpleDiGraph, SimpleDiGraphEdge """ @@ -28,15 +27,15 @@ abstract AbstractSimpleGraph <: AbstractGraph function show(io::IO, g::AbstractSimpleGraph) - if is_directed(g) - dir = "directed" - else - dir = "undirected" - end - if nv(g) == 0 - print(io, "empty $dir simple graph") - else - print(io, "{$(nv(g)), $(ne(g))} $dir simple graph") + if is_directed(g) + dir = "directed" + else + dir = "undirected" + end + if nv(g) == 0 + print(io, "empty $dir simple graph") + else + print(io, "{$(nv(g)), $(ne(g))} $dir simple graph") end end @@ -52,8 +51,8 @@ badj(x...) = _NI("badj") has_edge(g::AbstractSimpleGraph, u::Integer, v::Integer) = has_edge(g, edgetype(g)(u,v)) function add_edge!(g::AbstractSimpleGraph, u::Integer, v::Integer) - T = eltype(g) - add_edge!(g, edgetype(g)(T(u),T(v))) + T = eltype(g) + add_edge!(g, edgetype(g)(T(u),T(v))) end in_neighbors(g::AbstractSimpleGraph, v::Integer) = badj(g,v) @@ -66,14 +65,12 @@ function issubset{T<:AbstractSimpleGraph}(g::T, h::T) return (hmin <= gmin <= gmax <= hmax) && issubset(edges(g), edges(h)) end -in_edges(g::AbstractSimpleGraph, v::Integer) = [edgetype(g)(x,v) for x in in_neighbors(g, v)] -out_edges(g::AbstractSimpleGraph, v::Integer) = [edgetype(g)(v,x) for x in out_neighbors(g, v)] has_vertex(g::AbstractSimpleGraph, v::Integer) = v in vertices(g) ne(g::AbstractSimpleGraph) = g.ne function rem_edge!(g::AbstractSimpleGraph, u::Integer, v::Integer) - T = eltype(g) - rem_edge!(g, edgetype(g)(T(u), T(v))) + T = eltype(g) + rem_edge!(g, edgetype(g)(T(u), T(v))) end """ @@ -89,32 +86,38 @@ function rem_vertex!(g::AbstractSimpleGraph, v::Integer) v in vertices(g) || return false n = nv(g) - edgs = in_edges(g, v) - for e in edgs - rem_edge!(g, e) + # remove the in_edges from v + srcs = copy(in_neighbors(g, v)) + for s in srcs + rem_edge!(g, edgetype(g)(s, v)) end + # remove the in_edges from the last vertex neigs = copy(in_neighbors(g, n)) - for i in neigs - rem_edge!(g, edgetype(g)(i, n)) + for s in neigs + rem_edge!(g, edgetype(g)(s, n)) end if v != n - for i in neigs - add_edge!(g, edgetype(g)(i, v)) + # add the edges from n back to v + for s in neigs + add_edge!(g, edgetype(g)(s, v)) end end if is_directed(g) - edgs = out_edges(g, v) - for e in edgs - rem_edge!(g, e) + # remove the out_edges from v + dsts = copy(out_neighbors(g, v)) + for d in dsts + rem_edge!(g, edgetype(g)(v, d)) end + # remove the out_edges from the last vertex neigs = copy(out_neighbors(g, n)) - for i in neigs - rem_edge!(g, edgetype(g)(n, i)) + for d in neigs + rem_edge!(g, edgetype(g)(n, d)) end if v != n - for i in neigs - add_edge!(g, edgetype(g)(v, i)) + # add the out_edges back to v + for d in neigs + add_edge!(g, edgetype(g)(v, d)) end end end @@ -126,7 +129,7 @@ function rem_vertex!(g::AbstractSimpleGraph, v::Integer) end return true end -empty{T<:AbstractGraph}(g::T) = T() +empty{T<:AbstractSimpleGraph}(g::T) = T() include("simpleedge.jl") include("simpledigraph.jl") diff --git a/src/graphtypes/simplegraph/simpledigraph.jl b/src/graphtypes/simplegraph/simpledigraph.jl index d03e4cacb..8e5a60f2e 100644 --- a/src/graphtypes/simplegraph/simpledigraph.jl +++ b/src/graphtypes/simplegraph/simpledigraph.jl @@ -13,14 +13,14 @@ eltype{T<:Integer}(x::SimpleDiGraph{T}) = T # DiGraph{UInt8}(6), DiGraph{Int16}(7), DiGraph{Int8}() function (::Type{SimpleDiGraph{T}}){T<:Integer}(n::Integer = 0) - fadjlist = Vector{Vector{T}}() - badjlist = Vector{Vector{T}}() - for _ = one(T):n - push!(badjlist, Vector{T}()) - push!(fadjlist, Vector{T}()) - end - vertices = one(T):T(n) - return SimpleDiGraph(vertices, 0, fadjlist, badjlist) + fadjlist = Vector{Vector{T}}() + badjlist = Vector{Vector{T}}() + for _ = one(T):n + push!(badjlist, Vector{T}()) + push!(fadjlist, Vector{T}()) + end + vertices = one(T):T(n) + return SimpleDiGraph(vertices, 0, fadjlist, badjlist) end # DiGraph() @@ -69,10 +69,10 @@ SimpleDiGraph(adjmx::AbstractMatrix) = SimpleDiGraph{Int}(adjmx) # converts DiGraph{Int} to DiGraph{Int32} function (::Type{SimpleDiGraph{T}}){T<:Integer}(g::SimpleDiGraph) - h_vertices = one(T):T(nv(g)) - h_fadj = [Vector{T}(x) for x in fadj(g)] - h_badj = [Vector{T}(x) for x in badj(g)] - return SimpleDiGraph(h_vertices, ne(g), h_fadj, h_badj) + h_vertices = one(T):T(nv(g)) + h_fadj = [Vector{T}(x) for x in fadj(g)] + h_badj = [Vector{T}(x) for x in badj(g)] + return SimpleDiGraph(h_vertices, ne(g), h_fadj, h_badj) end @@ -93,21 +93,22 @@ badj(g::SimpleDiGraph, v::Integer) = badj(g)[v] copy{T<:Integer}(g::SimpleDiGraph{T}) = - SimpleDiGraph{T}(g.vertices, g.ne, deepcopy(g.fadjlist), deepcopy(g.badjlist)) +SimpleDiGraph{T}(g.vertices, g.ne, deepcopy(g.fadjlist), deepcopy(g.badjlist)) ==(g::SimpleDiGraph, h::SimpleDiGraph) = - vertices(g) == vertices(h) && - ne(g) == ne(h) && - fadj(g) == fadj(h) && - badj(g) == badj(h) +vertices(g) == vertices(h) && +ne(g) == ne(h) && +fadj(g) == fadj(h) && +badj(g) == badj(h) is_directed(g::SimpleDiGraph) = true is_directed(::Type{SimpleDiGraph}) = true is_directed{T}(::Type{SimpleDiGraph{T}}) = true -function add_edge!{T<:Integer}(g::SimpleDiGraph{T}, e::SimpleDiGraphEdge) - s, d = Tuple(e) +function add_edge!(g::SimpleDiGraph, e::SimpleDiGraphEdge) + T = eltype(g) + s, d = T.(Tuple(e)) (s in vertices(g) && d in vertices(g)) || return false inserted = _insert_and_dedup!(g.fadjlist[s], d) if inserted diff --git a/src/graphtypes/simplegraph/simpleedge.jl b/src/graphtypes/simplegraph/simpleedge.jl index 20bbe1853..23f28f1a7 100644 --- a/src/graphtypes/simplegraph/simpleedge.jl +++ b/src/graphtypes/simplegraph/simpleedge.jl @@ -11,7 +11,7 @@ end SimpleEdge(t::Tuple) = SimpleEdge(t[1], t[2]) SimpleEdge(p::Pair) = SimpleEdge(p.first, p.second) -eltype{T<:AbstractSimpleEdge}(e::T) = T +eltype{T<:AbstractSimpleEdge}(e::T) = eltype(src(e)) # Accessors src(e::AbstractSimpleEdge) = e.src @@ -24,6 +24,8 @@ show(io::IO, e::AbstractSimpleEdge) = print(io, "Edge $(e.src) => $(e.dst)") Pair(e::AbstractSimpleEdge) = Pair(src(e), dst(e)) Tuple(e::AbstractSimpleEdge) = (src(e), dst(e)) +(::Type{SimpleEdge{T}}){T<:Integer}(e::AbstractSimpleEdge) = SimpleEdge{T}(T(e.src), T(e.dst)) + # Convenience functions reverse{T<:AbstractSimpleEdge}(e::T) = T(dst(e), src(e)) ==(e1::AbstractSimpleEdge, e2::AbstractSimpleEdge) = (src(e1) == src(e2) && dst(e1) == dst(e2)) diff --git a/src/graphtypes/simplegraph/simplegraph.jl b/src/graphtypes/simplegraph/simplegraph.jl index 31921c2a1..3755b8c4d 100644 --- a/src/graphtypes/simplegraph/simplegraph.jl +++ b/src/graphtypes/simplegraph/simplegraph.jl @@ -45,9 +45,9 @@ end # converts Graph{Int} to Graph{Int32} function (::Type{SimpleGraph{T}}){T<:Integer}(g::SimpleGraph) - h_vertices = one(T):T(nv(g)) - h_fadj = [Vector{T}(x) for x in fadj(g)] - return SimpleGraph(h_vertices, ne(g), h_fadj) + h_vertices = one(T):T(nv(g)) + h_fadj = [Vector{T}(x) for x in fadj(g)] + return SimpleGraph(h_vertices, ne(g), h_fadj) end @@ -77,7 +77,8 @@ end edgetype{T<:Integer}(::SimpleGraph{T}) = SimpleGraphEdge{T} -"""Returns the backwards adjacency list of a graph. +""" +Returns the backwards adjacency list of a graph. For each vertex the Array of `dst` for each edge eminating from that vertex. NOTE: returns a reference, not a copy. Do not modify result. @@ -97,9 +98,9 @@ adj(g::SimpleGraph, v::Integer) = fadj(g, v) copy(g::SimpleGraph) = SimpleGraph(g.vertices, g.ne, deepcopy(g.fadjlist)) ==(g::SimpleGraph, h::SimpleGraph) = - vertices(g) == vertices(h) && - ne(g) == ne(h) && - fadj(g) == fadj(h) +vertices(g) == vertices(h) && +ne(g) == ne(h) && +fadj(g) == fadj(h) """Return `true` if `g` is a directed graph.""" @@ -117,8 +118,8 @@ function has_edge(g::SimpleGraph, e::SimpleGraphEdge) end function add_edge!(g::SimpleGraph, e::SimpleGraphEdge) - - s, d = Tuple(e) + T = eltype(g) + s, d = T.(Tuple(e)) (s in vertices(g) && d in vertices(g)) || return false inserted = _insert_and_dedup!(g.fadjlist[s], d) if inserted diff --git a/src/linalg/spectral.jl b/src/linalg/spectral.jl index 9c64189b7..90ab3d54c 100644 --- a/src/linalg/spectral.jl +++ b/src/linalg/spectral.jl @@ -1,12 +1,13 @@ import Base: * export adjacency_matrix, - laplacian_matrix, - incidence_matrix, - coo_sparse, - spectral_distance +laplacian_matrix, +incidence_matrix, +coo_sparse, +spectral_distance -"""Returns a sparse boolean adjacency matrix for a graph, indexed by `[u, v]` +""" +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 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 @@ -61,23 +62,19 @@ end adjacency_matrix(g::AbstractGraph, T::DataType) = adjacency_matrix(g, :out, T) -### TODO: re-roll this to be -# function adjacency_matrix(g::AbstractGraph, dir::Symbol=:out, T::DataType=Int) -# @traitfn adjacency_matrix{G<:AbstractGraph; !IsDirected{G}}(g::G, T::DataType) = adjacency_matrix(g, :out, T) -# @traitfn adjacency_matrix{G<:AbstractGraph; !IsDirected{G}}(g::G) = adjacency_matrix(g, :out, Int) - -"""Returns a sparse [Laplacian matrix](https://en.wikipedia.org/wiki/Laplacian_matrix) +""" +Returns 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. """ function laplacian_matrix(g::AbstractGraph, dir::Symbol=:unspec, T::DataType=Int) - if dir == :unspec - dir = is_directed(g)? :both : :out - end - A = adjacency_matrix(g, dir, T) - D = spdiagm(sum(A,2)[:]) - return D - A + if dir == :unspec + dir = is_directed(g)? :both : :out + end + A = adjacency_matrix(g, dir, T) + D = spdiagm(sum(A,2)[:]) + return D - A end doc"""Returns the eigenvalues of the Laplacian matrix for a graph `g`, indexed @@ -94,17 +91,12 @@ by vertex. Warning: Converts the matrix to dense with $nv^2$ memory usage. Use eigenvalues/eigenvectors. Default values for `dir` and `T` are the same as `adjacency_matrix`. """ -function adjacency_spectrum end - -#TODO: Re-roll this to adjacency_spectrum(g::Graph, dir::Symbol=:out, T::DataType=Int) = eigvals(full(adjacency_matrix(g, dir, T))) -@traitfn adjacency_spectrum{G<:AbstractGraph; !IsDirected{G}}(g::G, dir::Symbol, T::DataType) = eigvals(full(adjacency_matrix(g, dir, T))) -@traitfn adjacency_spectrum{G<:AbstractGraph; !IsDirected{G}}(g::G, dir::Symbol) = adjacency_spectrum(g, dir, Int) -@traitfn adjacency_spectrum{G<:AbstractGraph; !IsDirected{G}}(g::G) = adjacency_spectrum(g, :out, Int) - -#TODO: Re-roll this to adjacency_spectrum(g::DiGraph, dir::Symbol=:both, T::DataType=Int) = eigvals(full(adjacency_matrix(g, dir, T))) -@traitfn adjacency_spectrum{G<:AbstractGraph; IsDirected{G}}(g::G, dir::Symbol, T::DataType) = eigvals(full(adjacency_matrix(g, dir, T))) -@traitfn adjacency_spectrum{G<:AbstractGraph; IsDirected{G}}(g::G, dir::Symbol) = adjacency_spectrum(g, dir, Int) -@traitfn adjacency_spectrum{G<:AbstractGraph; IsDirected{G}}(g::G,) = adjacency_spectrum(g, :both, Int) +function adjacency_spectrum(g::AbstractGraph, dir::Symbol=:unspec, T::DataType=Int) + if dir == :unspec + dir = is_directed(g)? :both : :out + end + return eigvals(full(adjacency_matrix(g, dir, T))) +end """Returns 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 @@ -140,7 +132,8 @@ function incidence_matrix(g::AbstractGraph, T::DataType=Int; oriented=false) return spmx end -"""spectral_distance(G₁, G₂ [, k]) +""" +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. @@ -153,16 +146,16 @@ Graphs Based on their Different Matrix Representations function spectral_distance end @traitfn function spectral_distance{G<:AbstractGraph; !IsDirected{G}}(G₁::G, G₂::G, k::Integer) - A₁ = adjacency_matrix(G₁) - A₂ = adjacency_matrix(G₂) + A₁ = adjacency_matrix(G₁) + A₂ = adjacency_matrix(G₂) - λ₁ = k < nv(G₁)-1 ? eigs(A₁, nev=k, which=:LR)[1] : eigvals(full(A₁))[end:-1:end-(k-1)] - λ₂ = k < nv(G₂)-1 ? eigs(A₂, nev=k, which=:LR)[1] : eigvals(full(A₂))[end:-1:end-(k-1)] + λ₁ = k < nv(G₁)-1 ? eigs(A₁, nev=k, which=:LR)[1] : eigvals(full(A₁))[end:-1:end-(k-1)] + λ₂ = k < nv(G₂)-1 ? eigs(A₂, nev=k, which=:LR)[1] : eigvals(full(A₂))[end:-1:end-(k-1)] - sumabs(λ₁ - λ₂) + sumabs(λ₁ - λ₂) end @traitfn function spectral_distance{G<:AbstractGraph; !IsDirected{G}}(G₁::G, G₂::G) - @assert nv(G₁) == nv(G₂) "spectral distance not defined for |G₁| != |G₂|" - spectral_distance(G₁, G₂, nv(G₁)) + @assert nv(G₁) == nv(G₂) "spectral distance not defined for |G₁| != |G₂|" + spectral_distance(G₁, G₂, nv(G₁)) end diff --git a/src/operators.jl b/src/operators.jl index 45ff4d466..65e1eddf9 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -3,6 +3,7 @@ Produces the [graph complement](https://en.wikipedia.org/wiki/Complement_graph) of a graph. +Preserves the eltype of the input graph. """ function complement(g::Graph) gnv = nv(g) @@ -33,6 +34,7 @@ end Produces a graph where all edges are reversed from the original. +Preserves the eltype of the input graph. """ function reverse(g::DiGraph) gnv = nv(g) @@ -63,6 +65,8 @@ Produces a graph with $|V(g)| + |V(h)|$ vertices and $|E(g)| + |E(h)|$ edges. Put simply, the vertices and edges from graph `h` are appended to graph `g`. +Preserves the eltype of the input graph. Will error if the +number of vertices in the generated graph exceeds the eltype. """ function blkdiag{T<:AbstractGraph}(g::T, h::T) gnv = nv(g) @@ -82,6 +86,7 @@ end Produces 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. +Preserves the eltype of the input graph. """ function intersect{T<:AbstractGraph}(g::T, h::T) gnv = nv(g) @@ -100,6 +105,7 @@ end Produces a graph with edges in graph `g` that are not in graph `h`. Note that this function may produce a graph with 0-degree vertices. +Preserves the eltype of the input graph. """ function difference{T<:AbstractGraph}(g::T, h::T) gnv = nv(g) @@ -119,6 +125,8 @@ Produces a graph with edges from graph `g` that do not exist in graph `h`, and vice versa. 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. """ function symmetric_difference{T<:AbstractGraph}(g::T, h::T) gnv = nv(g) @@ -138,6 +146,8 @@ end union(g, h) Merges graphs `g` and `h` by taking the set union of all vertices and edges. +Preserves the eltype of the input graph. Will error if the +number of vertices in the generated graph exceeds the eltype. """ function union{T<:AbstractGraph}(g::T, h::T) gnv = nv(g) @@ -162,9 +172,11 @@ 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`. +the vertices in `g` and those in `h`. +Preserves the eltype of the input graph. Will error if the number of vertices +in the generated graph exceeds the eltype. """ -function join(g::Graph, h::Graph) +function join{T<:AbstractGraph}(g::T, h::T) r = blkdiag(g, h) for i in vertices(g) for j=nv(g)+1:nv(g)+nv(h) @@ -178,10 +190,17 @@ end """ crosspath(len::Integer, g::Graph) -Replicate `len` times `h` and connect each vertex with its copies in a path +Replicate `len` times `g` and connect each vertex with its copies in a path. +Preserves the eltype of the input graph. Will error if the number of vertices +in the generated graph exceeds the eltype. """ -crosspath(len::Integer, g::Graph) = cartesian_product(PathGraph(len), g) - +function crosspath end +@traitfn function crosspath{G<:AbstractGraph; !IsDirected{G}}(len::Integer, g::G) + T = eltype(g) + p = PathGraph(len) + h = Graph{T}(p) + return cartesian_product(h, g) +end # The following operators allow one to use a LightGraphs.Graph as a matrix in eigensolvers for spectral ranking and partitioning. # """Provides multiplication of a graph `g` by a vector `v` such that spectral @@ -238,6 +257,8 @@ 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` +Preserves the eltype of the input graph. Will error if the number of vertices +in the generated graph exceeds the eltype. """ function cartesian_product{G<:AbstractGraph}(g::G, h::G) z = G(nv(g)*nv(h)) @@ -262,6 +283,8 @@ end tensor_product(g, h) Returns the (tensor product)[https://en.wikipedia.org/wiki/Tensor_product_of_graphs] of `g` and `h` +Preserves the eltype of the input graph. Will error if the number of vertices +in the generated graph exceeds the eltype. """ function tensor_product{G<:AbstractGraph}(g::G, h::G) z = G(nv(g)*nv(h)) diff --git a/src/shortestpaths/astar.jl b/src/shortestpaths/astar.jl index 6789f5e4b..dce13e7d5 100644 --- a/src/shortestpaths/astar.jl +++ b/src/shortestpaths/astar.jl @@ -27,8 +27,8 @@ function a_star_impl!{T<:Number}( new_path = cat(1, path, Edge(u,v)) path_cost = cost_so_far + dist enqueue!(frontier, - (path_cost, new_path, v), - path_cost + heuristic(v)) + (path_cost, new_path, v), + path_cost + heuristic(v)) end end colormap[u] = 2 @@ -36,7 +36,8 @@ function a_star_impl!{T<:Number}( nothing end -"""Computes the shortest path between vertices `s` and `t` using the +""" +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. """ @@ -48,7 +49,7 @@ function a_star{T<:Number}( distmx::AbstractMatrix{T} = LightGraphs.DefaultDistance(), heuristic::Function = n -> 0 ) - # heuristic (under)estimating distance to target + # heuristic (under)estimating distance to target U = eltype(graph) frontier = PriorityQueue(Tuple{T,Vector{Edge},U},T) frontier[(zero(T), Vector{Edge}(), s)] = zero(T) diff --git a/src/shortestpaths/bellman-ford.jl b/src/shortestpaths/bellman-ford.jl index 31f8bab01..ab5b1b09a 100644 --- a/src/shortestpaths/bellman-ford.jl +++ b/src/shortestpaths/bellman-ford.jl @@ -22,7 +22,7 @@ function bellman_ford_shortest_paths!{R<:Real, T<:Integer}( sources::AbstractVector{T}, distmx::AbstractMatrix{R}, state::BellmanFordState -) + ) active = Set{T}() for v in sources @@ -71,9 +71,9 @@ function bellman_ford_shortest_paths{T, U<:Integer}( end bellman_ford_shortest_paths{T}( - graph::AbstractGraph, - v::Integer, - distmx::AbstractMatrix{T} = DefaultDistance() +graph::AbstractGraph, +v::Integer, +distmx::AbstractMatrix{T} = DefaultDistance() ) = bellman_ford_shortest_paths(graph, [v], distmx) function has_negative_edge_cycle(graph::AbstractGraph) @@ -108,7 +108,8 @@ end enumerate_paths(state::AbstractPathState, dest) = enumerate_paths(state, [dest])[1] enumerate_paths(state::AbstractPathState) = enumerate_paths(state, [1:length(state.parents);]) -"""Given a path state `state` of type `AbstractPathState` (see below), returns a +""" +Given a path state `state` of type `AbstractPathState` (see below), returns 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 diff --git a/src/shortestpaths/dijkstra.jl b/src/shortestpaths/dijkstra.jl index 9f40c6524..909074eef 100644 --- a/src/shortestpaths/dijkstra.jl +++ b/src/shortestpaths/dijkstra.jl @@ -27,7 +27,7 @@ function dijkstra_shortest_paths{T, U<:Integer}( srcs::Vector{U}, distmx::AbstractArray{T, 2}=DefaultDistance(); allpaths=false -) + ) nvg = nv(g) dists = fill(typemax(T), nvg) parents = zeros(U, nvg) @@ -47,7 +47,7 @@ function dijkstra_shortest_paths{T, U<:Integer}( while !isempty(H) hentry = heappop!(H) - # info("Popped H - got $(hentry.vertex)") + # info("Popped H - got $(hentry.vertex)") u = hentry.vertex for v in out_neighbors(g,u) alt = (dists[u] == typemax(T))? typemax(T) : dists[u] + distmx[u,v] @@ -88,4 +88,4 @@ function dijkstra_shortest_paths{T, U<:Integer}( end dijkstra_shortest_paths{T, U}(g::AbstractGraph, src::U, distmx::AbstractArray{T,2}=DefaultDistance(); allpaths=false) = - dijkstra_shortest_paths(g, [src;], distmx; allpaths=allpaths) +dijkstra_shortest_paths(g, [src;], distmx; allpaths=allpaths) diff --git a/src/shortestpaths/floyd-warshall.jl b/src/shortestpaths/floyd-warshall.jl index 47c6d1066..1bd55a3fa 100644 --- a/src/shortestpaths/floyd-warshall.jl +++ b/src/shortestpaths/floyd-warshall.jl @@ -7,7 +7,8 @@ type FloydWarshallState{T, U<:Integer}<:AbstractPathState parents::Matrix{U} end -doc"""Uses the [Floyd-Warshall algorithm](http://en.wikipedia.org/wiki/Floyd–Warshall_algorithm) +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 diff --git a/src/spanningtrees/kruskal.jl b/src/spanningtrees/kruskal.jl index 3b5001014..b80bc7a42 100644 --- a/src/spanningtrees/kruskal.jl +++ b/src/spanningtrees/kruskal.jl @@ -5,7 +5,8 @@ end isless(e1::KruskalHeapEntry, e2::KruskalHeapEntry) = e1.dist < e2.dist -""" Performs [Quick-Find algorithm](https://en.wikipedia.org/wiki/Disjoint-set_data_structure) +""" +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`. """ diff --git a/src/spanningtrees/prim.jl b/src/spanningtrees/prim.jl index f95f0815b..011740a97 100644 --- a/src/spanningtrees/prim.jl +++ b/src/spanningtrees/prim.jl @@ -5,7 +5,8 @@ end isless(e1::PrimHeapEntry, e2::PrimHeapEntry) = e1.dist < e2.dist -"""Performs [Prim's algorithm](https://en.wikipedia.org/wiki/Prim%27s_algorithm) +""" +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. diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index d78c45060..e738fd0ed 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -94,7 +94,6 @@ end type DFSCyclicTestVisitor <: AbstractGraphVisitor found_cycle::Bool - DFSCyclicTestVisitor() = new(false) end @@ -136,13 +135,12 @@ end type TopologicalSortVisitor{T} <: AbstractGraphVisitor vertices::Vector{T} - end function TopologicalSortVisitor{T<:Integer}(n::T) - vs = Vector{T}() - sizehint!(vs, n) - return TopologicalSortVisitor(vs) + vs = Vector{T}() + sizehint!(vs, n) + return TopologicalSortVisitor(vs) end function examine_neighbor!(visitor::TopologicalSortVisitor, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) diff --git a/src/traversals/randomwalks.jl b/src/traversals/randomwalks.jl index 81bb57c89..ecf3ed0da 100644 --- a/src/traversals/randomwalks.jl +++ b/src/traversals/randomwalks.jl @@ -2,20 +2,20 @@ a maximum of `niter` steps. Returns a vector of vertices visited in order. """ function randomwalk(g::AbstractGraph, s::Integer, niter::Integer) - T = eltype(g) - s in vertices(g) || throw(BoundsError()) - visited = Vector{T}() - sizehint!(visited, niter) - currs = s - i = 1 - while i <= niter - push!(visited, currs) - i += 1 - nbrs = out_neighbors(g,currs) - length(nbrs) == 0 && break - currs = rand(nbrs) - end - return visited[1:i-1] + T = eltype(g) + s in vertices(g) || throw(BoundsError()) + visited = Vector{T}() + sizehint!(visited, niter) + currs = s + i = 1 + while i <= niter + push!(visited, currs) + i += 1 + nbrs = out_neighbors(g,currs) + length(nbrs) == 0 && break + currs = rand(nbrs) + end + return visited[1:i-1] end """Performs a non-backtracking random walk on graph `g` starting at vertex `s` and continuing for @@ -92,21 +92,21 @@ on graph `g` starting at vertex `s` and continuing for a maximum of `niter` step Returns a vector of vertices visited in order. """ function saw(g::AbstractGraph, s::Integer, niter::Integer) - T = eltype(g) - s in vertices(g) || throw(BoundsError()) - visited = Vector{T}() - svisited = Set{T}() - sizehint!(visited, niter) - sizehint!(svisited, niter) - currs = s - i = 1 - while i <= niter - push!(visited, currs) - push!(svisited, currs) - i += 1 - nbrs = setdiff(Set(out_neighbors(g,currs)),svisited) - length(nbrs) == 0 && break - currs = rand(collect(nbrs)) - end - return visited[1:i-1] + T = eltype(g) + s in vertices(g) || throw(BoundsError()) + visited = Vector{T}() + svisited = Set{T}() + sizehint!(visited, niter) + sizehint!(svisited, niter) + currs = s + i = 1 + while i <= niter + push!(visited, currs) + push!(svisited, currs) + i += 1 + nbrs = setdiff(Set(out_neighbors(g,currs)),svisited) + length(nbrs) == 0 && break + currs = rand(collect(nbrs)) + end + return visited[1:i-1] end diff --git a/test/core.jl b/test/core.jl index 563d5e8cd..e6e888ec8 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1,6 +1,77 @@ @testset "Core" begin - d = DummyGraph() - for fn in [ degree, density, all_neighbors ] - @test_throws ErrorException fn(d) - end + d = DummyGraph() + for fn in [ degree, density, all_neighbors ] + @test_throws ErrorException fn(d) + end + + e2 = Edge(1,3) + e3 = Edge(1,4) + @test is_ordered(e2) + @test !is_ordered(reverse(e3)) + + gx = Graph(10) + for g in testgraphs(gx) + add_vertices!(g, 5) + @test nv(g) == 15 + end + + g5w = WheelGraph(5) + for g in testgraphs(g5w) + @test indegree(g,1) == outdegree(g,1) == degree(g,1) == 4 + @test indegree(g) == outdegree(g) == degree(g) == [4,3,3,3,3] + + @test Δout(g) == Δin(g) == Δ(g) == 4 + @test δout(g) == δin(g) == δ(g) == 3 + + z = degree_histogram(g) + @test z.weights == [4,0,1] + + @test neighbors(g, 2) == all_neighbors(g, 2) == [1,3,5] + @test common_neighbors(g, 1, 5) == [2, 4] + + gsl = copy(g) + add_edge!(gsl, 3, 3) + add_edge!(gsl, 2, 2) + + @test !has_self_loops(g) + @test has_self_loops(gsl) + @test num_self_loops(g) == 0 + @test num_self_loops(gsl) == 2 + + @test density(g) == 0.8 + end + + g5wd = WheelDiGraph(5) + for g in testdigraphs(g5wd) + @test indegree(g,2) == 2 + @test outdegree(g,2) == 1 + @test degree(g,2) == 3 + @test indegree(g) == [0,2,2,2,2] + @test outdegree(g) == [4,1,1,1,1] + @test degree(g) == [4,3,3,3,3] + + @test Δout(g) == Δ(g) == 4 + @test Δin(g) == 2 + @test δout(g) == 1 + @test δin(g) == 0 + @test δ(g) == 3 + + z = degree_histogram(g) + @test z.weights == [4,0,1] + + @test neighbors(g, 2) == [3] + @test Set(all_neighbors(g, 2)) == Set([1,3,5]) + @test common_neighbors(g, 1, 5) == [2] + + gsl = copy(g) + add_edge!(gsl, 3, 3) + add_edge!(gsl, 2, 2) + + @test !has_self_loops(g) + @test has_self_loops(gsl) + @test num_self_loops(g) == 0 + @test num_self_loops(gsl) == 2 + + @test density(g) == 0.4 + end end diff --git a/test/distance.jl b/test/distance.jl index bbda01857..2e59bddd5 100644 --- a/test/distance.jl +++ b/test/distance.jl @@ -7,21 +7,26 @@ distmx1 = [Inf 2.0 Inf; 2.0 Inf 4.2; Inf 4.2 Inf] distmx2 = [Inf 2.0 Inf; 3.2 Inf 4.2; 5.5 6.1 Inf] - @test_throws ErrorException eccentricity(g4) - z = eccentricity(a1, distmx1) - @test z == [6.2, 4.2, 6.2] - @test diameter(z) == diameter(a1, distmx1) == 6.2 - @test periphery(z) == periphery(a1, distmx1) == [1,3] - @test radius(z) == radius(a1, distmx1) == 4.2 - @test center(z) == center(a1, distmx1) == [2] - - z = eccentricity(a2, distmx2) - @test z == [6.2, 4.2, 6.1] - @test diameter(z) == diameter(a2, distmx2) == 6.2 - @test periphery(z) == periphery(a2, distmx2) == [1] - @test radius(z) == radius(a2, distmx2) == 4.2 - @test center(z) == center(a2, distmx2) == [2] + for g in testdigraphs(g4) + @test_throws ErrorException eccentricity(g) + end + for g in testgraphs(a1) + z = eccentricity(g, distmx1) + @test z == [6.2, 4.2, 6.2] + @test diameter(z) == diameter(g, distmx1) == 6.2 + @test periphery(z) == periphery(g, distmx1) == [1,3] + @test radius(z) == radius(g, distmx1) == 4.2 + @test center(z) == center(g, distmx1) == [2] + end + for g in testdigraphs(a2) + z = eccentricity(g, distmx2) + @test z == [6.2, 4.2, 6.1] + @test diameter(z) == diameter(g, distmx2) == 6.2 + @test periphery(z) == periphery(g, distmx2) == [1] + @test radius(z) == radius(g, distmx2) == 4.2 + @test center(z) == center(g, distmx2) == [2] + end @test size(LightGraphs.DefaultDistance()) == (typemax(Int), typemax(Int)) d = LightGraphs.DefaultDistance(3) @test size(d) == (3, 3) diff --git a/test/edit_distance.jl b/test/edit_distance.jl index 747a80525..86e698d48 100644 --- a/test/edit_distance.jl +++ b/test/edit_distance.jl @@ -1,25 +1,26 @@ @testset "Edit distance" begin - triangle = random_regular_graph(3,2) - quadrangle = random_regular_graph(4,2) - pentagon = random_regular_graph(5,2) + gtri = random_regular_graph(3,2) + gquad = random_regular_graph(4,2) + gpent = random_regular_graph(5,2) - d, λ = edit_distance(triangle, quadrangle, subst_cost=MinkowskiCost(1:3,1:4)) - @test d == 1.0 - @test λ == Tuple[(1,1),(2,2),(3,3),(0,4)] + for triangle in testgraphs(gtri), quadrangle in testgraphs(gquad), pentagon in testgraphs(gpent) + d, λ = edit_distance(triangle, quadrangle, subst_cost=MinkowskiCost(1:3,1:4)) + @test d == 1.0 + @test λ == Tuple[(1,1),(2,2),(3,3),(0,4)] - d, λ = edit_distance(quadrangle, triangle, subst_cost=MinkowskiCost(1:4,1:3)) - @test d == 1.0 - @test λ == Tuple[(1,1),(2,2),(3,3),(4,0)] + d, λ = edit_distance(quadrangle, triangle, subst_cost=MinkowskiCost(1:4,1:3)) + @test d == 1.0 + @test λ == Tuple[(1,1),(2,2),(3,3),(4,0)] - d, λ = edit_distance(triangle, pentagon, subst_cost=MinkowskiCost(1:3,1:5)) - @test d == 2.0 - @test λ == Tuple[(1,1),(2,2),(3,3),(0,4),(0,5)] - - d, λ = edit_distance(pentagon, triangle, subst_cost=MinkowskiCost(1:5,1:3)) - @test d == 2.0 - @test λ == Tuple[(1,1),(2,2),(3,3),(4,0),(5,0)] + d, λ = edit_distance(triangle, pentagon, subst_cost=MinkowskiCost(1:3,1:5)) + @test d == 2.0 + @test λ == Tuple[(1,1),(2,2),(3,3),(0,4),(0,5)] + d, λ = edit_distance(pentagon, triangle, subst_cost=MinkowskiCost(1:5,1:3)) + @test d == 2.0 + @test λ == Tuple[(1,1),(2,2),(3,3),(4,0),(5,0)] + end cost = MinkowskiCost(1:3,1:3) bcost = BoundedMinkowskiCost(1:3,1:3) for i=1:3 @@ -27,11 +28,12 @@ @test bcost(i,i) == 2/3 end - g1 = CompleteGraph(4) - g2 = CompleteGraph(4) - rem_edge!(g2, 1, 2) - d, λ = edit_distance(g1, g2) - @test d == 2.0 - @test λ == Tuple[(1,1),(2,2),(3,3),(4,4)] - + g1c = CompleteGraph(4) + g2c = CompleteGraph(4) + rem_edge!(g2c, 1, 2) + for g1 in testgraphs(g1c), g2 in testgraphs(g2c) + d, λ = edit_distance(g1, g2) + @test d == 2.0 + @test λ == Tuple[(1,1),(2,2),(3,3),(4,4)] + end end diff --git a/test/graphdigraph.jl b/test/graphdigraph.jl deleted file mode 100644 index 109d8a13e..000000000 --- a/test/graphdigraph.jl +++ /dev/null @@ -1,77 +0,0 @@ -@testset "Graph/DiGraph" begin - @test sprint(show, h1) == "{5, 0} undirected simple graph" - @test sprint(show, h3) == "empty undirected simple graph" - - @test Graph(DiGraph(g3)) == g3 - - @test degree(g3, 1) == 1 - # @test all_neighbors(g3, 3) == [2, 4] - @test density(g3) == 0.4 - - g = Graph(5) - @test add_edge!(g, 1, 2) - - e2 = Edge(1,3) - e3 = Edge(1,4) - e4 = Edge(2,5) - e5 = Edge(3,5) - - - @test add_edge!(g, e2) - @test add_edge!(g, e3) - @test add_edge!(g, e4) - @test add_edge!(g, e5) - - h = DiGraph(5) - @test add_edge!(h, 1, 2) - @test add_edge!(h, e2) - @test add_edge!(h, e3) - @test add_edge!(h, e4) - @test add_edge!(h, e5) - - @test LightGraphs.fadj(g)[1] == LightGraphs.fadj(g,1) == - LightGraphs.badj(g)[1] == LightGraphs.badj(g,1) == - LightGraphs.adj(g)[1] == LightGraphs.adj(g,1) == - [2,3,4] - - @test sprint(show, h4) == "{7, 0} directed simple graph" - @test sprint(show, h5) == "empty directed simple graph" - @test has_edge(g, e1) - @test has_edge(h, e1) - @test !has_edge(g, e0) - @test !has_edge(h, e0) - - @test degree(g4, 1) == 1 - # @test all_neighbors(g4, 3) == [4] - @test density(g4) == 0.2 - - @test nv(a1) == 3 - @test ne(a1) == 2 - @test nv(a2) == 3 - @test ne(a2) == 5 - - badadjmx = [ 0 1 0; 1 0 1] - @test_throws ErrorException Graph(badadjmx) - @test_throws ErrorException Graph(sparse(badadjmx)) - @test_throws ErrorException DiGraph(badadjmx) - @test_throws ErrorException DiGraph(sparse(badadjmx)) - @test_throws ErrorException Graph([1 0; 1 1]) - - - @test !add_edge!(g, 100, 100) - @test !add_edge!(h, 100, 100) - - @test_throws ErrorException Graph(sparse(adjmx2)) - - g = Graph(sparse(adjmx1)) - h = DiGraph(sparse(adjmx1)) - @test !is_directed(g) - @test is_directed(h) - - - @test (nv(g), ne(g)) == (3, 2) - @test (nv(h), ne(h)) == (3, 4) - @test Graph(h) == g - - @test sort(all_neighbors(WheelDiGraph(10),2)) == [1, 3, 10] -end diff --git a/test/graphtypes/simplegraphs/simplegraphs.jl b/test/graphtypes/simplegraphs/simplegraphs.jl index a1e1856c0..dd5bc7c6b 100644 --- a/test/graphtypes/simplegraphs/simplegraphs.jl +++ b/test/graphtypes/simplegraphs/simplegraphs.jl @@ -1,237 +1,143 @@ +importall LightGraphs.SimpleGraphs +import LightGraphs.SimpleGraphs: fadj, badj, adj type DummySimpleGraph <: AbstractSimpleGraph end @testset "SimpleGraphs" begin - dsg = DummySimpleGraph() + adjmx1 = [0 1 0; 1 0 1; 0 1 0] # graph + adjmx2 = [0 1 0; 1 0 1; 1 1 0] # digraph + # specific concrete generators - no need for loop + @test eltype(SimpleGraph()) == Int + @test eltype(SimpleGraph(adjmx1)) == Int + @test_throws ErrorException SimpleGraph(adjmx2) - @test_throws ErrorException badj(dsg) + @test ne(SimpleGraph(PathDiGraph(5))) == 4 + @test !is_directed(SimpleGraph) - e1 = SimpleEdge(1,2) - e2 = SimpleEdge(1,3) - e3 = SimpleEdge(1,4) - e4 = SimpleEdge(2,5) - e5 = SimpleEdge(3,5) + @test eltype(SimpleDiGraph()) == Int + @test eltype(SimpleDiGraph(adjmx2)) == Int + @test ne(SimpleDiGraph(PathGraph(5))) == 8 + @test is_directed(SimpleDiGraph) - re1 = Edge(2, 1) - pdict = load(joinpath(testdir,"testdata","tutte-pathdigraph.jgz")) - p1 = pdict["Tutte"] - g2 = smallgraph(:tutte) + gdx = PathDiGraph(4) + gx = SimpleGraph() - h1 = Graph(5) - h2 = Graph(3) + for g in testgraphs(gx) + @test sprint(show, g) == "empty undirected simple graph" + add_vertices!(g, 5) + @test sprint(show, g) == "{5, 0} undirected simple graph" - adjmx1 = [0 1 0; 1 0 1; 0 1 0] # graph - adjmx2 = [0 1 0; 1 0 1; 1 1 0] # digraph + end + gx = SimpleDiGraph() + for g in testdigraphs(gx) + @test sprint(show, g) == "empty directed simple graph" + add_vertices!(g, 5) + @test sprint(show, g) == "{5, 0} directed simple graph" + end - @test e1.src == src(e1) == 1 - @test e1.dst == dst(e1) == 2 - @test reverse(e1) == re1 - @test sprint(show, e1) == "Edge 1 => 2" - @test Pair(e1) == Pair(1,2) - @test Tuple(e1) == (1,2) + gx = PathGraph(4) + for g in testgraphs(gx) + @test vertices(g) == 1:4 + @test Edge(2,3) in edges(g) + @test nv(g) == 4 + @test fadj(g) == badj(g) == adj(g) == g.fadjlist + @test fadj(g,2) == badj(g,2) == adj(g,2) == g.fadjlist[2] - @test LightGraphs.is_ordered(e5) - @test !LightGraphs.is_ordered(reverse(e5)) + @test has_edge(g, 2, 3) + @test has_edge(g, 3, 2) + gc = copy(g) + @test add_edge!(gc, 4, 1) && gc == CycleGraph(4) - @test !is_directed(SimpleGraph) - @test is_directed(SimpleDiGraph) + @test in_neighbors(g, 2) == out_neighbors(g, 2) == neighbors(g,2) == [1,3] + @test add_vertex!(gc) # out of order, but we want it for issubset + @test g ⊆ gc + @test has_vertex(gc, 5) - @test edgetype(SimpleGraph()) == SimpleEdge{Int} - @test edgetype(SimpleDiGraph()) == SimpleEdge{Int} - - @test sprint(show, SimpleGraph()) == "empty undirected simple graph" - @test sprint(show, SimpleDiGraph()) == "empty directed simple graph" - @test sprint(show, SimpleDiGraph(5)) == "{5, 0} directed simple graph" - g = SimpleGraph(5) - @test sprint(show, g) == "{5, 0} undirected simple graph" - @test add_edge!(g, 1, 2) - @test add_edge!(g, e2) - @test add_edge!(g, e3) - @test add_edge!(g, e4) - @test add_edge!(g, e5) - - @test Graph(DiGraph(g)) == g - - h = SimpleDiGraph(5) - @test add_edge!(h, 1, 2) - @test add_edge!(h, e2) - @test add_edge!(h, e3) - @test add_edge!(h, e4) - @test add_edge!(h, e5) - - @test vertices(g) == 1:5 - i = 0 - for e in edges(g) - i+=1 - end - @test i == 5 - @test has_edge(g, 3, 5) - # @test edges(g) == Set([e1, e2, e3, e4, e5]) - # @test Set{Edge}(edges(g)) == Set([e1, e2, e3, e4, e5]) - @test fadj(g, 1) == adj(g, 1) == badj(g, 1) == out_neighbors(g, 1) - @test fadj(g) == badj(g) == adj(g) == g.fadjlist - - @test fadj(h, 1) == out_neighbors(h, 1) - @test badj(h, 1) == in_neighbors(h, 1) - @test fadj(h) == h.fadjlist - @test badj(h) == h.badjlist - - @test degree(g) == [3, 2, 2, 1, 2] - @test degree(g, 1) == 3 - @test indegree(g) == [3, 2, 2, 1, 2] - @test indegree(g, 1) == 3 - @test outdegree(g) == [3, 2, 2, 1, 2] - @test outdegree(g, 1) == 3 - @test in_neighbors(g, 5) == [2, 3] - @test out_neighbors(g, 1) == [2, 3, 4] - - @test density(g) == 0.5 - - @test degree(h) == [3, 2, 2, 1, 2] - @test degree(h, 1) == 3 - @test indegree(h) == [0, 1, 1, 1, 2] - @test indegree(h, 1) == 0 - @test outdegree(h) == [3, 1, 1, 0, 0] - @test outdegree(h, 1) == 3 - @test in_neighbors(h, 5) == [2, 3] - @test out_neighbors(h, 1) == [2, 3, 4] - @test all_neighbors(h, 1) == union(in_neighbors(h, 1), out_neighbors(h, 1)) - @test density(h) == 0.25 - - @test p1 == g2 - @test issubset(h2, h1) - - @test has_edge(g, 1, 2) - - @test add_vertex!(g) && nv(g) == 6 - @test add_vertices!(g,5) && nv(g) == 11 - @test has_vertex(g, 11) - @test ne(g) == 5 - @test !is_directed(g) - @test !is_directed(typeof(g)) - @test is_directed(h) - @test is_directed(typeof(h)) - - @test δ(g) == δin(g) == δout(g) == 0 - @test Δ(g) == Δout(g) == 3 - @test Δin(h) == 2 - @test δ(h) == 1 - @test δin(h) == 0 - @test δout(h) == 0 - @test CompleteGraph(4) == CompleteGraph(4) - @test CompleteGraph(4) != PathGraph(4) - @test CompleteDiGraph(4) != PathDiGraph(4) - @test CompleteDiGraph(4) == CompleteDiGraph(4) - - @test degree_histogram(CompleteDiGraph(10)).weights == [10] - @test degree_histogram(CompleteGraph(10)).weights == [10] - - @test neighbors(g, 1) == [2, 3, 4] - @test common_neighbors(g, 2, 3) == [1, 5] - @test common_neighbors(h, 2, 3) == [5] - - @test add_edge!(g, 1, 1) - @test has_self_loops(g) - @test num_self_loops(g) == 1 - @test !add_edge!(g, 1, 1) - @test rem_edge!(g, 1, 1) - @test !rem_edge!(g, 1, 1) - @test ne(g) == 5 - @test rem_edge!(g, 1, 2) - @test ne(g) == 4 - @test !rem_edge!(g, 2, 1) - add_edge!(g, 1, 2) - @test ne(g) == 5 - - @test has_edge(g,2,1) - @test has_edge(g,1,2) - @test rem_edge!(g, 2, 1) - @test add_edge!(h, 1, 1) - @test rem_edge!(h, 1, 1) - @test rem_edge!(h, 1, 2) - @test !rem_edge!(h, 1, 2) - - function test_rem_edge(g, srcv) - srcv = 2 - for dstv in collect(neighbors(g, srcv)) - rem_edge!(g, srcv, dstv) + @test ne(g) == 3 + + @test rem_edge!(gc, 1, 2) && !has_edge(gc, 1, 2) + ga = copy(g) + @test rem_vertex!(ga, 2) && ne(ga) == 1 + @test !rem_vertex!(ga, 10) + + @test empty(g) == SimpleGraph{eltype(g)}() + + # concrete tests below + + @test eltype(g) == eltype(fadj(g,1)) == eltype(nv(g)) + T = eltype(g) + @test nv(SimpleGraph{T}(6)) == 6 + + @test eltype(SimpleGraph(T)) == T + @test eltype(SimpleGraph{T}(adjmx1)) == T + + ga = SimpleGraph(10) + @test eltype(SimpleGraph{T}(ga)) == T + + for gd in testdigraphs(gdx) + U = eltype(gd) + @test eltype(SimpleGraph(gd)) == U end - @test length(neighbors(g,srcv)) == 0 - end - for v in vertices(g) - test_rem_edge(copy(g),v) - end - for v in vertices(h) - test_rem_edge(copy(h),v) + + @test edgetype(g) == SimpleGraphEdge{T} + @test copy(g) == g + @test !is_directed(g) + + e = first(edges(g)) + @test has_edge(g, e) end - @test g == copy(g) - @test !(g === copy(g)) - - g = CompleteGraph(5) - @test rem_vertex!(g, 1) - @test g == CompleteGraph(4) - @test rem_vertex!(g, 4) - @test g == CompleteGraph(3) - @test !rem_vertex!(g, 9) - - g = CompleteDiGraph(5) - @test rem_vertex!(g, 1) - @test g == CompleteDiGraph(4) - rem_vertex!(g, 4) - @test g == CompleteDiGraph(3) - @test !rem_vertex!(g, 9) - g = PathGraph(5) - @test rem_vertex!(g, 5) - @test g == PathGraph(4) - @test rem_vertex!(g, 4) - @test g == PathGraph(3) - - g = PathDiGraph(5) - @test rem_vertex!(g, 5) - @test g == PathDiGraph(4) - @test rem_vertex!(g, 4) - @test g == PathDiGraph(3) - - g = PathDiGraph(5) - @test rem_vertex!(g, 1) - h10 = PathDiGraph(6) - @test rem_vertex!(h10, 1) - @test rem_vertex!(h10, 1) - @test g == h10 - - g = CycleGraph(5) - @test rem_vertex!(g, 5) - @test g == PathGraph(4) - - g = PathGraph(3) - @test rem_vertex!(g, 2) - @test g == SimpleGraph(2) - - g = PathGraph(4) - @test rem_vertex!(g, 3) - h10 =Graph(3) - @test add_edge!(h10,1,2) - @test g == h10 - - g = CompleteGraph(5) - @test rem_vertex!(g, 3) - @test g == CompleteGraph(4) - - badadjmx = [ 0 1 0; 1 0 1] - @test_throws ErrorException SimpleGraph(badadjmx) - @test_throws ErrorException SimpleGraph(sparse(badadjmx)) - @test_throws ErrorException SimpleDiGraph(badadjmx) - @test_throws ErrorException SimpleDiGraph(sparse(badadjmx)) - @test_throws ErrorException SimpleGraph([1 0; 1 1]) - - g = CompleteGraph(5) - @test !add_edge!(g, 100, 100) - h = CompleteDiGraph(5) - @test !add_edge!(h, 100, 100) - - @test_throws ErrorException Graph(sparse(adjmx2)) - - g = Graph(sparse(adjmx1)) - h = DiGraph(sparse(adjmx1)) - @test !is_directed(g) - @test is_directed(h) + gdx = PathDiGraph(4) + for g in testdigraphs(gdx) + @test vertices(g) == 1:4 + @test Edge(2,3) in edges(g) + @test !(Edge(3,2) in edges(g)) + @test nv(g) == 4 + @test fadj(g)[2] == fadj(g, 2) == [3] + @test badj(g)[2] == badj(g, 2) == [1] + @test_throws MethodError adj(g) + + @test has_edge(g, 2, 3) + @test !has_edge(g, 3, 2) + gc = copy(g) + @test add_edge!(gc, 4, 1) && gc == CycleDiGraph(4) + + @test in_neighbors(g, 2) == [1] + @test out_neighbors(g, 2) == neighbors(g,2) == [3] + @test add_vertex!(gc) # out of order, but we want it for issubset + @test g ⊆ gc + @test has_vertex(gc, 5) + + @test ne(g) == 3 + + @test !rem_edge!(gc, 2, 1) + @test rem_edge!(gc, 1, 2) && !has_edge(gc, 1, 2) + ga = copy(g) + @test rem_vertex!(ga, 2) && ne(ga) == 1 + @test !rem_vertex!(ga, 10) + + @test empty(g) == SimpleDiGraph{eltype(g)}() + + # concrete tests below + + @test eltype(g) == eltype(fadj(g,1)) == eltype(nv(g)) + T = eltype(g) + @test nv(SimpleDiGraph{T}(6)) == 6 + + @test eltype(SimpleDiGraph(T)) == T + @test eltype(SimpleDiGraph{T}(adjmx2)) == T + + ga = SimpleDiGraph(10) + @test eltype(SimpleDiGraph{T}(ga)) == T + + for gu in testgraphs(gx) + U = eltype(gu) + @test eltype(SimpleDiGraph(gu)) == U + end + + @test edgetype(g) == SimpleDiGraphEdge{T} + @test copy(g) == g + @test is_directed(g) + + e = first(edges(g)) + @test has_edge(g, e) + end end diff --git a/test/interface.jl b/test/interface.jl index 8de0ece69..df7859114 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -16,7 +16,8 @@ type DummyEdge <: AbstractEdge end end for graphfunbasic in [ - nv, ne, vertices, edges, is_directed, add_vertex!, edgetype + nv, ne, vertices, edges, is_directed, + add_vertex!, edgetype, eltype, empty ] @test_throws ErrorException graphfunbasic(dummygraph) end diff --git a/test/operators.jl b/test/operators.jl index 9ae394012..1947ac197 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -2,110 +2,133 @@ g3 = PathGraph(5) g4 = PathDiGraph(5) - c3 = complement(g3) - c4 = complement(g4) + for g in testlargegraphs(g3) + T = eltype(g) + c = complement(g) + @test nv(c) == 5 + @test ne(c) == 6 + + gb = blkdiag(g, g) + @test nv(gb) == 10 + @test ne(gb) == 8 + + hp = PathGraph(2) + h = Graph{T}(hp) + @test intersect(g, h) == h + + hp = PathGraph(4) + h = Graph{T}(hp) + + z = difference(g, h) + @test nv(z) == 5 + @test ne(z) == 1 + z = difference(h, g) + @test nv(z) == 4 + @test ne(z) == 0 + z = symmetric_difference(h,g) + @test z == symmetric_difference(g,h) + @test nv(z) == 5 + @test ne(z) == 1 + + h = Graph{T}(6) + add_edge!(h, 5, 6) + e = SimpleEdge(5, 6) + + z = union(g, h) + @test has_edge(z, e) + @test z == PathGraph(6) + end + for g in testlargedigraphs(g4) + T = eltype(g) + c = complement(g) + @test nv(c) == 5 + @test ne(c) == 16 + + h = DiGraph{T}(6) + add_edge!(h, 5, 6) + e = SimpleEdge(5, 6) + + z = union(g, h) + @test has_edge(z, e) + @test z == PathDiGraph(6) + end re1 = Edge(2, 1) + gr = reverse(g4) + for g in testdigraphs(gr) + T = eltype(g) + @test re1 in edges(g) + reverse!(g) + @test g == DiGraph{T}(g4) + end - @test nv(c3) == 5 - @test ne(c3) == 6 - @test nv(c4) == 5 - @test ne(c4) == 16 - - g = reverse(g4) - @test re1 in edges(g) - reverse!(g) - @test g == g4 - - g = blkdiag(g3, g3) - @test nv(g) == 10 - @test ne(g) == 8 - - h = PathGraph(2) - @test intersect(g3, h) == h - - h = PathGraph(4) - z = difference(g3, h) - @test nv(z) == 5 - @test ne(z) == 1 - z = difference(h, g3) - @test nv(z) == 4 - @test ne(z) == 0 - - z = symmetric_difference(h,g3) - @test z == symmetric_difference(g3,h) - @test nv(z) == 5 - @test ne(z) == 1 - - h = Graph(6) - add_edge!(h, 5, 6) - e = SimpleEdge(5, 6) - z = union(g3, h) - @test has_edge(z, e) - @test z == PathGraph(6) - - h = DiGraph(6) - add_edge!(h, 5, 6) - e = SimpleEdge(5, 6) - z = union(g4, h) - @test has_edge(z, e) - @test z == PathDiGraph(6) - - g = CompleteGraph(2) - h = CompleteGraph(2) - z = blkdiag(g, h) - @test nv(z) == nv(g) + nv(h) - @test ne(z) == ne(g) + ne(h) - @test has_edge(z, 1, 2) - @test has_edge(z, 3, 4) - @test !has_edge(z, 1, 3) - @test !has_edge(z, 1, 4) - @test !has_edge(z, 2, 3) - @test !has_edge(z, 2, 4) - - g = Graph(2) - h = Graph(2) - z = join(g, h) - @test nv(z) == nv(g) + nv(h) - @test ne(z) == 4 - @test !has_edge(z, 1, 2) - @test !has_edge(z, 3, 4) - @test has_edge(z, 1, 3) - @test has_edge(z, 1, 4) - @test has_edge(z, 2, 3) - @test has_edge(z, 2, 4) - - p = PathGraph(10) - x = p*ones(10) - @test x[1]==1.0 && all(x[2:end-1].==2.0) && x[end]==1.0 - - @test size(p) == (10,10) - @test size(p, 1) == size(p, 2) == 10 - @test size(p, 3) == 1 - - - g = DiGraph(4) - add_edge!(g,1,2); add_edge!(g,2,3); add_edge!(g,1,3); add_edge!(g,3,4) - @test g * ones(nv(g)) == [2.0, 1.0, 1.0, 0.0] - @test sum(g, 1) == [0, 1, 2, 1] - @test sum(g, 2) == [2, 1, 1, 0] - @test sum(g) == 4 - @test sum(p,1) == sum(p,2) - @test_throws ErrorException sum(p,3) - - @test sparse(p) == adjacency_matrix(p) - @test eltype(p) == Int - @test length(p) == 100 - @test ndims(p) == 2 - @test issymmetric(p) - @test !issymmetric(g) + gx = CompleteGraph(2) + + for g in testlargegraphs(gx) + T = eltype(g) + hc = CompleteGraph(2) + h = Graph{T}(hc) + z = blkdiag(g, h) + @test nv(z) == nv(g) + nv(h) + @test ne(z) == ne(g) + ne(h) + @test has_edge(z, 1, 2) + @test has_edge(z, 3, 4) + @test !has_edge(z, 1, 3) + @test !has_edge(z, 1, 4) + @test !has_edge(z, 2, 3) + @test !has_edge(z, 2, 4) + end - nx = 20; ny = 21 - G = PathGraph(ny); H = PathGraph(nx) - c = cartesian_product(G, H) - g = crosspath(ny, PathGraph(nx)); - @test g == c + gx = Graph(2) + for g in testgraphs(gx) + T = eltype(g) + h = Graph{T}(2) + z = join(g, h) + @test nv(z) == nv(g) + nv(h) + @test ne(z) == 4 + @test !has_edge(z, 1, 2) + @test !has_edge(z, 3, 4) + @test has_edge(z, 1, 3) + @test has_edge(z, 1, 4) + @test has_edge(z, 2, 3) + @test has_edge(z, 2, 4) + end + px = PathGraph(10) + for p in testgraphs(px) + x = p*ones(10) + @test x[1]==1.0 && all(x[2:end-1].==2.0) && x[end]==1.0 + @test size(p) == (10,10) + @test size(p, 1) == size(p, 2) == 10 + @test size(p, 3) == 1 + @test sum(p,1) == sum(p,2) + @test_throws ErrorException sum(p,3) + @test sparse(p) == adjacency_matrix(p) + @test length(p) == 100 + @test ndims(p) == 2 + @test issymmetric(p) + end + + gx = DiGraph(4) + add_edge!(gx,1,2); add_edge!(gx,2,3); add_edge!(gx,1,3); add_edge!(gx,3,4) + for g in testdigraphs(gx) + @test g * ones(nv(g)) == [2.0, 1.0, 1.0, 0.0] + @test sum(g, 1) == [0, 1, 2, 1] + @test sum(g, 2) == [2, 1, 1, 0] + @test sum(g) == 4 + @test !issymmetric(g) + end + + nx = 20; ny = 21 + gx = PathGraph(ny) + for g in testlargegraphs(gx) + T = eltype(g) + hp = PathGraph(nx) + h = Graph{T}(hp) + c = cartesian_product(g, h) + gz = crosspath(ny, PathGraph(nx)); + @test gz == c + end function crosspath_slow(len, h) g = h m = nv(h) @@ -119,70 +142,78 @@ return g end - g = CompleteGraph(2) - h = cartesian_product(g, g) - @test nv(h) == 4 - @test ne(h)== 4 - - h = tensor_product(g, g) - @test nv(h) == 4 - @test ne(h) == 1 + gx = CompleteGraph(2) + for g in testgraphs(gx) + h = cartesian_product(g, g) + @test nv(h) == 4 + @test ne(h) == 4 + h = tensor_product(g, g) + @test nv(h) == 4 + @test ne(h) == 1 + end g2 = CompleteGraph(2) - @test crosspath_slow(2, g2) == crosspath(2, g2) - + for g in testgraphs(g2) + @test crosspath_slow(2, g) == crosspath(2, g) + end ## test subgraphs ## - g = smallgraph(:bull) - n = 3 - h = g[1:n] - @test nv(h) == n - @test ne(h) == 3 - - h = g[[1,2,4]] - @test nv(h) == n - @test ne(h) == 2 - - h = g[[1,5]] - @test nv(h) == 2 - @test ne(h) == 0 - @test typeof(h) == typeof(g) - - g = DiGraph(100,200) - h = g[5:26] - @test nv(h) == 22 - @test typeof(h) == typeof(g) - @test_throws ErrorException g[[1,1]] - - r = 5:26 - h2, vm = induced_subgraph(g, r) - @test h2 == h - @test vm == collect(r) - @test h2 == g[r] - - sg, vm = induced_subgraph(CompleteGraph(10), 5:8) - @test nv(sg) == 4 - @test ne(sg) == 6 - - sg2, vm = induced_subgraph(CompleteGraph(10), [5,6,7,8]) - @test sg2 == sg - @test vm[4] == 8 - - gg = CompleteGraph(10) - elist = [ - SimpleEdge(1, 2), SimpleEdge(2, 3), SimpleEdge(3, 4), - SimpleEdge(4, 5),SimpleEdge(5, 1) - ] - sg, vm = induced_subgraph(gg, elist) - @test sg == CycleGraph(5) - @test sort(vm) == [1:5;] - - - g = StarGraph(10) - @test egonet(g, 1, 0) == Graph(1,0) - @test egonet(g, 1, 1) == g - - @test eltype(g) == Int - @test ndims(g) == 2 + gb = smallgraph(:bull) + for g in testgraphs(gb) + n = 3 + h = g[1:n] + @test nv(h) == n + @test ne(h) == 3 + + h = g[[1,2,4]] + @test nv(h) == n + @test ne(h) == 2 + + h = g[[1,5]] + @test nv(h) == 2 + @test ne(h) == 0 + @test typeof(h) == typeof(g) + end + + gx = DiGraph(100,200) + for g in testdigraphs(gx) + h = g[5:26] + @test nv(h) == 22 + @test typeof(h) == typeof(g) + @test_throws ErrorException g[[1,1]] + + r = 5:26 + h2, vm = induced_subgraph(g, r) + @test h2 == h + @test vm == collect(r) + @test h2 == g[r] + end + + g10 = CompleteGraph(10) + for g in testgraphs(g10) + sg, vm = induced_subgraph(g, 5:8) + @test nv(sg) == 4 + @test ne(sg) == 6 + + sg2, vm = induced_subgraph(g, [5,6,7,8]) + @test sg2 == sg + @test vm[4] == 8 + + elist = [ + SimpleEdge(1, 2), SimpleEdge(2, 3), SimpleEdge(3, 4), + SimpleEdge(4, 5),SimpleEdge(5, 1) + ] + sg, vm = induced_subgraph(g, elist) + @test sg == CycleGraph(5) + @test sort(vm) == [1:5;] + end + + gs = StarGraph(10) + for g in testgraphs(gs) + T = eltype(g) + @test egonet(g, 1, 0) == Graph{T}(1) + @test egonet(g, 1, 1) == g + @test ndims(g) == 2 + end end diff --git a/test/runtests.jl b/test/runtests.jl index e81cb8961..2b5e576b8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -36,22 +36,14 @@ using Base.Test testdir = dirname(@__FILE__) -# pdict = load(joinpath(testdir,"testdata","tutte-pathdigraph.jgz")) -# p1 = pdict["Tutte"] -# p2 = pdict["pathdigraph"] -# -# -# adjmx1 = [0 1 0; 1 0 1; 0 1 0] # graph -# adjmx2 = [0 1 0; 1 0 1; 1 1 0] # digraph -# distmx1 = [Inf 2.0 Inf; 2.0 Inf 4.2; Inf 4.2 Inf] -# distmx2 = [Inf 2.0 Inf; 3.2 Inf 4.2; 5.5 6.1 Inf] -# -# a1 = Graph(adjmx1) -# a2 = DiGraph(adjmx2) - testgraphs(g) = (g, Graph{UInt8}(g), Graph{Int16}(g)) testdigraphs(g) = (g, DiGraph{UInt8}(g), DiGraph{Int16}(g)) +# some operations will create a large graph from two smaller graphs. We +# might error out on very small eltypes. +testlargegraphs(g) = (g, Graph{UInt16}(g), Graph{Int32}(g)) +testlargedigraphs(g) = (g, DiGraph{UInt16}(g), DiGraph{Int32}(g)) + tests = [ "interface", "core", diff --git a/test/transitivity.jl b/test/transitivity.jl index 4663c7ef7..9cc97c717 100644 --- a/test/transitivity.jl +++ b/test/transitivity.jl @@ -1,22 +1,30 @@ @testset "Transitivity" begin - complete = CompleteDiGraph(4) - circle = PathDiGraph(4) - add_edge!(circle, 4, 1) - newcircle = transitiveclosure(circle) - @test newcircle == complete - @test ne(circle) == 4 - @test newcircle == transitiveclosure!(circle) - @test ne(circle) == 12 + completedg = CompleteDiGraph(4) + circledg = PathDiGraph(4) + add_edge!(circledg, 4, 1) + for circle in testdigraphs(circledg) + T = eltype(circle) + complete = DiGraph{T}(completedg) + newcircle = transitiveclosure(circle) + @test newcircle == complete + @test ne(circle) == 4 + @test newcircle == transitiveclosure!(circle) + @test ne(circle) == 12 + end - loopedcomplete = copy(complete) - for i in vertices(loopedcomplete) - add_edge!(loopedcomplete, i, i) + loopedcompletedg = copy(completedg) + for i in vertices(loopedcompletedg) + add_edge!(loopedcompletedg, i, i) + end + circledg = PathDiGraph(4) + add_edge!(circledg, 4, 1) + for circle in testdigraphs(circledg) + T = eltype(circle) + loopedcomplete = DiGraph{T}(loopedcompletedg) + newcircle = transitiveclosure(circle, true) + @test newcircle == loopedcomplete + @test ne(circle) == 4 + @test newcircle == transitiveclosure!(circle, true) + @test ne(circle) == 16 end - circle = PathDiGraph(4) - add_edge!(circle, 4, 1) - newcircle = transitiveclosure(circle, true) - @test newcircle == loopedcomplete - @test ne(circle) == 4 - @test newcircle == transitiveclosure!(circle, true) - @test ne(circle) == 16 end From d7e80133632fb5d797c6c94af492fe51d5aa4323 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Fri, 17 Mar 2017 15:05:27 -0700 Subject: [PATCH 23/56] new simpletraits --- REQUIRE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/REQUIRE b/REQUIRE index 1dbec1c8b..8583f7028 100644 --- a/REQUIRE +++ b/REQUIRE @@ -6,4 +6,4 @@ JLD 0.6.3 Distributions 0.10.2 StatsBase 0.9.0 DataStructures 0.5.0 -SimpleTraits 0.3.0 +SimpleTraits 0.4.0 From 7b953ee8a5c89b05533b103e386f9633633c038a Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Fri, 17 Mar 2017 23:11:01 -0700 Subject: [PATCH 24/56] first cut at 0.6 compat --- .travis.yml | 4 +- REQUIRE | 2 +- benchmark/edges.jl | 2 +- benchmarks/core.jl | 41 +++ src/LightGraphs.jl | 6 +- src/centrality/pagerank.jl | 2 +- src/community/label_propagation.jl | 2 +- src/core.jl | 4 +- src/flow/boykov_kolmogorov.jl | 2 +- src/flow/ext_multiroute_flow.jl | 2 +- src/flow/maximum_flow.jl | 12 +- src/flow/multiroute_flow.jl | 6 +- src/generators/randgraphs.jl | 6 +- src/graphtypes/simplegraph/SimpleGraphs.jl | 10 +- src/graphtypes/simplegraph/simpledigraph.jl | 2 +- src/graphtypes/simplegraph/simpleedge.jl | 4 +- src/graphtypes/simplegraph/simpleedgeiter.jl | 2 +- src/graphtypes/simplegraph/simplegraph.jl | 2 +- src/interface.jl | 6 +- src/linalg/graphmatrices.jl | 253 +++++++------------ src/linalg/spectral.jl | 4 +- src/persistence/graph6.jl | 2 +- src/persistence/jld.jl | 4 +- src/shortestpaths/bellman-ford.jl | 2 +- src/shortestpaths/dijkstra.jl | 4 +- src/spanningtrees/kruskal.jl | 2 +- src/spanningtrees/prim.jl | 2 +- src/traversals/graphvisit.jl | 12 +- src/traversals/maxadjvisit.jl | 4 +- test/centrality/betweenness.jl | 4 +- test/centrality/katz.jl | 2 +- test/centrality/pagerank.jl | 2 +- test/flow/boykov_kolmogorov.jl | 16 +- test/generators/randgraphs.jl | 4 +- test/graphtypes/simplegraphs/simplegraphs.jl | 10 +- test/linalg/graphmatrices.jl | 52 ++-- test/linalg/spectral.jl | 8 +- test/runtests.jl | 8 +- 38 files changed, 239 insertions(+), 273 deletions(-) create mode 100644 benchmarks/core.jl diff --git a/.travis.yml b/.travis.yml index 9810d63af..0b2c04468 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,8 @@ os: # - osx julia: - - 0.5 -# - nightly + # - 0.5 + - nightly notifications: email: false diff --git a/REQUIRE b/REQUIRE index 8583f7028..4da924ef8 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,4 +1,4 @@ -julia 0.5 +julia 0.6- GZip 0.2.20 EzXML 0.3.0 ParserCombinator 1.7.11 diff --git a/benchmark/edges.jl b/benchmark/edges.jl index 1bb5bf892..a4b631117 100644 --- a/benchmark/edges.jl +++ b/benchmark/edges.jl @@ -1,5 +1,5 @@ import Base: convert -typealias P Pair{Int, Int} +const P = Pair{Int, Int} convert(::Type{Tuple}, e::Pair) = (e.first, e.second) diff --git a/benchmarks/core.jl b/benchmarks/core.jl new file mode 100644 index 000000000..24fa36850 --- /dev/null +++ b/benchmarks/core.jl @@ -0,0 +1,41 @@ +bg = BenchmarkGroup() +SUITE["core"] = bg + +function bench_iteredges(g::AbstractGraph) + i = 0 + for e in edges(g) + i += 1 + end + return i +end + +function bench_has_edge(g::AbstractGraph) + srand(1) + nvg = nv(g) + srcs = rand([1:nvg;], cld(nvg, 4)) + dsts = rand([1:nvg;], cld(nvg, 4)) + i = 0 + for (s, d) in zip(srcs, dsts) + if has_edge(g, s, d) + i += 1 + end + end + return i +end + + +EDGEFNS = [ + bench_iteredges, + bench_has_edge +] + +for fun in EDGEFNS + for (name, g) in GRAPHS + bg["edges","$fun","graph","$name"] = @benchmarkable $fun($g) + end + + for (name, g) in DIGRAPHS + bg["edges","$fun","digraph","$name"] = @benchmarkable $fun($g) + end + +end diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 82fc7d8f5..fba3c5dd3 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -137,9 +137,9 @@ include("interface.jl") include("deprecations.jl") include("core.jl") include("graphtypes/simplegraph/SimpleGraphs.jl") - typealias Graph SimpleGraphs.SimpleGraph - typealias DiGraph SimpleGraphs.SimpleDiGraph - typealias Edge SimpleGraphs.SimpleEdge + const Graph = SimpleGraphs.SimpleGraph + const DiGraph = SimpleGraphs.SimpleDiGraph + const Edge = SimpleGraphs.SimpleEdge include("digraph-transitivity.jl") include("traversals/graphvisit.jl") diff --git a/src/centrality/pagerank.jl b/src/centrality/pagerank.jl index e9c4a562b..4c2d8adc8 100644 --- a/src/centrality/pagerank.jl +++ b/src/centrality/pagerank.jl @@ -24,7 +24,7 @@ function pagerank end for _ in 1:n xlast = x x = α * (M * x + sum(x[is_dangling]) * dangling_weights) + (1 - α) * p - err = sum(abs(x - xlast)) + err = sum(abs, (x - xlast)) if (err < N * ϵ) return x end diff --git a/src/community/label_propagation.jl b/src/community/label_propagation.jl index b4fb65f1b..a55b18244 100644 --- a/src/community/label_propagation.jl +++ b/src/community/label_propagation.jl @@ -11,7 +11,7 @@ function label_propagation(g::AbstractGraph; maxiter=1000) active_nodes = IntSet(vertices(g)) c = NeighComm(collect(one(T):n), fill(-1,n), one(T)) convergence_hist = Vector{Int}() - random_order = Array(T, n) + random_order = Vector{T}(n) i = 0 while !isempty(active_nodes) && i < maxiter num_active = length(active_nodes) diff --git a/src/core.jl b/src/core.jl index 7ee3b84b9..9de985c25 100644 --- a/src/core.jl +++ b/src/core.jl @@ -1,4 +1,4 @@ -abstract AbstractPathState +abstract type AbstractPathState end # modified from http://stackoverflow.com/questions/25678112/insert-item-into-a-sorted-list-with-julia-with-and-without-duplicates # 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)) @@ -100,7 +100,7 @@ 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). """ -density(G::AbstractGraph) = _NI("density") +function density end @traitfn density{G<:AbstractGraph; IsDirected{G}}(g::G) = ne(g) / (nv(g) * (nv(g)-1)) @traitfn density{G<:AbstractGraph; !IsDirected{G}}(g::G) = diff --git a/src/flow/boykov_kolmogorov.jl b/src/flow/boykov_kolmogorov.jl index fd1d7e40e..2bd572974 100644 --- a/src/flow/boykov_kolmogorov.jl +++ b/src/flow/boykov_kolmogorov.jl @@ -31,7 +31,7 @@ function boykov_kolmogorov_impl end ) T = eltype(capacity_matrix) -U = eltype(residual_graph) + U = eltype(residual_graph) n = nv(residual_graph) diff --git a/src/flow/ext_multiroute_flow.jl b/src/flow/ext_multiroute_flow.jl index 4332c1cb9..c360728cf 100644 --- a/src/flow/ext_multiroute_flow.jl +++ b/src/flow/ext_multiroute_flow.jl @@ -78,7 +78,7 @@ function auxiliaryPoints end r, expectedflow = intersection(r1, f1, s1, r2, f2, s2) f, F, cut = maximum_flow(flow_graph, source, target, capacity_matrix, algorithm = BoykovKolmogorovAlgorithm(), restriction = r) - s = slope(flow_graph, capacity_matrix, max(cut, 1), r) # current slope + s = slope(flow_graph, capacity_matrix, max.(cut, 1), r) # current slope auxpoints[λ + 1 - s] = (r, f) # If the flow at the intersection (middle) is as expected if expectedflow ≉ f # approximatively not equal (enforced by floating precision) diff --git a/src/flow/maximum_flow.jl b/src/flow/maximum_flow.jl index 8d39c5c38..f4ce78a22 100644 --- a/src/flow/maximum_flow.jl +++ b/src/flow/maximum_flow.jl @@ -1,30 +1,30 @@ """ Abstract type that allows users to pass in their preferred Algorithm """ -abstract AbstractFlowAlgorithm +abstract type AbstractFlowAlgorithm end """ Forces the maximum_flow function to use the Edmonds–Karp algorithm. """ -type EdmondsKarpAlgorithm <: AbstractFlowAlgorithm +struct EdmondsKarpAlgorithm <: AbstractFlowAlgorithm end """ Forces the maximum_flow function to use Dinic\'s algorithm. """ -type DinicAlgorithm <: AbstractFlowAlgorithm +struct DinicAlgorithm <: AbstractFlowAlgorithm end """ Forces the maximum_flow function to use the Boykov-Kolmogorov algorithm. """ -type BoykovKolmogorovAlgorithm <: AbstractFlowAlgorithm +struct BoykovKolmogorovAlgorithm <: AbstractFlowAlgorithm end """ Forces the maximum_flow function to use the Push-Relabel algorithm. """ -type PushRelabelAlgorithm <: AbstractFlowAlgorithm +struct PushRelabelAlgorithm <: AbstractFlowAlgorithm end """ @@ -179,7 +179,7 @@ function maximum_flow( restriction::Real = 0 # keyword argument for restriction max-flow ) if restriction > 0 - return maximum_flow(flow_graph, source, target, min(restriction, capacity_matrix), algorithm) + return maximum_flow(flow_graph, source, target, min.(restriction, capacity_matrix), algorithm) end return maximum_flow(flow_graph, source, target, capacity_matrix, algorithm) end diff --git a/src/flow/multiroute_flow.jl b/src/flow/multiroute_flow.jl index c663f12d3..79ab07ac6 100644 --- a/src/flow/multiroute_flow.jl +++ b/src/flow/multiroute_flow.jl @@ -1,18 +1,18 @@ """ Abstract type that allows users to pass in their preferred Algorithm """ -abstract AbstractMultirouteFlowAlgorithm +abstract type AbstractMultirouteFlowAlgorithm end """ Forces the multiroute_flow function to use the Kishimoto algorithm. """ -type KishimotoAlgorithm <: AbstractMultirouteFlowAlgorithm +struct KishimotoAlgorithm <: AbstractMultirouteFlowAlgorithm end """ Forces the multiroute_flow function to use the Extended Multiroute Flow algorithm. """ -type ExtendedMultirouteFlowAlgorithm <: AbstractMultirouteFlowAlgorithm +struct ExtendedMultirouteFlowAlgorithm <: AbstractMultirouteFlowAlgorithm end # Methods when the number of routes is more than the connectivity diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 2f02d4799..7a5634c50 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -45,7 +45,7 @@ function erdos_renyi(n::Integer, p::Real; is_directed=false, seed::Integer=-1) m = is_directed ? n*(n-1) : div(n*(n-1),2) if seed >= 0 # init dsfmt generator without altering GLOBAL_RNG - Base.dSFMT.dsfmt_gv_init_by_array(MersenneTwister(seed).seed+1) + Base.dSFMT.dsfmt_gv_init_by_array(MersenneTwister(seed).seed+0x01) end ne = rand(Binomial(m, p)) # sadly StatsBase doesn't support non-global RNG return is_directed ? DiGraph(n, ne, seed=seed) : Graph(n, ne, seed=seed) @@ -477,8 +477,8 @@ function random_regular_digraph(n::Int, k::Int; dir::Symbol=:out, seed::Int=-1) rng = getRNG(seed) cs = collect(2:n) i = 1 - I = Array(Int, n*k) - J = Array(Int, n*k) + I = Vector{Int}(n*k) + J = Vector{Int}(n*k) V = fill(true, n*k) for r in 1:n l = (r-1)*k+1 : r*k diff --git a/src/graphtypes/simplegraph/SimpleGraphs.jl b/src/graphtypes/simplegraph/SimpleGraphs.jl index 67ce45c95..9d83c0178 100644 --- a/src/graphtypes/simplegraph/SimpleGraphs.jl +++ b/src/graphtypes/simplegraph/SimpleGraphs.jl @@ -22,9 +22,7 @@ AbstractSimpleGraphs must have the following elements: - fadjlist::Vector{Vector{Integer}} - ne::Integer """ -abstract AbstractSimpleGraph <: AbstractGraph - - +abstract type AbstractSimpleGraph <: AbstractGraph end function show(io::IO, g::AbstractSimpleGraph) if is_directed(g) @@ -33,9 +31,9 @@ function show(io::IO, g::AbstractSimpleGraph) dir = "undirected" end if nv(g) == 0 - print(io, "empty $dir simple graph") + print(io, "empty $dir simple $(eltype(g)) graph") else - print(io, "{$(nv(g)), $(ne(g))} $dir simple graph") + print(io, "{$(nv(g)), $(ne(g))} $dir simple $(eltype(g)) graph") end end @@ -50,6 +48,7 @@ fadj(g::AbstractSimpleGraph, v::Integer) = g.fadjlist[v] badj(x...) = _NI("badj") has_edge(g::AbstractSimpleGraph, u::Integer, v::Integer) = has_edge(g, edgetype(g)(u,v)) + function add_edge!(g::AbstractSimpleGraph, u::Integer, v::Integer) T = eltype(g) add_edge!(g, edgetype(g)(T(u),T(v))) @@ -58,7 +57,6 @@ end in_neighbors(g::AbstractSimpleGraph, v::Integer) = badj(g,v) out_neighbors(g::AbstractSimpleGraph, v::Integer) = fadj(g,v) - function issubset{T<:AbstractSimpleGraph}(g::T, h::T) (gmin, gmax) = extrema(vertices(g)) (hmin, hmax) = extrema(vertices(h)) diff --git a/src/graphtypes/simplegraph/simpledigraph.jl b/src/graphtypes/simplegraph/simpledigraph.jl index 8e5a60f2e..1eac3e854 100644 --- a/src/graphtypes/simplegraph/simpledigraph.jl +++ b/src/graphtypes/simplegraph/simpledigraph.jl @@ -1,4 +1,4 @@ -typealias SimpleDiGraphEdge SimpleEdge +const SimpleDiGraphEdge = SimpleEdge """A type representing a directed graph.""" type SimpleDiGraph{T<:Integer} <: AbstractSimpleGraph diff --git a/src/graphtypes/simplegraph/simpleedge.jl b/src/graphtypes/simplegraph/simpleedge.jl index 23f28f1a7..25a686429 100644 --- a/src/graphtypes/simplegraph/simpleedge.jl +++ b/src/graphtypes/simplegraph/simpleedge.jl @@ -1,9 +1,9 @@ import Base: Pair, Tuple, show, == import LightGraphs: AbstractEdge, src, dst, reverse -abstract AbstractSimpleEdge <: AbstractEdge +abstract type AbstractSimpleEdge <: AbstractEdge end -immutable SimpleEdge{T<:Integer} <: AbstractSimpleEdge +struct SimpleEdge{T<:Integer} <: AbstractSimpleEdge src::T dst::T end diff --git a/src/graphtypes/simplegraph/simpleedgeiter.jl b/src/graphtypes/simplegraph/simpleedgeiter.jl index d8051ac37..270d60c6b 100644 --- a/src/graphtypes/simplegraph/simpleedgeiter.jl +++ b/src/graphtypes/simplegraph/simpleedgeiter.jl @@ -4,7 +4,7 @@ type SimpleEdgeIter{T<:Integer} <: AbstractEdgeIter directed::Bool end -immutable SimpleEdgeIterState{T<:Integer} +struct SimpleEdgeIterState{T<:Integer} s::T # src vertex di::Int # index into adj of dest vertex fin::Bool diff --git a/src/graphtypes/simplegraph/simplegraph.jl b/src/graphtypes/simplegraph/simplegraph.jl index 3755b8c4d..648f2be81 100644 --- a/src/graphtypes/simplegraph/simplegraph.jl +++ b/src/graphtypes/simplegraph/simplegraph.jl @@ -1,4 +1,4 @@ -typealias SimpleGraphEdge SimpleEdge +const SimpleGraphEdge = SimpleEdge """A type representing an undirected graph.""" type SimpleGraph{T<:Integer} <: AbstractSimpleGraph diff --git a/src/interface.jl b/src/interface.jl index 1114dda5c..8b53b1c04 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -3,13 +3,13 @@ _NI(m) = error("Not implemented: $m") """A type representing a single edge between two vertices of a graph.""" -abstract AbstractEdge +abstract type AbstractEdge end """A type representing an edge iterator""" -abstract AbstractEdgeIter +abstract type AbstractEdgeIter end """An abstract type representing a graph.""" -abstract AbstractGraph +abstract type AbstractGraph end @traitdef IsDirected{AbstractGraph} diff --git a/src/linalg/graphmatrices.jl b/src/linalg/graphmatrices.jl index c569229de..867f6c8aa 100644 --- a/src/linalg/graphmatrices.jl +++ b/src/linalg/graphmatrices.jl @@ -1,6 +1,7 @@ __precompile__(true) -@doc "A package for using the type system to check types of graph matrices." -> LinAlg -import Base: convert, sparse, size, scale, diag, eltype, ndims, ==, *, .*, issymmetric, A_mul_B!, length, Diagonal + + +import Base: convert, sparse, size, diag, eltype, ndims, ==, *, .*, issymmetric, A_mul_B!, length, Diagonal export convert, SparseMatrix, GraphMatrix, @@ -25,13 +26,14 @@ export convert, -typealias SparseMatrix{T} SparseMatrixCSC{T,Int64} +const SparseMatrix{T} = SparseMatrixCSC{T,Int64} -@doc "An abstract type to allow opertions on any type of graph matrix" -> -abstract GraphMatrix{T} +"""An abstract type to allow opertions on any type of graph matrix""" +abstract type GraphMatrix{T} end -@doc "The core Adjacency matrix structure. Keeps the vertex degrees around. +""" +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. @@ -39,11 +41,12 @@ Adjacency(lapl::Laplacian) provide 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 -is the final subtype that corresponds to this type of Laplacian" -> -abstract Adjacency{T} <: GraphMatrix{T} -abstract Laplacian{T} <: GraphMatrix +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 -@doc "Combinatorial Adjacency matrix is the standard adjacency matrix from math" -> +"""Combinatorial Adjacency matrix is the standard adjacency matrix from math""" type CombinatorialAdjacency{T,S,V} <: Adjacency{T} A::S D::V @@ -55,150 +58,107 @@ function CombinatorialAdjacency{T}(A::SparseMatrix{T}) end -@doc "Normalized Adjacency matrix is \$\\hat{A} = D^{-1/2} A D^{-1/2}\$. +""" +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]." -> +with real eigenvalues bounded by [-1, 1]. +""" type NormalizedAdjacency{T} <: Adjacency{T} A::CombinatorialAdjacency{T} scalefactor::Vector{T} - - function NormalizedAdjacency(adjmat::CombinatorialAdjacency) - sf = adjmat.D.^(-1/2) - return new(adjmat, sf) - end end -function NormalizedAdjacency{T}(adjmat::CombinatorialAdjacency{T}) - return NormalizedAdjacency{T}(adjmat) +function NormalizedAdjacency(adjmat::CombinatorialAdjacency) + sf = adjmat.D.^(-1/2) + return NormalizedAdjacency(adjmat, sf) end -@doc "Transition matrix for the random walk." -> +"""Transition matrix for the random walk.""" type StochasticAdjacency{T} <: Adjacency{T} A::CombinatorialAdjacency{T} scalefactor::Vector{T} - function StochasticAdjacency(adjmat::CombinatorialAdjacency) - sf = adjmat.D.^(-1) - return new(adjmat, sf) - end end -function StochasticAdjacency{T}(adjmat::CombinatorialAdjacency{T}) - return StochasticAdjacency{T}(adjmat) +function StochasticAdjacency(adjmat::CombinatorialAdjacency) + sf = adjmat.D.^(-1) + return StochasticAdjacency(adjmat, sf) end -@doc "The matrix whos action is to average over each neighborhood." -> + +"""The matrix whos action is to average over each neighborhood.""" type AveragingAdjacency{T} <: Adjacency{T} A::CombinatorialAdjacency{T} scalefactor::Vector{T} - - function AveragingAdjacency(adjmat::CombinatorialAdjacency) - sf = adjmat.D.^(-1) - return new(adjmat, sf) - end end -function AveragingAdjacency{T}(adjmat::CombinatorialAdjacency{T}) - return AveragingAdjacency{T}(adjmat) +function AveragingAdjacency(adjmat::CombinatorialAdjacency) + sf = adjmat.D.^(-1) + return AveragingAdjacency(adjmat, sf) end -perron(adjmat::NormalizedAdjacency) = sqrt(adjmat.A.D)/norm(sqrt(adjmat.A.D)) +perron(adjmat::NormalizedAdjacency) = sqrt.(adjmat.A.D)/norm(sqrt.(adjmat.A.D)) type PunchedAdjacency{T} <: Adjacency{T} A::NormalizedAdjacency{T} perron::Vector{T} - - function PunchedAdjacency(adjmat::CombinatorialAdjacency) - perron=sqrt(adjmat.D)/norm(sqrt(adjmat.D)) - return new(NormalizedAdjacency(adjmat), perron) - end end - -function PunchedAdjacency{T}(adjmat::CombinatorialAdjacency{T}) - return PunchedAdjacency{T}(adjmat) +function PunchedAdjacency(adjmat::CombinatorialAdjacency) + perron=sqrt.(adjmat.D)/norm(sqrt.(adjmat.D)) + return PunchedAdjacency(NormalizedAdjacency(adjmat), perron) end + perron(m::PunchedAdjacency) = m.perron -@doc "Noop: a type to represent don't do anything. -The purpose is to help write more general code for the different scaled GraphMatrix types." -> -type Noop +""" +Noop: a type to represent don't do anything. +The purpose is to help write more general code for the different scaled GraphMatrix types. +""" +immutable Noop end -function .*(::Noop, x::Any) - return x -end +Base.broadcast(::typeof(*), ::Noop, x) = x -function Diagonal(::Noop) - return Noop() -end +Diagonal(::Noop) = Noop() +A_mul_B!(Y, A::Noop, B) = copy!(Y, B) -function A_mul_B!(Y, A::Noop, B) - return copy!(Y, B) -end -function ==(g::GraphMatrix, h::GraphMatrix) - if typeof(g) != typeof(h) - return false - end - if g.A == h.A - return true - end -end +==(g::GraphMatrix, h::GraphMatrix) = typeof(g) == typeof(h) && (g.A == h.A) -@doc "postscalefactor(M)*M.A*prescalefactor(M) == M " -> -function postscalefactor(::Adjacency) - return Noop() -end -function postscalefactor(adjmat::NormalizedAdjacency) - return adjmat.scalefactor -end -function postscalefactor(adjmat::AveragingAdjacency) - return adjmat.scalefactor -end +postscalefactor(::Adjacency)= Noop() +postscalefactor(adjmat::NormalizedAdjacency) = adjmat.scalefactor -@doc "postscalefactor(M)*M.A*prescalefactor(M) == M " -> -function prescalefactor(::Adjacency) - return Noop() -end -function prescalefactor(adjmat::NormalizedAdjacency) - return adjmat.scalefactor -end -function prescalefactor(adjmat::StochasticAdjacency) - return adjmat.scalefactor -end +postscalefactor(adjmat::AveragingAdjacency) = adjmat.scalefactor + +prescalefactor(::Adjacency) = Noop() + +prescalefactor(adjmat::NormalizedAdjacency) = adjmat.scalefactor + +prescalefactor(adjmat::StochasticAdjacency) = adjmat.scalefactor -@doc "Combinatorial Laplacian L = D-A" -> type CombinatorialLaplacian{T} <: Laplacian{T} A::CombinatorialAdjacency{T} end -@doc "Normalized Laplacian is \$\\hat{L} = I - D^{-1/2} A D^{-1/2}\$. +doc""" +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." -> +with positive eigenvalues bounded by 2. +""" type NormalizedLaplacian{T} <: Laplacian{T} A::NormalizedAdjacency{T} end -@doc "Laplacian version of the StochasticAdjacency matrix." -> +"""Laplacian version of the StochasticAdjacency matrix.""" type StochasticLaplacian{T} <: Laplacian{T} A::StochasticAdjacency{T} end -@doc "Laplacian version of the AveragingAdjacency matrix." -> +"""Laplacian version of the AveragingAdjacency matrix.""" type AveragingLaplacian{T} <: Laplacian{T} A::AveragingAdjacency{T} end -# function passthrough{T: -function degrees(adjmat::CombinatorialAdjacency) - return adjmat.D -end -function degrees(mat::GraphMatrix) - return degrees(adjacency(mat)) -end -# function degrees(lapl::Laplacian) -# return degrees(adjacency(lapl)) -# end - -function adjacency(lapl::Laplacian) - return lapl.A -end +"""degrees of a graph as a Vector.""" +degrees(adjmat::CombinatorialAdjacency) = adjmat.D +degrees(mat::GraphMatrix) = degrees(adjacency(mat)) -function adjacency(lapl::GraphMatrix) - return lapl.A -end +adjacency(lapl::Laplacian) = lapl.A +adjacency(lapl::GraphMatrix) = lapl.A -function convert(::Type{Adjacency}, lapl::Laplacian) - return lapl.A -end -function convert(::Type{CombinatorialAdjacency}, adjmat::Adjacency) - return adjmat.A -end +convert(::Type{Adjacency}, lapl::Laplacian) = lapl.A +convert(::Type{CombinatorialAdjacency}, adjmat::Adjacency) = adjmat.A +convert(::Type{SparseMatrix}, adjmat::CombinatorialAdjacency) = adjmat.A -function convert(::Type{SparseMatrix}, adjmat::CombinatorialAdjacency) - return adjmat.A -end function sparse{M <: Laplacian}(lapl::M) adjmat = adjacency(lapl) @@ -264,35 +206,21 @@ function convert{T}(::Type{SparseMatrix{T}}, lapl::Laplacian{T}) return L end -function diag(lapl::CombinatorialLaplacian) - d = lapl.A.D - return d -end +diag(lapl::CombinatorialLaplacian) = lapl.A.D +diag(lapl::Laplacian) = ones(size(lapl)[2]) -function diag(lapl::Laplacian) - return ones(size(lapl)[2]) -end +*(x::AbstractArray, ::Noop) = x +*(::Noop, x) = x +*{T<:Number}(adjmat::Adjacency{T}, x::AbstractVector{T}) = + postscalefactor(adjmat) .* (adjmat.A * (prescalefactor(adjmat) .* x)) -function *(x::AbstractArray, ::Noop) - return x -end -function *(::Noop, x::Any) - return x -end -function *{T<:Number}(adjmat::Adjacency{T}, x::AbstractVector{T}) - return postscalefactor(adjmat) .* (adjmat.A * (prescalefactor(adjmat) .* x)) -end +*{T<:Number}(adjmat::CombinatorialAdjacency{T}, x::AbstractVector{T}) = + adjmat.A * x -function *{T<:Number}(adjmat::CombinatorialAdjacency{T}, x::AbstractVector{T}) - return adjmat.A * x -end +*{T<:Number}(lapl::Laplacian{T}, x::AbstractVector{T}) = + (diag(lapl) .* x) - (adjacency(lapl)*x) -function *{T<:Number}(lapl::Laplacian{T}, x::AbstractVector{T}) - y = adjacency(lapl)*x - z = diag(lapl) .* x - return z - y -end function *{T<:Number}(adjmat::PunchedAdjacency{T}, x::AbstractVector{T}) y=adjmat.A*x @@ -311,9 +239,7 @@ function A_mul_B!(Y, A::Adjacency, B) return A_mul_B!(Y, Diagonal(postscalefactor(A)), tmp) end -function A_mul_B!(Y, A::CombinatorialAdjacency, B) - return A_mul_B!(Y, A.A, B) -end +A_mul_B!(Y, A::CombinatorialAdjacency, B) = A_mul_B!(Y, A.A, B) # You can compute the StochasticAdjacency product without allocating a similar of Y. # This is true for all Adjacency where the postscalefactor is a Noop @@ -338,9 +264,11 @@ function A_mul_B!(Y, lapl::Laplacian, B) end -@doc "Symmetrize the matrix. +""" +Symmetrize the matrix. :triu, :tril, :sum, :or. -use :sum for weighted graphs."-> +use :sum for weighted graphs. +""" function symmetrize(A::SparseMatrix, which=:or) if which==:or M = A + A' @@ -361,14 +289,11 @@ function symmetrize(A::SparseMatrix, which=:or) return M end -@doc "Only works on Adjacency because the normalizations don't commute with symmetrization"-> -function symmetrize(adjmat::CombinatorialAdjacency, which=:or) - # T = typeof(adjmat) - # @show T - # if T <: StochasticAdjacency || T <: AveragingAdjacency - # TypeError("StochasticAdjacency and AveragingAdjacency matrices are nonsymmetric. - # Use NormalizedAdjacency for this purpose") - # end - Aprime = symmetrize(adjmat.A, which) - return CombinatorialAdjacency(Aprime) -end +""" +Only works on Adjacency because the normalizations don't commute with symmetrization. +""" +symmetrize(adjmat::CombinatorialAdjacency, which=:or) = + CombinatorialAdjacency(symmetrize(adjmat.A, which)) + +"""A package for using the type system to check types of graph matrices.""" +LinAlg diff --git a/src/linalg/spectral.jl b/src/linalg/spectral.jl index 90ab3d54c..b9d0097cf 100644 --- a/src/linalg/spectral.jl +++ b/src/linalg/spectral.jl @@ -152,10 +152,10 @@ function spectral_distance end λ₁ = k < nv(G₁)-1 ? eigs(A₁, nev=k, which=:LR)[1] : eigvals(full(A₁))[end:-1:end-(k-1)] λ₂ = k < nv(G₂)-1 ? eigs(A₂, nev=k, which=:LR)[1] : eigvals(full(A₂))[end:-1:end-(k-1)] - sumabs(λ₁ - λ₂) + return sum(abs, (λ₁ - λ₂)) end @traitfn function spectral_distance{G<:AbstractGraph; !IsDirected{G}}(G₁::G, G₂::G) @assert nv(G₁) == nv(G₂) "spectral distance not defined for |G₁| != |G₂|" - spectral_distance(G₁, G₂, nv(G₁)) + return spectral_distance(G₁, G₂, nv(G₁)) end diff --git a/src/persistence/graph6.jl b/src/persistence/graph6.jl index 34988efdc..daec8dbb0 100644 --- a/src/persistence/graph6.jl +++ b/src/persistence/graph6.jl @@ -84,7 +84,7 @@ function _graphToG6String(g::Graph) return join([">>graph6<<", String(_g6_N(n)), String(_g6_R(x))]) end -function _g6StringToGraph(s::String) +function _g6StringToGraph(s::AbstractString) if startswith(s, ">>graph6<<") s = s[11:end] end diff --git a/src/persistence/jld.jl b/src/persistence/jld.jl index 6f442a407..465e5426c 100644 --- a/src/persistence/jld.jl +++ b/src/persistence/jld.jl @@ -19,7 +19,7 @@ This format is fine for storage on disk because the data is not changing. JLD.readas(gs::GraphSerializer) creates the adjacency list representation directly instead of calling add_edge! repeatedly in an attempt to improve performance. -This type has not been tested with mmaped files or compression in JLD. +This type has not been tested with mmaped files or compression in JLD. """ type GraphSerializer vertices::UnitRange{Int} @@ -31,7 +31,7 @@ end function JLD.writeas(g::Graph) n_adjlist = zeros(Int,nv(g)) @assert sum(degree(g))/2 == ne(g) - packed_adjlist = Array(Int, 2*ne(g)) + packed_adjlist = Vector{Int}(2*ne(g)) k = 0 degree(g), sum(degree(g)) for (i,lst) in enumerate(g.fadjlist) diff --git a/src/shortestpaths/bellman-ford.jl b/src/shortestpaths/bellman-ford.jl index ab5b1b09a..023b5dab3 100644 --- a/src/shortestpaths/bellman-ford.jl +++ b/src/shortestpaths/bellman-ford.jl @@ -89,7 +89,7 @@ function enumerate_paths{T<:Integer}(state::AbstractPathState, dest::Vector{T}) parents = state.parents num_dest = length(dest) - all_paths = Array(Vector{T},num_dest) + all_paths = Vector{Vector{T}}(num_dest) for i=1:num_dest all_paths[i] = Vector{T}() index = dest[i] diff --git a/src/shortestpaths/dijkstra.jl b/src/shortestpaths/dijkstra.jl index 909074eef..d8a57d90d 100644 --- a/src/shortestpaths/dijkstra.jl +++ b/src/shortestpaths/dijkstra.jl @@ -1,6 +1,6 @@ -abstract AbstractDijkstraState<:AbstractPathState +abstract type AbstractDijkstraState<:AbstractPathState end -immutable DijkstraHeapEntry{T, U<:Integer} +struct DijkstraHeapEntry{T, U<:Integer} vertex::U dist::T end diff --git a/src/spanningtrees/kruskal.jl b/src/spanningtrees/kruskal.jl index b80bc7a42..bb7aa426f 100644 --- a/src/spanningtrees/kruskal.jl +++ b/src/spanningtrees/kruskal.jl @@ -1,4 +1,4 @@ -immutable KruskalHeapEntry{T<:Real} +struct KruskalHeapEntry{T<:Real} edge::Edge dist::T end diff --git a/src/spanningtrees/prim.jl b/src/spanningtrees/prim.jl index 011740a97..14e2b8a24 100644 --- a/src/spanningtrees/prim.jl +++ b/src/spanningtrees/prim.jl @@ -1,4 +1,4 @@ -immutable PrimHeapEntry{T<:Real} +struct PrimHeapEntry{T<:Real} edge::Edge dist::T end diff --git a/src/traversals/graphvisit.jl b/src/traversals/graphvisit.jl index dc538495e..f37a81a53 100644 --- a/src/traversals/graphvisit.jl +++ b/src/traversals/graphvisit.jl @@ -3,7 +3,7 @@ # The concept and trivial implementation of graph visitors -abstract AbstractGraphVisitor +abstract type AbstractGraphVisitor end # trivial implementation @@ -21,17 +21,17 @@ examine_neighbor!(vis::AbstractGraphVisitor, u, v, ucolor::Int, vcolor::Int, eco close_vertex!(vis::AbstractGraphVisitor, v) = true -type TrivialGraphVisitor <: AbstractGraphVisitor +struct TrivialGraphVisitor <: AbstractGraphVisitor end # This is the common base for BreadthFirst and DepthFirst -abstract AbstractGraphVisitAlgorithm +abstract type AbstractGraphVisitAlgorithm end -typealias AbstractEdgeMap{T} Associative{Edge,T} -typealias AbstractVertexMap{T<:Integer, U} Union{AbstractVector{T},Associative{T, U}} +const AbstractEdgeMap{T} = Associative{Edge,T} +const AbstractVertexMap{T<:Integer, U} = Union{AbstractVector{T},Associative{T, U}} -type DummyEdgeMap <: AbstractEdgeMap{Int} +struct DummyEdgeMap <: AbstractEdgeMap{Int} end getindex(d::DummyEdgeMap, e::Edge) = 0 diff --git a/src/traversals/maxadjvisit.jl b/src/traversals/maxadjvisit.jl index 17ef7e4f0..44fbbd212 100644 --- a/src/traversals/maxadjvisit.jl +++ b/src/traversals/maxadjvisit.jl @@ -10,10 +10,10 @@ # ################################################# -type MaximumAdjacency <: AbstractGraphVisitAlgorithm +struct MaximumAdjacency <: AbstractGraphVisitAlgorithm end -abstract AbstractMASVisitor <: AbstractGraphVisitor +abstract type AbstractMASVisitor <: AbstractGraphVisitor end function maximum_adjacency_visit_impl!{T}( g::AbstractGraph, # the graph diff --git a/test/centrality/betweenness.jl b/test/centrality/betweenness.jl index 30b582284..a6d75d997 100644 --- a/test/centrality/betweenness.jl +++ b/test/centrality/betweenness.jl @@ -24,8 +24,8 @@ @test map(Float32, z) == map(Float32, c) y = betweenness_centrality(g, endpoints=true, normalize=false) - @test round(y[1:3],4) == - round([122.10760591498584, 159.0072453120582, 176.39547945994505], 4) + @test round.(y[1:3],4) == + round.([122.10760591498584, 159.0072453120582, 176.39547945994505], 4) x = betweenness_centrality(g,3) @test length(x) == 50 diff --git a/test/centrality/katz.jl b/test/centrality/katz.jl index f9532a22e..b7458a63a 100644 --- a/test/centrality/katz.jl +++ b/test/centrality/katz.jl @@ -3,6 +3,6 @@ add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) for g in testdigraphs(g5) z = katz_centrality(g, 0.4) - @test round(z, 2) == [0.32, 0.44, 0.62, 0.56] + @test round.(z, 2) == [0.32, 0.44, 0.62, 0.56] end end diff --git a/test/centrality/pagerank.jl b/test/centrality/pagerank.jl index d603f8960..3ee6fbf6c 100644 --- a/test/centrality/pagerank.jl +++ b/test/centrality/pagerank.jl @@ -2,7 +2,7 @@ g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) for g in testdigraphs(g5) - @test_approx_eq_eps(pagerank(g)[3], 0.318, 0.001) + @test pagerank(g)[3] ≈ 0.318 atol=0.001 @test_throws ErrorException pagerank(g, 2) @test_throws ErrorException pagerank(g, 0.85, 2) end diff --git a/test/flow/boykov_kolmogorov.jl b/test/flow/boykov_kolmogorov.jl index 78a49fe1b..70f91d211 100644 --- a/test/flow/boykov_kolmogorov.jl +++ b/test/flow/boykov_kolmogorov.jl @@ -1,14 +1,14 @@ @testset "Boykov Kolmogorov" begin # construct graph - G = DiGraph(3) - add_edge!(G,1,2) - add_edge!(G,2,3) + gg = DiGraph(3) + add_edge!(gg,1,2) + add_edge!(gg,2,3) # source and sink terminals source, target = 1, 3 - for g in testdigraphs(G) + for g in testdigraphs(gg) # default capacity capacity_matrix = LightGraphs.DefaultCapacity(g) residual_graph = LightGraphs.residual(g) @@ -19,8 +19,12 @@ TREE[target] = T(2) PARENT = zeros(T, 3) A = [T(source),T(target)] - path = LightGraphs.find_path!(residual_graph, source, target, flow_matrix, capacity_matrix, PARENT, TREE, A) +# see https://github.com/JuliaLang/julia/issues/21077 +# @show("testing $g with eltype $T, residual_graph type is $(eltype(residual_graph)), flow_matrix type is $(eltype(flow_matrix)), capacity_matrix type is $(eltype(capacity_matrix))") + path = LightGraphs.find_path!( + residual_graph, source, target, flow_matrix, + capacity_matrix, PARENT, TREE, A) @test path == [1,2,3] - end + end end diff --git a/test/generators/randgraphs.jl b/test/generators/randgraphs.jl index f6c125c69..2e8319c0e 100644 --- a/test/generators/randgraphs.jl +++ b/test/generators/randgraphs.jl @@ -213,7 +213,7 @@ bc = blockcounts(sbm, g) bp = blockfractions(sbm, g) ./ (sizes * sizes') ratios = bp ./ (sbm.affinities ./ sum(sbm.affinities)) - @test norm(ratios) < 0.25 + @test norm(Array(ratios)) < 0.25 sizes = [200, 200, 100] internaldeg = 15 @@ -229,7 +229,7 @@ bc = blockcounts(sbm, g) bp = blockfractions(sbm, g) ./ (sizes * sizes') ratios = bp ./ (sbm.affinities ./ sum(sbm.affinities)) - @test norm(ratios) < 0.25 + @test norm(Array(ratios)) < 0.25 # check that average degree is not too high # factor of two is cushion for random process diff --git a/test/graphtypes/simplegraphs/simplegraphs.jl b/test/graphtypes/simplegraphs/simplegraphs.jl index dd5bc7c6b..1e5693bd5 100644 --- a/test/graphtypes/simplegraphs/simplegraphs.jl +++ b/test/graphtypes/simplegraphs/simplegraphs.jl @@ -21,16 +21,18 @@ type DummySimpleGraph <: AbstractSimpleGraph end gx = SimpleGraph() for g in testgraphs(gx) - @test sprint(show, g) == "empty undirected simple graph" + T = eltype(g) + @test sprint(show, g) == "empty undirected simple $T graph" add_vertices!(g, 5) - @test sprint(show, g) == "{5, 0} undirected simple graph" + @test sprint(show, g) == "{5, 0} undirected simple $T graph" end gx = SimpleDiGraph() for g in testdigraphs(gx) - @test sprint(show, g) == "empty directed simple graph" + T = eltype(g) + @test sprint(show, g) == "empty directed simple $T graph" add_vertices!(g, 5) - @test sprint(show, g) == "{5, 0} directed simple graph" + @test sprint(show, g) == "{5, 0} directed simple $T graph" end gx = PathGraph(4) diff --git a/test/linalg/graphmatrices.jl b/test/linalg/graphmatrices.jl index 977091f03..05baf912d 100644 --- a/test/linalg/graphmatrices.jl +++ b/test/linalg/graphmatrices.jl @@ -43,22 +43,18 @@ export test_adjacency, test_laplacian, test_accessors, test_arithmetic, test_oth @test typeof(StochasticAdjacency(adj)) <: StochasticAdjacency @test typeof(NormalizedAdjacency(adj)) <: NormalizedAdjacency @test typeof(AveragingAdjacency(adj)) <: AveragingAdjacency - if VERSION >= v"0.4" - @test typeof(adjacency(lapl)) <: CombinatorialAdjacency - converttest(SparseMatrix{Float64},lapl) - else - @test adjacency(lapl) != nothing - @test sparse(lapl) != nothing - end + + @test typeof(adjacency(lapl)) <: CombinatorialAdjacency + converttest(SparseMatrix{Float64},lapl) adjmat, stochmat, adjhat, avgmat = constructors(mat) @test typeof(adjacency(lapl)) <: CombinatorialAdjacency - stochlapl = StochasticLaplacian(StochasticAdjacency{Float64}(adjmat)) + stochlapl = StochasticLaplacian(StochasticAdjacency(adjmat)) @test typeof(adjacency(stochlapl)) <: StochasticAdjacency - averaginglapl = AveragingLaplacian(AveragingAdjacency{Float64}(adjmat)) + averaginglapl = AveragingLaplacian(AveragingAdjacency(adjmat)) @test typeof(adjacency(averaginglapl)) <: AveragingAdjacency - normalizedlapl = NormalizedLaplacian(NormalizedAdjacency{Float64}(adjmat)) + normalizedlapl = NormalizedLaplacian(NormalizedAdjacency(adjmat)) @test typeof(adjacency(normalizedlapl)) <: NormalizedAdjacency @test !( typeof(adjacency(normalizedlapl)) <: CombinatorialAdjacency) @@ -69,7 +65,7 @@ export test_adjacency, test_laplacian, test_accessors, test_arithmetic, test_oth @test_throws MethodError AveragingLaplacian(lapl) @test_throws MethodError convert(CombinatorialAdjacency, lapl) L = convert(SparseMatrix{Float64}, lapl) - @test sum(abs(sum(L,1))) == 0 + @test sum(abs, (sum(L,1))) == 0 end function test_accessors(mat, n) @@ -92,17 +88,17 @@ export test_adjacency, test_laplacian, test_accessors, test_arithmetic, test_oth lapl = CombinatorialLaplacian(adjmat) onevec = ones(Float64, n) v = adjmat*ones(Float64, n) - @test sum(abs(adjmat*onevec)) > 0.0 - @test_approx_eq sum(abs(stochmat*onevec/sum(onevec))) 1.0 - @test sum(abs(lapl*onevec)) == 0 - g(a) = sum(abs(sum(sparse(a),1))) + @test sum(abs, (adjmat*onevec)) > 0.0 + @test sum(abs, ((stochmat * onevec) / sum(onevec))) ≈ 1.0 + @test sum(abs, (lapl*onevec)) == 0 + g(a) = sum(abs, (sum(sparse(a),1))) @test g(lapl) == 0 @test g(NormalizedLaplacian(adjhat)) > 1e-13 @test g(StochasticLaplacian(stochmat)) > 1e-13 @test eigs(adjmat, which=:LR)[1][1] > 1.0 - @test_approx_eq eigs(stochmat, which=:LR)[1][1] 1.0 - @test_approx_eq eigs(avgmat, which=:LR)[1][1] 1.0 + @test eigs(stochmat, which=:LR)[1][1] ≈ 1.0 + @test eigs(avgmat, which=:LR)[1][1] ≈ 1.0 @test eigs(lapl, which=:LR)[1][1] > 2.0 @test_throws MethodError eigs(lapl, which=:SM)[1][1] # --> greater_than(-0.0) lhat = NormalizedLaplacian(adjhat) @@ -116,10 +112,10 @@ export test_adjacency, test_laplacian, test_accessors, test_arithmetic, test_oth @test size(lapl, 2) == n @test size(lapl, 3) == 1 - @test_throws MethodError symmetrize(StochasticAdjacency{Float64}(adjmat)) - @test_throws MethodError symmetrize(AveragingAdjacency{Float64}(adjmat)) - @test !issymmetric(AveragingAdjacency{Float64}(adjmat)) - @test !issymmetric(StochasticAdjacency{Float64}(adjmat)) + @test_throws MethodError symmetrize(StochasticAdjacency(adjmat)) + @test_throws MethodError symmetrize(AveragingAdjacency(adjmat)) + @test !issymmetric(AveragingAdjacency(adjmat)) + @test !issymmetric(StochasticAdjacency(adjmat)) @test_throws MethodError symmetrize(NormalizedAdjacency(adjmat)).A # --> adjmat.A begin @@ -142,8 +138,8 @@ export test_adjacency, test_laplacian, test_accessors, test_arithmetic, test_oth @test size(lapl, 2) == n @test size(lapl, 3) == 1 - @test_throws MethodError symmetrize(StochasticAdjacency{Float64}(adjmat)) - @test_throws MethodError symmetrize(AveragingAdjacency{Float64}(adjmat)) + @test_throws MethodError symmetrize(StochasticAdjacency(adjmat)) + @test_throws MethodError symmetrize(AveragingAdjacency(adjmat)) @test_throws MethodError symmetrize(NormalizedAdjacency(adjmat)).A # --> adjmat.A @test symmetrize(adjmat).A == adjmat.A # these tests are basically the code @@ -158,16 +154,16 @@ export test_adjacency, test_laplacian, test_accessors, test_arithmetic, test_oth adjmat = CombinatorialAdjacency(mat) ahatp = PunchedAdjacency(adjmat) y = ahatp * perron(ahatp) - @test_approx_eq_eps dot(y, ahatp.perron) 0.0 1e-8 - @test_approx_eq_eps sumabs(y) 0.0 1e-8 + @test dot(y, ahatp.perron) ≈ 0.0 atol=1.0e-8 + @test sum(abs, y) ≈ 0.0 atol=1.0e-8 eval, evecs = eigs(ahatp, which=:LM) @test eval[1]-1 <= 0 - @test_approx_eq_eps dot(perron(ahatp), evecs[:,1]) 0.0 1e-8 + @test dot(perron(ahatp), evecs[:,1]) ≈ 0.0 atol=1e-8 ahat = ahatp.A @test isa(ahat, NormalizedAdjacency) z = ahatp * perron(ahat) - @test_approx_eq_eps norm(z) 0.0 1e-8 + @test norm(z) ≈ 0.0 atol=1e-8 end begin @@ -202,7 +198,7 @@ export test_adjacency, test_laplacian, test_accessors, test_arithmetic, test_oth end - @doc "Computes the stationary distribution of a random walk" -> + """Computes the stationary distribution of a random walk""" function stationarydistribution(R::StochasticAdjacency; kwargs...) er = eigs(R, nev=1, which=:LR; kwargs...) l1 = er[1][1] diff --git a/test/linalg/spectral.jl b/test/linalg/spectral.jl index 48461bcae..43c133839 100644 --- a/test/linalg/spectral.jl +++ b/test/linalg/spectral.jl @@ -58,7 +58,7 @@ full(nbt::Nonbacktracking) = full(sparse(nbt)) end for g in testdigraphs(g5) - @test_approx_eq_eps(adjacency_spectrum(g)[3],0.311, 0.001) + @test (adjacency_spectrum(g))[3] ≈ 0.311 atol=0.001 end for g in testgraphs(g3) @@ -89,7 +89,7 @@ full(nbt::Nonbacktracking) = full(sparse(nbt)) @test isa(lmat, SparseMatrixCSC{Float64, Int64}) evals = eigvals(full(lmat)) @test all(evals .>= -1e-15) # positive semidefinite - @test_approx_eq_eps minimum(evals) 0 1e-13 + @test (minimum(evals)) ≈ 0 atol=1e-13 end end @@ -125,7 +125,7 @@ full(nbt::Nonbacktracking) = full(sparse(nbt)) B, emap = non_backtracking_matrix(g) Bs = sparse(nbt) @test sparse(B) == Bs - @test_approx_eq_eps(eigs(nbt, nev=1)[1], eigs(B, nev=1)[1], 1e-5) + @test eigs(nbt, nev=1)[1] ≈ eigs(B, nev=1)[1] atol=1e-5 # check that matvec works x = ones(Float64, nbt.m) @@ -147,7 +147,7 @@ full(nbt::Nonbacktracking) = full(sparse(nbt)) @test full(B₁) == full(B) @test B₁ * ones(size(B₁)[2]) == B*ones(size(B)[2]) @test size(B₁) == size(B) - # @test_approx_eq_eps norm(eigs(B₁)[1] - eigs(B)[1]) 0.0 1e-8 + # @test norm(eigs(B₁)[1] - eigs(B)[1]) ≈ 0.0 atol=1e-8 @test !issymmetric(B₁) @test eltype(B₁) == Float64 end diff --git a/test/runtests.jl b/test/runtests.jl index 2b5e576b8..4a82c0992 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -36,13 +36,13 @@ using Base.Test testdir = dirname(@__FILE__) -testgraphs(g) = (g, Graph{UInt8}(g), Graph{Int16}(g)) -testdigraphs(g) = (g, DiGraph{UInt8}(g), DiGraph{Int16}(g)) +testgraphs(g) = [g, Graph{UInt8}(g), Graph{Int16}(g)] +testdigraphs(g) = [g, DiGraph{UInt8}(g), DiGraph{Int16}(g)] # some operations will create a large graph from two smaller graphs. We # might error out on very small eltypes. -testlargegraphs(g) = (g, Graph{UInt16}(g), Graph{Int32}(g)) -testlargedigraphs(g) = (g, DiGraph{UInt16}(g), DiGraph{Int32}(g)) +testlargegraphs(g) = [g, Graph{UInt16}(g), Graph{Int32}(g)] +testlargedigraphs(g) = [g, DiGraph{UInt16}(g), DiGraph{Int32}(g)] tests = [ "interface", From de342ea36971c2e46d5754f8ba40e0f9742fe7f4 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sat, 18 Mar 2017 00:07:20 -0700 Subject: [PATCH 25/56] squash --- src/LightGraphs.jl | 2 +- src/core.jl | 22 ++++++++++++++++++---- test/core.jl | 8 +++----- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index fba3c5dd3..625629d86 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -25,7 +25,7 @@ has_vertex, has_edge, in_neighbors, out_neighbors, empty, is_ordered, add_vertices!, indegree, outdegree, degree, Δout, Δin, δout, δin, Δ, δ, degree_histogram, neighbors, all_neighbors, common_neighbors, -has_self_loops, num_self_loops, density, +has_self_loops, num_self_loops, density, squash, # distance eccentricity, diameter, periphery, radius, center, diff --git a/src/core.jl b/src/core.jl index 9de985c25..f025fecc1 100644 --- a/src/core.jl +++ b/src/core.jl @@ -15,18 +15,18 @@ 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::AbstractGraph, v::Integer) = length(in_neighbors(g, v)) -indegree{T<:Integer}(g::AbstractGraph, v::AbstractArray{T,1} = vertices(g)) = [indegree(g,x) for x in v] +indegree{T<:Integer}(g::AbstractGraph, v::AbstractVector{T} = vertices(g)) = [indegree(g,x) for x in v] """Return the number of edges which start at vertex `v`.""" outdegree(g::AbstractGraph, v::Integer) = length(out_neighbors(g, v)) -outdegree{T<:Integer}(g::AbstractGraph, v::AbstractArray{T,1} = vertices(g)) = [outdegree(g,x) for x in v] +outdegree{T<:Integer}(g::AbstractGraph, v::AbstractVector{T} = vertices(g)) = [outdegree(g,x) for x in v] """ Return the number of edges from the vertex `v`. For directed graphs, this value equals the incoming plus outgoing edges. For undirected graphs, it equals the connected edges. """ -degree(G::AbstractGraph, x...) = _NI("degree") +function degree end @traitfn degree{G<:AbstractGraph; IsDirected{G}}(g::G, v::Integer) = indegree(g, v) + outdegree(g, v) @traitfn degree{G<:AbstractGraph; !IsDirected{G}}(g::G, v::Integer) = indegree(g, v) @@ -77,7 +77,7 @@ 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`. """ -all_neighbors(x...) = _NI("all_neighbors") +function all_neighbors end @traitfn all_neighbors{G<:AbstractGraph; IsDirected{G}}(g::G, v::Integer) = union(out_neighbors(g, v), in_neighbors(g, v)) @traitfn all_neighbors{G<:AbstractGraph; !IsDirected{G}}(g::G, v::Integer) = @@ -105,3 +105,17 @@ function density end ne(g) / (nv(g) * (nv(g)-1)) @traitfn density{G<:AbstractGraph; !IsDirected{G}}(g::G) = (2*ne(g)) / (nv(g) * (nv(g)-1)) + + +""" +Returns a copy of a graph with the smallest practical type that +can accommodate all vertices. +""" +function squash(g::AbstractGraph) + gtype = is_directed(g)? DiGraph : Graph + validtypes = [UInt8, UInt16, UInt32, UInt64, Int] + nvg = nv(g) + for T in validtypes + nvg < typemax(T) && return gtype{T}(g) + end +end diff --git a/test/core.jl b/test/core.jl index e6e888ec8..cf3bc8606 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1,9 +1,4 @@ @testset "Core" begin - d = DummyGraph() - for fn in [ degree, density, all_neighbors ] - @test_throws ErrorException fn(d) - end - e2 = Edge(1,3) e3 = Edge(1,4) @test is_ordered(e2) @@ -39,6 +34,8 @@ @test num_self_loops(gsl) == 2 @test density(g) == 0.8 + + @test eltype(squash(g)) == UInt8 end g5wd = WheelDiGraph(5) @@ -73,5 +70,6 @@ @test num_self_loops(gsl) == 2 @test density(g) == 0.4 + @test eltype(squash(g)) == UInt8 end end From 293d331a35c53a39e2aed9efa00cb88714036ea0 Mon Sep 17 00:00:00 2001 From: James Fairbanks Date: Sun, 12 Mar 2017 20:16:59 -0400 Subject: [PATCH 26/56] update randgraphs.jl to use Channels over Tasks Fixes deprecation warnings introduced in: https://github.com/JuliaLang/julia/pull/19841 Changes an API interface: -function Graph(nvg::Int, neg::Int, edgestream::Task) +function Graph(nvg::Int, neg::Int, edgestream::Channel) Iteration over Tasks is deprecated so now we iterate over the Channel. --- src/generators/randgraphs.jl | 67 ++++++++++-------- test/generators/randgraphs.jl | 125 ++++++++++++++++++---------------- 2 files changed, 102 insertions(+), 90 deletions(-) diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 7a5634c50..34d3fc5a7 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -500,8 +500,8 @@ stochastic_block_model(cin::Float64, coff::Float64, n::Vector{Int}; seed::Int = Returns 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]$. + Only the upper triangular part is considered, since the lower traingular is + determined by $c[b,a] = c[a,b] * 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`. @@ -552,12 +552,12 @@ function stochastic_block_model{T<:Real}(cint::T, cext::T, n::Vector{Int}; seed: end """ -type StochasticBlockModel{T<:Integer,P<:Real} -n::T -nodemap::Array{T} -affinities::Matrix{P} -rng::MersenneTwister -end + type StochasticBlockModel{T<:Integer,P<:Real} + n::T + nodemap::Array{T} + affinities::Matrix{P} + rng::MersenneTwister + end A type capturing the parameters of the SBM. Each vertex is assigned to a block and the probability of edge `(i,j)` @@ -580,7 +580,7 @@ type StochasticBlockModel{T<:Integer,P<:Real} end ==(sbm::StochasticBlockModel, other::StochasticBlockModel) = -(sbm.n == other.n) && (sbm.nodemap == other.nodemap) && (sbm.affinities == other.affinities) + (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 @@ -611,17 +611,17 @@ function sbmaffinity(internalp::Vector{Float64}, externalp::Float64, sizes::Vect end function StochasticBlockModel(internalp::Float64, - externalp::Float64, - size::Int, - numblocks::Int; - seed::Int = -1) + externalp::Float64, + size::Int, + numblocks::Int; + seed::Int = -1) sizes = fill(size, numblocks) B = sbmaffinity(fill(internalp, numblocks), externalp, sizes) StochasticBlockModel(sizes, B, seed=seed) end function StochasticBlockModel(internalp::Vector{Float64}, externalp::Float64 - , sizes::Vector{Int}; seed::Int = -1) + , sizes::Vector{Int}; seed::Int = -1) B = sbmaffinity(internalp, externalp, sizes) return StochasticBlockModel(sizes, B, seed=seed) end @@ -634,8 +634,8 @@ 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` - The blocks are connected with probability `between`. +Each half is connected as a random bipartite graph with probability `intra` +The blocks are connected with probability `between`. """ function nearbipartiteaffinity(sizes::Vector{Int}, between::Float64, intra::Float64) numblocks = div(length(sizes), 2) @@ -656,9 +656,12 @@ end """Generates a stream of random pairs in 1:n""" function random_pair(rng::AbstractRNG, n::Int) - while true - produce( rand(rng, 1:n), rand(rng, 1:n) ) + f(ch) = begin + while true + put!(ch, Edge(rand(rng, 1:n), rand(rng, 1:n))) + end end + return f end @@ -669,24 +672,28 @@ Take an infinite sample from the sbm. Pass to `Graph(nvg, neg, edgestream)` to get a Graph object. """ function make_edgestream(sbm::StochasticBlockModel) - pairs = @task random_pair(sbm.rng, sbm.n) - for (i,j) in pairs - if i == j - continue - end - p = sbm.affinities[sbm.nodemap[i], sbm.nodemap[j]] - if rand(sbm.rng) < p - produce(i, j) + pairs = Channel(random_pair(sbm.rng, sbm.n), ctype=Edge, csize=32) + edges(ch) = begin + for e in pairs + i, j = Tuple(e) + if i == j + continue + end + p = sbm.affinities[sbm.nodemap[i], sbm.nodemap[j]] + if rand(sbm.rng) < p + put!(ch, e) + end end end + return Channel(edges, ctype=Edge, csize=32) end -function Graph(nvg::Int, neg::Int, edgestream::Task) +function Graph(nvg::Int, neg::Int, edgestream::Channel) g = Graph(nvg) # println(g) - for (i,j) in edgestream + for e in edgestream # print("$count, $i,$j\n") - add_edge!(g, Edge(i, j)) + add_edge!(g, e) ne(g) >= neg && break end # println(g) @@ -694,7 +701,7 @@ function Graph(nvg::Int, neg::Int, edgestream::Task) end Graph(nvg::Int, neg::Int, sbm::StochasticBlockModel) = -Graph(nvg, neg, @task make_edgestream(sbm)) + Graph(nvg, neg, make_edgestream(sbm)) """counts the number of edges that go between each block""" function blockcounts(sbm::StochasticBlockModel, A::AbstractMatrix) diff --git a/test/generators/randgraphs.jl b/test/generators/randgraphs.jl index 2e8319c0e..221e6b882 100644 --- a/test/generators/randgraphs.jl +++ b/test/generators/randgraphs.jl @@ -1,4 +1,8 @@ @testset "Randgraphs" begin +@test nv(r1) == 10 +@test ne(r1) == 20 +@test nv(r2) == 5 +@test ne(r2) == 10 r1 = Graph(10,20) r2 = DiGraph(5,10) @@ -182,66 +186,67 @@ @test degree(rr, v) == 8 end - rd = random_regular_digraph(10, 8, dir=:out, seed=4) - @test nv(rd) == 10 - @test ne(rd) == 80 - @test is_directed(rd) - - g = stochastic_block_model(2., 3., [100,100]) - @test 4.5 < mean(degree(g)) < 5.5 - g = stochastic_block_model(3., 4., [100,100,100]) - @test 10.5 < mean(degree(g)) < 11.5 - - function generate_nbp_sbm(numedges, sizes) - density = 1 - # print(STDERR, "Generating communites with sizes: $sizes\n") - between = density * 0.90 - intra = density * -0.005 - noise = density * 0.00501 - sbm = nearbipartiteSBM(sizes, between, intra, noise) - edgestream = @task make_edgestream(sbm) - g = Graph(sum(sizes), numedges, edgestream) - return sbm, g - end +function generate_nbp_sbm(numedges, sizes) + density = 1 + # print(STDERR, "Generating communites with sizes: $sizes\n") + between = density * 0.90 + intra = density * -0.005 + noise = density * 0.00501 + sbm = nearbipartiteSBM(sizes, between, intra, noise) + edgestream = make_edgestream(sbm) + g = Graph(sum(sizes), numedges, edgestream) + return sbm, g +end - numedges = 100 - sizes = [10, 10, 10, 10] - - n = sum(sizes) - sbm, g = generate_nbp_sbm(numedges, sizes) - bc = blockcounts(sbm, g) - bp = blockfractions(sbm, g) ./ (sizes * sizes') - ratios = bp ./ (sbm.affinities ./ sum(sbm.affinities)) - @test norm(Array(ratios)) < 0.25 - - sizes = [200, 200, 100] - internaldeg = 15 - externaldeg = 6 - internalp = Float64[internaldeg/i for i in sizes] - externalp = externaldeg/sum(sizes) - numedges = internaldeg + externaldeg #+ sum(externaldeg.*sizes[2:end]) - numedges *= div(sum(sizes), 2) - sbm = StochasticBlockModel(internalp, externalp, sizes) - g = Graph(sum(sizes), numedges, sbm) - @test ne(g) <= numedges - @test nv(g) == sum(sizes) - bc = blockcounts(sbm, g) - bp = blockfractions(sbm, g) ./ (sizes * sizes') - ratios = bp ./ (sbm.affinities ./ sum(sbm.affinities)) - @test norm(Array(ratios)) < 0.25 - - # check that average degree is not too high - # factor of two is cushion for random process - @test mean(degree(g)) <= 4//2*numedges/sum(sizes) - # check that the internal degrees are higher than the external degrees - # 5//4 is cushion for random process. - @test all(sum(bc-diagm(diag(bc)), 1) .<= 5//4 .* diag(bc)) - - - sbm2 = StochasticBlockModel(0.5*ones(4), 0.3, 10*ones(Int,4)) - sbm = StochasticBlockModel(0.5, 0.3, 10, 4) - @test sbm == sbm2 - sbm.affinities[1,1] = 0 - @test sbm != sbm2 +function test_sbm(sbm, bp) + @test sum(sbm.affinities) != NaN + @test all(sbm.affinities .> 0) + @test sum(sbm.affinities) != 0 + @test all(bp .>= 0) + @test all(bp .!= NaN) end + +numedges = 100 +sizes = [10, 10, 10, 10] + +n = sum(sizes) +sbm, g = generate_nbp_sbm(numedges, sizes) +@test ne(g) >= 0.9numedges +bc = blockcounts(sbm, g) +bp = blockfractions(sbm, g) ./ (sizes * sizes') +ratios = bp ./ (sbm.affinities ./ sum(sbm.affinities)) +test_sbm(sbm, bp) +@test norm(collect(ratios)) < 0.25 + +sizes = [200, 200, 100] +internaldeg = 15 +externaldeg = 6 +internalp = Float64[internaldeg/i for i in sizes] +externalp = externaldeg/sum(sizes) +numedges = internaldeg + externaldeg #+ sum(externaldeg.*sizes[2:end]) +numedges *= div(sum(sizes), 2) +sbm = StochasticBlockModel(internalp, externalp, sizes) +g = Graph(sum(sizes), numedges, sbm) +@test ne(g) >= 0.9numedges +@test ne(g) <= numedges +@test nv(g) == sum(sizes) +bc = blockcounts(sbm, g) +bp = blockfractions(sbm, g) ./ (sizes * sizes') +test_sbm(sbm, bp) +ratios = bp ./ (sbm.affinities ./ sum(sbm.affinities)) +@test norm(collect(ratios)) < 0.25 + +# check that average degree is not too high +# factor of two is cushion for random process +@test mean(degree(g)) <= 4//2*numedges/sum(sizes) +# check that the internal degrees are higher than the external degrees +# 5//4 is cushion for random process. +@test all(sum(bc-diagm(diag(bc)), 1) .<= 5//4 .* diag(bc)) + + +sbm2 = StochasticBlockModel(0.5*ones(4), 0.3, 10*ones(Int,4)) +sbm = StochasticBlockModel(0.5, 0.3, 10, 4) +@test sbm == sbm2 +sbm.affinities[1,1] = 0 +@test sbm != sbm2 From ccb04292788123a4f8f6fc200617cb528b223982 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 19 Mar 2017 09:43:37 -0700 Subject: [PATCH 27/56] got rid of tasks in randgraphs --- test/generators/randgraphs.jl | 129 +++++++++++++++++----------------- 1 file changed, 63 insertions(+), 66 deletions(-) diff --git a/test/generators/randgraphs.jl b/test/generators/randgraphs.jl index 221e6b882..b2e98031b 100644 --- a/test/generators/randgraphs.jl +++ b/test/generators/randgraphs.jl @@ -1,9 +1,4 @@ @testset "Randgraphs" begin -@test nv(r1) == 10 -@test ne(r1) == 20 -@test nv(r2) == 5 -@test ne(r2) == 10 - r1 = Graph(10,20) r2 = DiGraph(5,10) @@ -186,67 +181,69 @@ @test degree(rr, v) == 8 end -function generate_nbp_sbm(numedges, sizes) - density = 1 - # print(STDERR, "Generating communites with sizes: $sizes\n") - between = density * 0.90 - intra = density * -0.005 - noise = density * 0.00501 - sbm = nearbipartiteSBM(sizes, between, intra, noise) - edgestream = make_edgestream(sbm) - g = Graph(sum(sizes), numedges, edgestream) - return sbm, g -end + function generate_nbp_sbm(numedges, sizes) + density = 1 + # print(STDERR, "Generating communites with sizes: $sizes\n") + between = density * 0.90 + intra = density * -0.005 + noise = density * 0.00501 + sbm = nearbipartiteSBM(sizes, between, intra, noise) + edgestream = make_edgestream(sbm) + g = Graph(sum(sizes), numedges, edgestream) + return sbm, g + end -function test_sbm(sbm, bp) - @test sum(sbm.affinities) != NaN - @test all(sbm.affinities .> 0) - @test sum(sbm.affinities) != 0 - @test all(bp .>= 0) - @test all(bp .!= NaN) + function test_sbm(sbm, bp) + @test sum(sbm.affinities) != NaN + @test all(sbm.affinities .> 0) + @test sum(sbm.affinities) != 0 + @test all(bp .>= 0) + @test all(bp .!= NaN) + end + + numedges = 100 + sizes = [10, 10, 10, 10] + + n = sum(sizes) + sbm, g = generate_nbp_sbm(numedges, sizes) + @test ne(g) >= 0.9numedges + bc = blockcounts(sbm, g) + bp = blockfractions(sbm, g) ./ (sizes * sizes') + ratios = bp ./ (sbm.affinities ./ sum(sbm.affinities)) + test_sbm(sbm, bp) + @test norm(collect(ratios)) < 0.25 + + sizes = [200, 200, 100] + internaldeg = 15 + externaldeg = 6 + internalp = Float64[internaldeg/i for i in sizes] + externalp = externaldeg/sum(sizes) + numedges = internaldeg + externaldeg #+ sum(externaldeg.*sizes[2:end]) + numedges *= div(sum(sizes), 2) + sbm = StochasticBlockModel(internalp, externalp, sizes) + g = Graph(sum(sizes), numedges, sbm) + @test ne(g) >= 0.9numedges + @test ne(g) <= numedges + @test nv(g) == sum(sizes) + bc = blockcounts(sbm, g) + bp = blockfractions(sbm, g) ./ (sizes * sizes') + test_sbm(sbm, bp) + ratios = bp ./ (sbm.affinities ./ sum(sbm.affinities)) + @test norm(collect(ratios)) < 0.25 + + # check that average degree is not too high + # factor of two is cushion for random process + @test mean(degree(g)) <= 4//2*numedges/sum(sizes) + # check that the internal degrees are higher than the external degrees + # 5//4 is cushion for random process. + @test all(sum(bc-diagm(diag(bc)), 1) .<= 5//4 .* diag(bc)) + + + sbm2 = StochasticBlockModel(0.5*ones(4), 0.3, 10*ones(Int,4)) + sbm = StochasticBlockModel(0.5, 0.3, 10, 4) + @test sbm == sbm2 + sbm.affinities[1,1] = 0 + @test sbm != sbm2 + end - -numedges = 100 -sizes = [10, 10, 10, 10] - -n = sum(sizes) -sbm, g = generate_nbp_sbm(numedges, sizes) -@test ne(g) >= 0.9numedges -bc = blockcounts(sbm, g) -bp = blockfractions(sbm, g) ./ (sizes * sizes') -ratios = bp ./ (sbm.affinities ./ sum(sbm.affinities)) -test_sbm(sbm, bp) -@test norm(collect(ratios)) < 0.25 - -sizes = [200, 200, 100] -internaldeg = 15 -externaldeg = 6 -internalp = Float64[internaldeg/i for i in sizes] -externalp = externaldeg/sum(sizes) -numedges = internaldeg + externaldeg #+ sum(externaldeg.*sizes[2:end]) -numedges *= div(sum(sizes), 2) -sbm = StochasticBlockModel(internalp, externalp, sizes) -g = Graph(sum(sizes), numedges, sbm) -@test ne(g) >= 0.9numedges -@test ne(g) <= numedges -@test nv(g) == sum(sizes) -bc = blockcounts(sbm, g) -bp = blockfractions(sbm, g) ./ (sizes * sizes') -test_sbm(sbm, bp) -ratios = bp ./ (sbm.affinities ./ sum(sbm.affinities)) -@test norm(collect(ratios)) < 0.25 - -# check that average degree is not too high -# factor of two is cushion for random process -@test mean(degree(g)) <= 4//2*numedges/sum(sizes) -# check that the internal degrees are higher than the external degrees -# 5//4 is cushion for random process. -@test all(sum(bc-diagm(diag(bc)), 1) .<= 5//4 .* diag(bc)) - - -sbm2 = StochasticBlockModel(0.5*ones(4), 0.3, 10*ones(Int,4)) -sbm = StochasticBlockModel(0.5, 0.3, 10, 4) -@test sbm == sbm2 -sbm.affinities[1,1] = 0 -@test sbm != sbm2 From b147c81acfc1cbb504a79f099b279b7599561197 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 19 Mar 2017 10:25:39 -0700 Subject: [PATCH 28/56] graph -> g --- src/biconnectivity/biconnect.jl | 7 ++++--- src/traversals/bfs.jl | 14 +++++++------- src/traversals/dfs.jl | 12 ++++++------ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/biconnectivity/biconnect.jl b/src/biconnectivity/biconnect.jl index 1c39efb00..1a08f25dd 100644 --- a/src/biconnectivity/biconnect.jl +++ b/src/biconnectivity/biconnect.jl @@ -9,7 +9,7 @@ type Biconnections id::Int end -function Biconnections(g::AbstractGraph) +@traitfn function Biconnections{G<:AbstractGraph; !IsDirected{G}}(g::G) n = nv(g) return Biconnections(zeros(Int, n), zeros(Int, n), Vector{Edge}(), Vector{Vector{Edge}}(), 0) end @@ -19,7 +19,8 @@ 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(g::Graph) +function biconnected_components end +@traitfn function biconnected_components{G<:AbstractGraph; !IsDirected{G}}(g::G) state = Biconnections(g) for u in vertices(g) if state.depth[u] == 0 @@ -37,7 +38,7 @@ end """ Does a DFS visit and stores the depth and low-points of each vertex """ -function visit!(g::Graph, state::Biconnections, u::Integer, v::Integer) +function visit!(g::AbstractGraph, state::Biconnections, u::Integer, v::Integer) children = 0 state.id += 1 state.depth[v] = state.id diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 8590a678b..0b289ec02 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -24,7 +24,7 @@ type BreadthFirst <: AbstractGraphVisitAlgorithm end function breadth_first_visit_impl!{T<:Integer}( - graph::AbstractGraph, # the graph + g::AbstractGraph, # the graph queue::Vector{T}, # an (initialized) queue that stores the active vertices vertexcolormap::AbstractVertexMap, # an (initialized) color-map to indicate status of vertices (-1=unseen, otherwise distance from root) edgecolormap::AbstractEdgeMap, # an (initialized) color-map to indicate status of edges @@ -37,7 +37,7 @@ function breadth_first_visit_impl!{T<:Integer}( open_vertex!(visitor, u) u_color = vertexcolormap[u] - for v in fneig(graph, u) + for v in fneig(g, u) v_color = get(vertexcolormap, v, 0) v_edge = Edge(u,v) e_color = get(edgecolormap, v_edge, 0) @@ -55,13 +55,13 @@ function breadth_first_visit_impl!{T<:Integer}( end function traverse_graph!( - graph::AbstractGraph, + g::AbstractGraph, alg::BreadthFirst, source, visitor::AbstractGraphVisitor; - vertexcolormap::AbstractVertexMap = Dict{eltype(graph), Int}(), + vertexcolormap::AbstractVertexMap = Dict{eltype(g), Int}(), edgecolormap::AbstractEdgeMap = DummyEdgeMap(), - queue = Vector{eltype(graph)}(), + queue = Vector{eltype(g)}(), dir = :out) for s in source @@ -70,7 +70,7 @@ function traverse_graph!( push!(queue, s) end - breadth_first_visit_impl!(graph, queue, vertexcolormap, edgecolormap + breadth_first_visit_impl!(g, queue, vertexcolormap, edgecolormap , visitor, dir) end @@ -100,7 +100,7 @@ end """tree converts a parents array into a DiGraph""" function tree{T<:Integer}(parents::AbstractVector{T}) n = T(length(parents)) - t = DiGraph{T}(n) + t = DiGraph(n) for i in one(T):n parent = parents[i] if parent > zero(T) && parent != i diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index e738fd0ed..1dd31ec6c 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -25,7 +25,7 @@ type DepthFirst <: AbstractGraphVisitAlgorithm end function depth_first_visit_impl!( - graph::AbstractGraph, # the graph + g::AbstractGraph, # the graph stack, # an (initialized) stack of vertex vertexcolormap::AbstractVertexMap, # an (initialized) color-map to indicate status of vertices edgecolormap::AbstractEdgeMap, # an (initialized) color-map to indicate status of edges @@ -53,7 +53,7 @@ function depth_first_visit_impl!( push!(stack, (u, udsts, tstate)) open_vertex!(visitor, v) - vdsts = out_neighbors(graph, v) + vdsts = out_neighbors(g, v) push!(stack, (v, vdsts, start(vdsts))) end end @@ -151,14 +151,14 @@ function close_vertex!(visitor::TopologicalSortVisitor, v::Integer) push!(visitor.vertices, v) end -function topological_sort_by_dfs(graph::AbstractGraph) - nvg = nv(graph) +function topological_sort_by_dfs(g::AbstractGraph) + nvg = nv(g) cmap = zeros(Int, nvg) visitor = TopologicalSortVisitor(nvg) - for s in vertices(graph) + for s in vertices(g) if cmap[s] == 0 - traverse_graph!(graph, DepthFirst(), s, visitor, vertexcolormap=cmap) + traverse_graph!(g, DepthFirst(), s, visitor, vertexcolormap=cmap) end end From 5578ab7268a5864583170cdefd79f8de0720c240 Mon Sep 17 00:00:00 2001 From: Christopher Rackauckas Date: Sun, 19 Mar 2017 10:48:34 -0700 Subject: [PATCH 29/56] Add tutorials to section on docs (#547) * Update README.md * Update README.md Made tutorials separate line and consistent with the other lines. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6e79ccddb..27514835a 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Additional functionality may be found in the companion package [LightGraphsExtra ## 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. +Additional tutorials can be found at [JuliaGraphsTutorials](https://github.com/JuliaGraphs/JuliaGraphsTutorials). ## Core Concepts A graph *G* is described by a set of vertices *V* and edges *E*: From dee052850d564e28da0be1b7c2655ce019e42cc4 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 19 Mar 2017 11:50:02 -0700 Subject: [PATCH 30/56] type -> mutable struct --- src/biconnectivity/biconnect.jl | 2 +- src/connectivity.jl | 4 ++-- src/graphtypes/simplegraph/simpleedgeiter.jl | 2 +- src/graphtypes/simplegraph/simplegraph.jl | 2 +- src/traversals/bfs.jl | 8 ++++---- src/traversals/dfs.jl | 8 ++++---- test/interface.jl | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/biconnectivity/biconnect.jl b/src/biconnectivity/biconnect.jl index 1a08f25dd..97dd796ce 100644 --- a/src/biconnectivity/biconnect.jl +++ b/src/biconnectivity/biconnect.jl @@ -1,7 +1,7 @@ """ Biconnections: A state type for Depth First Search that finds the biconnected components """ -type Biconnections +mutable struct Biconnections low::Vector{Int} depth::Vector{Int} stack::Vector{Edge} diff --git a/src/connectivity.jl b/src/connectivity.jl index 636591928..99e86746c 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -113,7 +113,7 @@ function is_weakly_connected end @traitfn is_weakly_connected{G<:AbstractGraph; IsDirected{G}}(g::G) = length(weakly_connected_components(g)) == 1 # Adapated from Graphs.jl -type TarjanVisitor{T<:Integer} <: AbstractGraphVisitor +mutable struct TarjanVisitor{T<:Integer} <: AbstractGraphVisitor stack::Vector{T} onstack::BitVector lowlink::Vector{T} @@ -251,7 +251,7 @@ function attracting_components end return scc[attracting] end -type NeighborhoodVisitor{T<:Integer} <: AbstractGraphVisitor +mutable struct NeighborhoodVisitor{T<:Integer} <: AbstractGraphVisitor d::T neigs::Vector{T} end diff --git a/src/graphtypes/simplegraph/simpleedgeiter.jl b/src/graphtypes/simplegraph/simpleedgeiter.jl index 270d60c6b..4a45fc711 100644 --- a/src/graphtypes/simplegraph/simpleedgeiter.jl +++ b/src/graphtypes/simplegraph/simpleedgeiter.jl @@ -1,4 +1,4 @@ -type SimpleEdgeIter{T<:Integer} <: AbstractEdgeIter +mutable struct SimpleEdgeIter{T<:Integer} <: AbstractEdgeIter m::Int adj::Vector{Vector{T}} directed::Bool diff --git a/src/graphtypes/simplegraph/simplegraph.jl b/src/graphtypes/simplegraph/simplegraph.jl index 648f2be81..660f458e8 100644 --- a/src/graphtypes/simplegraph/simplegraph.jl +++ b/src/graphtypes/simplegraph/simplegraph.jl @@ -1,7 +1,7 @@ const SimpleGraphEdge = SimpleEdge """A type representing an undirected graph.""" -type SimpleGraph{T<:Integer} <: AbstractSimpleGraph +mutable struct SimpleGraph{T<:Integer} <: AbstractSimpleGraph vertices::UnitRange{T} ne::Int fadjlist::Vector{Vector{T}} # [src]: (dst, dst, dst) diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 0b289ec02..e8954a3dd 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -20,7 +20,7 @@ EdgeColorMap : - color == 1 => examined """ -type BreadthFirst <: AbstractGraphVisitAlgorithm +mutable struct BreadthFirst <: AbstractGraphVisitAlgorithm end function breadth_first_visit_impl!{T<:Integer}( @@ -89,7 +89,7 @@ end """TreeBFSVisitorVector is a type for representing a BFS traversal of the graph as a parents array. This type allows for a more performant implementation. """ -type TreeBFSVisitorVector{T<:Integer} <: AbstractGraphVisitor +mutable struct TreeBFSVisitorVector{T<:Integer} <: AbstractGraphVisitor tree::Vector{T} end @@ -154,7 +154,7 @@ end # Connected Components with BFS # ############################################ """Performing connected components with BFS starting from seed""" -type ComponentVisitorVector{T<:Integer} <: AbstractGraphVisitor +mutable struct ComponentVisitorVector{T<:Integer} <: AbstractGraphVisitor labels::Vector{T} seed::T end @@ -170,7 +170,7 @@ end ############################################ # Test graph for bipartiteness # ############################################ -type BipartiteVisitor <: AbstractGraphVisitor +mutable struct BipartiteVisitor <: AbstractGraphVisitor bipartitemap::Vector{UInt8} is_bipartite::Bool end diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index 1dd31ec6c..c56934282 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -21,7 +21,7 @@ EdgeColorMap : - color == 1 => examined """ -type DepthFirst <: AbstractGraphVisitAlgorithm +mutable struct DepthFirst <: AbstractGraphVisitAlgorithm end function depth_first_visit_impl!( @@ -92,7 +92,7 @@ end # Test whether a graph is cyclic -type DFSCyclicTestVisitor <: AbstractGraphVisitor +mutable struct DFSCyclicTestVisitor <: AbstractGraphVisitor found_cycle::Bool DFSCyclicTestVisitor() = new(false) end @@ -133,7 +133,7 @@ end # Topological sort using DFS -type TopologicalSortVisitor{T} <: AbstractGraphVisitor +mutable struct TopologicalSortVisitor{T} <: AbstractGraphVisitor vertices::Vector{T} end @@ -166,7 +166,7 @@ function topological_sort_by_dfs(g::AbstractGraph) end -type TreeDFSVisitor{T} <:AbstractGraphVisitor +mutable struct TreeDFSVisitor{T} <:AbstractGraphVisitor tree::DiGraph predecessor::Vector{T} end diff --git a/test/interface.jl b/test/interface.jl index df7859114..baf12a5e9 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -1,4 +1,4 @@ -type DummyGraph <: AbstractGraph end +mutable struct DummyGraph <: AbstractGraph end type DummyDiGraph <: AbstractGraph end type DummyEdge <: AbstractEdge end From 8165d33abc8194821d2e150d70a0f5554524d777 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 19 Mar 2017 12:36:35 -0700 Subject: [PATCH 31/56] more type -> mutable struct, plus OF detection for add_vertex! --- src/biconnectivity/articulation.jl | 2 +- src/community/label_propagation.jl | 2 +- src/distance.jl | 2 +- src/flow/maximum_flow.jl | 2 +- src/generators/randgraphs.jl | 2 +- src/graphtypes/simplegraph/simpledigraph.jl | 3 ++- src/graphtypes/simplegraph/simplegraph.jl | 1 + src/linalg/graphmatrices.jl | 21 ++++++++++---------- src/linalg/nonbacktracking.jl | 2 +- src/persistence/jld.jl | 4 ++-- src/shortestpaths/bellman-ford.jl | 4 ++-- src/shortestpaths/dijkstra.jl | 2 +- src/shortestpaths/floyd-warshall.jl | 2 +- src/traversals/graphvisit.jl | 4 ++-- src/traversals/maxadjvisit.jl | 4 ++-- test/graphtypes/simplegraphs/simplegraphs.jl | 10 +++++++--- test/interface.jl | 4 ++-- 17 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/biconnectivity/articulation.jl b/src/biconnectivity/articulation.jl index e445e7dd9..a36578f8d 100644 --- a/src/biconnectivity/articulation.jl +++ b/src/biconnectivity/articulation.jl @@ -15,7 +15,7 @@ end """ Articulations: a state type for the Depth first search that finds the articulation points in a graph. """ -type Articulations{T<:Integer} +mutable struct Articulations{T<:Integer} low::Vector{T} depth::Vector{T} articulation_points::BitArray diff --git a/src/community/label_propagation.jl b/src/community/label_propagation.jl index a55b18244..d97e2ed61 100644 --- a/src/community/label_propagation.jl +++ b/src/community/label_propagation.jl @@ -42,7 +42,7 @@ function label_propagation(g::AbstractGraph; maxiter=1000) end """Type to record neighbor labels and their counts.""" -type NeighComm{T<:Integer} +mutable struct NeighComm{T<:Integer} neigh_pos::Vector{T} neigh_cnt::Vector{Int} neigh_last::T diff --git a/src/distance.jl b/src/distance.jl index ef4f418d9..558c6449c 100644 --- a/src/distance.jl +++ b/src/distance.jl @@ -2,7 +2,7 @@ # has_distances{T}(distmx::AbstractArray{T,2}) = # issparse(distmx)? (nnz(distmx) > 0) : !isempty(distmx) -type DefaultDistance<:AbstractArray{Int, 2} +struct DefaultDistance<:AbstractArray{Int, 2} nv::Int DefaultDistance(nv::Int=typemax(Int)) = new(nv) end diff --git a/src/flow/maximum_flow.jl b/src/flow/maximum_flow.jl index f4ce78a22..0314a1899 100644 --- a/src/flow/maximum_flow.jl +++ b/src/flow/maximum_flow.jl @@ -30,7 +30,7 @@ end """ Type that returns 1 if a forward edge exists, and 0 otherwise """ -type DefaultCapacity{T<:Integer} <: AbstractMatrix{T} +struct DefaultCapacity{T<:Integer} <: AbstractMatrix{T} flow_graph::DiGraph nv::T end diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 34d3fc5a7..04656d2a8 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -572,7 +572,7 @@ block k and any vertex in block `l`. We are generating the graphs by taking random `i,j in vertices(g)` and flipping a coin with probability `affinities[nodemap[i],nodemap[j]]`. """ -type StochasticBlockModel{T<:Integer,P<:Real} +mutable struct StochasticBlockModel{T<:Integer,P<:Real} n::T nodemap::Array{T} affinities::Matrix{P} diff --git a/src/graphtypes/simplegraph/simpledigraph.jl b/src/graphtypes/simplegraph/simpledigraph.jl index 1eac3e854..bad194647 100644 --- a/src/graphtypes/simplegraph/simpledigraph.jl +++ b/src/graphtypes/simplegraph/simpledigraph.jl @@ -1,7 +1,7 @@ const SimpleDiGraphEdge = SimpleEdge """A type representing a directed graph.""" -type SimpleDiGraph{T<:Integer} <: AbstractSimpleGraph +mutable struct SimpleDiGraph{T<:Integer} <: AbstractSimpleGraph vertices::UnitRange{T} ne::Int fadjlist::Vector{Vector{T}} # [src]: (dst, dst, dst) @@ -130,6 +130,7 @@ end function add_vertex!{T<:Integer}(g::SimpleDiGraph{T}) + (nv(g) + one(T) <= nv(g)) && return false # test for overflow g.vertices = 1:nv(g)+1 push!(g.badjlist, Vector{T}()) push!(g.fadjlist, Vector{T}()) diff --git a/src/graphtypes/simplegraph/simplegraph.jl b/src/graphtypes/simplegraph/simplegraph.jl index 660f458e8..d551b03e9 100644 --- a/src/graphtypes/simplegraph/simplegraph.jl +++ b/src/graphtypes/simplegraph/simplegraph.jl @@ -147,6 +147,7 @@ end """Add a new vertex to the graph `g`.""" function add_vertex!{T<:Integer}(g::SimpleGraph{T}) + (nv(g) + one(T) <= nv(g)) && return false # test for overflow g.vertices = one(T):nv(g)+one(T) push!(g.fadjlist, Vector{T}()) diff --git a/src/linalg/graphmatrices.jl b/src/linalg/graphmatrices.jl index 867f6c8aa..27b48a247 100644 --- a/src/linalg/graphmatrices.jl +++ b/src/linalg/graphmatrices.jl @@ -47,7 +47,7 @@ abstract type Adjacency{T} <: GraphMatrix{T} end abstract type Laplacian{T} <: GraphMatrix{T} end """Combinatorial Adjacency matrix is the standard adjacency matrix from math""" -type CombinatorialAdjacency{T,S,V} <: Adjacency{T} +struct CombinatorialAdjacency{T,S,V} <: Adjacency{T} A::S D::V end @@ -63,7 +63,7 @@ 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]. """ -type NormalizedAdjacency{T} <: Adjacency{T} +struct NormalizedAdjacency{T} <: Adjacency{T} A::CombinatorialAdjacency{T} scalefactor::Vector{T} end @@ -73,7 +73,7 @@ function NormalizedAdjacency(adjmat::CombinatorialAdjacency) end """Transition matrix for the random walk.""" -type StochasticAdjacency{T} <: Adjacency{T} +struct StochasticAdjacency{T} <: Adjacency{T} A::CombinatorialAdjacency{T} scalefactor::Vector{T} @@ -84,7 +84,7 @@ function StochasticAdjacency(adjmat::CombinatorialAdjacency) end """The matrix whos action is to average over each neighborhood.""" -type AveragingAdjacency{T} <: Adjacency{T} +struct AveragingAdjacency{T} <: Adjacency{T} A::CombinatorialAdjacency{T} scalefactor::Vector{T} end @@ -95,7 +95,7 @@ end perron(adjmat::NormalizedAdjacency) = sqrt.(adjmat.A.D)/norm(sqrt.(adjmat.A.D)) -type PunchedAdjacency{T} <: Adjacency{T} +struct PunchedAdjacency{T} <: Adjacency{T} A::NormalizedAdjacency{T} perron::Vector{T} end @@ -110,8 +110,7 @@ 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. """ -immutable Noop -end +struct Noop end Base.broadcast(::typeof(*), ::Noop, x) = x @@ -136,7 +135,7 @@ prescalefactor(adjmat::NormalizedAdjacency) = adjmat.scalefactor prescalefactor(adjmat::StochasticAdjacency) = adjmat.scalefactor -type CombinatorialLaplacian{T} <: Laplacian{T} +struct CombinatorialLaplacian{T} <: Laplacian{T} A::CombinatorialAdjacency{T} end @@ -145,17 +144,17 @@ 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. """ -type NormalizedLaplacian{T} <: Laplacian{T} +struct NormalizedLaplacian{T} <: Laplacian{T} A::NormalizedAdjacency{T} end """Laplacian version of the StochasticAdjacency matrix.""" -type StochasticLaplacian{T} <: Laplacian{T} +struct StochasticLaplacian{T} <: Laplacian{T} A::StochasticAdjacency{T} end """Laplacian version of the AveragingAdjacency matrix.""" -type AveragingLaplacian{T} <: Laplacian{T} +struct AveragingLaplacian{T} <: Laplacian{T} A::AveragingAdjacency{T} end diff --git a/src/linalg/nonbacktracking.jl b/src/linalg/nonbacktracking.jl index 0b8eaee9e..34526def6 100644 --- a/src/linalg/nonbacktracking.jl +++ b/src/linalg/nonbacktracking.jl @@ -61,7 +61,7 @@ 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. """ -type Nonbacktracking{G} +struct Nonbacktracking{G} g::G edgeidmap::Dict{Edge,Int} m::Int diff --git a/src/persistence/jld.jl b/src/persistence/jld.jl index 465e5426c..7dabc260b 100644 --- a/src/persistence/jld.jl +++ b/src/persistence/jld.jl @@ -21,7 +21,7 @@ repeatedly in an attempt to improve performance. This type has not been tested with mmaped files or compression in JLD. """ -type GraphSerializer +mutable struct GraphSerializer vertices::UnitRange{Int} ne::Int packed_adjlist::Vector{Int} @@ -68,7 +68,7 @@ function JLD.readas(gs::GraphSerializer) return g end -type Network{G,V,E} +mutable struct Network{G,V,E} graph::G vprop::Vector{V} eprop::Dict{Edge,E} diff --git a/src/shortestpaths/bellman-ford.jl b/src/shortestpaths/bellman-ford.jl index 023b5dab3..1cca52e05 100644 --- a/src/shortestpaths/bellman-ford.jl +++ b/src/shortestpaths/bellman-ford.jl @@ -9,10 +9,10 @@ # ################################################################### -type NegativeCycleError <: Exception end +struct NegativeCycleError <: Exception end # AbstractPathState is defined in core -type BellmanFordState{T<:Number, U<:Integer}<:AbstractPathState +struct BellmanFordState{T<:Number, U<:Integer}<:AbstractPathState parents::Vector{U} dists::Vector{T} end diff --git a/src/shortestpaths/dijkstra.jl b/src/shortestpaths/dijkstra.jl index d8a57d90d..dae86bb6a 100644 --- a/src/shortestpaths/dijkstra.jl +++ b/src/shortestpaths/dijkstra.jl @@ -7,7 +7,7 @@ end isless(e1::DijkstraHeapEntry, e2::DijkstraHeapEntry) = e1.dist < e2.dist -type DijkstraState{T, U<:Integer}<: AbstractDijkstraState +struct DijkstraState{T, U<:Integer}<: AbstractDijkstraState parents::Vector{U} dists::Vector{T} predecessors::Vector{Vector{U}} diff --git a/src/shortestpaths/floyd-warshall.jl b/src/shortestpaths/floyd-warshall.jl index 1bd55a3fa..4201167f5 100644 --- a/src/shortestpaths/floyd-warshall.jl +++ b/src/shortestpaths/floyd-warshall.jl @@ -2,7 +2,7 @@ # licensing details. -type FloydWarshallState{T, U<:Integer}<:AbstractPathState +struct FloydWarshallState{T, U<:Integer}<:AbstractPathState dists::Matrix{T} parents::Matrix{U} end diff --git a/src/traversals/graphvisit.jl b/src/traversals/graphvisit.jl index f37a81a53..9dc9d878c 100644 --- a/src/traversals/graphvisit.jl +++ b/src/traversals/graphvisit.jl @@ -47,7 +47,7 @@ get(d::DummyEdgeMap, e::Edge, x::Int) = x # List vertices by the order of being discovered -type VertexListVisitor{T<:Integer} <: AbstractGraphVisitor +struct VertexListVisitor{T<:Integer} <: AbstractGraphVisitor vertices::Vector{T} end @@ -75,7 +75,7 @@ end # Print visit log -type LogGraphVisitor{S<:IO} <: AbstractGraphVisitor +struct LogGraphVisitor{S<:IO} <: AbstractGraphVisitor io::S end diff --git a/src/traversals/maxadjvisit.jl b/src/traversals/maxadjvisit.jl index 44fbbd212..9e47684c4 100644 --- a/src/traversals/maxadjvisit.jl +++ b/src/traversals/maxadjvisit.jl @@ -77,7 +77,7 @@ end # ################################################# -type MinCutVisitor{T, U<:Integer} <: AbstractMASVisitor +mutable struct MinCutVisitor{T, U<:Integer} <: AbstractMASVisitor graph::AbstractGraph parities::BitVector colormap::Vector{Int} @@ -144,7 +144,7 @@ end # ################################################# -type MASVisitor{T, U<:Integer} <: AbstractMASVisitor +struct MASVisitor{T, U<:Integer} <: AbstractMASVisitor io::IO vertices::Vector{U} distmx::AbstractMatrix{T} diff --git a/test/graphtypes/simplegraphs/simplegraphs.jl b/test/graphtypes/simplegraphs/simplegraphs.jl index 1e5693bd5..f1037e07e 100644 --- a/test/graphtypes/simplegraphs/simplegraphs.jl +++ b/test/graphtypes/simplegraphs/simplegraphs.jl @@ -1,6 +1,6 @@ importall LightGraphs.SimpleGraphs import LightGraphs.SimpleGraphs: fadj, badj, adj -type DummySimpleGraph <: AbstractSimpleGraph end +struct DummySimpleGraph <: AbstractSimpleGraph end @testset "SimpleGraphs" begin adjmx1 = [0 1 0; 1 0 1; 0 1 0] # graph adjmx2 = [0 1 0; 1 0 1; 1 1 0] # digraph @@ -17,15 +17,19 @@ type DummySimpleGraph <: AbstractSimpleGraph end @test ne(SimpleDiGraph(PathGraph(5))) == 8 @test is_directed(SimpleDiGraph) + + for gbig in [Graph(0xff), DiGraph(0xff)] + @test !add_vertex!(gbig) # overflow + @test !add_vertices!(gbig, 10) + end + gdx = PathDiGraph(4) gx = SimpleGraph() - for g in testgraphs(gx) T = eltype(g) @test sprint(show, g) == "empty undirected simple $T graph" add_vertices!(g, 5) @test sprint(show, g) == "{5, 0} undirected simple $T graph" - end gx = SimpleDiGraph() for g in testdigraphs(gx) diff --git a/test/interface.jl b/test/interface.jl index baf12a5e9..e9b488714 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -1,6 +1,6 @@ mutable struct DummyGraph <: AbstractGraph end -type DummyDiGraph <: AbstractGraph end -type DummyEdge <: AbstractEdge end +mutable struct DummyDiGraph <: AbstractGraph end +mutable struct DummyEdge <: AbstractEdge end @testset "Interface" begin dummygraph = DummyGraph() From 7298222c3865f1694a1a333b1a6cefc054b22a53 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 19 Mar 2017 17:16:07 -0700 Subject: [PATCH 32/56] foo{T}(x::T) -> foo(x::T) where T --- CONTRIBUTING.md | 2 +- src/biconnectivity/articulation.jl | 2 +- src/biconnectivity/biconnect.jl | 4 +- src/centrality/pagerank.jl | 2 +- src/community/cliques.jl | 4 +- src/community/core-periphery.jl | 2 +- src/community/modularity.jl | 2 +- src/connectivity.jl | 26 +++---- src/core.jl | 14 ++-- src/distance.jl | 36 ++++----- src/flow/boykov_kolmogorov.jl | 12 +-- src/flow/dinic.jl | 14 ++-- src/flow/edmonds_karp.jl | 24 +++--- src/flow/ext_multiroute_flow.jl | 33 ++++---- src/flow/kishimoto.jl | 10 +-- src/flow/maximum_flow.jl | 29 +++---- src/flow/multiroute_flow.jl | 40 +++++----- src/flow/push_relabel.jl | 80 ++++++++++---------- src/generators/randgraphs.jl | 31 ++++---- src/generators/smallgraphs.jl | 4 +- src/generators/staticgraphs.jl | 12 +-- src/graphtypes/simplegraph/SimpleGraphs.jl | 2 +- src/graphtypes/simplegraph/simpledigraph.jl | 21 +++-- src/graphtypes/simplegraph/simpleedgeiter.jl | 11 ++- src/graphtypes/simplegraph/simplegraph.jl | 19 ++--- src/interface.jl | 2 +- src/linalg/graphmatrices.jl | 14 ++-- src/linalg/nonbacktracking.jl | 4 +- src/linalg/spectral.jl | 2 + src/operators.jl | 26 +++---- src/persistence/common.jl | 4 +- src/shortestpaths/astar.jl | 8 +- src/shortestpaths/bellman-ford.jl | 21 +++-- src/shortestpaths/dijkstra.jl | 8 +- src/shortestpaths/floyd-warshall.jl | 2 +- src/spanningtrees/kruskal.jl | 4 +- src/spanningtrees/prim.jl | 17 ++--- src/traversals/bfs.jl | 25 +++--- src/traversals/dfs.jl | 2 +- src/traversals/graphvisit.jl | 2 +- src/traversals/maxadjvisit.jl | 2 +- src/traversals/randomwalks.jl | 8 +- test/flow/maximum_flow.jl | 2 +- 43 files changed, 296 insertions(+), 293 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a61e07127..07a280b2f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,7 +40,7 @@ function f(g::AbstractGraph, v::Integer) return inner!(storage, g, v) end -function inner!(storage::AbstractArray{Int,1}, g::AbstractGraph, v::Integer) +function inner!(storage::AbstractVector{Int}, g::AbstractGraph, v::Integer) # some code operating on storage, g, and v. for i in 1:nv(g) storage[i] = v-i diff --git a/src/biconnectivity/articulation.jl b/src/biconnectivity/articulation.jl index a36578f8d..9bd0ee7cb 100644 --- a/src/biconnectivity/articulation.jl +++ b/src/biconnectivity/articulation.jl @@ -2,7 +2,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) :: AbstractArray +function articulation(g::AbstractGraph) state = Articulations(g) for u in vertices(g) if state.depth[u] == 0 diff --git a/src/biconnectivity/biconnect.jl b/src/biconnectivity/biconnect.jl index 97dd796ce..26fe054a0 100644 --- a/src/biconnectivity/biconnect.jl +++ b/src/biconnectivity/biconnect.jl @@ -9,7 +9,7 @@ mutable struct Biconnections id::Int end -@traitfn function Biconnections{G<:AbstractGraph; !IsDirected{G}}(g::G) +@traitfn function Biconnections(g::::(!IsDirected)) n = nv(g) return Biconnections(zeros(Int, n), zeros(Int, n), Vector{Edge}(), Vector{Vector{Edge}}(), 0) end @@ -20,7 +20,7 @@ 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<:AbstractGraph; !IsDirected{G}}(g::G) +@traitfn function biconnected_components(g::::(!IsDirected)) state = Biconnections(g) for u in vertices(g) if state.depth[u] == 0 diff --git a/src/centrality/pagerank.jl b/src/centrality/pagerank.jl index 4c2d8adc8..9abed102f 100644 --- a/src/centrality/pagerank.jl +++ b/src/centrality/pagerank.jl @@ -8,7 +8,7 @@ reached within `n` iterations, an error will be returned. """ function pagerank end -@traitfn function pagerank{G<:AbstractGraph; IsDirected{G}}(g::G, α=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 ef8660e9e..d0def23af 100644 --- a/src/community/cliques.jl +++ b/src/community/cliques.jl @@ -20,7 +20,7 @@ julia> maximal_cliques(g) ``` """ function maximal_cliques end -@traitfn function maximal_cliques{G<:AbstractGraph; !IsDirected{G}}(g::G) +@traitfn function maximal_cliques(g::::(!IsDirected)) T = eltype(g) # Cache nbrs and find first pivot (highest degree) maxconn = -1 @@ -44,7 +44,7 @@ function maximal_cliques end nnbrs[n] = nbrs end end - + # Initial setup cand = Set{T}(vertices(g)) # union!(cand, keys(nnbrs)) diff --git a/src/community/core-periphery.jl b/src/community/core-periphery.jl index 96b827bf2..93831af77 100644 --- a/src/community/core-periphery.jl +++ b/src/community/core-periphery.jl @@ -5,7 +5,7 @@ A simple degree-based core-periphery detection algorithm (see [Lip](http://arxiv Returns the vertex assignments (1 for core and 2 for periphery). """ function core_periphery_deg end -@traitfn function core_periphery_deg{G<:AbstractGraph; !IsDirected{G}}(g::G) +@traitfn function core_periphery_deg(g::::(!IsDirected)) degs = degree(g) p = sortperm(degs, rev=true) s = sum(degs) / 2. diff --git a/src/community/modularity.jl b/src/community/modularity.jl index 799f58cdf..05ac2f0d5 100644 --- a/src/community/modularity.jl +++ b/src/community/modularity.jl @@ -5,7 +5,7 @@ Computes Newman's modularity `Q` for graph `g` given the partitioning `c`. """ function modularity end -@traitfn function modularity{G<:AbstractGraph; !IsDirected{G}}(g::G, c::Vector) +@traitfn function modularity(g::::(!IsDirected), c::Vector) m = 2*ne(g) m == 0 && return 0. nc = maximum(c) diff --git a/src/connectivity.jl b/src/connectivity.jl index 99e86746c..e13a8a75d 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -12,7 +12,7 @@ Output: c = labels[i] => vertex i belongs to component c. c is the smallest vertex id in the component. """ -function connected_components!{T<:Integer}(label::Vector{T}, g::AbstractGraph) +function connected_components!(label::Vector{T}, g::AbstractGraph) where T<:Integer # this version of connected components uses Breadth First Traversal # with custom visitor type in order to improve performance. # one BFS is performed for each component. @@ -42,7 +42,7 @@ Arguments: Output: vs = d[c] => vertices in vs belong to component c. """ -function components_dict{T<:Integer}(labels::Vector{T}) +function components_dict(labels::Vector{T}) where T<:Integer d = Dict{T,Vector{T}}() for (v,l) in enumerate(labels) vec = get(d, l, Vector{T}()) @@ -63,7 +63,7 @@ Output: vs = c[i] => vertices in vs belong to component i. a = d[i] => if labels[v]==i then v in c[a] end """ -function components{T<:Integer}(labels::Vector{T}) +function components(labels::Vector{T}) where T<:Integer d = Dict{T, T}() c = Vector{Vector{T}}() i = one(T) @@ -101,16 +101,16 @@ Returns `true` if `g` is connected. For DiGraphs, this is equivalent to a test of weak connectivity. """ function is_connected end -@traitfn is_connected{G<:AbstractGraph; !IsDirected{G}}(g::G) = length(connected_components(g)) == 1 -@traitfn is_connected{G<:AbstractGraph; IsDirected{G}}(g::G) = is_weakly_connected(g) +@traitfn is_connected(g::::(!IsDirected)) = length(connected_components(g)) == 1 +@traitfn is_connected(g::::IsDirected) = is_weakly_connected(g) """Returns connected components of the undirected graph of `g`.""" function weakly_connected_components end -@traitfn weakly_connected_components{G<:AbstractGraph; IsDirected{G}}(g::G) = connected_components(Graph(g)) +@traitfn weakly_connected_components(g::::IsDirected) = connected_components(Graph(g)) """Returns `true` if the undirected graph of `g` is connected.""" function is_weakly_connected end -@traitfn is_weakly_connected{G<:AbstractGraph; IsDirected{G}}(g::G) = length(weakly_connected_components(g)) == 1 +@traitfn is_weakly_connected(g::::IsDirected) = length(weakly_connected_components(g)) == 1 # Adapated from Graphs.jl mutable struct TarjanVisitor{T<:Integer} <: AbstractGraphVisitor @@ -158,7 +158,7 @@ end """Computes the (strongly) connected components of a directed graph.""" function strongly_connected_components end -@traitfn function strongly_connected_components{G<:AbstractGraph; IsDirected{G}}(g::G) +@traitfn function strongly_connected_components(g::::IsDirected) T = eltype(g) nvg = nv(g) cmap = zeros(Int, nvg) @@ -178,11 +178,11 @@ end """Returns `true` if `g` is (strongly) connected.""" function is_strongly_connected end -@traitfn is_strongly_connected{G<:AbstractGraph; IsDirected{G}}(g::G) = length(strongly_connected_components(g)) == 1 +@traitfn is_strongly_connected(g::::IsDirected) = length(strongly_connected_components(g)) == 1 """Computes the (common) period for all nodes in a strongly connected graph.""" function period end -@traitfn function period{G<:AbstractGraph; IsDirected{G}}(g::G) +@traitfn function period(g::::IsDirected) T = eltype(g) !is_strongly_connected(g) && error("Graph must be strongly connected") @@ -206,7 +206,7 @@ end """Computes the condensation graph of the strongly connected components.""" function condensation end -@traitfn function condensation{T<:Integer, G<:AbstractGraph; IsDirected{G}}(g::G, scc::Vector{Vector{T}}) +@traitfn function condensation{T<:Integer}(g::::IsDirected, scc::Vector{Vector{T}}) h = DiGraph{T}(length(scc)) component = Vector{T}(nv(g)) @@ -236,7 +236,7 @@ condensation(g) = condensation(g,strongly_connected_components(g)) 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<:AbstractGraph; IsDirected{G}}(g::G) +@traitfn function attracting_components(g::::IsDirected) T = eltype(g) scc = strongly_connected_components(g) cond = condensation(g,scc) @@ -256,7 +256,7 @@ mutable struct NeighborhoodVisitor{T<:Integer} <: AbstractGraphVisitor neigs::Vector{T} end -NeighborhoodVisitor{T<:Integer}(d::T) = NeighborhoodVisitor(d, Vector{T}()) +NeighborhoodVisitor(d::T) where T<:Integer = NeighborhoodVisitor(d, Vector{T}()) function examine_neighbor!(visitor::NeighborhoodVisitor, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) -ucolor > visitor.d && return false # color is negative for not-closed vertices diff --git a/src/core.jl b/src/core.jl index f025fecc1..994bcf16c 100644 --- a/src/core.jl +++ b/src/core.jl @@ -27,10 +27,10 @@ For directed graphs, this value equals the incoming plus outgoing edges. For undirected graphs, it equals the connected edges. """ function degree end -@traitfn degree{G<:AbstractGraph; IsDirected{G}}(g::G, v::Integer) = indegree(g, v) + outdegree(g, v) -@traitfn degree{G<:AbstractGraph; !IsDirected{G}}(g::G, v::Integer) = indegree(g, v) +@traitfn degree(g::::IsDirected, v::Integer) = indegree(g, v) + outdegree(g, v) +@traitfn degree(g::::(!IsDirected), v::Integer) = indegree(g, v) -degree{T<:Integer}(g::AbstractGraph, v::AbstractArray{T,1} = vertices(g)) = [degree(g, x) for x in v] +degree(g::AbstractGraph, v::AbstractVector = vertices(g)) = [degree(g, x) for x in v] "Return the maximum `outdegree` of vertices in `g`." Δout(g) = noallocextreme(outdegree,(>), typemin(Int), g) @@ -78,9 +78,9 @@ For undirected graphs, this is equivalent to `out_neighbors` and `in_neighbors`. """ function all_neighbors end -@traitfn all_neighbors{G<:AbstractGraph; IsDirected{G}}(g::G, v::Integer) = +@traitfn all_neighbors(g::::IsDirected, v::Integer) = union(out_neighbors(g, v), in_neighbors(g, v)) -@traitfn all_neighbors{G<:AbstractGraph; !IsDirected{G}}(g::G, v::Integer) = +@traitfn all_neighbors(g::::(!IsDirected), v::Integer) = neighbors(g, v) @@ -101,9 +101,9 @@ number of possible edges ( |v| |v-1| for directed graphs and (|v| |v-1|) / 2 for undirected graphs). """ function density end -@traitfn density{G<:AbstractGraph; IsDirected{G}}(g::G) = +@traitfn density(g::::IsDirected) = ne(g) / (nv(g) * (nv(g)-1)) -@traitfn density{G<:AbstractGraph; !IsDirected{G}}(g::G) = +@traitfn density(g::::(!IsDirected)) = (2*ne(g)) / (nv(g) * (nv(g)-1)) diff --git a/src/distance.jl b/src/distance.jl index 558c6449c..b9745bc64 100644 --- a/src/distance.jl +++ b/src/distance.jl @@ -1,8 +1,6 @@ # used in shortest path calculations -# has_distances{T}(distmx::AbstractArray{T,2}) = -# issparse(distmx)? (nnz(distmx) > 0) : !isempty(distmx) -struct DefaultDistance<:AbstractArray{Int, 2} +struct DefaultDistance<:AbstractMatrix{Int} nv::Int DefaultDistance(nv::Int=typemax(Int)) = new(nv) end @@ -33,47 +31,45 @@ 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, store, and pass the eccentricities if multiple distance measures are desired. """ -function eccentricity{T, U<:Integer}( +function eccentricity( g::AbstractGraph, - v::U, - distmx::AbstractArray{T, 2} = DefaultDistance() -) + v::Integer, + distmx::AbstractMatrix{T} = DefaultDistance() +) where T e = maximum(dijkstra_shortest_paths(g,v,distmx).dists) e == typemax(T) && error("Infinite path length detected") return e end -eccentricity{T, U<:Integer}( +eccentricity( g::AbstractGraph, - vs::AbstractArray{U, 1}=vertices(g), - distmx::AbstractArray{T, 2} = DefaultDistance() -) = - [eccentricity(g,v,distmx) for v in vs] + vs::AbstractVector = vertices(g), + distmx::AbstractMatrix = DefaultDistance() +) = [eccentricity(g,v,distmx) for v in vs] -eccentricity{T}(g::AbstractGraph, distmx::AbstractArray{T, 2}) = +eccentricity(g::AbstractGraph, distmx::AbstractMatrix) = eccentricity(g, vertices(g), distmx) """Returns the maximum eccentricity of the graph.""" -diameter{T}(all_e::Vector{T}) = maximum(all_e) -diameter{T}(g::AbstractGraph, distmx::AbstractArray{T, 2} = DefaultDistance()) = +diameter(all_e::Vector{T}) where T = maximum(all_e) +diameter(g::AbstractGraph, distmx::AbstractMatrix{T} = DefaultDistance()) where T = 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{T}(all_e::Vector{T}) - +function periphery(all_e::Vector{T}) where T diam = maximum(all_e) return filter((x)->all_e[x] == diam, 1:length(all_e)) end -periphery{T}(g::AbstractGraph, distmx::AbstractArray{T, 2} = DefaultDistance()) = +periphery(g::AbstractGraph, distmx::AbstractMatrix = DefaultDistance()) = periphery(eccentricity(g, distmx)) """Returns the minimum eccentricity of the graph.""" radius{T}(all_e::Vector{T}) = minimum(all_e) -radius{T}(g::AbstractGraph, distmx::AbstractArray{T, 2} = DefaultDistance()) = +radius{T}(g::AbstractGraph, distmx::AbstractMatrix{T} = DefaultDistance()) = minimum(eccentricity(g, distmx)) """Returns the set of all vertices whose eccentricity is equal to the graph's @@ -84,5 +80,5 @@ function center{T}(all_e::Vector{T}) return filter((x)->all_e[x] == rad, 1:length(all_e)) end -center{T}(g::AbstractGraph, distmx::AbstractArray{T, 2} = DefaultDistance()) = +center{T}(g::AbstractGraph, distmx::AbstractMatrix{T} = DefaultDistance()) = center(eccentricity(g, distmx)) diff --git a/src/flow/boykov_kolmogorov.jl b/src/flow/boykov_kolmogorov.jl index 2bd572974..4ff134e68 100644 --- a/src/flow/boykov_kolmogorov.jl +++ b/src/flow/boykov_kolmogorov.jl @@ -23,8 +23,8 @@ capacity_matrix::AbstractMatrix # edge flow capacities Author: Júlio Hoffimann Mendes (juliohm@stanford.edu) """ function boykov_kolmogorov_impl end -@traitfn function boykov_kolmogorov_impl{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@traitfn function boykov_kolmogorov_impl( + residual_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix # edge flow capacities @@ -63,8 +63,8 @@ function boykov_kolmogorov_impl end return flow, flow_matrix, TREE end -@traitfn function find_path!{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@traitfn function find_path!( + residual_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex flow_matrix::AbstractMatrix, # the current flow matrix @@ -157,8 +157,8 @@ function augment!( return Δ end -@traitfn function adopt!{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@traitfn function adopt!( + residual_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex flow_matrix::AbstractMatrix, # the current flow matrix diff --git a/src/flow/dinic.jl b/src/flow/dinic.jl index f47920b92..ac93dc259 100644 --- a/src/flow/dinic.jl +++ b/src/flow/dinic.jl @@ -9,11 +9,11 @@ Requires arguments: residual_graph::DiGraph # the input graph source::Integer # the source vertex target::Integer # the target vertex -capacity_matrix::AbstractArray{T,2} # edge flow capacities +capacity_matrix::AbstractMatrix # edge flow capacities """ function dinic_impl end -@traitfn function dinic_impl{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@traitfn function dinic_impl( + residual_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix # edge flow capacities @@ -44,13 +44,13 @@ Requires arguments: residual_graph::DiGraph # the input graph source::Integer # the source vertex target::Integer # the target vertex -capacity_matrix::AbstractArray{T,2} # edge flow capacities -flow_matrix::AbstractArray{T,2} # the current flow matrix +capacity_matrix::AbstractMatrix # edge flow capacities +flow_matrix::AbstractMatrix # the current flow matrix P::AbstractVector{Int} # Parent vector to store Level Graph """ function blocking_flow! end -@traitfn function blocking_flow!{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@traitfn function blocking_flow!( + residual_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix, # edge flow capacities diff --git a/src/flow/edmonds_karp.jl b/src/flow/edmonds_karp.jl index 20a1b11be..dcbc30aa4 100644 --- a/src/flow/edmonds_karp.jl +++ b/src/flow/edmonds_karp.jl @@ -7,11 +7,11 @@ Requires arguments: - residual_graph::DiGraph # the input graph - source::Integer # the source vertex - target::Integer # the target vertex -- capacity_matrix::AbstractArray{T,2} # edge flow capacities +- capacity_matrix::AbstracMatrix # edge flow capacities """ function edmonds_karp_impl end -@traitfn function edmonds_karp_impl{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@traitfn function edmonds_karp_impl( + residual_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix # edge flow capacities @@ -57,9 +57,9 @@ 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::AbstractArray{T,2} # the current flow matrix -- capacity_matrix::AbstractArray{T,2} # edge flow capacities +- path::Vector{Int} # input path +- flow_matrix::AbstractMatrix # the current flow matrix +- capacity_matrix::AbstractMatrix # edge flow capacities """ function augment_path!( @@ -95,8 +95,8 @@ Flag Values: 2 => No Path to source """ function fetch_path end -@traitfn function fetch_path{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@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 @@ -127,14 +127,14 @@ Requires arguments: residual_graph::DiGraph # the input graph source::Int # the source vertex target::Int # the target vertex -flow_matrix::AbstractArray{T,2} # the current flow matrix -capacity_matrix::AbstractArray{T,2} # edge flow capacities +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 """ function fetch_path! end -@traitfn function fetch_path!{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@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 diff --git a/src/flow/ext_multiroute_flow.jl b/src/flow/ext_multiroute_flow.jl index c360728cf..98ab00052 100644 --- a/src/flow/ext_multiroute_flow.jl +++ b/src/flow/ext_multiroute_flow.jl @@ -12,7 +12,7 @@ Requires arguments: - flow_graph::DiGraph # the input graph - source::Int # the source vertex - target::Int # the target vertex -- capacity_matrix::AbstractArray{T, 2} # edge flow capacities +- capacity_matrix::AbstractMatrix{T} # edge flow capacities - flow_algorithm::AbstractFlowAlgorithm # keyword argument for algorithm - routes::Int # keyword argument for routes """ @@ -41,11 +41,11 @@ Requires arguments: - flow_graph::DiGraph # the input graph - source::Int # the source vertex - target::Int # the target vertex -- capacity_matrix::AbstractArray{T, 2} # edge flow capacities +- capacity_matrix::AbstractMatrix{T} # edge flow capacities """ function auxiliaryPoints end -@traitfn function auxiliaryPoints{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph +@traitfn function auxiliaryPoints( + flow_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix # edge flow capacities @@ -106,11 +106,11 @@ Requires arguments: - flow_graph::DiGraph # the input graph - source::Int # the source vertex - target::Int # the target vertex -- capacity_matrix::AbstractArray{T, 2} # edge flow capacities +- capacity_matrix::AbstractMatrix{T} # edge flow capacities """ function breakingPoints end -@traitfn function breakingPoints{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph +@traitfn function breakingPoints( + flow_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix # edge flow capacities @@ -144,7 +144,7 @@ Note: this is more efficient than maximum() / minimum() / extrema() since we have to ignore zero values.since we have to ignore zero values. Requires argument: -- capacity_matrix::AbstractArray{T, 2} # edge flow capacities +- capacity_matrix::AbstractMatrix{T} # edge flow capacities """ # Function to get the nonzero min and max function of a Matrix @@ -169,14 +169,14 @@ 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::AbstractArray{T, 2}, # edge flow capacities +capacity_matrix::AbstractMatrix{T}, # edge flow capacities cut::Vector{Int}, # cut information for vertices restriction::T # value of the restriction """ function slope end # Function to get the slope of the restricted flow -@traitfn function slope{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph +@traitfn function slope( + flow_graph::::IsDirected, # the input graph capacity_matrix::AbstractMatrix, # edge flow capacities cut::Vector, # cut information for vertices restriction::Number # value of the restriction @@ -208,14 +208,14 @@ Requires argument: """ # Compute the (expected) intersection of two lines -function intersection{T<:AbstractFloat, R<:Real}( +function intersection( x1::T, # x coordinate of point 1 y1::T, # y coordinate of point 1 a1::Integer, # slope passing by point 1 x2::T, # x coordinate of point 2 y2::T, # y coordinate of point 2 a2::R # slope passing by point 2 - ) + ) where T<:AbstractFloat where R<:Real (a1 == a2) && return -1., -1. # result will be ignored in other intersection method b1 = y1 - a1 * x1 @@ -225,10 +225,10 @@ function intersection{T<:AbstractFloat, R<:Real}( return x, y end # Compute the intersection between a set of segment and a line of slope k passing by the origin -function intersection{T<:AbstractFloat, I<:Integer, R<:Real}( +function intersection( points::Vector{Tuple{T, T, I}}, # vector of breaking points k::R # number of routes (slope of the line) - ) + ) where T<:AbstractFloat where I<:Integer where R<:Real λ = points[1][1] # Connectivity # Loop over the segments (pair of breaking points) @@ -250,4 +250,5 @@ Requires argument: a::Tuple{T, T}, # Point A with floating coordinates b::Tuple{T, T} # Point B with floating coordinates """ -≈{T<:AbstractFloat}(a::Tuple{T, T}, b::Tuple{T, T}) = a[1] ≈ b[1] && a[2] ≈ b[2] +≈(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 6aa462f8a..50d05d1a9 100644 --- a/src/flow/kishimoto.jl +++ b/src/flow/kishimoto.jl @@ -1,8 +1,8 @@ # Method when using Boykov-Kolmogorov as a subroutine # Kishimoto algorithm -@traitfn function kishimoto{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph +@traitfn function kishimoto( + flow_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix, # edge flow capacities @@ -41,13 +41,13 @@ Requires arguments: - flow_graph::DiGraph # the input graph - source::Integer # the source vertex - target::Integer # the target vertex -- capacity_matrix::AbstractArray{T, 2} # edge flow capacities +- 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{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph +@traitfn function kishimoto( + flow_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix, # edge flow capacities diff --git a/src/flow/maximum_flow.jl b/src/flow/maximum_flow.jl index 0314a1899..8a8c17eb1 100644 --- a/src/flow/maximum_flow.jl +++ b/src/flow/maximum_flow.jl @@ -35,7 +35,8 @@ struct DefaultCapacity{T<:Integer} <: AbstractMatrix{T} nv::T end -@traitfn DefaultCapacity{G<:AbstractGraph; IsDirected{G}}(flow_graph::G) = DefaultCapacity(DiGraph(flow_graph), nv(flow_graph)) +@traitfn DefaultCapacity(flow_graph::::IsDirected) = + DefaultCapacity(DiGraph(flow_graph), nv(flow_graph)) getindex{T<:Integer}(d::DefaultCapacity{T}, s::Integer, t::Integer) = if has_edge(d.flow_graph, s , t) one(T) else zero(T) end # isassigned{T<:Integer}(d::DefaultCapacity{T}, u::T, v::T) = (u in 1:d.nv) && (v in 1:d.nv) @@ -58,15 +59,17 @@ residual graph and the modified capacity_matrix (when DefaultDistance is used.) Requires arguments: - flow_graph::DiGraph, # the input graph -- capacity_matrix::AbstractArray{T,2} # input capacity matrix +- source::Integer # the source vertex +- target::Integer # the target vertex +- capacity_matrix::AbstractMatrix # input capacity matrix """ function residual end -@traitfn residual{G<:AbstractGraph; IsDirected{G}}(flow_graph::G) = DiGraph(Graph(flow_graph)) +@traitfn residual(flow_graph::::IsDirected) = DiGraph(Graph(flow_graph)) # Method for Edmonds–Karp algorithm -@traitfn function maximum_flow{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph +@traitfn function maximum_flow( + flow_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix, # edge flow capacities @@ -78,8 +81,8 @@ end # Method for Dinic's algorithm -@traitfn function maximum_flow{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph +@traitfn function maximum_flow( + flow_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix, # edge flow capacities @@ -91,8 +94,8 @@ end # Method for Boykov-Kolmogorov algorithm -@traitfn function maximum_flow{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph +@traitfn function maximum_flow( + flow_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix, # edge flow capacities @@ -104,8 +107,8 @@ end # Method for Push-relabel algorithm -@traitfn function maximum_flow{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph +@traitfn function maximum_flow( + flow_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix, # edge flow capacities @@ -121,9 +124,9 @@ Generic maximum_flow function. Requires arguments: - flow_graph::DiGraph # the input graph - source::Integer # the source vertex - target::Integer # the target vertex -- capacity_matrix::AbstractArray{T,2} # edge flow capacities +- capacity_matrix::AbstractMatrix # edge flow capacities - algorithm::AbstractFlowAlgorithm # keyword argument for algorithm -- restriction::T # keyword argument for a restriction +- restriction::Real # keyword argument for a 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 diff --git a/src/flow/multiroute_flow.jl b/src/flow/multiroute_flow.jl index 79ab07ac6..956ec5f7f 100644 --- a/src/flow/multiroute_flow.jl +++ b/src/flow/multiroute_flow.jl @@ -18,25 +18,25 @@ end # Methods when the number of routes is more than the connectivity # 1) When using Boykov-Kolmogorov as a flow subroutine # 2) Other flow algorithm -function empty_flow{T<:Real}( +function empty_flow( capacity_matrix::AbstractMatrix{T}, # edge flow capacities flow_algorithm::BoykovKolmogorovAlgorithm # keyword argument for algorithm - ) + ) where T<:Real n = size(capacity_matrix, 1) return zero(T), zeros(T, n, n), zeros(T, n) end # 2) Other flow algorithm -function empty_flow{T<:Real}( +function empty_flow( capacity_matrix::AbstractMatrix{T}, # edge flow capacities flow_algorithm::AbstractFlowAlgorithm # keyword argument for algorithm - ) + ) where T<:Real n = size(capacity_matrix, 1) return zero(T), zeros(T, n, n) end # Method for Kishimoto algorithm -@traitfn function multiroute_flow{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph +@traitfn function multiroute_flow( + flow_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix, # edge flow capacities @@ -49,8 +49,8 @@ end ## Methods for Extended Multiroute Flow Algorithm #1 When the breaking points are not already known -@traitfn function multiroute_flow{G<:AbstractGraph; IsDirected{G}}( - flow_graph::G, # the input graph +@traitfn function multiroute_flow( + flow_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix, # edge flow capacities @@ -62,14 +62,14 @@ end end #2 When the breaking points are already known #2-a Output: flow value (paired with the associated restriction) -function multiroute_flow{T<:Real, R<:Real}( +multiroute_flow( breakingpoints::Vector{Tuple{T, T, Int}}, # vector of breaking points routes::R # keyword argument for routes - ) - return intersection(breakingpoints, routes) -end + ) where T<:Real where R<:Real = + intersection(breakingpoints, routes) + #2-b Output: flow value, flows(, labels) -function multiroute_flow{T1<:Real, R<:Real}( +function multiroute_flow( breakingpoints::AbstractVector{Tuple{T1, T1, Int}}, # vector of breaking points routes::R, # keyword argument for routes flow_graph::AbstractGraph, # the input graph @@ -79,12 +79,12 @@ function multiroute_flow{T1<:Real, R<:Real}( DefaultCapacity(flow_graph); flow_algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm PushRelabelAlgorithm() - ) + ) where T1<:Real where R<:Real x, f = intersection(breakingpoints, routes) T2 = eltype(capacity_matrix) # For other cases, capacities need to be Floats if !(T2<:AbstractFloat) - capacity_matrix = convert(AbstractArray{Float64, 2}, capacity_matrix) + capacity_matrix = convert(AbstractMatrix{Float64}, capacity_matrix) end return maximum_flow(flow_graph, source, target, capacity_matrix, @@ -107,7 +107,7 @@ 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::AbstractArray{T, 2} # edge flow capacities with T<:Real +- 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 @@ -126,7 +126,7 @@ and the network descriptors, it requires the following arguments: - flow_graph::DiGraph # the input graph - source::Integer # the source vertex - target::Integer # the target vertex -- capacity_matrix::AbstractArray{T2, 2} # optional edge flow capacities (T2<:Real) +- capacity_matrix::AbstractMatrix # optional edge flow capacities (T2<:Real) - flow_algorithm::AbstractFlowAlgorithm # keyword argument for algorithm The function defaults to the Push-relabel (classical flow) and Kishimoto @@ -178,18 +178,18 @@ algorithm = BoykovKolmogorovAlgorithm(), routes = 2) ``` """ -function multiroute_flow{R<:Real}( +function multiroute_flow( flow_graph::AbstractGraph, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix = # edge flow capacities - DefaultCapacity(flow_graph); + DefaultCapacity(flow_graph); flow_algorithm::AbstractFlowAlgorithm = # keyword argument for algorithm PushRelabelAlgorithm(), mrf_algorithm::AbstractMultirouteFlowAlgorithm = # keyword argument for algorithm KishimotoAlgorithm(), 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 (routes == 1) && diff --git a/src/flow/push_relabel.jl b/src/flow/push_relabel.jl index 4d4eb211d..54572dd75 100644 --- a/src/flow/push_relabel.jl +++ b/src/flow/push_relabel.jl @@ -14,11 +14,11 @@ Requires arguments: - residual_graph::DiGraph # the input graph - source::Integer # the source vertex - target::Integer # the target vertex -- capacity_matrix::AbstractArray{T,2} # edge flow capacities +- capacity_matrix::AbstractMatrix # edge flow capacities """ function push_relabel end -@traitfn function push_relabel{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@traitfn function push_relabel( + residual_graph::::IsDirected, # the input graph source::Integer, # the source vertex target::Integer, # the target vertex capacity_matrix::AbstractMatrix # edge flow capacities @@ -64,10 +64,10 @@ Pushes inactive nodes into the queue and activates them. Requires arguments: -- Q::AbstractArray{Int,1} +- Q::AbstractVector - v::Integer -- active::AbstractArray{Bool,1} -- excess::AbstractArray{T,1} +- active::AbstractVector{Bool} +- excess::AbstractVector """ function enqueue_vertex!( @@ -91,16 +91,16 @@ Requires arguements: - residual_graph::DiGraph # the input graph - u::Integer # input from-vertex - v::Integer # input to-vetex -- capacity_matrix::AbstractArray{T,2} -- flow_matrix::AbstractArray{T,2} -- excess::AbstractArray{T,1} -- height::AbstractArray{Int,1} -- active::AbstractArray{Bool,1} -- Q::AbstractArray{Integer,1} +- capacity_matrix::AbstractMatrix +- flow_matrix::AbstractMatrix +- excess::AbstractVector +- height::AbstractVector{Int} +- active::AbstractVector{Bool} +- Q::AbstractVector """ function push_flow! end -@traitfn function push_flow!{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@traitfn function push_flow!( + residual_graph::::IsDirected, # the input graph u::Integer, # input from-vertex v::Integer, # input to-vetex capacity_matrix::AbstractMatrix, @@ -133,15 +133,15 @@ Requires arguments: - residual_graph::DiGraph # the input graph - h::Int # cutoff height -- excess::AbstractArray{T,1} -- height::AbstractArray{Int,1} -- active::AbstractArray{Bool,1} -- count::AbstractArray{Int,1} -- Q::AbstractArray{Integer,1} +- excess::AbstractVector +- height::AbstractVector{Int} +- active::AbstractVector{Bool} +- count::AbstractVector{Int} +- Q::AbstractVector """ function gap! end -@traitfn function gap!{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@traitfn function gap!( + residual_graph::::IsDirected, # the input graph h::Int, # cutoff height excess::AbstractVector, height::AbstractVector{Int}, @@ -168,17 +168,17 @@ Requires arguments: - residual_graph::DiGraph # the input graph - v::Integer # input vertex to be relabeled -- capacity_matrix::AbstractArray{T,2} -- flow_matrix::AbstractArray{T,2} -- excess::AbstractArray{T,1} -- height::AbstractArray{Int,1} -- active::AbstractArray{Bool,1} -- count::AbstractArray{Int,1} -- Q::AbstractArray{Integer,1} +- capacity_matrix::AbstractMatrix +- flow_matrix::AbstractMatrix +- excess::AbstractVector +- height::AbstractVector{Int} +- active::AbstractVector{Bool} +- count::AbstractVector{Int} +- Q::AbstractVector """ function relabel! end -@traitfn function relabel!{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@traitfn function relabel!( + residual_graph::::IsDirected, # the input graph v::Integer, # input vertex to be relabeled capacity_matrix::AbstractMatrix, flow_matrix::AbstractMatrix, @@ -210,17 +210,17 @@ Requires arguments: - residual_graph::DiGraph # the input graph - v::Integer # vertex to be discharged -- capacity_matrix::AbstractArray{T,2} -- flow_matrix::AbstractArray{T,2} -- excess::AbstractArray{T,1} -- height::AbstractArray{Int,1} -- active::AbstractArray{Bool,1} -- count::AbstractArray{Int,1} -- Q::AbstractArray{Integer,1} +- capacity_matrix::AbstractMatrix +- flow_matrix::AbstractMatrix +- excess::AbstractVector +- height::AbstractVector{Int} +- active::AbstractVector{Bool} +- count::AbstractVector{Int} +- Q::AbstractVector """ function discharge! end -@traitfn function discharge!{G<:AbstractGraph; IsDirected{G}}( - residual_graph::G, # the input graph +@traitfn function discharge!( + residual_graph::::IsDirected, # the input graph v::Integer, # vertex to be discharged capacity_matrix::AbstractMatrix, flow_matrix::AbstractMatrix, @@ -228,7 +228,7 @@ function discharge! end height::AbstractVector{Int}, active::AbstractVector{Bool}, count::AbstractVector{Int}, - Q::AbstractArray # FIFO queue + Q::AbstractVector # FIFO queue ) for to in out_neighbors(residual_graph, v) excess[v] == 0 && break diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 04656d2a8..5444f7668 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -1,10 +1,12 @@ -function Graph{T<:Integer}(nv::T, ne::Integer; seed::Int = -1) - maxe = div(nv * (nv-1), 2) +function Graph(nv::Integer, ne::Integer; seed::Int = -1) + T = eltype(nv) + maxe = div(Int(nv) * (nv-1), 2) @assert(ne <= maxe, "Maximum number of edges for this graph is $maxe") ne > 2/3 * maxe && return complement(Graph(nv, maxe-ne)) rng = getRNG(seed) g = Graph(nv) + while g.ne < ne source = rand(rng, one(T):nv) dest = rand(rng, one(T):nv) @@ -13,8 +15,9 @@ function Graph{T<:Integer}(nv::T, ne::Integer; seed::Int = -1) return g end -function DiGraph{T<:Integer}(nv::T, ne::Integer; seed::Int = -1) - maxe = nv * (nv-1) +function DiGraph(nv::Integer, ne::Integer; seed::Int = -1) + T = eltype(nv) + maxe = Int(nv) * (nv-1) @assert(ne <= maxe, "Maximum number of edges for this graph is $maxe") ne > 2/3 * maxe && return complement(DiGraph(nv, maxe-ne)) @@ -93,7 +96,7 @@ function watts_strogatz(n::Integer, k::Integer, β::Real; is_directed=false, see return g end -function _suitable{T<:Integer}(edges::Set{Edge}, potential_edges::Dict{T, T}) +function _suitable(edges::Set{Edge}, potential_edges::Dict{T, T}) where T<:Integer isempty(potential_edges) && return true list = keys(potential_edges) for s1 in list, s2 in list @@ -105,7 +108,7 @@ end _try_creation(n::Integer, k::Integer, rng::AbstractRNG) = _try_creation(n, fill(k,n), rng) -function _try_creation{T<:Integer}(n::T, k::Vector{T}, rng::AbstractRNG) +function _try_creation(n::T, k::Vector{T}, rng::AbstractRNG) where T<:Integer edges = Set{Edge}() m = 0 stubs = zeros(T, sum(k)) @@ -268,7 +271,7 @@ Reference: * 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{T<:Real}(m::Int, fitness::Vector{T}; seed::Int=-1) +function static_fitness_model(m::Int, fitness::Vector{T}; seed::Int=-1) where T<:Real @assert(m >= 0, "invalid number of edges") n = length(fitness) m == 0 && return Graph(n) @@ -288,7 +291,7 @@ function static_fitness_model{T<:Real}(m::Int, fitness::Vector{T}; seed::Int=-1) return g end -function static_fitness_model{T<:Real,S<:Real}(m::Int, fitness_out::Vector{T}, fitness_in::Vector{S}; seed::Int=-1) +function static_fitness_model(m::Int, 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) @assert(length(fitness_in) == n, "fitness_in must have the same size as fitness_out") @@ -312,7 +315,7 @@ function static_fitness_model{T<:Real,S<:Real}(m::Int, fitness_out::Vector{T}, f return g end -function _create_static_fitness_graph!{T<:Real,S<:Real}(g::AbstractGraph, m::Int, cum_fitness_out::Vector{T}, cum_fitness_in::Vector{S}, seed::Int) +function _create_static_fitness_graph!(g::AbstractGraph, m::Int, cum_fitness_out::Vector{T}, cum_fitness_in::Vector{S}, seed::Int) where T<:Real where S<:Real rng = getRNG(seed) max_out = cum_fitness_out[end] max_in = cum_fitness_in[end] @@ -429,7 +432,7 @@ approximately $nc^2$ time. If `check_graphical=true` makes sure that `k` is a graphical sequence (see `isgraphical`). """ -function random_configuration_model(n::Int, k::Array{Int}; seed::Int=-1, check_graphical::Bool=false) +function random_configuration_model(n::Integer, k::Array{Int}; seed::Int=-1, check_graphical::Bool=false) @assert(n == length(k), "a degree sequence of length n has to be provided") m = sum(k) @assert(iseven(m), "sum(k) must be even") @@ -453,7 +456,7 @@ function random_configuration_model(n::Int, k::Array{Int}; seed::Int=-1, check_g end doc""" -random_regular_digraph(n::Int, k::Int; dir::Symbol=:out, seed=-1) +random_regular_digraph(n::Integer, k::Int; dir::Symbol=:out, seed=-1) Creates a random directed [regular graph](https://en.wikipedia.org/wiki/Regular_graph) with `n` vertices, @@ -509,7 +512,7 @@ The second form samples from a SBM with `c[a,a]=cin`, and `c[a,b]=coff`. For a dynamic version of the SBM see the `StochasticBlockModel` type and related functions. """ -function stochastic_block_model{T<:Real}(c::Matrix{T}, n::Vector{Int}; seed::Int = -1) +function stochastic_block_model(c::Matrix{T}, n::Vector{Int}; seed::Int = -1) where T<:Real @assert size(c,1) == length(n) @assert size(c,2) == length(n) # init dsfmt generator without altering GLOBAL_RNG @@ -545,7 +548,7 @@ function stochastic_block_model{T<:Real}(c::Matrix{T}, n::Vector{Int}; seed::Int return g end -function stochastic_block_model{T<:Real}(cint::T, cext::T, n::Vector{Int}; seed::Int=-1) +function stochastic_block_model(cint::T, cext::T, n::Vector{Int}; seed::Int=-1) where T<:Real K = length(n) c = [ifelse(a==b, cint, cext) for a=1:K,b=1:K] stochastic_block_model(c, n, seed=seed) @@ -586,7 +589,7 @@ end and the affinity matrix. This construction implies that consecutive vertices will be in the same blocks, except for the block boundaries. """ -function StochasticBlockModel{T,P}(sizes::Vector{T}, affinities::Matrix{P}; seed::Int = -1) +function StochasticBlockModel(sizes::AbstractVector, affinities::AbstractMatrix; seed::Int = -1) csum = cumsum(sizes) j = 1 nodemap = zeros(Int, csum[end]) diff --git a/src/generators/smallgraphs.jl b/src/generators/smallgraphs.jl index 6d725d651..5bb431fde 100644 --- a/src/generators/smallgraphs.jl +++ b/src/generators/smallgraphs.jl @@ -2,7 +2,7 @@ # STATIC SMALL GRAPHS ##################### -function _make_simple_undirected_graph{T<:Integer}(n::T, edgelist::Vector{Tuple{T,T}}) +function _make_simple_undirected_graph(n::T, edgelist::Vector{Tuple{T,T}}) where T<:Integer g = Graph(n) for (s,d) in edgelist add_edge!(g, Edge(s,d)) @@ -10,7 +10,7 @@ function _make_simple_undirected_graph{T<:Integer}(n::T, edgelist::Vector{Tuple{ return g end -function _make_simple_directed_graph{T<:Integer}(n::T, edgelist::Vector{Tuple{T,T}}) +function _make_simple_directed_graph(n::T, edgelist::Vector{Tuple{T,T}}) where T<:Integer g = DiGraph(n) for (s,d) in edgelist add_edge!(g, Edge(s,d)) diff --git a/src/generators/staticgraphs.jl b/src/generators/staticgraphs.jl index 260de7889..8879ef444 100644 --- a/src/generators/staticgraphs.jl +++ b/src/generators/staticgraphs.jl @@ -134,12 +134,12 @@ function WheelDiGraph(n::Integer) end """ -Grid{T<:Integer}(dims::AbstractVector{T}; periodic=false) +Grid(dims::AbstractVector; periodic=false) 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. """ -function Grid{T<:Integer}(dims::AbstractVector{T}; periodic=false) +function Grid(dims::AbstractVector; periodic=false) func = periodic ? CycleGraph : PathGraph g = func(dims[1]) for d in dims[2:end] @@ -149,8 +149,8 @@ function Grid{T<:Integer}(dims::AbstractVector{T}; periodic=false) end """create a binary tree with k-levels vertices are numbered 1:2^levels-1""" -function BinaryTree(levels::Int) - g = Graph(2^levels-1) +function BinaryTree(levels::Integer) + g = Graph(Int(2^levels-1)) for i in 0:levels-2 for j in 2^i:2^(i+1)-1 add_edge!(g, j, 2j) @@ -164,7 +164,7 @@ end Create a double complete binary tree with k-levels used as an example for spectral clustering by Guattery and Miller 1998. """ -function DoubleBinaryTree(levels::Int) +function DoubleBinaryTree(levels::Integer) gl = BinaryTree(levels) gr = BinaryTree(levels) g = blkdiag(gl, gr) @@ -174,7 +174,7 @@ end """The Roach Graph from Guattery and Miller 1998""" -function RoachGraph(k::Int) +function RoachGraph(k::Integer) dipole = CompleteGraph(2) nopole = Graph(2) antannae = crosspath(k, nopole) diff --git a/src/graphtypes/simplegraph/SimpleGraphs.jl b/src/graphtypes/simplegraph/SimpleGraphs.jl index 9d83c0178..5d0e20189 100644 --- a/src/graphtypes/simplegraph/SimpleGraphs.jl +++ b/src/graphtypes/simplegraph/SimpleGraphs.jl @@ -57,7 +57,7 @@ end in_neighbors(g::AbstractSimpleGraph, v::Integer) = badj(g,v) out_neighbors(g::AbstractSimpleGraph, v::Integer) = fadj(g,v) -function issubset{T<:AbstractSimpleGraph}(g::T, h::T) +function issubset(g::T, h::T) where T<:AbstractSimpleGraph (gmin, gmax) = extrema(vertices(g)) (hmin, hmax) = extrema(vertices(h)) return (hmin <= gmin <= gmax <= hmax) && issubset(edges(g), edges(h)) diff --git a/src/graphtypes/simplegraph/simpledigraph.jl b/src/graphtypes/simplegraph/simpledigraph.jl index bad194647..7a516aea1 100644 --- a/src/graphtypes/simplegraph/simpledigraph.jl +++ b/src/graphtypes/simplegraph/simpledigraph.jl @@ -12,7 +12,7 @@ eltype{T<:Integer}(x::SimpleDiGraph{T}) = T # DiGraph{UInt8}(6), DiGraph{Int16}(7), DiGraph{Int8}() -function (::Type{SimpleDiGraph{T}}){T<:Integer}(n::Integer = 0) +function (::Type{SimpleDiGraph{T}})(n::Integer = 0) where T<:Integer fadjlist = Vector{Vector{T}}() badjlist = Vector{Vector{T}}() for _ = one(T):n @@ -27,13 +27,13 @@ end SimpleDiGraph() = SimpleDiGraph{Int}() # DiGraph(6), DiGraph(0x5) -SimpleDiGraph{T<:Integer}(n::T) = SimpleDiGraph{T}(n) +SimpleDiGraph(n::T) where T<:Integer = SimpleDiGraph{T}(n) # SimpleDiGraph(UInt8) -SimpleDiGraph{T<:Integer}(::Type{T}) = SimpleDiGraph{T}(zero(T)) +SimpleDiGraph(::Type{T}) where T<:Integer = SimpleDiGraph{T}(zero(T)) # sparse adjacency matrix constructor: DiGraph(adjmx) -function (::Type{SimpleDiGraph{T}}){T<:Integer, U}(adjmx::SparseMatrixCSC{U}) +function (::Type{SimpleDiGraph{T}})(adjmx::SparseMatrixCSC{U}) where T<:Integer where U<:Real dima, dimb = size(adjmx) isequal(dima,dimb) || error("Adjacency / distance matrices must be square") @@ -52,7 +52,7 @@ function (::Type{SimpleDiGraph{T}}){T<:Integer, U}(adjmx::SparseMatrixCSC{U}) end # dense adjacency matrix constructor: DiGraph{UInt8}(adjmx) -function (::Type{SimpleDiGraph{T}}){T<:Integer}(adjmx::AbstractMatrix) +function (::Type{SimpleDiGraph{T}})(adjmx::AbstractMatrix) where T<:Integer dima,dimb = size(adjmx) isequal(dima,dimb) || error("Adjacency / distance matrices must be square") @@ -68,7 +68,7 @@ end SimpleDiGraph(adjmx::AbstractMatrix) = SimpleDiGraph{Int}(adjmx) # converts DiGraph{Int} to DiGraph{Int32} -function (::Type{SimpleDiGraph{T}}){T<:Integer}(g::SimpleDiGraph) +function (::Type{SimpleDiGraph{T}})(g::SimpleDiGraph) where T<:Integer h_vertices = one(T):T(nv(g)) h_fadj = [Vector{T}(x) for x in fadj(g)] h_badj = [Vector{T}(x) for x in badj(g)] @@ -85,14 +85,14 @@ function SimpleDiGraph(g::AbstractSimpleGraph) return h end -edgetype{T<:Integer}(::SimpleDiGraph{T}) = SimpleGraphEdge{T} +edgetype(::SimpleDiGraph{T}) where T<: Integer = SimpleGraphEdge{T} badj(g::SimpleDiGraph) = g.badjlist badj(g::SimpleDiGraph, v::Integer) = badj(g)[v] -copy{T<:Integer}(g::SimpleDiGraph{T}) = +copy(g::SimpleDiGraph{T}) where T<:Integer = SimpleDiGraph{T}(g.vertices, g.ne, deepcopy(g.fadjlist), deepcopy(g.badjlist)) @@ -129,7 +129,8 @@ function rem_edge!(g::SimpleDiGraph, e::SimpleDiGraphEdge) end -function add_vertex!{T<:Integer}(g::SimpleDiGraph{T}) +function add_vertex!(g::SimpleDiGraph) + T = eltype(g) (nv(g) + one(T) <= nv(g)) && return false # test for overflow g.vertices = 1:nv(g)+1 push!(g.badjlist, Vector{T}()) @@ -148,5 +149,3 @@ function has_edge(g::SimpleDiGraph, e::SimpleDiGraphEdge) return length(searchsorted(badj(g,v), u)) > 0 end end - -empty{T<:Integer}(g::SimpleDiGraph{T}) = SimpleDiGraph{T}() diff --git a/src/graphtypes/simplegraph/simpleedgeiter.jl b/src/graphtypes/simplegraph/simpleedgeiter.jl index 4a45fc711..e7a2e3e23 100644 --- a/src/graphtypes/simplegraph/simpleedgeiter.jl +++ b/src/graphtypes/simplegraph/simpleedgeiter.jl @@ -11,12 +11,15 @@ struct SimpleEdgeIterState{T<:Integer} end -eltype{T}(::Type{SimpleEdgeIter{T}}) = SimpleEdge +eltype(::Type{SimpleEdgeIter{T}}) where T<:Integer = SimpleEdge SimpleEdgeIter(g::SimpleGraph) = SimpleEdgeIter(ne(g), g.fadjlist, false) SimpleEdgeIter(g::SimpleDiGraph) = SimpleEdgeIter(ne(g), g.fadjlist, true) -function _next{T<:Integer}(eit::SimpleEdgeIter{T}, state::SimpleEdgeIterState{T} = SimpleEdgeIterState(one(T),1,false), first::Bool = true) +function _next( + eit::SimpleEdgeIter{T}, + state::SimpleEdgeIterState{T} = SimpleEdgeIterState(one(T),1,false), + first::Bool = true) where T <: Integer s = state.s di = state.di if !first @@ -58,8 +61,8 @@ function _isequal(e1::SimpleEdgeIter, e2) end return true end -==(e1::SimpleEdgeIter, e2::AbstractArray{SimpleEdge,1}) = _isequal(e1, e2) -==(e1::AbstractArray{SimpleEdge,1}, e2::SimpleEdgeIter) = _isequal(e2, e1) +==(e1::SimpleEdgeIter, e2::AbstractVector{SimpleEdge}) = _isequal(e1, e2) +==(e1::AbstractVector{SimpleEdge}, e2::SimpleEdgeIter) = _isequal(e2, e1) ==(e1::SimpleEdgeIter, e2::Set{SimpleEdge}) = _isequal(e1, e2) ==(e1::Set{SimpleEdge}, e2::SimpleEdgeIter) = _isequal(e2, e1) diff --git a/src/graphtypes/simplegraph/simplegraph.jl b/src/graphtypes/simplegraph/simplegraph.jl index d551b03e9..69d1782f5 100644 --- a/src/graphtypes/simplegraph/simplegraph.jl +++ b/src/graphtypes/simplegraph/simplegraph.jl @@ -7,10 +7,10 @@ mutable struct SimpleGraph{T<:Integer} <: AbstractSimpleGraph fadjlist::Vector{Vector{T}} # [src]: (dst, dst, dst) end -eltype{T<:Integer}(x::SimpleGraph{T}) = T +eltype(x::SimpleGraph{T}) where T<:Integer = T # Graph{UInt8}(6), Graph{Int16}(7), Graph{UInt8}() -function (::Type{SimpleGraph{T}}){T<:Integer}(n::Integer = 0) +function (::Type{SimpleGraph{T}})(n::Integer = 0) where T<:Integer fadjlist = Vector{Vector{T}}() sizehint!(fadjlist, n) for _ = one(T):n @@ -24,13 +24,13 @@ end SimpleGraph() = SimpleGraph{Int}() # Graph(6), Graph(0x5) -SimpleGraph{T<:Integer}(n::T) = SimpleGraph{T}(n) +SimpleGraph(n::T) where T<:Integer = SimpleGraph{T}(n) # SimpleGraph(UInt8) -SimpleGraph{T<:Integer}(::Type{T}) = SimpleGraph{T}(zero(T)) +SimpleGraph(::Type{T}) where T<:Integer = SimpleGraph{T}(zero(T)) # Graph{UInt8}(adjmx) -function (::Type{SimpleGraph{T}}){T<:Integer}(adjmx::AbstractMatrix) +function (::Type{SimpleGraph{T}})(adjmx::AbstractMatrix) where T<:Integer dima,dimb = size(adjmx) isequal(dima,dimb) || error("Adjacency / distance matrices must be square") issymmetric(adjmx) || error("Adjacency / distance matrices must be symmetric") @@ -44,7 +44,7 @@ function (::Type{SimpleGraph{T}}){T<:Integer}(adjmx::AbstractMatrix) end # converts Graph{Int} to Graph{Int32} -function (::Type{SimpleGraph{T}}){T<:Integer}(g::SimpleGraph) +function (::Type{SimpleGraph{T}})(g::SimpleGraph) where T<:Integer h_vertices = one(T):T(nv(g)) h_fadj = [Vector{T}(x) for x in fadj(g)] return SimpleGraph(h_vertices, ne(g), h_fadj) @@ -75,7 +75,7 @@ function SimpleGraph(g::SimpleDiGraph) return SimpleGraph(vertices(g), edgect ÷ 2, newfadj) end -edgetype{T<:Integer}(::SimpleGraph{T}) = SimpleGraphEdge{T} +edgetype(::SimpleGraph{T}) where T<:Integer = SimpleGraphEdge{T} """ Returns the backwards adjacency list of a graph. @@ -105,7 +105,7 @@ fadj(g) == fadj(h) """Return `true` if `g` is a directed graph.""" is_directed(::Type{SimpleGraph}) = false -is_directed{T}(::Type{SimpleGraph{T}}) = false +is_directed(::Type{SimpleGraph{T}}) where T = false is_directed(g::SimpleGraph) = false function has_edge(g::SimpleGraph, e::SimpleGraphEdge) @@ -146,7 +146,8 @@ end """Add a new vertex to the graph `g`.""" -function add_vertex!{T<:Integer}(g::SimpleGraph{T}) +function add_vertex!(g::SimpleGraph) + T = eltype(g) (nv(g) + one(T) <= nv(g)) && return false # test for overflow g.vertices = one(T):nv(g)+one(T) push!(g.fadjlist, Vector{T}()) diff --git a/src/interface.jl b/src/interface.jl index 8b53b1c04..3d99a081c 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -12,7 +12,7 @@ abstract type AbstractEdgeIter end abstract type AbstractGraph end -@traitdef IsDirected{AbstractGraph} +@traitdef IsDirected{G<:AbstractGraph} @traitimpl IsDirected{G} <- is_directed(G) diff --git a/src/linalg/graphmatrices.jl b/src/linalg/graphmatrices.jl index 27b48a247..0c2091833 100644 --- a/src/linalg/graphmatrices.jl +++ b/src/linalg/graphmatrices.jl @@ -52,7 +52,7 @@ struct CombinatorialAdjacency{T,S,V} <: Adjacency{T} D::V end -function CombinatorialAdjacency{T}(A::SparseMatrix{T}) +function CombinatorialAdjacency(A::SparseMatrix{T}) where T D = vec(sum(A,1)) return CombinatorialAdjacency{T,SparseMatrix{T},typeof(D)}(A,D) end @@ -180,7 +180,7 @@ convert(::Type{CombinatorialAdjacency}, adjmat::Adjacency) = adjmat.A convert(::Type{SparseMatrix}, adjmat::CombinatorialAdjacency) = adjmat.A -function sparse{M <: Laplacian}(lapl::M) +function sparse(lapl::M) where M<:Laplacian adjmat = adjacency(lapl) A = sparse(adjmat) L = spdiagm(diag(lapl)) - A @@ -192,13 +192,13 @@ function sparse(adjmat::Adjacency) return Diagonal(prescalefactor(adjmat)) * (A * Diagonal(postscalefactor(adjmat))) end -function convert{T}(::Type{SparseMatrix{T}}, adjmat::Adjacency{T}) +function convert(::Type{SparseMatrix{T}}, adjmat::Adjacency{T}) where T A = sparse(adjmat.A) return Diagonal(prescalefactor(adjmat)) * (A * Diagonal(postscalefactor(adjmat))) end -function convert{T}(::Type{SparseMatrix{T}}, lapl::Laplacian{T}) +function convert(::Type{SparseMatrix{T}}, lapl::Laplacian{T}) where T adjmat = adjacency(lapl) A = convert(SparseMatrix{T}, adjmat) L = spdiagm(diag(lapl)) - A @@ -214,14 +214,14 @@ diag(lapl::Laplacian) = ones(size(lapl)[2]) postscalefactor(adjmat) .* (adjmat.A * (prescalefactor(adjmat) .* x)) -*{T<:Number}(adjmat::CombinatorialAdjacency{T}, x::AbstractVector{T}) = +*(adjmat::CombinatorialAdjacency{T}, x::AbstractVector{T}) where T<:Number= adjmat.A * x -*{T<:Number}(lapl::Laplacian{T}, x::AbstractVector{T}) = +*(lapl::Laplacian{T}, x::AbstractVector{T}) where T<:Number= (diag(lapl) .* x) - (adjacency(lapl)*x) -function *{T<:Number}(adjmat::PunchedAdjacency{T}, x::AbstractVector{T}) +function *(adjmat::PunchedAdjacency{T}, x::AbstractVector{T}) where T<:Number y=adjmat.A*x return y - dot(adjmat.perron, y)*adjmat.perron end diff --git a/src/linalg/nonbacktracking.jl b/src/linalg/nonbacktracking.jl index 34526def6..5c7b771a8 100644 --- a/src/linalg/nonbacktracking.jl +++ b/src/linalg/nonbacktracking.jl @@ -87,7 +87,7 @@ size(nbt::Nonbacktracking) = (nbt.m,nbt.m) eltype(nbt::Nonbacktracking) = Float64 issymmetric(nbt::Nonbacktracking) = false -function *{G, T<:Number}(nbt::Nonbacktracking{G}, x::Vector{T}) +function *(nbt::Nonbacktracking{G}, x::Vector{T}) where G where T<:Number length(x) == nbt.m || error("dimension mismatch") y = zeros(T, length(x)) for (e,u) in nbt.edgeidmap @@ -128,7 +128,7 @@ end sparse(nbt::Nonbacktracking) = sparse(coo_sparse(nbt)..., nbt.m,nbt.m) -function *{G, T<:Number}(nbt::Nonbacktracking{G}, x::AbstractMatrix{T}) +function *(nbt::Nonbacktracking{G}, x::AbstractMatrix{T}) where G where T<:Number y = zeros(x) for i in 1:nbt.m y[:,i] = nbt * x[:,i] diff --git a/src/linalg/spectral.jl b/src/linalg/spectral.jl index b9d0097cf..c7a632459 100644 --- a/src/linalg/spectral.jl +++ b/src/linalg/spectral.jl @@ -145,6 +145,7 @@ Graphs Based on their Different Matrix Representations """ function spectral_distance end +# can't use Traitor syntax here (https://github.com/mauro3/SimpleTraits.jl/issues/36) @traitfn function spectral_distance{G<:AbstractGraph; !IsDirected{G}}(G₁::G, G₂::G, k::Integer) A₁ = adjacency_matrix(G₁) A₂ = adjacency_matrix(G₂) @@ -155,6 +156,7 @@ function spectral_distance end return sum(abs, (λ₁ - λ₂)) end +# can't use Traitor syntax here (https://github.com/mauro3/SimpleTraits.jl/issues/36) @traitfn function spectral_distance{G<:AbstractGraph; !IsDirected{G}}(G₁::G, G₂::G) @assert nv(G₁) == nv(G₂) "spectral distance not defined for |G₁| != |G₂|" return spectral_distance(G₁, G₂, nv(G₁)) diff --git a/src/operators.jl b/src/operators.jl index 65e1eddf9..bafa4de53 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -68,7 +68,7 @@ Put simply, the vertices and edges from graph `h` are appended to graph `g`. Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. """ -function blkdiag{T<:AbstractGraph}(g::T, h::T) +function blkdiag(g::T, h::T) where T<:AbstractGraph gnv = nv(g) r = T(gnv + nv(h)) for e in edges(g) @@ -88,7 +88,7 @@ Produces 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. Preserves the eltype of the input graph. """ -function intersect{T<:AbstractGraph}(g::T, h::T) +function intersect(g::T, h::T) where T<:AbstractGraph gnv = nv(g) hnv = nv(h) @@ -107,7 +107,7 @@ Produces a graph with edges in graph `g` that are not in graph `h`. Note that this function may produce a graph with 0-degree vertices. Preserves the eltype of the input graph. """ -function difference{T<:AbstractGraph}(g::T, h::T) +function difference(g::T, h::T) where T<:AbstractGraph gnv = nv(g) hnv = nv(h) @@ -128,7 +128,7 @@ 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. """ -function symmetric_difference{T<:AbstractGraph}(g::T, h::T) +function symmetric_difference(g::T, h::T) where T<:AbstractGraph gnv = nv(g) hnv = nv(h) @@ -149,7 +149,7 @@ Merges graphs `g` and `h` by taking the set union of all vertices and edges. Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. """ -function union{T<:AbstractGraph}(g::T, h::T) +function union(g::T, h::T) where T<:AbstractGraph gnv = nv(g) hnv = nv(h) @@ -176,7 +176,7 @@ the vertices in `g` and those in `h`. Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. """ -function join{T<:AbstractGraph}(g::T, h::T) +function join(g::T, h::T) where T<:AbstractGraph r = blkdiag(g, h) for i in vertices(g) for j=nv(g)+1:nv(g)+nv(h) @@ -195,7 +195,7 @@ Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. """ function crosspath end -@traitfn function crosspath{G<:AbstractGraph; !IsDirected{G}}(len::Integer, g::G) +@traitfn function crosspath(len::Integer, g::::(!IsDirected)) T = eltype(g) p = PathGraph(len) h = Graph{T}(p) @@ -206,7 +206,7 @@ end # """Provides multiplication of a graph `g` by a vector `v` such that spectral # graph functions in [GraphMatrices.jl](https://github.com/jpfairbanks/GraphMatrices.jl) can utilize LightGraphs natively. # """ -function *{T<:Real}(g::Graph, v::Vector{T}) +function *(g::Graph, v::Vector{T}) where T<:Real length(v) == nv(g) || error("Vector size must equal number of vertices") y = zeros(T, nv(g)) for e in edges(g) @@ -218,7 +218,7 @@ function *{T<:Real}(g::Graph, v::Vector{T}) return y end -function *{T<:Real}(g::DiGraph, v::Vector{T}) +function *(g::DiGraph, v::Vector{T}) where T<:Real length(v) == nv(g) || error("Vector size must equal number of vertices") y = zeros(T, nv(g)) for e in edges(g) @@ -260,7 +260,7 @@ Returns the (cartesian product)[https://en.wikipedia.org/wiki/Tensor_product_of_ Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. """ -function cartesian_product{G<:AbstractGraph}(g::G, h::G) +function cartesian_product(g::G, h::G) where G<:AbstractGraph z = G(nv(g)*nv(h)) id(i, j) = (i-1)*nv(h) + j for e in edges(g) @@ -286,7 +286,7 @@ Returns the (tensor product)[https://en.wikipedia.org/wiki/Tensor_product_of_gra Preserves the eltype of the input graph. Will error if the number of vertices in the generated graph exceeds the eltype. """ -function tensor_product{G<:AbstractGraph}(g::G, h::G) +function tensor_product(g::G, h::G) where G<:AbstractGraph z = G(nv(g)*nv(h)) id(i, j) = (i-1)*nv(h) + j for e1 in edges(g) @@ -338,7 +338,7 @@ sg, vmap = subgraph(g, elist) @assert sg == g[elist] ``` """ -function induced_subgraph{T<:AbstractGraph, U<:Integer}(g::T, vlist::AbstractVector{U}) +function induced_subgraph(g::T, vlist::AbstractVector{U}) where T<:AbstractGraph where U<:Integer allunique(vlist) || error("Vertices in subgraph list must be unique") h = T(length(vlist)) newvid = Dict{U, U}() @@ -362,7 +362,7 @@ function induced_subgraph{T<:AbstractGraph, U<:Integer}(g::T, vlist::AbstractVec end -function induced_subgraph{T<:AbstractGraph, U<:AbstractEdge}(g::T, elist::AbstractVector{U}) +function induced_subgraph(g::T, elist::AbstractVector{U}) where T<:AbstractGraph where U<:AbstractEdge h = empty(g) et = eltype(h) newvid = Dict{et, et}() diff --git a/src/persistence/common.jl b/src/persistence/common.jl index 0ac86f8b0..ab9bfdcf9 100644 --- a/src/persistence/common.jl +++ b/src/persistence/common.jl @@ -81,7 +81,7 @@ For some graph formats, multiple graphs in a `dict` `"name"=>g` can be saved in Returns the number of graphs written. """ -function save{T<:AbstractGraph}(io::IO, g::T, gname::String, t::Symbol=:lg) +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))") return filemap[t][3](io, g, gname) end @@ -91,7 +91,7 @@ save(io::IO, g::AbstractGraph, t::Symbol=:lg) = save(io, g, is_directed(g)? "digraph" : "graph", t) # save a dictionary of graphs {"name" => graph} -function save{T<:String, U<:AbstractGraph}(io::IO, d::Dict{T, U}, t::Symbol=:lg) +function save(io::IO, d::Dict{T, U}, t::Symbol=:lg) where T<:AbstractString where U<:AbstractGraph t in keys(filemap) || error("Please select a supported graph format: one of $(keys(filemap))") return filemap[t][4](io, d) end diff --git a/src/shortestpaths/astar.jl b/src/shortestpaths/astar.jl index dce13e7d5..ea41bc6f4 100644 --- a/src/shortestpaths/astar.jl +++ b/src/shortestpaths/astar.jl @@ -3,12 +3,12 @@ # A* shortest-path algorithm -function a_star_impl!{T<:Number}( +function a_star_impl!( graph::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 - distmx::AbstractMatrix{T}, + distmx::AbstractMatrix, heuristic::Function # heuristic fn (under)estimating distance to target ) @@ -41,14 +41,14 @@ 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. """ -function a_star{T<:Number}( +function a_star( graph::AbstractGraph, # the graph s::Integer, # the start vertex t::Integer, # the end vertex distmx::AbstractMatrix{T} = LightGraphs.DefaultDistance(), heuristic::Function = n -> 0 - ) + ) where T # heuristic (under)estimating distance to target U = eltype(graph) frontier = PriorityQueue(Tuple{T,Vector{Edge},U},T) diff --git a/src/shortestpaths/bellman-ford.jl b/src/shortestpaths/bellman-ford.jl index 1cca52e05..9d8c9c97c 100644 --- a/src/shortestpaths/bellman-ford.jl +++ b/src/shortestpaths/bellman-ford.jl @@ -17,12 +17,12 @@ struct BellmanFordState{T<:Number, U<:Integer}<:AbstractPathState dists::Vector{T} end -function bellman_ford_shortest_paths!{R<:Real, T<:Integer}( +function bellman_ford_shortest_paths!( graph::AbstractGraph, sources::AbstractVector{T}, distmx::AbstractMatrix{R}, state::BellmanFordState - ) + ) where R<:Real where T<:Integer active = Set{T}() for v in sources @@ -59,22 +59,21 @@ 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). """ -function bellman_ford_shortest_paths{T, U<:Integer}( +function bellman_ford_shortest_paths( graph::AbstractGraph, - sources::AbstractVector{U}, distmx::AbstractMatrix{T} = DefaultDistance() - ) + ) where T where U<:Integer nvg = nv(graph) state = BellmanFordState(zeros(U,nvg), fill(typemax(T), nvg)) bellman_ford_shortest_paths!(graph, sources, distmx, state) end -bellman_ford_shortest_paths{T}( -graph::AbstractGraph, -v::Integer, -distmx::AbstractMatrix{T} = DefaultDistance() -) = bellman_ford_shortest_paths(graph, [v], distmx) +bellman_ford_shortest_paths( + graph::AbstractGraph, + v::Integer, + distmx::AbstractMatrix = DefaultDistance() + ) = bellman_ford_shortest_paths(graph, [v], distmx) function has_negative_edge_cycle(graph::AbstractGraph) try @@ -85,7 +84,7 @@ function has_negative_edge_cycle(graph::AbstractGraph) return false end -function enumerate_paths{T<:Integer}(state::AbstractPathState, dest::Vector{T}) +function enumerate_paths(state::AbstractPathState, dest::Vector{T}) where T<:Integer parents = state.parents num_dest = length(dest) diff --git a/src/shortestpaths/dijkstra.jl b/src/shortestpaths/dijkstra.jl index dae86bb6a..c9c1c189b 100644 --- a/src/shortestpaths/dijkstra.jl +++ b/src/shortestpaths/dijkstra.jl @@ -22,12 +22,12 @@ information (see below). With `allpaths=true`, returns a `DijkstraState` that keeps track of all predecessors of a given vertex (see below). """ -function dijkstra_shortest_paths{T, U<:Integer}( +function dijkstra_shortest_paths( g::AbstractGraph, srcs::Vector{U}, - distmx::AbstractArray{T, 2}=DefaultDistance(); + distmx::AbstractMatrix{T}=DefaultDistance(); allpaths=false - ) + ) where T where U<:Integer nvg = nv(g) dists = fill(typemax(T), nvg) parents = zeros(U, nvg) @@ -87,5 +87,5 @@ function dijkstra_shortest_paths{T, U<:Integer}( return DijkstraState{T, U}(parents, dists, preds, pathcounts) end -dijkstra_shortest_paths{T, U}(g::AbstractGraph, src::U, distmx::AbstractArray{T,2}=DefaultDistance(); allpaths=false) = +dijkstra_shortest_paths(g::AbstractGraph, src::Integer, distmx::AbstractMatrix = DefaultDistance(); allpaths=false) = dijkstra_shortest_paths(g, [src;], distmx; allpaths=allpaths) diff --git a/src/shortestpaths/floyd-warshall.jl b/src/shortestpaths/floyd-warshall.jl index 4201167f5..88589e1aa 100644 --- a/src/shortestpaths/floyd-warshall.jl +++ b/src/shortestpaths/floyd-warshall.jl @@ -59,7 +59,7 @@ function floyd_warshall_shortest_paths{T}( return fws end -function enumerate_paths{T, U<:Integer}(s::FloydWarshallState{T, U}, v::Integer) +function enumerate_paths(s::FloydWarshallState{T, U}, v::Integer) where T where U<:Integer pathinfo = s.parents[v,:] paths = Vector{Vector{U}}() for i in 1:length(pathinfo) diff --git a/src/spanningtrees/kruskal.jl b/src/spanningtrees/kruskal.jl index bb7aa426f..02ee51fd1 100644 --- a/src/spanningtrees/kruskal.jl +++ b/src/spanningtrees/kruskal.jl @@ -25,10 +25,10 @@ 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{T<:Real}( +function kruskal_mst( g::AbstractGraph, 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 14e2b8a24..0337c23a2 100644 --- a/src/spanningtrees/prim.jl +++ b/src/spanningtrees/prim.jl @@ -11,12 +11,11 @@ on a connected, non-directional graph `g`, having adjacency matrix `distmx`, and computes minimum spanning tree. Returns a `Vector{Edge}`, that contains the edges. """ -function prim_mst{T<:Real}( +function prim_mst( g::AbstractGraph, - distmx::AbstractMatrix{T} = DefaultDistance() - ) :: Vector{Edge} - - pq = Vector{PrimHeapEntry{T}}() + distmx::AbstractMatrix = DefaultDistance() + ) + pq = Vector{PrimHeapEntry}() mst = Vector{Edge}() marked = zeros(Bool, nv(g)) @@ -43,12 +42,12 @@ 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. """ -function visit!{T<:Real}( +function visit!( g::AbstractGraph, v::Integer, - marked::AbstractArray{Bool, 1}, - pq::Vector{PrimHeapEntry{T}}, - distmx::AbstractMatrix{T} + marked::AbstractVector{Bool}, + pq::AbstractVector, + distmx::AbstractMatrix ) marked[v] = true for w in out_neighbors(g, v) diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index e8954a3dd..4eaa4815f 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -23,13 +23,14 @@ EdgeColorMap : mutable struct BreadthFirst <: AbstractGraphVisitAlgorithm end -function breadth_first_visit_impl!{T<:Integer}( - g::AbstractGraph, # the graph - queue::Vector{T}, # an (initialized) queue that stores the active vertices - vertexcolormap::AbstractVertexMap, # an (initialized) color-map to indicate status of vertices (-1=unseen, otherwise distance from root) - edgecolormap::AbstractEdgeMap, # an (initialized) color-map to indicate status of edges - visitor::AbstractGraphVisitor, # the visitor - dir::Symbol) # direction [:in,:out] +function breadth_first_visit_impl!( + g::AbstractGraph, # the graph + queue::Vector, # an (initialized) queue that stores the active vertices + vertexcolormap::AbstractVertexMap, # an (initialized) color-map to indicate status of vertices (-1=unseen, otherwise distance from root) + edgecolormap::AbstractEdgeMap, # an (initialized) color-map to indicate status of edges + visitor::AbstractGraphVisitor, # the visitor + dir::Symbol # direction [:in,:out] + ) fneig = dir == :out ? out_neighbors : in_neighbors while !isempty(queue) @@ -93,12 +94,12 @@ mutable struct TreeBFSVisitorVector{T<:Integer} <: AbstractGraphVisitor tree::Vector{T} end -function TreeBFSVisitorVector{T<:Integer}(n::T) - return TreeBFSVisitorVector(fill(zero(T), n)) +function TreeBFSVisitorVector(n::Integer) + return TreeBFSVisitorVector(fill(zero(n), n)) end """tree converts a parents array into a DiGraph""" -function tree{T<:Integer}(parents::AbstractVector{T}) +function tree(parents::AbstractVector{T}) where T<:Integer n = T(length(parents)) t = DiGraph(n) for i in one(T):n @@ -120,11 +121,11 @@ function examine_neighbor!(visitor::TreeBFSVisitorVector, u::Integer, v::Integer return true end -function bfs_tree!{T<:Integer}(visitor::TreeBFSVisitorVector{T}, +function bfs_tree!(visitor::TreeBFSVisitorVector{T}, g::AbstractGraph, s::Integer; vertexcolormap = Dict{T,Int}(), - queue = Vector{T}()) + queue = Vector{T}()) where T<:Integer # this version of bfs_tree! allows one to reuse the memory necessary to compute the tree # the output is stored in the visitor.tree array whose entries are the vertex id of the # parent of the index. This function checks if the scratch space is too small for the graph. diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index c56934282..a7ccda017 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -137,7 +137,7 @@ mutable struct TopologicalSortVisitor{T} <: AbstractGraphVisitor vertices::Vector{T} end -function TopologicalSortVisitor{T<:Integer}(n::T) +function TopologicalSortVisitor(n::T) where T<:Integer vs = Vector{T}() sizehint!(vs, n) return TopologicalSortVisitor(vs) diff --git a/src/traversals/graphvisit.jl b/src/traversals/graphvisit.jl index 9dc9d878c..17f1c120a 100644 --- a/src/traversals/graphvisit.jl +++ b/src/traversals/graphvisit.jl @@ -51,7 +51,7 @@ struct VertexListVisitor{T<:Integer} <: AbstractGraphVisitor vertices::Vector{T} end -function VertexListVisitor{T<:Integer}(n::T=0) +function VertexListVisitor(n::T=0) where T<:Integer vs = Vector{T}() sizehint!(vs, n) return VertexListVisitor(vs) diff --git a/src/traversals/maxadjvisit.jl b/src/traversals/maxadjvisit.jl index 9e47684c4..f628e5b82 100644 --- a/src/traversals/maxadjvisit.jl +++ b/src/traversals/maxadjvisit.jl @@ -199,7 +199,7 @@ displayed. """ function maximum_adjacency_visit{T}( g::AbstractGraph, - distmx::AbstractArray{T, 2}, + distmx::AbstractMatrix{T}, log::Bool, io::IO ) diff --git a/src/traversals/randomwalks.jl b/src/traversals/randomwalks.jl index ecf3ed0da..30a533f14 100644 --- a/src/traversals/randomwalks.jl +++ b/src/traversals/randomwalks.jl @@ -22,9 +22,7 @@ end a maximum of `niter` steps. Returns a vector of vertices visited in order. """ function non_backtracking_randomwalk end -@traitfn function non_backtracking_randomwalk{G<:AbstractGraph; !IsDirected{G}}( - g::G, s::Integer, niter::Integer - ) +@traitfn function non_backtracking_randomwalk(g::::(!IsDirected), s::Integer, niter::Integer) T = eltype(g) s in vertices(g) || throw(BoundsError()) visited = Vector{T}() @@ -56,9 +54,7 @@ function non_backtracking_randomwalk end return visited[1:i-1] end -@traitfn function non_backtracking_randomwalk{G<:AbstractGraph; IsDirected{G}}( - g::G, s::Integer, niter::Integer - ) +@traitfn function non_backtracking_randomwalk(g::::IsDirected, s::Integer, niter::Integer) T = eltype(g) s in vertices(g) || throw(BoundsError()) visited = Vector{T}() diff --git a/test/flow/maximum_flow.jl b/test/flow/maximum_flow.jl index 1f3111466..f40b38ae4 100644 --- a/test/flow/maximum_flow.jl +++ b/test/flow/maximum_flow.jl @@ -40,7 +40,7 @@ # Test DefaultCapacity d = LightGraphs.DefaultCapacity(g) T = eltype(d) - @test typeof(d) <: AbstractArray{T, 2} + @test typeof(d) <: AbstractMatrix{T} @test d[s,t] == 0 @test size(d) == (nvertices,nvertices) @test typeof(transpose(d)) <: LightGraphs.DefaultCapacity From 3e3b34b167d89b521c63bcb34c1b2d1816ee29c4 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 19 Mar 2017 17:52:01 -0700 Subject: [PATCH 33/56] test negative cycles --- test/shortestpaths/bellman-ford.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/shortestpaths/bellman-ford.jl b/test/shortestpaths/bellman-ford.jl index 187dbc2e4..7f695c0ee 100644 --- a/test/shortestpaths/bellman-ford.jl +++ b/test/shortestpaths/bellman-ford.jl @@ -14,4 +14,10 @@ z = bellman_ford_shortest_paths(g, 2) @test z.dists == [typemax(Int), 0, 1, 2, 3] end + + gx = CompleteGraph(3) + d = [1 -3 1; -3 1 1; 1 1 1] + for g in testgraphs(gx) + @test_throws LightGraphs.NegativeCycleError bellman_ford_shortest_paths(g, 1, d) + end end From 1ad6e725c724a6da04e12154042e6a1f59cb9bd3 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 19 Mar 2017 18:32:01 -0700 Subject: [PATCH 34/56] test coverage --- test/generators/randgraphs.jl | 10 ++++++++++ test/traversals/randomwalks.jl | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/test/generators/randgraphs.jl b/test/generators/randgraphs.jl index b2e98031b..d3b1078c4 100644 --- a/test/generators/randgraphs.jl +++ b/test/generators/randgraphs.jl @@ -181,6 +181,16 @@ @test degree(rr, v) == 8 end + rd = random_regular_digraph(10, 8, dir=:out, seed=4) + @test nv(rd) == 10 + @test ne(rd) == 80 + @test is_directed(rd) + + g = stochastic_block_model(2., 3., [100,100]) + @test 4.5 < mean(degree(g)) < 5.5 + g = stochastic_block_model(3., 4., [100,100,100]) + @test 10.5 < mean(degree(g)) < 11.5 + function generate_nbp_sbm(numedges, sizes) density = 1 # print(STDERR, "Generating communites with sizes: $sizes\n") diff --git a/test/traversals/randomwalks.jl b/test/traversals/randomwalks.jl index 8ef3c1057..96edff7d2 100644 --- a/test/traversals/randomwalks.jl +++ b/test/traversals/randomwalks.jl @@ -44,8 +44,8 @@ @test_throws BoundsError non_backtracking_randomwalk(g, 20, 20) end - g = DiGraph(PathGraph(10)) - for g in testgraphs(gx) + gx = DiGraph(PathGraph(10)) + for g in testdigraphs(gx) @test non_backtracking_randomwalk(g, 1, 20) == [1:10;] @test_throws BoundsError non_backtracking_randomwalk(g, 20, 20) end From 97517a4b9a5b36b4acddc54cfb783422e89f7783 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 19 Mar 2017 19:22:43 -0700 Subject: [PATCH 35/56] manual cherry-pick of #551 --- test/shortestpaths/bellman-ford.jl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/shortestpaths/bellman-ford.jl b/test/shortestpaths/bellman-ford.jl index 7f695c0ee..a31afccda 100644 --- a/test/shortestpaths/bellman-ford.jl +++ b/test/shortestpaths/bellman-ford.jl @@ -15,8 +15,19 @@ @test z.dists == [typemax(Int), 0, 1, 2, 3] end + # Negative Cycle gx = CompleteGraph(3) - d = [1 -3 1; -3 1 1; 1 1 1] + for g in testgraphs(gx) + d = [1 -3 1; -3 1 1; 1 1 1] + @test_throws LightGraphs.NegativeCycleError bellman_ford_shortest_paths(g, 1, d) + + d = [1 -1 1; -1 1 1; 1 1 1] + @test_throws LightGraphs.NegativeCycleError bellman_ford_shortest_paths(g, 1, d) + end + + # Negative cycle of length 3 in graph of diameter 4 + gx = CompleteGraph(4) + d = [1 -1 1 1; 1 1 1 -1; 1 1 1 1; 1 1 1 1] for g in testgraphs(gx) @test_throws LightGraphs.NegativeCycleError bellman_ford_shortest_paths(g, 1, d) end From bcbe2a9d98a7dcb9d1ad45455cbf31b53d5b2da8 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 19 Mar 2017 19:59:42 -0700 Subject: [PATCH 36/56] simplegraph/ -> simplegraphs, optimization for is_connected, some type simplifications --- src/LightGraphs.jl | 2 +- src/connectivity.jl | 6 +++--- src/core.jl | 4 ++-- src/distance.jl | 14 +++++++------- src/flow/maximum_flow.jl | 2 +- .../{simplegraph => simplegraphs}/SimpleGraphs.jl | 3 ++- .../{simplegraph => simplegraphs}/simpledigraph.jl | 4 ++-- .../{simplegraph => simplegraphs}/simpleedge.jl | 4 ++-- .../simpleedgeiter.jl | 0 .../{simplegraph => simplegraphs}/simplegraph.jl | 2 +- src/linalg/nonbacktracking.jl | 6 +++--- src/persistence/common.jl | 2 +- src/traversals/dfs.jl | 2 +- test/traversals/randomwalks.jl | 4 ++-- 14 files changed, 28 insertions(+), 27 deletions(-) rename src/graphtypes/{simplegraph => simplegraphs}/SimpleGraphs.jl (98%) rename src/graphtypes/{simplegraph => simplegraphs}/simpledigraph.jl (97%) rename src/graphtypes/{simplegraph => simplegraphs}/simpleedge.jl (87%) rename src/graphtypes/{simplegraph => simplegraphs}/simpleedgeiter.jl (100%) rename src/graphtypes/{simplegraph => simplegraphs}/simplegraph.jl (99%) diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 625629d86..adf7cd17d 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -136,7 +136,7 @@ LightGraphs include("interface.jl") include("deprecations.jl") include("core.jl") - include("graphtypes/simplegraph/SimpleGraphs.jl") + include("graphtypes/simplegraphs/SimpleGraphs.jl") const Graph = SimpleGraphs.SimpleGraph const DiGraph = SimpleGraphs.SimpleDiGraph const Edge = SimpleGraphs.SimpleEdge diff --git a/src/connectivity.jl b/src/connectivity.jl index e13a8a75d..14d86a409 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -101,8 +101,8 @@ Returns `true` if `g` is connected. For DiGraphs, this is equivalent to a test of weak connectivity. """ function is_connected end -@traitfn is_connected(g::::(!IsDirected)) = length(connected_components(g)) == 1 -@traitfn is_connected(g::::IsDirected) = is_weakly_connected(g) +@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`.""" function weakly_connected_components end @@ -121,7 +121,7 @@ mutable struct TarjanVisitor{T<:Integer} <: AbstractGraphVisitor components::Vector{Vector{T}} end -TarjanVisitor{T<:Integer}(n::T) = TarjanVisitor( +TarjanVisitor(n::T) where T<:Integer = TarjanVisitor( Vector{T}(), falses(n), Vector{T}(), diff --git a/src/core.jl b/src/core.jl index 994bcf16c..c901e7a35 100644 --- a/src/core.jl +++ b/src/core.jl @@ -15,11 +15,11 @@ 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::AbstractGraph, v::Integer) = length(in_neighbors(g, v)) -indegree{T<:Integer}(g::AbstractGraph, v::AbstractVector{T} = vertices(g)) = [indegree(g,x) for x in 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::AbstractGraph, v::Integer) = length(out_neighbors(g, v)) -outdegree{T<:Integer}(g::AbstractGraph, v::AbstractVector{T} = vertices(g)) = [outdegree(g,x) for x in v] +outdegree(g::AbstractGraph, v::AbstractVector = vertices(g)) = [outdegree(g,x) for x in v] """ Return the number of edges from the vertex `v`. diff --git a/src/distance.jl b/src/distance.jl index b9745bc64..e3bed74a4 100644 --- a/src/distance.jl +++ b/src/distance.jl @@ -52,14 +52,14 @@ eccentricity(g::AbstractGraph, distmx::AbstractMatrix) = eccentricity(g, vertices(g), distmx) """Returns the maximum eccentricity of the graph.""" -diameter(all_e::Vector{T}) where T = maximum(all_e) -diameter(g::AbstractGraph, distmx::AbstractMatrix{T} = DefaultDistance()) where T = +diameter(all_e::Vector) = maximum(all_e) +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{T}) where T +function periphery(all_e::Vector) diam = maximum(all_e) return filter((x)->all_e[x] == diam, 1:length(all_e)) end @@ -68,17 +68,17 @@ periphery(g::AbstractGraph, distmx::AbstractMatrix = DefaultDistance()) = periphery(eccentricity(g, distmx)) """Returns the minimum eccentricity of the graph.""" -radius{T}(all_e::Vector{T}) = minimum(all_e) -radius{T}(g::AbstractGraph, distmx::AbstractMatrix{T} = DefaultDistance()) = +radius(all_e::Vector) = minimum(all_e) +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{T}(all_e::Vector{T}) +function center(all_e::Vector) rad = radius(all_e) return filter((x)->all_e[x] == rad, 1:length(all_e)) end -center{T}(g::AbstractGraph, distmx::AbstractMatrix{T} = DefaultDistance()) = +center(g::AbstractGraph, distmx::AbstractMatrix = DefaultDistance()) = center(eccentricity(g, distmx)) diff --git a/src/flow/maximum_flow.jl b/src/flow/maximum_flow.jl index 8a8c17eb1..c5dc53ab2 100644 --- a/src/flow/maximum_flow.jl +++ b/src/flow/maximum_flow.jl @@ -38,7 +38,7 @@ end @traitfn DefaultCapacity(flow_graph::::IsDirected) = DefaultCapacity(DiGraph(flow_graph), nv(flow_graph)) -getindex{T<:Integer}(d::DefaultCapacity{T}, s::Integer, t::Integer) = if has_edge(d.flow_graph, s , t) one(T) else zero(T) end +getindex(d::DefaultCapacity{T}, s::Integer, t::Integer) where T = if has_edge(d.flow_graph, s , t) one(T) else zero(T) end # isassigned{T<:Integer}(d::DefaultCapacity{T}, u::T, v::T) = (u in 1:d.nv) && (v in 1:d.nv) size(d::DefaultCapacity) = (Int(d.nv), Int(d.nv)) transpose(d::DefaultCapacity) = DefaultCapacity(reverse(d.flow_graph)) diff --git a/src/graphtypes/simplegraph/SimpleGraphs.jl b/src/graphtypes/simplegraphs/SimpleGraphs.jl similarity index 98% rename from src/graphtypes/simplegraph/SimpleGraphs.jl rename to src/graphtypes/simplegraphs/SimpleGraphs.jl index 5d0e20189..d28e37282 100644 --- a/src/graphtypes/simplegraph/SimpleGraphs.jl +++ b/src/graphtypes/simplegraphs/SimpleGraphs.jl @@ -127,7 +127,8 @@ function rem_vertex!(g::AbstractSimpleGraph, v::Integer) end return true end -empty{T<:AbstractSimpleGraph}(g::T) = T() + +empty(g::T) where T<:AbstractSimpleGraph = T() include("simpleedge.jl") include("simpledigraph.jl") diff --git a/src/graphtypes/simplegraph/simpledigraph.jl b/src/graphtypes/simplegraphs/simpledigraph.jl similarity index 97% rename from src/graphtypes/simplegraph/simpledigraph.jl rename to src/graphtypes/simplegraphs/simpledigraph.jl index 7a516aea1..f42f082a1 100644 --- a/src/graphtypes/simplegraph/simpledigraph.jl +++ b/src/graphtypes/simplegraphs/simpledigraph.jl @@ -8,7 +8,7 @@ mutable struct SimpleDiGraph{T<:Integer} <: AbstractSimpleGraph badjlist::Vector{Vector{T}} # [dst]: (src, src, src) end -eltype{T<:Integer}(x::SimpleDiGraph{T}) = T +eltype(x::SimpleDiGraph{T}) where T = T # DiGraph{UInt8}(6), DiGraph{Int16}(7), DiGraph{Int8}() @@ -104,7 +104,7 @@ badj(g) == badj(h) is_directed(g::SimpleDiGraph) = true is_directed(::Type{SimpleDiGraph}) = true -is_directed{T}(::Type{SimpleDiGraph{T}}) = true +is_directed(::Type{SimpleDiGraph{T}}) where T = true function add_edge!(g::SimpleDiGraph, e::SimpleDiGraphEdge) T = eltype(g) diff --git a/src/graphtypes/simplegraph/simpleedge.jl b/src/graphtypes/simplegraphs/simpleedge.jl similarity index 87% rename from src/graphtypes/simplegraph/simpleedge.jl rename to src/graphtypes/simplegraphs/simpleedge.jl index 25a686429..cb29aec21 100644 --- a/src/graphtypes/simplegraph/simpleedge.jl +++ b/src/graphtypes/simplegraphs/simpleedge.jl @@ -11,7 +11,7 @@ end SimpleEdge(t::Tuple) = SimpleEdge(t[1], t[2]) SimpleEdge(p::Pair) = SimpleEdge(p.first, p.second) -eltype{T<:AbstractSimpleEdge}(e::T) = eltype(src(e)) +eltype(e::T) where T<:AbstractSimpleEdge= eltype(src(e)) # Accessors src(e::AbstractSimpleEdge) = e.src @@ -27,5 +27,5 @@ Tuple(e::AbstractSimpleEdge) = (src(e), dst(e)) (::Type{SimpleEdge{T}}){T<:Integer}(e::AbstractSimpleEdge) = SimpleEdge{T}(T(e.src), T(e.dst)) # Convenience functions -reverse{T<:AbstractSimpleEdge}(e::T) = T(dst(e), src(e)) +reverse(e::T) where T<:AbstractSimpleEdge = T(dst(e), src(e)) ==(e1::AbstractSimpleEdge, e2::AbstractSimpleEdge) = (src(e1) == src(e2) && dst(e1) == dst(e2)) diff --git a/src/graphtypes/simplegraph/simpleedgeiter.jl b/src/graphtypes/simplegraphs/simpleedgeiter.jl similarity index 100% rename from src/graphtypes/simplegraph/simpleedgeiter.jl rename to src/graphtypes/simplegraphs/simpleedgeiter.jl diff --git a/src/graphtypes/simplegraph/simplegraph.jl b/src/graphtypes/simplegraphs/simplegraph.jl similarity index 99% rename from src/graphtypes/simplegraph/simplegraph.jl rename to src/graphtypes/simplegraphs/simplegraph.jl index 69d1782f5..fdb451275 100644 --- a/src/graphtypes/simplegraph/simplegraph.jl +++ b/src/graphtypes/simplegraphs/simplegraph.jl @@ -75,7 +75,7 @@ function SimpleGraph(g::SimpleDiGraph) return SimpleGraph(vertices(g), edgect ÷ 2, newfadj) end -edgetype(::SimpleGraph{T}) where T<:Integer = SimpleGraphEdge{T} +edgetype(::SimpleGraph{T}) where T<:Integer = SimpleGraphEdge{T} """ Returns the backwards adjacency list of a graph. diff --git a/src/linalg/nonbacktracking.jl b/src/linalg/nonbacktracking.jl index 5c7b771a8..90d3a4150 100644 --- a/src/linalg/nonbacktracking.jl +++ b/src/linalg/nonbacktracking.jl @@ -61,7 +61,7 @@ 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. """ -struct Nonbacktracking{G} +struct Nonbacktracking{G<:AbstractGraph} g::G edgeidmap::Dict{Edge,Int} m::Int @@ -87,7 +87,7 @@ size(nbt::Nonbacktracking) = (nbt.m,nbt.m) eltype(nbt::Nonbacktracking) = Float64 issymmetric(nbt::Nonbacktracking) = false -function *(nbt::Nonbacktracking{G}, x::Vector{T}) where G where T<:Number +function *(nbt::Nonbacktracking, x::Vector{T}) where T<:Number length(x) == nbt.m || error("dimension mismatch") y = zeros(T, length(x)) for (e,u) in nbt.edgeidmap @@ -128,7 +128,7 @@ end sparse(nbt::Nonbacktracking) = sparse(coo_sparse(nbt)..., nbt.m,nbt.m) -function *(nbt::Nonbacktracking{G}, x::AbstractMatrix{T}) where G where T<:Number +function *(nbt::Nonbacktracking, x::AbstractMatrix) y = zeros(x) for i in 1:nbt.m y[:,i] = nbt * x[:,i] diff --git a/src/persistence/common.jl b/src/persistence/common.jl index ab9bfdcf9..be696a122 100644 --- a/src/persistence/common.jl +++ b/src/persistence/common.jl @@ -65,7 +65,7 @@ end """ Save a graph to file. See [`save`](@ref). """ -savegraph{T<:AbstractGraph}(f, g::T, x...; kws...) = save(f, g::T, x...; kws...) +savegraph(f, g::AbstractGraph, x...; kws...) = save(f, g, x...; kws...) """ save(file, g, t=:lg) diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index a7ccda017..4e815a2c4 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -171,7 +171,7 @@ mutable struct TreeDFSVisitor{T} <:AbstractGraphVisitor predecessor::Vector{T} end -TreeDFSVisitor{T<:Integer}(n::T) = TreeDFSVisitor(DiGraph(n), zeros(T,n)) +TreeDFSVisitor(n::T) where T<:Integer = TreeDFSVisitor(DiGraph(n), zeros(T,n)) function examine_neighbor!(visitor::TreeDFSVisitor, u::Integer, v::Integer, ucolor::Int, vcolor::Int, ecolor::Int) if (vcolor == 0) diff --git a/test/traversals/randomwalks.jl b/test/traversals/randomwalks.jl index 96edff7d2..56bb24b04 100644 --- a/test/traversals/randomwalks.jl +++ b/test/traversals/randomwalks.jl @@ -77,8 +77,8 @@ add_edge!(gx, 1, 1) for g in testgraphs(gx) for len = 1:3*n - @test test_nbw(g,1,len) == true - @test test_nbw(g,2,len) == true + @test test_nbw(g,1,len) + @test test_nbw(g,2,len) end end end From f06aa93b1bfbdfd0dde335d98d9e51265bad0869 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 19 Mar 2017 20:49:48 -0700 Subject: [PATCH 37/56] re-add b-f tests --- test/shortestpaths/bellman-ford.jl | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/test/shortestpaths/bellman-ford.jl b/test/shortestpaths/bellman-ford.jl index a31afccda..fa7088a36 100644 --- a/test/shortestpaths/bellman-ford.jl +++ b/test/shortestpaths/bellman-ford.jl @@ -4,15 +4,21 @@ d1 = float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) d2 = sparse(float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) for g in testdigraphs(g4) - y = bellman_ford_shortest_paths(g, 2, d1) - z = bellman_ford_shortest_paths(g, 2, d2) - @test y.dists == z.dists == [Inf, 0, 6, 17, 33] - @test enumerate_paths(z)[2] == [] - @test enumerate_paths(z)[4] == enumerate_paths(z,4) == [2,3,4] - @test !has_negative_edge_cycle(g) + y = bellman_ford_shortest_paths(g, 2, d1) + z = bellman_ford_shortest_paths(g, 2, d2) + @test y.dists == z.dists == [Inf, 0, 6, 17, 33] + @test enumerate_paths(z)[2] == [] + @test enumerate_paths(z)[4] == enumerate_paths(z,4) == [2,3,4] + @test !has_negative_edge_cycle(g) - z = bellman_ford_shortest_paths(g, 2) - @test z.dists == [typemax(Int), 0, 1, 2, 3] + y = bellman_ford_shortest_paths(g, 2, d1) + z = bellman_ford_shortest_paths(g, 2, d2) + @test y.dists == z.dists == [Inf, 0, 6, 17, 33] + @test enumerate_paths(z)[2] == [] + @test enumerate_paths(z)[4] == enumerate_paths(z,4) == [2,3,4] + @test !has_negative_edge_cycle(g) + z = bellman_ford_shortest_paths(g, 2) + @test z.dists == [typemax(Int), 0, 1, 2, 3] end # Negative Cycle From c61011fb80deaac5de9326959cb1ad31275c0fd4 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Mon, 20 Mar 2017 20:39:58 -0700 Subject: [PATCH 38/56] Inferred (#554) * core * @inferred wherever possible * empty -> zero * test grid periodic=true * oops --- src/LightGraphs.jl | 4 +- src/generators/staticgraphs.jl | 14 +- src/graphtypes/simplegraphs/SimpleGraphs.jl | 6 +- src/interface.jl | 4 +- src/operators.jl | 2 +- src/shortestpaths/astar.jl | 3 +- test/biconnectivity/articulation.jl | 6 +- test/biconnectivity/biconnect.jl | 4 +- test/centrality/betweenness.jl | 18 +-- test/centrality/closeness.jl | 6 +- test/centrality/degree.jl | 6 +- test/centrality/katz.jl | 2 +- test/centrality/pagerank.jl | 2 +- test/community/cliques.jl | 4 +- test/community/clustering.jl | 10 +- test/community/core-periphery.jl | 30 ++-- test/community/label_propagation.jl | 4 +- test/community/modularity.jl | 4 +- test/connectivity.jl | 80 +++++----- test/core.jl | 104 ++++++------- test/distance.jl | 22 +-- test/edit_distance.jl | 14 +- test/flow/boykov_kolmogorov.jl | 4 +- test/flow/dinic.jl | 14 +- test/flow/edmonds_karp.jl | 6 +- test/flow/maximum_flow.jl | 86 +++++------ test/flow/multiroute_flow.jl | 142 +++++++++-------- test/flow/push_relabel.jl | 20 +-- test/generators/euclideangraphs.jl | 4 +- test/generators/randgraphs.jl | 14 +- test/generators/staticgraphs.jl | 45 +++--- .../graphtypes/simplegraphs/simpleedgeiter.jl | 28 ++-- test/graphtypes/simplegraphs/simplegraphs.jl | 144 +++++++++--------- test/interface.jl | 2 +- test/linalg/spectral.jl | 2 +- test/operators.jl | 64 ++++---- test/shortestpaths/astar.jl | 8 +- test/shortestpaths/bellman-ford.jl | 22 +-- test/shortestpaths/dijkstra.jl | 20 +-- test/shortestpaths/floyd-warshall.jl | 6 +- test/spanningtrees/kruskal.jl | 4 +- test/spanningtrees/prim.jl | 4 +- test/transitivity.jl | 8 +- test/traversals/bfs.jl | 32 ++-- test/traversals/dfs.jl | 6 +- test/traversals/graphvisit.jl | 12 +- test/traversals/maxadjvisit.jl | 7 +- test/traversals/randomwalks.jl | 20 +-- test/utils.jl | 6 +- 49 files changed, 544 insertions(+), 535 deletions(-) diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index adf7cd17d..8e87a7fc9 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -13,13 +13,13 @@ import Base: write, ==, <, *, ≈, convert, isless, issubset, union, intersect, reverse, reverse!, blkdiag, isassigned, getindex, setindex!, show, print, copy, in, sum, size, sparse, eltype, length, ndims, transpose, ctranspose, join, start, next, done, eltype, get, issymmetric, A_mul_B!, - Pair, Tuple + Pair, Tuple, zero export # Interface AbstractGraph, AbstractDiGraph, AbstractEdge, AbstractEdgeInter, Edge, Graph, DiGraph, vertices, edges, edgetype, nv, ne, src, dst, is_directed, add_vertex!, add_edge!, rem_vertex!, rem_edge!, -has_vertex, has_edge, in_neighbors, out_neighbors, empty, +has_vertex, has_edge, in_neighbors, out_neighbors, # core is_ordered, add_vertices!, indegree, outdegree, degree, diff --git a/src/generators/staticgraphs.jl b/src/generators/staticgraphs.jl index 8879ef444..951683eb8 100644 --- a/src/generators/staticgraphs.jl +++ b/src/generators/staticgraphs.jl @@ -140,10 +140,16 @@ Creates a `d`-dimensional cubic lattice, with `d=length(dims)` and length `dims If `periodic=true` the resulting lattice will have periodic boundary condition in each dimension. """ function Grid(dims::AbstractVector; periodic=false) - func = periodic ? CycleGraph : PathGraph - g = func(dims[1]) - for d in dims[2:end] - g = cartesian_product(func(d), g) + if periodic + g = CycleGraph(dims[1]) + for d in dims[2:end] + g = cartesian_product(CycleGraph(d), g) + end + else + g = PathGraph(dims[1]) + for d in dims[2:end] + g = cartesian_product(PathGraph(d), g) + end end return g end diff --git a/src/graphtypes/simplegraphs/SimpleGraphs.jl b/src/graphtypes/simplegraphs/SimpleGraphs.jl index d28e37282..e9e266262 100644 --- a/src/graphtypes/simplegraphs/SimpleGraphs.jl +++ b/src/graphtypes/simplegraphs/SimpleGraphs.jl @@ -1,7 +1,7 @@ module SimpleGraphs import Base: - eltype, show, ==, Pair, Tuple, copy, length, start, next, done, issubset + eltype, show, ==, Pair, Tuple, copy, length, start, next, done, issubset, zero import LightGraphs: _NI, _insert_and_dedup!, AbstractGraph, AbstractEdge, AbstractEdgeIter, @@ -9,7 +9,7 @@ import LightGraphs: add_vertex!, add_edge!, rem_vertex!, rem_edge!, has_vertex, has_edge, in_neighbors, out_neighbors, - indegree, outdegree, degree, has_self_loops, num_self_loops, empty + indegree, outdegree, degree, has_self_loops, num_self_loops export AbstractSimpleGraph, AbstractSimpleDiGraph, AbstractSimpleEdge, SimpleEdge, SimpleGraph, SimpleGraphEdge, @@ -128,7 +128,7 @@ function rem_vertex!(g::AbstractSimpleGraph, v::Integer) return true end -empty(g::T) where T<:AbstractSimpleGraph = T() +zero(g::T) where T<:AbstractSimpleGraph = T() include("simpleedge.jl") include("simpledigraph.jl") diff --git a/src/interface.jl b/src/interface.jl index 3d99a081c..404ec30dd 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -102,6 +102,6 @@ NOTE: returns a reference, not a copy. Do not modify result. out_neighbors(x...) = _NI("out_neighbors") """ -Return an empty version of the same type of graph. +Return a zero-vertex, zero-edge version of the same type of graph. """ -empty(x...) = _NI("empty") +zero(x...) = _NI("zero") diff --git a/src/operators.jl b/src/operators.jl index bafa4de53..1d0f5af4c 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -363,7 +363,7 @@ end function induced_subgraph(g::T, elist::AbstractVector{U}) where T<:AbstractGraph where U<:AbstractEdge - h = empty(g) + h = zero(g) et = eltype(h) newvid = Dict{et, et}() vmap = Vector{et}() diff --git a/src/shortestpaths/astar.jl b/src/shortestpaths/astar.jl index ea41bc6f4..49e32bd9d 100644 --- a/src/shortestpaths/astar.jl +++ b/src/shortestpaths/astar.jl @@ -22,7 +22,6 @@ function a_star_impl!( if colormap[v] < 2 dist = distmx[u, v] - colormap[v] = 1 new_path = cat(1, path, Edge(u,v)) path_cost = cost_so_far + dist @@ -33,7 +32,7 @@ function a_star_impl!( end colormap[u] = 2 end - nothing + Vector{Edge}() end """ diff --git a/test/biconnectivity/articulation.jl b/test/biconnectivity/articulation.jl index 002ced521..78a7cdcc8 100644 --- a/test/biconnectivity/articulation.jl +++ b/test/biconnectivity/articulation.jl @@ -19,14 +19,14 @@ add_edge!(gint, 7, 12) for g in testgraphs(gint) - art = articulation(g) + art = @inferred(articulation(g)) ans = [1, 7, 8, 12] @test art == ans end for level in 1:6 btree = LightGraphs.BinaryTree(level) for tree in [btree, Graph{UInt8}(btree), Graph{Int16}(btree)] - artpts = articulation(tree) + artpts = @inferred(articulation(tree)) @test artpts == collect(1:(2^(level-1)-1)) end end @@ -34,6 +34,6 @@ hint = LightGraphs.blkdiag(WheelGraph(5), WheelGraph(5)) add_edge!(hint, 5, 6) for h in (hint, Graph{UInt8}(hint), Graph{Int16}(hint)) - @test articulation(h) == [5, 6] + @test @inferred(articulation(h)) == [5, 6] end end diff --git a/test/biconnectivity/biconnect.jl b/test/biconnectivity/biconnect.jl index 178f7fcf6..44a021b44 100644 --- a/test/biconnectivity/biconnect.jl +++ b/test/biconnectivity/biconnect.jl @@ -22,7 +22,7 @@ [Edge(11, 12)]] for g in testgraphs(gint) - bcc = biconnected_components(g) + bcc = @inferred(biconnected_components(g)) @test bcc == a end @@ -44,7 +44,7 @@ a = [[Edge(5, 8),Edge(7, 8),Edge(6, 7),Edge(5, 6)], [Edge(4, 5)], [Edge(1, 4),Edge(3, 4),Edge(2, 3),Edge(1, 2)]] for g in testgraphs(gint) - bcc = biconnected_components(g) + bcc = @inferred(biconnected_components(g)) @test bcc == a end end diff --git a/test/centrality/betweenness.jl b/test/centrality/betweenness.jl index a6d75d997..e25f38672 100644 --- a/test/centrality/betweenness.jl +++ b/test/centrality/betweenness.jl @@ -20,30 +20,30 @@ c = readcentrality(joinpath(testdir,"testdata","graph-50-500-bc.txt")) for g in testdigraphs(gint) - z = betweenness_centrality(g) + z = @inferred(betweenness_centrality(g)) @test map(Float32, z) == map(Float32, c) - y = betweenness_centrality(g, endpoints=true, normalize=false) + y = @inferred(betweenness_centrality(g, endpoints=true, normalize=false)) @test round.(y[1:3],4) == round.([122.10760591498584, 159.0072453120582, 176.39547945994505], 4) - x = betweenness_centrality(g,3) + x = @inferred(betweenness_centrality(g,3)) @test length(x) == 50 end - @test betweenness_centrality(s1) == [0, 1, 0] - @test betweenness_centrality(s2) == [0, 0.5, 0] + @test @inferred(betweenness_centrality(s1)) == [0, 1, 0] + @test @inferred(betweenness_centrality(s2)) == [0, 0.5, 0] g = Graph(2) add_edge!(g,1,2) - z = betweenness_centrality(g; normalize=true) + z = @inferred(betweenness_centrality(g; normalize=true)) @test z[1] == z[2] == 0.0 - z2 = betweenness_centrality(g, vertices(g)) - z3 = betweenness_centrality(g, [vertices(g);]) + z2 = @inferred(betweenness_centrality(g, vertices(g))) + z3 = @inferred(betweenness_centrality(g, [vertices(g);])) @test z == z2 == z3 - z = betweenness_centrality(g3; normalize=false) + z = @inferred(betweenness_centrality(g3; normalize=false)) @test z[1] == z[5] == 0.0 end diff --git a/test/centrality/closeness.jl b/test/centrality/closeness.jl index 99b37f627..4cda3e586 100644 --- a/test/centrality/closeness.jl +++ b/test/centrality/closeness.jl @@ -2,8 +2,8 @@ g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) for g in testdigraphs(g5) - y = closeness_centrality(g; normalize=false) - z = closeness_centrality(g) + y = @inferred(closeness_centrality(g; normalize=false)) + z = @inferred(closeness_centrality(g)) @test y == [0.75, 0.6666666666666666, 1.0, 0.0] @test z == [0.75, 0.4444444444444444, 0.3333333333333333, 0.0] end @@ -11,7 +11,7 @@ g5 = Graph(5) add_edge!(g5,1,2) for g in testgraphs(g5) - z = closeness_centrality(g) + z = @inferred(closeness_centrality(g)) @test z[1] == z[2] == 0.25 @test z[3] == z[4] == z[5] == 0.0 end diff --git a/test/centrality/degree.jl b/test/centrality/degree.jl index 548262cf1..837b847a2 100644 --- a/test/centrality/degree.jl +++ b/test/centrality/degree.jl @@ -2,8 +2,8 @@ g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) for g in testdigraphs(g5) - @test degree_centrality(g) == [0.6666666666666666, 0.6666666666666666, 1.0, 0.3333333333333333] - @test indegree_centrality(g, normalize=false) == [0.0, 1.0, 2.0, 1.0] - @test outdegree_centrality(g; normalize=false) == [2.0, 1.0, 1.0, 0.0] + @test @inferred(degree_centrality(g)) == [0.6666666666666666, 0.6666666666666666, 1.0, 0.3333333333333333] + @test @inferred(indegree_centrality(g, normalize=false)) == [0.0, 1.0, 2.0, 1.0] + @test @inferred(outdegree_centrality(g; normalize=false)) == [2.0, 1.0, 1.0, 0.0] end end diff --git a/test/centrality/katz.jl b/test/centrality/katz.jl index b7458a63a..fe83ef29a 100644 --- a/test/centrality/katz.jl +++ b/test/centrality/katz.jl @@ -2,7 +2,7 @@ g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) for g in testdigraphs(g5) - z = katz_centrality(g, 0.4) + z = @inferred(katz_centrality(g, 0.4)) @test round.(z, 2) == [0.32, 0.44, 0.62, 0.56] end end diff --git a/test/centrality/pagerank.jl b/test/centrality/pagerank.jl index 3ee6fbf6c..3e5074342 100644 --- a/test/centrality/pagerank.jl +++ b/test/centrality/pagerank.jl @@ -2,7 +2,7 @@ g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) for g in testdigraphs(g5) - @test pagerank(g)[3] ≈ 0.318 atol=0.001 + @test @inferred(pagerank(g))[3] ≈ 0.318 atol=0.001 @test_throws ErrorException pagerank(g, 2) @test_throws ErrorException pagerank(g, 0.85, 2) end diff --git a/test/community/cliques.jl b/test/community/cliques.jl index aaf349991..310492756 100644 --- a/test/community/cliques.jl +++ b/test/community/cliques.jl @@ -12,7 +12,7 @@ function test_cliques(graph, expected) # Make test results insensitive to ordering - setofsets(maximal_cliques(graph)) == setofsets(expected) + setofsets(@inferred(maximal_cliques(graph))) == setofsets(expected) end gx = Graph(3) @@ -36,7 +36,7 @@ add_edge!(h, 5, 6) for g in testgraphs(h) - @test maximal_cliques(g) != [] + @test !isempty(@inferred(maximal_cliques(g))) end # test for extra cliques bug diff --git a/test/community/clustering.jl b/test/community/clustering.jl index a65e2439e..95df102ce 100644 --- a/test/community/clustering.jl +++ b/test/community/clustering.jl @@ -1,10 +1,10 @@ @testset "Clustering" begin g10 = CompleteGraph(10) for g in testgraphs(g10) - @test local_clustering_coefficient(g) == ones(10) - @test global_clustering_coefficient(g) == 1 - @test local_clustering(g) == (fill(36, 10), fill(36, 10)) - @test triangles(g) == fill(36, 10) - @test triangles(g, 1) == 36 + @test @inferred(local_clustering_coefficient(g)) == ones(10) + @test @inferred(global_clustering_coefficient(g)) == 1 + @test @inferred(local_clustering(g)) == (fill(36, 10), fill(36, 10)) + @test @inferred(triangles(g)) == fill(36, 10) + @test @inferred(triangles(g, 1)) == 36 end end diff --git a/test/community/core-periphery.jl b/test/community/core-periphery.jl index c516cc661..8dc96fcfe 100644 --- a/test/community/core-periphery.jl +++ b/test/community/core-periphery.jl @@ -1,26 +1,26 @@ @testset "Core periphery" begin g10 = StarGraph(10) for g in testgraphs(g10) - c = core_periphery_deg(g) - @test degree(g, 1) == 9 - @test c[1] == 1 - for i=2:10 - @test c[i] == 2 - end + c = core_periphery_deg(g) + @test @inferred(degree(g, 1)) == 9 + @test c[1] == 1 + for i=2:10 + @test c[i] == 2 + end end g10 = StarGraph(10) g10 = blkdiag(g10,g10) add_edge!(g10, 1, 11) for g in testgraphs(g10) - c = core_periphery_deg(g) - @test c[1] == 1 - @test c[11] == 1 - for i=2:10 - @test c[i] == 2 - end - for i=12:20 - @test c[i] == 2 - end + c = @inferred(core_periphery_deg(g)) + @test c[1] == 1 + @test c[11] == 1 + for i=2:10 + @test c[i] == 2 + end + for i=12:20 + @test c[i] == 2 + end end end diff --git a/test/community/label_propagation.jl b/test/community/label_propagation.jl index f05f6b409..3f6152611 100644 --- a/test/community/label_propagation.jl +++ b/test/community/label_propagation.jl @@ -6,10 +6,10 @@ for k=2:5 z = blkdiag(z, g) add_edge!(z, (k-1)*n, k*n) - c, ch = label_propagation(z) + c, ch = @inferred(label_propagation(z)) a = collect(n:n:k*n) a = Int[div(i-1,n)+1 for i=1:k*n] - # check the number of community + # check the number of communities @test length(unique(a)) == length(unique(c)) # check the partition @test a == c diff --git a/test/community/modularity.jl b/test/community/modularity.jl index c084e2028..98fe437d6 100644 --- a/test/community/modularity.jl +++ b/test/community/modularity.jl @@ -4,11 +4,11 @@ c = ones(Int, n) gint = CompleteGraph(n) for g in testgraphs(gint) - @test modularity(g, c) == 0 + @test @inferred(modularity(g, c)) == 0 end gint = Graph(n) for g in testgraphs(gint) - @test modularity(g, c) == 0 + @test @inferred(modularity(g, c)) == 0 end end diff --git a/test/connectivity.jl b/test/connectivity.jl index 5ad84d779..d286e00f4 100644 --- a/test/connectivity.jl +++ b/test/connectivity.jl @@ -9,20 +9,20 @@ for g in testgraphs(gx) - @test !is_connected(g) - cc = connected_components(g) + @test @inferred(!is_connected(g)) + cc = @inferred(connected_components(g)) label = zeros(eltype(g), nv(g)) - LightGraphs.connected_components!(label, g) + @inferred(LightGraphs.connected_components!(label, g)) @test label[1:10] == [1,1,1,1,5,5,5,8,8,8] import LightGraphs: components, components_dict - cclab = components_dict(label) + cclab = @inferred(components_dict(label)) @test cclab[1] == [1,2,3,4] @test cclab[5] == [5,6,7] @test cclab[8] == [8,9,10] @test length(cc) >= 3 && sort(cc[3]) == [8,9,10] end for g in testgraphs(g6) - @test is_connected(g) + @test @inferred(is_connected(g)) end @@ -30,11 +30,11 @@ add_edge!(g10,1,3) add_edge!(g10,2,4) for g in testdigraphs(g10) - @test is_bipartite(g) + @test @inferred(is_bipartite(g)) end add_edge!(g10,1,4) for g in testdigraphs(g10) - @test is_bipartite(g) + @test @inferred(is_bipartite(g)) end g10 = DiGraph(20) @@ -47,7 +47,7 @@ end if !has_edge(g, i, j) add_edge!(g, i, j) - @test is_bipartite(g) + @test @inferred(is_bipartite(g)) end end end @@ -60,9 +60,9 @@ add_edge!(h,5,6); add_edge!(h,6,7); add_edge!(h,7,6); add_edge!(h,8,4); add_edge!(h,8,7) for g in testdigraphs(h) - @test is_connected(g) - scc = strongly_connected_components(g) - wcc = weakly_connected_components(g) + @test @inferred(is_connected(g)) + scc = @inferred(strongly_connected_components(g)) + wcc = @inferred(weakly_connected_components(g)) @test length(scc) == 3 && sort(scc[3]) == [1,2,5] @test length(wcc) == 1 && length(wcc[1]) == nv(g) @@ -72,7 +72,7 @@ function scc_ok(graph) """Check that all SCC really are strongly connected""" - scc = strongly_connected_components(graph) + scc = @inferred(strongly_connected_components(graph)) scc_as_subgraphs = map(i -> graph[i], scc) return all(is_strongly_connected, scc_as_subgraphs) end @@ -92,14 +92,14 @@ add_edge!(h,1,3); add_edge!(h,3,4); add_edge!(h,4,2); add_edge!(h,2,1) add_edge!(h,3,5); add_edge!(h,5,6); add_edge!(h,6,4) for g in testdigraphs(h) - scc = strongly_connected_components(g) + scc = @inferred(strongly_connected_components(g)) @test length(scc) == 1 && sort(scc[1]) == [1:6;] end # tests from Graphs.jl h = DiGraph(4) add_edge!(h,1,2); add_edge!(h,2,3); add_edge!(h,3,1); add_edge!(h,4,1) for g in testdigraphs(h) - scc = strongly_connected_components(g) + scc = @inferred(strongly_connected_components(g)) @test length(scc) == 2 && sort(scc[1]) == [1:3;] && sort(scc[2]) == [4] end @@ -111,7 +111,7 @@ add_edge!(h,10,9); add_edge!(h,10,11); add_edge!(h,11,12); add_edge!(h,12,10) for g in testdigraphs(h) - scc = strongly_connected_components(g) + scc = @inferred(strongly_connected_components(g)) @test length(scc) == 4 @test sort(scc[1]) == [7,8,9,10,11,12] @test sort(scc[2]) == [3, 6] @@ -155,39 +155,39 @@ fig8[[2,10,13,21,24,27,35]] = 1 fig8 = DiGraph(fig8) - @test Set(strongly_connected_components(fig1)) == Set(scc_fig1) - @test Set(strongly_connected_components(fig3)) == Set(scc_fig3) + @test Set(@inferred(strongly_connected_components(fig1))) == Set(scc_fig1) + @test Set(@inferred(strongly_connected_components(fig3))) == Set(scc_fig3) - @test period(n_ring) == n - @test period(n_ring_shortcut) == 2 + @test @inferred(period(n_ring)) == n + @test @inferred(period(n_ring_shortcut)) == 2 - @test condensation(fig3) == fig3_cond + @test @inferred(condensation(fig3)) == fig3_cond - @test attracting_components(fig1) == Vector[[2,5]] - @test attracting_components(fig3) == Vector[[3,4],[8]] + @test @inferred(attracting_components(fig1)) == Vector[[2,5]] + @test @inferred(attracting_components(fig3)) == Vector[[3,4],[8]] g10 = StarGraph(10) for g in testgraphs(g10) - @test neighborhood(g, 1 , 0) == [1] - @test length(neighborhood(g, 1, 1)) == 10 - @test length(neighborhood(g, 2, 1)) == 2 - @test length(neighborhood(g, 1, 2)) == 10 - @test length(neighborhood(g, 2, 2)) == 10 + @test @inferred(neighborhood(g, 1 , 0)) == [1] + @test length(@inferred(neighborhood(g, 1, 1))) == 10 + @test length(@inferred(neighborhood(g, 2, 1))) == 2 + @test length(@inferred(neighborhood(g, 1, 2))) == 10 + @test length(@inferred(neighborhood(g, 2, 2))) == 10 end g10 = StarDiGraph(10) for g in testdigraphs(g10) - @test neighborhood(g10, 1 , 0, dir=:out) == [1] - @test length(neighborhood(g, 1, 1, dir=:out)) == 10 - @test length(neighborhood(g, 2, 1, dir=:out)) == 1 - @test length(neighborhood(g, 1, 2, dir=:out)) == 10 - @test length(neighborhood(g, 2, 2, dir=:out)) == 1 - @test neighborhood(g, 1 , 0, dir=:in) == [1] - @test length(neighborhood(g, 1, 1, dir=:in)) == 1 - @test length(neighborhood(g, 2, 1, dir=:in)) == 2 - @test length(neighborhood(g, 1, 2, dir=:in)) == 1 - @test length(neighborhood(g, 2, 2, dir=:in)) == 2 + @test @inferred(neighborhood(g10, 1 , 0, dir=:out)) == [1] + @test length(@inferred(neighborhood(g, 1, 1, dir=:out))) == 10 + @test length(@inferred(neighborhood(g, 2, 1, dir=:out))) == 1 + @test length(@inferred(neighborhood(g, 1, 2, dir=:out))) == 10 + @test length(@inferred(neighborhood(g, 2, 2, dir=:out))) == 1 + @test @inferred(neighborhood(g, 1 , 0, dir=:in)) == [1] + @test length(@inferred(neighborhood(g, 1, 1, dir=:in))) == 1 + @test length(@inferred(neighborhood(g, 2, 1, dir=:in))) == 2 + @test length(@inferred(neighborhood(g, 1, 2, dir=:in))) == 1 + @test length(@inferred(neighborhood(g, 2, 2, dir=:in))) == 2 end - @test !isgraphical([1,1,1]) - @test isgraphical([2,2,2]) - @test isgraphical(fill(3,10)) + @test @inferred(!isgraphical([1,1,1])) + @test @inferred(isgraphical([2,2,2])) + @test @inferred(isgraphical(fill(3,10))) end diff --git a/test/core.jl b/test/core.jl index cf3bc8606..241f84767 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1,75 +1,75 @@ @testset "Core" begin - e2 = Edge(1,3) - e3 = Edge(1,4) - @test is_ordered(e2) - @test !is_ordered(reverse(e3)) - - gx = Graph(10) - for g in testgraphs(gx) - add_vertices!(g, 5) - @test nv(g) == 15 - end +e2 = Edge(1,3) +e3 = Edge(1,4) +@test @inferred(is_ordered(e2)) +@test @inferred(!is_ordered(reverse(e3))) + +gx = Graph(10) +for g in testgraphs(gx) + add_vertices!(g, 5) + @test @inferred(nv(g)) == 15 +end - g5w = WheelGraph(5) - for g in testgraphs(g5w) - @test indegree(g,1) == outdegree(g,1) == degree(g,1) == 4 - @test indegree(g) == outdegree(g) == degree(g) == [4,3,3,3,3] +g5w = WheelGraph(5) +for g in testgraphs(g5w) + @test @inferred(indegree(g,1)) == @inferred(outdegree(g,1)) == @inferred(degree(g,1)) == 4 + @test @inferred(indegree(g)) == @inferred(outdegree(g)) == @inferred(degree(g)) == [4,3,3,3,3] - @test Δout(g) == Δin(g) == Δ(g) == 4 - @test δout(g) == δin(g) == δ(g) == 3 + @test @inferred(Δout(g)) == @inferred(Δin(g)) == @inferred(Δ(g)) == 4 + @test @inferred(δout(g)) == @inferred(δin(g)) == @inferred(δ(g)) == 3 - z = degree_histogram(g) - @test z.weights == [4,0,1] + z = @inferred(degree_histogram(g)) + @test z.weights == [4,0,1] - @test neighbors(g, 2) == all_neighbors(g, 2) == [1,3,5] - @test common_neighbors(g, 1, 5) == [2, 4] + @test @inferred(neighbors(g, 2)) == @inferred(all_neighbors(g, 2)) == [1,3,5] + @test @inferred(common_neighbors(g, 1, 5)) == [2, 4] - gsl = copy(g) - add_edge!(gsl, 3, 3) - add_edge!(gsl, 2, 2) + gsl = copy(g) + add_edge!(gsl, 3, 3) + add_edge!(gsl, 2, 2) - @test !has_self_loops(g) - @test has_self_loops(gsl) - @test num_self_loops(g) == 0 - @test num_self_loops(gsl) == 2 + @test @inferred(!has_self_loops(g)) + @test @inferred(has_self_loops(gsl)) + @test @inferred(num_self_loops(g)) == 0 + @test @inferred(num_self_loops(gsl)) == 2 - @test density(g) == 0.8 + @test @inferred(density(g)) == 0.8 - @test eltype(squash(g)) == UInt8 + @test eltype(squash(g)) == UInt8 end g5wd = WheelDiGraph(5) for g in testdigraphs(g5wd) - @test indegree(g,2) == 2 - @test outdegree(g,2) == 1 - @test degree(g,2) == 3 - @test indegree(g) == [0,2,2,2,2] - @test outdegree(g) == [4,1,1,1,1] - @test degree(g) == [4,3,3,3,3] - - @test Δout(g) == Δ(g) == 4 - @test Δin(g) == 2 - @test δout(g) == 1 - @test δin(g) == 0 - @test δ(g) == 3 - - z = degree_histogram(g) + @test @inferred(indegree(g,2)) == 2 + @test @inferred(outdegree(g,2)) == 1 + @test @inferred(degree(g,2)) == 3 + @test @inferred(indegree(g)) == [0,2,2,2,2] + @test @inferred(outdegree(g)) == [4,1,1,1,1] + @test @inferred(degree(g)) == [4,3,3,3,3] + + @test @inferred(Δout(g)) == @inferred(Δ(g)) == 4 + @test @inferred(Δin(g)) == 2 + @test @inferred(δout(g)) == 1 + @test @inferred(δin(g)) == 0 + @test @inferred(δ(g)) == 3 + + z = @inferred(degree_histogram(g)) @test z.weights == [4,0,1] - @test neighbors(g, 2) == [3] - @test Set(all_neighbors(g, 2)) == Set([1,3,5]) - @test common_neighbors(g, 1, 5) == [2] + @test @inferred(neighbors(g, 2)) == [3] + @test Set(@inferred(all_neighbors(g, 2))) == Set([1,3,5]) + @test @inferred(common_neighbors(g, 1, 5)) == [2] gsl = copy(g) add_edge!(gsl, 3, 3) add_edge!(gsl, 2, 2) - @test !has_self_loops(g) - @test has_self_loops(gsl) - @test num_self_loops(g) == 0 - @test num_self_loops(gsl) == 2 + @test @inferred(!has_self_loops(g)) + @test @inferred(has_self_loops(gsl)) + @test @inferred(num_self_loops(g)) == 0 + @test @inferred(num_self_loops(gsl)) == 2 - @test density(g) == 0.4 + @test @inferred(density(g)) == 0.4 @test eltype(squash(g)) == UInt8 + end end -end diff --git a/test/distance.jl b/test/distance.jl index 2e59bddd5..60de14450 100644 --- a/test/distance.jl +++ b/test/distance.jl @@ -11,24 +11,24 @@ @test_throws ErrorException eccentricity(g) end for g in testgraphs(a1) - z = eccentricity(g, distmx1) + z = @inferred(eccentricity(g, distmx1)) @test z == [6.2, 4.2, 6.2] - @test diameter(z) == diameter(g, distmx1) == 6.2 - @test periphery(z) == periphery(g, distmx1) == [1,3] - @test radius(z) == radius(g, distmx1) == 4.2 - @test center(z) == center(g, distmx1) == [2] + @test @inferred(diameter(z)) == diameter(g, distmx1) == 6.2 + @test @inferred(periphery(z)) == periphery(g, distmx1) == [1,3] + @test @inferred(radius(z)) == radius(g, distmx1) == 4.2 + @test @inferred(center(z)) == center(g, distmx1) == [2] end for g in testdigraphs(a2) - z = eccentricity(g, distmx2) + z = @inferred(eccentricity(g, distmx2)) @test z == [6.2, 4.2, 6.1] - @test diameter(z) == diameter(g, distmx2) == 6.2 - @test periphery(z) == periphery(g, distmx2) == [1] - @test radius(z) == radius(g, distmx2) == 4.2 - @test center(z) == center(g, distmx2) == [2] + @test @inferred(diameter(z)) == diameter(g, distmx2) == 6.2 + @test @inferred(periphery(z)) == periphery(g, distmx2) == [1] + @test @inferred(radius(z)) == radius(g, distmx2) == 4.2 + @test @inferred(center(z)) == center(g, distmx2) == [2] end @test size(LightGraphs.DefaultDistance()) == (typemax(Int), typemax(Int)) - d = LightGraphs.DefaultDistance(3) + d = @inferred(LightGraphs.DefaultDistance(3)) @test size(d) == (3, 3) @test d[1,1] == getindex(d, 1, 1) == 1 @test d[1:2, 1:2] == LightGraphs.DefaultDistance(2) diff --git a/test/edit_distance.jl b/test/edit_distance.jl index 86e698d48..e8fe182ea 100644 --- a/test/edit_distance.jl +++ b/test/edit_distance.jl @@ -5,24 +5,24 @@ gpent = random_regular_graph(5,2) for triangle in testgraphs(gtri), quadrangle in testgraphs(gquad), pentagon in testgraphs(gpent) - d, λ = edit_distance(triangle, quadrangle, subst_cost=MinkowskiCost(1:3,1:4)) + d, λ = @inferred(edit_distance(triangle, quadrangle, subst_cost=MinkowskiCost(1:3,1:4))) @test d == 1.0 @test λ == Tuple[(1,1),(2,2),(3,3),(0,4)] - d, λ = edit_distance(quadrangle, triangle, subst_cost=MinkowskiCost(1:4,1:3)) + d, λ = @inferred(edit_distance(quadrangle, triangle, subst_cost=MinkowskiCost(1:4,1:3))) @test d == 1.0 @test λ == Tuple[(1,1),(2,2),(3,3),(4,0)] - d, λ = edit_distance(triangle, pentagon, subst_cost=MinkowskiCost(1:3,1:5)) + d, λ = @inferred(edit_distance(triangle, pentagon, subst_cost=MinkowskiCost(1:3,1:5))) @test d == 2.0 @test λ == Tuple[(1,1),(2,2),(3,3),(0,4),(0,5)] - d, λ = edit_distance(pentagon, triangle, subst_cost=MinkowskiCost(1:5,1:3)) + d, λ = @inferred(edit_distance(pentagon, triangle, subst_cost=MinkowskiCost(1:5,1:3))) @test d == 2.0 @test λ == Tuple[(1,1),(2,2),(3,3),(4,0),(5,0)] end - cost = MinkowskiCost(1:3,1:3) - bcost = BoundedMinkowskiCost(1:3,1:3) + cost = @inferred(MinkowskiCost(1:3,1:3)) + bcost = @inferred(BoundedMinkowskiCost(1:3,1:3)) for i=1:3 @test cost(i,i) == 0. @test bcost(i,i) == 2/3 @@ -32,7 +32,7 @@ g2c = CompleteGraph(4) rem_edge!(g2c, 1, 2) for g1 in testgraphs(g1c), g2 in testgraphs(g2c) - d, λ = edit_distance(g1, g2) + d, λ = @inferred(edit_distance(g1, g2)) @test d == 2.0 @test λ == Tuple[(1,1),(2,2),(3,3),(4,4)] end diff --git a/test/flow/boykov_kolmogorov.jl b/test/flow/boykov_kolmogorov.jl index 70f91d211..03bfd6403 100644 --- a/test/flow/boykov_kolmogorov.jl +++ b/test/flow/boykov_kolmogorov.jl @@ -11,7 +11,7 @@ for g in testdigraphs(gg) # default capacity capacity_matrix = LightGraphs.DefaultCapacity(g) - residual_graph = LightGraphs.residual(g) + residual_graph = @inferred(LightGraphs.residual(g)) T = eltype(g) flow_matrix = zeros(T, 3, 3) TREE = zeros(T, 3) @@ -19,7 +19,7 @@ TREE[target] = T(2) PARENT = zeros(T, 3) A = [T(source),T(target)] -# see https://github.com/JuliaLang/julia/issues/21077 +# see https://github.com/JuliaLang/julia/issues/21077 # @show("testing $g with eltype $T, residual_graph type is $(eltype(residual_graph)), flow_matrix type is $(eltype(flow_matrix)), capacity_matrix type is $(eltype(capacity_matrix))") path = LightGraphs.find_path!( residual_graph, source, target, flow_matrix, diff --git a/test/flow/dinic.jl b/test/flow/dinic.jl index 1db63b7d1..0787964bc 100644 --- a/test/flow/dinic.jl +++ b/test/flow/dinic.jl @@ -19,13 +19,13 @@ # Construct the residual graph for fg in (flow_graph, DiGraph{UInt8}(flow_graph), DiGraph{Int16}(flow_graph)) - residual_graph = LightGraphs.residual(fg) + residual_graph = @inferred(LightGraphs.residual(fg)) # Test with default distances - @test LightGraphs.dinic_impl(residual_graph, 1, 8, LightGraphs.DefaultCapacity(residual_graph))[1] == 3 + @test @inferred(LightGraphs.dinic_impl(residual_graph, 1, 8, LightGraphs.DefaultCapacity(residual_graph)))[1] == 3 # Test with capacity matrix - @test LightGraphs.dinic_impl(residual_graph, 1, 8, capacity_matrix)[1] == 28 + @test @inferred(LightGraphs.dinic_impl(residual_graph, 1, 8, capacity_matrix))[1] == 28 # Test on disconnected graphs function test_blocking_flow(residual_graph, source, target, capacity_matrix, flow_matrix) @@ -34,14 +34,14 @@ for dst in collect(neighbors(residual_graph, source)) rem_edge!(h, source, dst) end - @test 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 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 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 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)) diff --git a/test/flow/edmonds_karp.jl b/test/flow/edmonds_karp.jl index c9db0e795..5cacd1b5c 100644 --- a/test/flow/edmonds_karp.jl +++ b/test/flow/edmonds_karp.jl @@ -19,13 +19,13 @@ capacity_matrix[u,v] = f end - residual_graph = LightGraphs.residual(fg) + residual_graph = @inferred(LightGraphs.residual(fg)) # Test with default distances - @test LightGraphs.edmonds_karp_impl(residual_graph, 1, 8, LightGraphs.DefaultCapacity(residual_graph))[1] == 3 + @test @inferred(LightGraphs.edmonds_karp_impl(residual_graph, 1, 8, LightGraphs.DefaultCapacity(residual_graph)))[1] == 3 # Test with capacity matrix - @test LightGraphs.edmonds_karp_impl(residual_graph,1,8,capacity_matrix)[1] == 28 + @test @inferred(LightGraphs.edmonds_karp_impl(residual_graph,1,8,capacity_matrix))[1] == 28 # Test the types of the values returned by fetch_path function test_find_path_types(residual_graph, s, t, flow_matrix, capacity_matrix) diff --git a/test/flow/maximum_flow.jl b/test/flow/maximum_flow.jl index f40b38ae4..fe7731f89 100644 --- a/test/flow/maximum_flow.jl +++ b/test/flow/maximum_flow.jl @@ -1,57 +1,57 @@ @testset "Maximum flow" begin #### Graphs for testing graphs = [ - # Graph with 8 vertices - (8, - [ - (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) - ], - 1,8, # source/target - 3, # answer for default capacity - 28, # answer for custom capacity - 15,5 # answer for restricted capacity/restriction - ), + # Graph with 8 vertices + (8, + [ + (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) + ], + 1,8, # source/target + 3, # answer for default capacity + 28, # answer for custom capacity + 15,5 # answer for restricted capacity/restriction + ), - # Graph with 6 vertices - (6, - [ - (1,2,9),(1,3,9),(2,3,10),(2,4,8),(3,4,1), - (3,5,3),(5,4,8),(4,6,10),(5,6,7) - ], - 1,6, # source/target - 2, # answer for default capacity - 12, # answer for custom capacity - 8,5 # answer for restricted capacity/restriction - ) + # Graph with 6 vertices + (6, + [ + (1,2,9),(1,3,9),(2,3,10),(2,4,8),(3,4,1), + (3,5,3),(5,4,8),(4,6,10),(5,6,7) + ], + 1,6, # source/target + 2, # answer for default capacity + 12, # answer for custom capacity + 8,5 # answer for restricted capacity/restriction + ) ] for (nvertices,flow_edges,s,t,fdefault,fcustom,frestrict,caprestrict) in graphs flow_graph = DiGraph(nvertices) for g in testdigraphs(flow_graph) - capacity_matrix = zeros(Int,nvertices,nvertices) - for e in flow_edges - u,v,f = e - add_edge!(g,u,v) - capacity_matrix[u,v] = f - end + capacity_matrix = zeros(Int,nvertices,nvertices) + for e in flow_edges + u,v,f = e + add_edge!(g,u,v) + capacity_matrix[u,v] = f + end - # Test DefaultCapacity - d = LightGraphs.DefaultCapacity(g) - T = eltype(d) - @test typeof(d) <: AbstractMatrix{T} - @test d[s,t] == 0 - @test size(d) == (nvertices,nvertices) - @test typeof(transpose(d)) <: LightGraphs.DefaultCapacity - @test typeof(ctranspose(d)) <: LightGraphs.DefaultCapacity + # Test DefaultCapacity + d = @inferred(LightGraphs.DefaultCapacity(g)) + T = eltype(d) + @test typeof(d) <: AbstractMatrix{T} + @test d[s,t] == 0 + @test size(d) == (nvertices,nvertices) + @test typeof(transpose(d)) <: LightGraphs.DefaultCapacity + @test typeof(ctranspose(d)) <: LightGraphs.DefaultCapacity - # Test all algorithms - for ALGO in [EdmondsKarpAlgorithm, DinicAlgorithm, BoykovKolmogorovAlgorithm, PushRelabelAlgorithm] - @test maximum_flow(g,s,t,algorithm=ALGO())[1] == fdefault - @test maximum_flow(g,s,t,capacity_matrix,algorithm=ALGO())[1] == fcustom - @test maximum_flow(g,s,t,capacity_matrix,algorithm=ALGO(),restriction=caprestrict)[1] == frestrict - end + # Test all algorithms - type instability in PushRelabel #553 + for ALGO in [EdmondsKarpAlgorithm, DinicAlgorithm, BoykovKolmogorovAlgorithm, PushRelabelAlgorithm] + @test maximum_flow(g,s,t,algorithm=ALGO())[1] == fdefault + @test maximum_flow(g,s,t,capacity_matrix,algorithm=ALGO())[1] == fcustom + @test maximum_flow(g,s,t,capacity_matrix,algorithm=ALGO(),restriction=caprestrict)[1] == frestrict + end end end end diff --git a/test/flow/multiroute_flow.jl b/test/flow/multiroute_flow.jl index e9d68a53a..8ac5dff1a 100644 --- a/test/flow/multiroute_flow.jl +++ b/test/flow/multiroute_flow.jl @@ -1,88 +1,86 @@ @testset "Multiroute flow" begin #### Graphs for testing graphs = [ - # Graph with 8 vertices - (8, - [ - (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), - (8, 1, 8) # Reverse edge to test the slope in EMRF - ], - 1, 8, # source/target - [28, 28, 15, 0], # answer for 1 to 4 route(s) flows - [(0., 0., 3), (5., 15., 2), # breaking points - (10., 25., 1), (13., 28., 0)], - 28. # value for 1.5 routes - ), + # Graph with 8 vertices + (8, + [ + (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), + (8, 1, 8) # Reverse edge to test the slope in EMRF + ], + 1, 8, # source/target + [28, 28, 15, 0], # answer for 1 to 4 route(s) flows + [(0., 0., 3), (5., 15., 2), # breaking points + (10., 25., 1), (13., 28., 0)], + 28. # value for 1.5 routes + ), - # Graph with 6 vertices - (6, - [ - (1, 2, 9), (1, 3, 9), (2, 3, 10), (2, 4, 8), (3, 4, 1), - (3, 5, 3), (5, 4, 8), (4, 6, 10), (5, 6, 7) - ], - 1, 6, # source/target - [12, 6, 0], # answer for 1 to 3 route(s) flows - [(0., 0., 2), (3., 6., 1), (9., 12., 0)], # breaking points - 9. # value for 1.5 routes - ), + # Graph with 6 vertices + (6, + [ + (1, 2, 9), (1, 3, 9), (2, 3, 10), (2, 4, 8), (3, 4, 1), + (3, 5, 3), (5, 4, 8), (4, 6, 10), (5, 6, 7) + ], + 1, 6, # source/target + [12, 6, 0], # answer for 1 to 3 route(s) flows + [(0., 0., 2), (3., 6., 1), (9., 12., 0)], # breaking points + 9. # value for 1.5 routes + ), - # Graph with 7 vertices - (7, - [ - (1, 2, 1), (1, 3, 2), (1, 4, 3), (1, 5, 4), (1, 6, 5), - (2, 7, 1), (3, 7, 2), (4, 7, 3), (5, 7, 4), (6, 7, 5) - ], - 1, 7, # source/target - [15, 15, 15, 12, 5, 0], # answer for 1 to 6 route(s) flows - [(0., 0., 5), (1., 5., 4), (2., 9., 3), # breaking points - (3., 12., 2), (4., 14., 1), (5., 15., 0)], - 15. # value for 1.5 routes - ), + # Graph with 7 vertices + (7, + [ + (1, 2, 1), (1, 3, 2), (1, 4, 3), (1, 5, 4), (1, 6, 5), + (2, 7, 1), (3, 7, 2), (4, 7, 3), (5, 7, 4), (6, 7, 5) + ], + 1, 7, # source/target + [15, 15, 15, 12, 5, 0], # answer for 1 to 6 route(s) flows + [(0., 0., 5), (1., 5., 4), (2., 9., 3), # breaking points + (3., 12., 2), (4., 14., 1), (5., 15., 0)], + 15. # value for 1.5 routes + ), - # Graph with 6 vertices - (6, - [ - (1, 2, 1), (1, 3, 1), (1, 4, 2), (1, 5, 2), - (2, 6, 1), (3, 6, 1), (4, 6, 2), (5, 6, 2), - ], - 1, 6, # source/target - [6, 6, 6, 4, 0], # answer for 1 to 5 route(s) flows - [(0., 0., 4), (1., 4., 2), (2., 6., 0)], # breaking points - 6. # value for 1.5 routes - ) + # Graph with 6 vertices + (6, + [ + (1, 2, 1), (1, 3, 1), (1, 4, 2), (1, 5, 2), + (2, 6, 1), (3, 6, 1), (4, 6, 2), (5, 6, 2), + ], + 1, 6, # source/target + [6, 6, 6, 4, 0], # answer for 1 to 5 route(s) flows + [(0., 0., 4), (1., 4., 2), (2., 6., 0)], # breaking points + 6. # value for 1.5 routes + ) ] for (nvertices, flow_edges, s, t, froutes, breakpts, ffloat) in graphs flow_graph = DiGraph(nvertices) for g in testdigraphs(flow_graph) - capacity_matrix = zeros(Int, nvertices, nvertices) - for e in flow_edges - u, v, f = e - add_edge!(g, u, v) - capacity_matrix[u, v] = f - end - - - # Test ExtendedMultirouteFlowAlgorithm when the number of routes is either - # Noninteger or 0 (the algorithm returns the breaking points) - @test multiroute_flow(g, s, t, capacity_matrix) == breakpts - @test multiroute_flow(g, s, t, capacity_matrix, routes=1.5)[1] ≈ ffloat - @test multiroute_flow(breakpts, 1.5)[2] ≈ ffloat + capacity_matrix = zeros(Int, nvertices, nvertices) + for e in flow_edges + u, v, f = e + add_edge!(g, u, v) + capacity_matrix[u, v] = f + end + # Test ExtendedMultirouteFlowAlgorithm when the number of routes is either + # Noninteger or 0 (the algorithm returns the breaking points) + @test multiroute_flow(g, s, t, capacity_matrix) == breakpts + @test multiroute_flow(g, s, t, capacity_matrix, routes=1.5)[1] ≈ ffloat + @test multiroute_flow(breakpts, 1.5)[2] ≈ ffloat - # Test all other algorithms - for AlgoFlow in [EdmondsKarpAlgorithm, DinicAlgorithm, BoykovKolmogorovAlgorithm, PushRelabelAlgorithm] - # When the input are breaking points and routes number - @test multiroute_flow(breakpts, 1.5, g, s, t, capacity_matrix)[1] ≈ ffloat - for AlgoMrf in [ExtendedMultirouteFlowAlgorithm, KishimotoAlgorithm] - for (k, val) in enumerate(froutes) - @test multiroute_flow(g, s, t, capacity_matrix, - flow_algorithm = AlgoFlow(), mrf_algorithm = AlgoMrf(), - routes = k)[1] ≈ val - end + # Test all other algorithms - PR is unstable - see #553 + for AlgoFlow in [EdmondsKarpAlgorithm, DinicAlgorithm, BoykovKolmogorovAlgorithm, PushRelabelAlgorithm] + # When the input are breaking points and routes number + @test multiroute_flow(breakpts, 1.5, g, s, t, capacity_matrix)[1] ≈ ffloat + for AlgoMrf in [ExtendedMultirouteFlowAlgorithm, KishimotoAlgorithm] + for (k, val) in enumerate(froutes) + @test multiroute_flow(g, s, t, capacity_matrix, + flow_algorithm = AlgoFlow(), mrf_algorithm = AlgoMrf(), + routes = k)[1] ≈ val + end + end end - end end end end diff --git a/test/flow/push_relabel.jl b/test/flow/push_relabel.jl index 54ec82635..f9525f94c 100644 --- a/test/flow/push_relabel.jl +++ b/test/flow/push_relabel.jl @@ -17,17 +17,17 @@ capacity_matrix[u,v] = f end for g in testdigraphs(flow_graph) - residual_graph = LightGraphs.residual(g) + residual_graph = @inferred(LightGraphs.residual(g)) # Test enqueue_vertex Q = Array{Int,1}() excess = [0, 1, 0, 1] active = [false, false, true, true] - @test LightGraphs.enqueue_vertex!(Q, 1, active, excess) == nothing - @test LightGraphs.enqueue_vertex!(Q, 3, active, excess) == nothing - @test LightGraphs.enqueue_vertex!(Q, 4, active, excess) == nothing + @test @inferred(LightGraphs.enqueue_vertex!(Q, 1, active, excess)) == nothing + @test @inferred(LightGraphs.enqueue_vertex!(Q, 3, active, excess)) == nothing + @test @inferred(LightGraphs.enqueue_vertex!(Q, 4, active, excess)) == nothing @test length(Q) == 0 - @test LightGraphs.enqueue_vertex!(Q, 2, active, excess) == nothing + @test @inferred(LightGraphs.enqueue_vertex!(Q, 2, active, excess)) == nothing @test length(Q) == 1 # Test push_flow @@ -36,10 +36,10 @@ height = [8, 0, 0, 0, 0, 0, 0, 0] active = [true, false, false, false, false, false, false, true] flow_matrix = zeros(Int, 8, 8) - @test LightGraphs.push_flow!(residual_graph, 1, 2, capacity_matrix, flow_matrix, excess, height, active, Q) == nothing + @test @inferred(LightGraphs.push_flow!(residual_graph, 1, 2, capacity_matrix, flow_matrix, excess, height, active, Q)) == nothing @test length(Q) == 1 @test flow_matrix[1,2] == 10 - @test LightGraphs.push_flow!(residual_graph, 2, 3, capacity_matrix, flow_matrix, excess, height, active, Q) == nothing + @test @inferred(LightGraphs.push_flow!(residual_graph, 2, 3, capacity_matrix, flow_matrix, excess, height, active, Q)) == nothing @test length(Q) == 1 @test flow_matrix[2,3] == 0 @@ -51,7 +51,7 @@ count = [0, 1, 2, 2, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0] flow_matrix = zeros(Int, 8, 8) - @test LightGraphs.gap!(residual_graph, 1, excess, height, active, count, Q) == nothing + @test @inferred(LightGraphs.gap!(residual_graph, 1, excess, height, active, count, Q)) == nothing @test length(Q) == 2 # Test relabel @@ -62,7 +62,7 @@ count = [1, 6, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0] flow_matrix = zeros(Int, 8, 8) - @test LightGraphs.relabel!(residual_graph, 2, capacity_matrix, flow_matrix, excess, height, active, count, Q) == nothing + @test @inferred(LightGraphs.relabel!(residual_graph, 2, capacity_matrix, flow_matrix, excess, height, active, count, Q)) == nothing @test length(Q) == 1 # Test discharge @@ -73,7 +73,7 @@ count = [7, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] flow_matrix = zeros(Int, 8, 8) - @test LightGraphs.discharge!(residual_graph, 1, capacity_matrix, flow_matrix, excess, height, active, count, Q) == nothing + @test @inferred(LightGraphs.discharge!(residual_graph, 1, capacity_matrix, flow_matrix, excess, height, active, count, Q)) == nothing @test length(Q) == 3 # Test with default distances diff --git a/test/generators/euclideangraphs.jl b/test/generators/euclideangraphs.jl index 375ee3430..27be1b6e0 100644 --- a/test/generators/euclideangraphs.jl +++ b/test/generators/euclideangraphs.jl @@ -1,7 +1,7 @@ @testset "Euclidean graphs" begin N = 10 d = 2 - g, weights, points = euclidean_graph(N, d) + g, weights, points = @inferred(euclidean_graph(N, d)) @test nv(g) == N @test ne(g) == N*(N-1) ÷ 2 @test (d,N) == size(points) @@ -10,7 +10,7 @@ @test maximum(points) <= 1 @test minimum(points) >= 0. - g, weights, points = euclidean_graph(N, d, bc=:periodic) + g, weights, points = @inferred(euclidean_graph(N, d, bc=:periodic)) @test maximum(x->x[2], weights) <= sqrt(d/2) @test minimum(x->x[2], weights) >= 0. @test maximum(points) <= 1 diff --git a/test/generators/randgraphs.jl b/test/generators/randgraphs.jl index d3b1078c4..9c94a74eb 100644 --- a/test/generators/randgraphs.jl +++ b/test/generators/randgraphs.jl @@ -211,10 +211,10 @@ @test all(bp .>= 0) @test all(bp .!= NaN) end - + numedges = 100 sizes = [10, 10, 10, 10] - + n = sum(sizes) sbm, g = generate_nbp_sbm(numedges, sizes) @test ne(g) >= 0.9numedges @@ -223,7 +223,7 @@ ratios = bp ./ (sbm.affinities ./ sum(sbm.affinities)) test_sbm(sbm, bp) @test norm(collect(ratios)) < 0.25 - + sizes = [200, 200, 100] internaldeg = 15 externaldeg = 6 @@ -241,19 +241,19 @@ test_sbm(sbm, bp) ratios = bp ./ (sbm.affinities ./ sum(sbm.affinities)) @test norm(collect(ratios)) < 0.25 - + # check that average degree is not too high # factor of two is cushion for random process @test mean(degree(g)) <= 4//2*numedges/sum(sizes) # check that the internal degrees are higher than the external degrees # 5//4 is cushion for random process. @test all(sum(bc-diagm(diag(bc)), 1) .<= 5//4 .* diag(bc)) - - + + sbm2 = StochasticBlockModel(0.5*ones(4), 0.3, 10*ones(Int,4)) sbm = StochasticBlockModel(0.5, 0.3, 10, 4) @test sbm == sbm2 sbm.affinities[1,1] = 0 @test sbm != sbm2 - + end diff --git a/test/generators/staticgraphs.jl b/test/generators/staticgraphs.jl index 6766e7e86..2a4836aa8 100644 --- a/test/generators/staticgraphs.jl +++ b/test/generators/staticgraphs.jl @@ -1,45 +1,52 @@ @testset "Static graphs" begin - g = CompleteDiGraph(5) + g = @inferred(CompleteDiGraph(5)) @test nv(g) == 5 && ne(g) == 20 - g = CompleteGraph(5) + g = @inferred(CompleteGraph(5)) @test nv(g) == 5 && ne(g) == 10 - g = CompleteBipartiteGraph(5, 8) + g = @inferred(CompleteBipartiteGraph(5, 8)) @test nv(g) == 13 && ne(g) == 40 - g = StarDiGraph(5) + g = @inferred(StarDiGraph(5)) @test nv(g) == 5 && ne(g) == 4 - g = StarGraph(5) + g = @inferred(StarGraph(5)) @test nv(g) == 5 && ne(g) == 4 - g = StarGraph(1) + g = @inferred(StarGraph(1)) @test nv(g) == 1 && ne(g) == 0 - g = PathDiGraph(5) + g = @inferred(PathDiGraph(5)) @test nv(g) == 5 && ne(g) == 4 - g = PathGraph(5) + g = @inferred(PathGraph(5)) @test nv(g) == 5 && ne(g) == 4 - g = CycleDiGraph(5) + g = @inferred(CycleDiGraph(5)) @test nv(g) == 5 && ne(g) == 5 - g = CycleGraph(5) + g = @inferred(CycleGraph(5)) @test nv(g) == 5 && ne(g) == 5 - g = WheelDiGraph(5) + g = @inferred(WheelDiGraph(5)) @test nv(g) == 5 && ne(g) == 8 - g = WheelGraph(5) + g = @inferred(WheelGraph(5)) @test nv(g) == 5 && ne(g) == 8 - g = Grid([3,3,4]) + g = @inferred(Grid([3,3,4])) @test nv(g) == 3*3*4 @test ne(g) == 75 - @test maximum(degree(g)) == 6 - @test minimum(degree(g)) == 3 + @test Δ(g) == 6 + @test δ(g) == 3 - g = CliqueGraph(3,5) + g = @inferred(Grid([3,3,4], periodic=true)) + @test nv(g) == 3*3*4 + @test ne(g) == 108 + @test Δ(g) == 6 + @test δ(g) == 6 + + + g = @inferred(CliqueGraph(3,5)) @test nv(g) == 15 && ne(g) == 20 @test g[1:3] == CompleteGraph(3) - g = crosspath(3, BinaryTree(2)) + g = @inferred(crosspath(3, BinaryTree(2))) # f = Vector{Vector{Int}}[[2 3 4]; # [1 5]; # [1 6]; @@ -56,7 +63,7 @@ Adj = sparse(I,J,V) @test Adj == sparse(g) - g = DoubleBinaryTree(3) + g = @inferred(DoubleBinaryTree(3)) # [[3,2,8] # [4,1,5] # [1,6,7] @@ -77,7 +84,7 @@ Adj = sparse(I,J,V) @test Adj == sparse(g) - rg3 = RoachGraph(3) + rg3 = @inferred(RoachGraph(3)) # [3] # [4] # [1,5] diff --git a/test/graphtypes/simplegraphs/simpleedgeiter.jl b/test/graphtypes/simplegraphs/simpleedgeiter.jl index a0d146728..5c47bc0ef 100644 --- a/test/graphtypes/simplegraphs/simpleedgeiter.jl +++ b/test/graphtypes/simplegraphs/simpleedgeiter.jl @@ -1,26 +1,26 @@ @testset "SimpleEdgeIter" begin - ga = SimpleGraph(10,20; seed=1) - gb = SimpleGraph(10,20; seed=1) + ga = @inferred(SimpleGraph(10,20; seed=1)) + gb = @inferred(SimpleGraph(10,20; seed=1)) @test sprint(show,edges(ga)) == "SimpleEdgeIter 20" @test sprint(show, start(edges(ga))) == "SimpleEdgeIterState [1, 1, false]" @test length(collect(edges(Graph(0,0)))) == 0 - @test edges(ga) == edges(gb) - @test edges(ga) == collect(Edge, edges(gb)) + @test @inferred(edges(ga)) == edges(gb) + @test @inferred(edges(ga)) == collect(Edge, edges(gb)) @test collect(Edge, edges(gb)) == edges(ga) @test Set{Edge}(collect(Edge, edges(gb))) == edges(ga) - @test edges(ga) == Set{Edge}(collect(Edge, edges(gb))) + @test @inferred(edges(ga)) == Set{Edge}(collect(Edge, edges(gb))) - @test eltype(edges(ga)) == eltype(typeof(edges(ga))) == SimpleEdge + @test @inferred(eltype(edges(ga))) == eltype(typeof(edges(ga))) == SimpleEdge - ga = SimpleDiGraph(10,20; seed=1) - gb = SimpleDiGraph(10,20; seed=1) - @test edges(ga) == edges(gb) - @test edges(ga) == collect(SimpleEdge, edges(gb)) + ga = @inferred(SimpleDiGraph(10,20; seed=1)) + gb = @inferred(SimpleDiGraph(10,20; seed=1)) + @test @inferred(edges(ga)) == edges(gb) + @test @inferred(edges(ga)) == collect(SimpleEdge, edges(gb)) @test collect(SimpleEdge, edges(gb)) == edges(ga) @test Set{Edge}(collect(SimpleEdge, edges(gb))) == edges(ga) - @test edges(ga) == Set{SimpleEdge}(collect(SimpleEdge, edges(gb))) + @test @inferred(edges(ga)) == Set{SimpleEdge}(collect(SimpleEdge, edges(gb))) ga = SimpleGraph(10) add_edge!(ga, 3, 2) @@ -29,7 +29,7 @@ add_edge!(ga, 10, 3) eit = edges(ga) - es = start(eit) + es = @inferred(start(eit)) @test es.s == 2 @test es.di == 1 @@ -42,8 +42,8 @@ add_edge!(ga, 5, 10) add_edge!(ga, 10, 3) - eit = edges(ga) - es = start(eit) + eit = @inferred(edges(ga)) + es = @inferred(start(eit)) @test es.s == 3 @test es.di == 1 diff --git a/test/graphtypes/simplegraphs/simplegraphs.jl b/test/graphtypes/simplegraphs/simplegraphs.jl index f1037e07e..57d228740 100644 --- a/test/graphtypes/simplegraphs/simplegraphs.jl +++ b/test/graphtypes/simplegraphs/simplegraphs.jl @@ -5,22 +5,22 @@ struct DummySimpleGraph <: AbstractSimpleGraph end adjmx1 = [0 1 0; 1 0 1; 0 1 0] # graph adjmx2 = [0 1 0; 1 0 1; 1 1 0] # digraph # specific concrete generators - no need for loop - @test eltype(SimpleGraph()) == Int - @test eltype(SimpleGraph(adjmx1)) == Int + @test @inferred(eltype(SimpleGraph())) == Int + @test @inferred(eltype(SimpleGraph(adjmx1))) == Int @test_throws ErrorException SimpleGraph(adjmx2) - @test ne(SimpleGraph(PathDiGraph(5))) == 4 - @test !is_directed(SimpleGraph) + @test @inferred(ne(SimpleGraph(PathDiGraph(5)))) == 4 + @test @inferred(!is_directed(SimpleGraph)) - @test eltype(SimpleDiGraph()) == Int - @test eltype(SimpleDiGraph(adjmx2)) == Int - @test ne(SimpleDiGraph(PathGraph(5))) == 8 - @test is_directed(SimpleDiGraph) + @test @inferred(eltype(SimpleDiGraph())) == Int + @test @inferred(eltype(SimpleDiGraph(adjmx2))) == Int + @test @inferred(ne(SimpleDiGraph(PathGraph(5)))) == 8 + @test @inferred(is_directed(SimpleDiGraph)) for gbig in [Graph(0xff), DiGraph(0xff)] - @test !add_vertex!(gbig) # overflow - @test !add_vertices!(gbig, 10) + @test @inferred(!add_vertex!(gbig)) # overflow + @test @inferred(!add_vertices!(gbig, 10)) end gdx = PathDiGraph(4) @@ -28,122 +28,122 @@ struct DummySimpleGraph <: AbstractSimpleGraph end for g in testgraphs(gx) T = eltype(g) @test sprint(show, g) == "empty undirected simple $T graph" - add_vertices!(g, 5) + @inferred(add_vertices!(g, 5)) @test sprint(show, g) == "{5, 0} undirected simple $T graph" end gx = SimpleDiGraph() for g in testdigraphs(gx) T = eltype(g) @test sprint(show, g) == "empty directed simple $T graph" - add_vertices!(g, 5) + @inferred(add_vertices!(g, 5)) @test sprint(show, g) == "{5, 0} directed simple $T graph" end gx = PathGraph(4) for g in testgraphs(gx) - @test vertices(g) == 1:4 + @test @inferred(vertices(g)) == 1:4 @test Edge(2,3) in edges(g) - @test nv(g) == 4 - @test fadj(g) == badj(g) == adj(g) == g.fadjlist - @test fadj(g,2) == badj(g,2) == adj(g,2) == g.fadjlist[2] + @test @inferred(nv(g)) == 4 + @test @inferred(fadj(g)) == badj(g) == adj(g) == g.fadjlist + @test @inferred(fadj(g,2)) == badj(g,2) == adj(g,2) == g.fadjlist[2] - @test has_edge(g, 2, 3) - @test has_edge(g, 3, 2) + @test @inferred(has_edge(g, 2, 3)) + @test @inferred(has_edge(g, 3, 2)) gc = copy(g) @test add_edge!(gc, 4, 1) && gc == CycleGraph(4) - @test in_neighbors(g, 2) == out_neighbors(g, 2) == neighbors(g,2) == [1,3] - @test add_vertex!(gc) # out of order, but we want it for issubset - @test g ⊆ gc - @test has_vertex(gc, 5) + @test @inferred(in_neighbors(g, 2)) == @inferred(out_neighbors(g, 2)) == @inferred(neighbors(g,2)) == [1,3] + @test @inferred(add_vertex!(gc)) # out of order, but we want it for issubset + @test @inferred(g ⊆ gc) + @test @inferred(has_vertex(gc, 5)) - @test ne(g) == 3 + @test @inferred(ne(g)) == 3 - @test rem_edge!(gc, 1, 2) && !has_edge(gc, 1, 2) - ga = copy(g) - @test rem_vertex!(ga, 2) && ne(ga) == 1 - @test !rem_vertex!(ga, 10) + @test @inferred(rem_edge!(gc, 1, 2)) && @inferred(!has_edge(gc, 1, 2)) + ga = @inferred(copy(g)) + @test @inferred(rem_vertex!(ga, 2)) && ne(ga) == 1 + @test @inferred(!rem_vertex!(ga, 10)) - @test empty(g) == SimpleGraph{eltype(g)}() + @test @inferred(zero(g)) == SimpleGraph{eltype(g)}() # concrete tests below - @test eltype(g) == eltype(fadj(g,1)) == eltype(nv(g)) - T = eltype(g) - @test nv(SimpleGraph{T}(6)) == 6 + @test @inferred(eltype(g)) == eltype(fadj(g,1)) == eltype(nv(g)) + T = @inferred(eltype(g)) + @test @inferred(nv(SimpleGraph{T}(6))) == 6 - @test eltype(SimpleGraph(T)) == T - @test eltype(SimpleGraph{T}(adjmx1)) == T + @test @inferred(eltype(SimpleGraph(T))) == T + @test @inferred(eltype(SimpleGraph{T}(adjmx1))) == T ga = SimpleGraph(10) - @test eltype(SimpleGraph{T}(ga)) == T + @test @inferred(eltype(SimpleGraph{T}(ga))) == T for gd in testdigraphs(gdx) U = eltype(gd) - @test eltype(SimpleGraph(gd)) == U + @test @inferred(eltype(SimpleGraph(gd))) == U end - @test edgetype(g) == SimpleGraphEdge{T} - @test copy(g) == g - @test !is_directed(g) + @test @inferred(edgetype(g)) == SimpleGraphEdge{T} + @test @inferred(copy(g)) == g + @test @inferred(!is_directed(g)) e = first(edges(g)) - @test has_edge(g, e) + @test @inferred(has_edge(g, e)) end gdx = PathDiGraph(4) for g in testdigraphs(gdx) - @test vertices(g) == 1:4 + @test @inferred(vertices(g)) == 1:4 @test Edge(2,3) in edges(g) @test !(Edge(3,2) in edges(g)) - @test nv(g) == 4 - @test fadj(g)[2] == fadj(g, 2) == [3] - @test badj(g)[2] == badj(g, 2) == [1] + @test @inferred(nv(g)) == 4 + @test @inferred(fadj(g)[2]) == fadj(g, 2) == [3] + @test @inferred(badj(g)[2]) == badj(g, 2) == [1] @test_throws MethodError adj(g) - @test has_edge(g, 2, 3) - @test !has_edge(g, 3, 2) - gc = copy(g) - @test add_edge!(gc, 4, 1) && gc == CycleDiGraph(4) + @test @inferred(has_edge(g, 2, 3)) + @test @inferred(!has_edge(g, 3, 2)) + gc = @inferred(copy(g)) + @test @inferred(add_edge!(gc, 4, 1)) && gc == CycleDiGraph(4) - @test in_neighbors(g, 2) == [1] - @test out_neighbors(g, 2) == neighbors(g,2) == [3] - @test add_vertex!(gc) # out of order, but we want it for issubset - @test g ⊆ gc - @test has_vertex(gc, 5) + @test @inferred(in_neighbors(g, 2)) == [1] + @test @inferred(out_neighbors(g, 2)) == @inferred(neighbors(g,2)) == [3] + @test @inferred(add_vertex!(gc)) # out of order, but we want it for issubset + @test @inferred(g ⊆ gc) + @test @inferred(has_vertex(gc, 5)) - @test ne(g) == 3 + @test @inferred(ne(g)) == 3 - @test !rem_edge!(gc, 2, 1) - @test rem_edge!(gc, 1, 2) && !has_edge(gc, 1, 2) - ga = copy(g) - @test rem_vertex!(ga, 2) && ne(ga) == 1 - @test !rem_vertex!(ga, 10) + @test @inferred(!rem_edge!(gc, 2, 1)) + @test @inferred(rem_edge!(gc, 1, 2)) && @inferred(!has_edge(gc, 1, 2)) + ga = @inferred(copy(g)) + @test @inferred(rem_vertex!(ga, 2)) && ne(ga) == 1 + @test @inferred(!rem_vertex!(ga, 10)) - @test empty(g) == SimpleDiGraph{eltype(g)}() + @test @inferred(zero(g)) == SimpleDiGraph{eltype(g)}() # concrete tests below - @test eltype(g) == eltype(fadj(g,1)) == eltype(nv(g)) - T = eltype(g) - @test nv(SimpleDiGraph{T}(6)) == 6 + @test @inferred(eltype(g)) == eltype(@inferred(fadj(g,1))) == eltype(nv(g)) + T = @inferred(eltype(g)) + @test @inferred(nv(SimpleDiGraph{T}(6))) == 6 - @test eltype(SimpleDiGraph(T)) == T - @test eltype(SimpleDiGraph{T}(adjmx2)) == T + @test @inferred(eltype(SimpleDiGraph(T))) == T + @test @inferred(eltype(SimpleDiGraph{T}(adjmx2))) == T ga = SimpleDiGraph(10) - @test eltype(SimpleDiGraph{T}(ga)) == T + @test @inferred(eltype(SimpleDiGraph{T}(ga))) == T for gu in testgraphs(gx) - U = eltype(gu) - @test eltype(SimpleDiGraph(gu)) == U + U = @inferred(eltype(gu)) + @test @inferred(eltype(SimpleDiGraph(gu))) == U end - @test edgetype(g) == SimpleDiGraphEdge{T} - @test copy(g) == g - @test is_directed(g) + @test @inferred(edgetype(g)) == SimpleDiGraphEdge{T} + @test @inferred(copy(g)) == g + @test @inferred(is_directed(g)) - e = first(edges(g)) - @test has_edge(g, e) + e = first(@inferred(edges(g))) + @test @inferred(has_edge(g, e)) end end diff --git a/test/interface.jl b/test/interface.jl index e9b488714..074b63552 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -17,7 +17,7 @@ mutable struct DummyEdge <: AbstractEdge end for graphfunbasic in [ nv, ne, vertices, edges, is_directed, - add_vertex!, edgetype, eltype, empty + add_vertex!, edgetype, eltype, zero ] @test_throws ErrorException graphfunbasic(dummygraph) end diff --git a/test/linalg/spectral.jl b/test/linalg/spectral.jl index 43c133839..91f69bb9b 100644 --- a/test/linalg/spectral.jl +++ b/test/linalg/spectral.jl @@ -58,7 +58,7 @@ full(nbt::Nonbacktracking) = full(sparse(nbt)) end for g in testdigraphs(g5) - @test (adjacency_spectrum(g))[3] ≈ 0.311 atol=0.001 + @test adjacency_spectrum(g)[3] ≈ 0.311 atol=0.001 end for g in testgraphs(g3) diff --git a/test/operators.jl b/test/operators.jl index 1947ac197..c193bd845 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -4,28 +4,28 @@ for g in testlargegraphs(g3) T = eltype(g) - c = complement(g) + c = @inferred(complement(g)) @test nv(c) == 5 @test ne(c) == 6 - gb = blkdiag(g, g) + gb = @inferred(blkdiag(g, g)) @test nv(gb) == 10 @test ne(gb) == 8 hp = PathGraph(2) h = Graph{T}(hp) - @test intersect(g, h) == h + @test @inferred(intersect(g, h)) == h hp = PathGraph(4) h = Graph{T}(hp) - z = difference(g, h) + z = @inferred(difference(g, h)) @test nv(z) == 5 @test ne(z) == 1 - z = difference(h, g) + z = @inferred(difference(h, g)) @test nv(z) == 4 @test ne(z) == 0 - z = symmetric_difference(h,g) + z = @inferred(symmetric_difference(h,g)) @test z == symmetric_difference(g,h) @test nv(z) == 5 @test ne(z) == 1 @@ -34,13 +34,13 @@ add_edge!(h, 5, 6) e = SimpleEdge(5, 6) - z = union(g, h) + z = @inferred(union(g, h)) @test has_edge(z, e) @test z == PathGraph(6) end for g in testlargedigraphs(g4) T = eltype(g) - c = complement(g) + c = @inferred(complement(g)) @test nv(c) == 5 @test ne(c) == 16 @@ -48,17 +48,17 @@ add_edge!(h, 5, 6) e = SimpleEdge(5, 6) - z = union(g, h) + z = @inferred(union(g, h)) @test has_edge(z, e) @test z == PathDiGraph(6) end re1 = Edge(2, 1) - gr = reverse(g4) + gr = @inferred(reverse(g4)) for g in testdigraphs(gr) T = eltype(g) @test re1 in edges(g) - reverse!(g) + @inferred(reverse!(g)) @test g == DiGraph{T}(g4) end @@ -68,7 +68,7 @@ T = eltype(g) hc = CompleteGraph(2) h = Graph{T}(hc) - z = blkdiag(g, h) + z = @inferred(blkdiag(g, h)) @test nv(z) == nv(g) + nv(h) @test ne(z) == ne(g) + ne(h) @test has_edge(z, 1, 2) @@ -83,7 +83,7 @@ for g in testgraphs(gx) T = eltype(g) h = Graph{T}(2) - z = join(g, h) + z = @inferred(join(g, h)) @test nv(z) == nv(g) + nv(h) @test ne(z) == 4 @test !has_edge(z, 1, 2) @@ -96,8 +96,8 @@ px = PathGraph(10) for p in testgraphs(px) - x = p*ones(10) - @test x[1]==1.0 && all(x[2:end-1].==2.0) && x[end]==1.0 + x = @inferred(p*ones(10)) + @test x[1] ==1.0 && all(x[2:end-1].==2.0) && x[end]==1.0 @test size(p) == (10,10) @test size(p, 1) == size(p, 2) == 10 @test size(p, 3) == 1 @@ -112,11 +112,11 @@ gx = DiGraph(4) add_edge!(gx,1,2); add_edge!(gx,2,3); add_edge!(gx,1,3); add_edge!(gx,3,4) for g in testdigraphs(gx) - @test g * ones(nv(g)) == [2.0, 1.0, 1.0, 0.0] + @test @inferred(g * ones(nv(g))) == [2.0, 1.0, 1.0, 0.0] @test sum(g, 1) == [0, 1, 2, 1] @test sum(g, 2) == [2, 1, 1, 0] @test sum(g) == 4 - @test !issymmetric(g) + @test @inferred(!issymmetric(g)) end nx = 20; ny = 21 @@ -125,8 +125,8 @@ T = eltype(g) hp = PathGraph(nx) h = Graph{T}(hp) - c = cartesian_product(g, h) - gz = crosspath(ny, PathGraph(nx)); + c = @inferred(cartesian_product(g, h)) + gz = @inferred(crosspath(ny, PathGraph(nx))) @test gz == c end function crosspath_slow(len, h) @@ -144,11 +144,11 @@ gx = CompleteGraph(2) for g in testgraphs(gx) - h = cartesian_product(g, g) + h = @inferred(cartesian_product(g, g)) @test nv(h) == 4 @test ne(h) == 4 - h = tensor_product(g, g) + h = @inferred(tensor_product(g, g)) @test nv(h) == 4 @test ne(h) == 1 end @@ -162,15 +162,15 @@ gb = smallgraph(:bull) for g in testgraphs(gb) n = 3 - h = g[1:n] + h = @inferred(g[1:n]) @test nv(h) == n @test ne(h) == 3 - h = g[[1,2,4]] + h = @inferred(g[[1,2,4]]) @test nv(h) == n @test ne(h) == 2 - h = g[[1,5]] + h = @inferred(g[[1,5]]) @test nv(h) == 2 @test ne(h) == 0 @test typeof(h) == typeof(g) @@ -178,13 +178,13 @@ gx = DiGraph(100,200) for g in testdigraphs(gx) - h = g[5:26] + h = @inferred(g[5:26]) @test nv(h) == 22 @test typeof(h) == typeof(g) @test_throws ErrorException g[[1,1]] r = 5:26 - h2, vm = induced_subgraph(g, r) + h2, vm = @inferred(induced_subgraph(g, r)) @test h2 == h @test vm == collect(r) @test h2 == g[r] @@ -192,11 +192,11 @@ g10 = CompleteGraph(10) for g in testgraphs(g10) - sg, vm = induced_subgraph(g, 5:8) + sg, vm = @inferred(induced_subgraph(g, 5:8)) @test nv(sg) == 4 @test ne(sg) == 6 - sg2, vm = induced_subgraph(g, [5,6,7,8]) + sg2, vm = @inferred(induced_subgraph(g, [5,6,7,8])) @test sg2 == sg @test vm[4] == 8 @@ -204,7 +204,7 @@ SimpleEdge(1, 2), SimpleEdge(2, 3), SimpleEdge(3, 4), SimpleEdge(4, 5),SimpleEdge(5, 1) ] - sg, vm = induced_subgraph(g, elist) + sg, vm = @inferred(induced_subgraph(g, elist)) @test sg == CycleGraph(5) @test sort(vm) == [1:5;] end @@ -212,8 +212,8 @@ gs = StarGraph(10) for g in testgraphs(gs) T = eltype(g) - @test egonet(g, 1, 0) == Graph{T}(1) - @test egonet(g, 1, 1) == g - @test ndims(g) == 2 + @test @inferred(egonet(g, 1, 0)) == Graph{T}(1) + @test @inferred(egonet(g, 1, 1)) == g + @test @inferred(ndims(g)) == 2 end end diff --git a/test/shortestpaths/astar.jl b/test/shortestpaths/astar.jl index c59fab47e..014fd9773 100644 --- a/test/shortestpaths/astar.jl +++ b/test/shortestpaths/astar.jl @@ -5,9 +5,9 @@ d1 = float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) d2 = sparse(float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) for g in testgraphs(g3), dg in testdigraphs(g4) - @test a_star(g, 1, 4, d1) == - a_star(dg, 1, 4, d1) == - a_star(g, 1, 4, d2) - @test a_star(dg, 4, 1) == nothing + @test @inferred(a_star(g, 1, 4, d1)) == + @inferred(a_star(dg, 1, 4, d1)) == + @inferred(a_star(g, 1, 4, d2)) + @test isempty(@inferred(a_star(dg, 4, 1))) end end diff --git a/test/shortestpaths/bellman-ford.jl b/test/shortestpaths/bellman-ford.jl index fa7088a36..5da0ac112 100644 --- a/test/shortestpaths/bellman-ford.jl +++ b/test/shortestpaths/bellman-ford.jl @@ -4,20 +4,20 @@ d1 = float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) d2 = sparse(float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) for g in testdigraphs(g4) - y = bellman_ford_shortest_paths(g, 2, d1) - z = bellman_ford_shortest_paths(g, 2, d2) + y = @inferred(bellman_ford_shortest_paths(g, 2, d1)) + z = @inferred(bellman_ford_shortest_paths(g, 2, d2)) @test y.dists == z.dists == [Inf, 0, 6, 17, 33] - @test enumerate_paths(z)[2] == [] - @test enumerate_paths(z)[4] == enumerate_paths(z,4) == [2,3,4] - @test !has_negative_edge_cycle(g) + @test @inferred(enumerate_paths(z))[2] == [] + @test @inferred(enumerate_paths(z))[4] == enumerate_paths(z,4) == [2,3,4] + @test @inferred(!has_negative_edge_cycle(g)) - y = bellman_ford_shortest_paths(g, 2, d1) - z = bellman_ford_shortest_paths(g, 2, d2) + y = @inferred(bellman_ford_shortest_paths(g, 2, d1)) + z = @inferred(bellman_ford_shortest_paths(g, 2, d2)) @test y.dists == z.dists == [Inf, 0, 6, 17, 33] - @test enumerate_paths(z)[2] == [] - @test enumerate_paths(z)[4] == enumerate_paths(z,4) == [2,3,4] - @test !has_negative_edge_cycle(g) - z = bellman_ford_shortest_paths(g, 2) + @test @inferred(enumerate_paths(z))[2] == [] + @test @inferred(enumerate_paths(z))[4] == enumerate_paths(z,4) == [2,3,4] + @test @inferred(!has_negative_edge_cycle(g)) + z = @inferred(bellman_ford_shortest_paths(g, 2)) @test z.dists == [typemax(Int), 0, 1, 2, 3] end diff --git a/test/shortestpaths/dijkstra.jl b/test/shortestpaths/dijkstra.jl index 27ed3f16f..5c1216122 100644 --- a/test/shortestpaths/dijkstra.jl +++ b/test/shortestpaths/dijkstra.jl @@ -4,18 +4,18 @@ d2 = sparse(float([ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) for g in testdigraphs(g4) - y = dijkstra_shortest_paths(g, 2, d1) - z = dijkstra_shortest_paths(g, 2, d2) + y = @inferred(dijkstra_shortest_paths(g, 2, d1)) + z = @inferred(dijkstra_shortest_paths(g, 2, d2)) @test y.parents == z.parents == [0, 0, 2, 3, 4] @test y.dists == z.dists == [Inf, 0, 6, 17, 33] - y = dijkstra_shortest_paths(g, 2, d1; allpaths=true) - z = dijkstra_shortest_paths(g, 2, d2; allpaths=true) + y = @inferred(dijkstra_shortest_paths(g, 2, d1; allpaths=true)) + z = @inferred(dijkstra_shortest_paths(g, 2, d2; allpaths=true)) @test z.predecessors[3] == y.predecessors[3] == [2] - @test enumerate_paths(z) == enumerate_paths(y) - @test enumerate_paths(z)[4] == + @test @inferred(enumerate_paths(z)) == enumerate_paths(y) + @test @inferred(enumerate_paths(z))[4] == enumerate_paths(z,4) == enumerate_paths(y,4) == [2,3,4] end @@ -25,14 +25,14 @@ d = ones(Int, 5,5) d[2,3] = 100 for g in testgraphs(gx) - z = dijkstra_shortest_paths(g,1,d) + z = @inferred(dijkstra_shortest_paths(g,1,d)) @test z.dists == [0, 1, 3, 2, 3] @test z.parents == [0, 1, 4, 2, 4] end # small function to reconstruct the shortest path; I copied it from somewhere, can't find the original source to give the credits # @Beatzekatze on github - spath(target, dijkstraStruct, sourse) = target == sourse ? target : [spath(dijkstraStruct.parents[target], dijkstraStruct, sourse) target] + spath(target, dijkstraStruct, source) = target == source ? target : [spath(dijkstraStruct.parents[target], dijkstraStruct, source) target] function spaths(ds, targets, source) shortest_paths = [] for i in targets @@ -55,7 +55,7 @@ 1. 0. 3. 0.] for g in testgraphs(G) - ds = dijkstra_shortest_paths(g,2,w) + ds = @inferred(dijkstra_shortest_paths(g,2,w)) # this loop reconstructs the shortest path for nodes 1, 3 and 4 @test spaths(ds, [1,3,4], 2) == Array[[2 1], [2 3], @@ -63,7 +63,7 @@ # here a selflink at source is introduced; it should not change the shortest paths w[2,2] = 10.0 - ds = dijkstra_shortest_paths(g,2,w) + ds = @inferred(dijkstra_shortest_paths(g,2,w)) shortest_paths = [] # this loop reconstructs the shortest path for nodes 1, 3 and 4 @test spaths(ds, [1,3,4], 2) == Array[[2 1], diff --git a/test/shortestpaths/floyd-warshall.jl b/test/shortestpaths/floyd-warshall.jl index 6f09c9d34..0d01d2761 100644 --- a/test/shortestpaths/floyd-warshall.jl +++ b/test/shortestpaths/floyd-warshall.jl @@ -2,11 +2,11 @@ g3 = PathGraph(5) d = [ 0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0] for g in testgraphs(g3) - z = floyd_warshall_shortest_paths(g, d) + z = @inferred(floyd_warshall_shortest_paths(g, d)) @test z.dists[3,:][:] == [7, 6, 0, 11, 27] @test z.parents[3,:][:] == [2, 3, 0, 3, 4] - @test enumerate_paths(z)[2][2] == [] - @test enumerate_paths(z)[2][4] == enumerate_paths(z,2)[4] == enumerate_paths(z,2,4) == [2,3,4] + @test @inferred(enumerate_paths(z))[2][2] == [] + @test @inferred(enumerate_paths(z))[2][4] == enumerate_paths(z,2)[4] == enumerate_paths(z,2,4) == [2,3,4] end end diff --git a/test/spanningtrees/kruskal.jl b/test/spanningtrees/kruskal.jl index 5613820f6..7c134290a 100644 --- a/test/spanningtrees/kruskal.jl +++ b/test/spanningtrees/kruskal.jl @@ -11,7 +11,7 @@ vec_mst = Vector{Edge}([Edge(1, 2), Edge(3, 4), Edge(2, 3)]) for g in testgraphs(g4) # Testing Kruskal's algorithm - mst = kruskal_mst(g, distmx) + mst = @inferred(kruskal_mst(g, distmx)) @test mst == vec_mst end #second test @@ -29,7 +29,7 @@ gx = Graph(distmx_sec) vec2 = Vector{Edge}([Edge(1, 8),Edge(3, 4),Edge(2, 8),Edge(1, 3),Edge(6, 8),Edge(5, 6),Edge(3, 7)]) for g in testgraphs(gx) - mst2 = kruskal_mst(g, distmx_sec) + mst2 = @inferred(kruskal_mst(g, distmx_sec)) @test mst2 == vec2 end end diff --git a/test/spanningtrees/prim.jl b/test/spanningtrees/prim.jl index 2d38a5255..5363eb529 100644 --- a/test/spanningtrees/prim.jl +++ b/test/spanningtrees/prim.jl @@ -11,7 +11,7 @@ vec_mst = Vector{Edge}([Edge(1, 2), Edge(2, 3), Edge(3, 4)]) for g in testgraphs(g4) # Testing Prim's algorithm - mst = prim_mst(g, distmx) + mst = @inferred(prim_mst(g, distmx)) @test mst == vec_mst end @@ -30,7 +30,7 @@ vec2 = Vector{Edge}([Edge(1, 8),Edge(2, 8),Edge(1, 3),Edge(3, 4),Edge(6, 8),Edge(5, 6),Edge(3, 7)]) gx = Graph(distmx_sec) for g in testgraphs(gx) - mst2 = prim_mst(g, distmx_sec) + mst2 = @inferred(prim_mst(g, distmx_sec)) @test mst2 == vec2 end end diff --git a/test/transitivity.jl b/test/transitivity.jl index 9cc97c717..af9eb2d4a 100644 --- a/test/transitivity.jl +++ b/test/transitivity.jl @@ -5,10 +5,10 @@ for circle in testdigraphs(circledg) T = eltype(circle) complete = DiGraph{T}(completedg) - newcircle = transitiveclosure(circle) + newcircle = @inferred(transitiveclosure(circle)) @test newcircle == complete @test ne(circle) == 4 - @test newcircle == transitiveclosure!(circle) + @test newcircle == @inferred(transitiveclosure!(circle)) @test ne(circle) == 12 end @@ -21,10 +21,10 @@ for circle in testdigraphs(circledg) T = eltype(circle) loopedcomplete = DiGraph{T}(loopedcompletedg) - newcircle = transitiveclosure(circle, true) + newcircle = @inferred(transitiveclosure(circle, true)) @test newcircle == loopedcomplete @test ne(circle) == 4 - @test newcircle == transitiveclosure!(circle, true) + @test newcircle == @inferred(transitiveclosure!(circle, true)) @test ne(circle) == 16 end end diff --git a/test/traversals/bfs.jl b/test/traversals/bfs.jl index db1947c7e..198ca2f78 100644 --- a/test/traversals/bfs.jl +++ b/test/traversals/bfs.jl @@ -5,7 +5,7 @@ import LightGraphs: TreeBFSVisitorVector, bfs_tree!, tree g6 = smallgraph(:house) for g in testdigraphs(g5) - z = bfs_tree(g, 1) + z = @inferred(bfs_tree(g, 1)) visitor = LightGraphs.TreeBFSVisitorVector(zeros(eltype(g),nv(g))) LightGraphs.bfs_tree!(visitor, g, 1) t = visitor.tree @@ -13,11 +13,11 @@ import LightGraphs: TreeBFSVisitorVector, bfs_tree!, tree @test nv(z) == 4 && ne(z) == 3 && !has_edge(z, 2, 3) end for g in testgraphs(g6) - @test gdistances(g, 2) == [1, 0, 2, 1, 2] - @test gdistances(g, [1,2]) == [0, 0, 1, 1, 2] - @test gdistances(g, []) == [-1, -1, -1, -1, -1] - @test !is_bipartite(g) - @test !is_bipartite(g, 2) + @test @inferred(gdistances(g, 2)) == [1, 0, 2, 1, 2] + @test @inferred(gdistances(g, [1,2])) == [0, 0, 1, 1, 2] + @test @inferred(gdistances(g, [])) == [-1, -1, -1, -1, -1] + @test @inferred(!is_bipartite(g)) + @test @inferred(!is_bipartite(g, 2)) end gx = Graph(5) @@ -26,8 +26,8 @@ import LightGraphs: TreeBFSVisitorVector, bfs_tree!, tree add_edge!(gx,3,4) for g in testgraphs(gx) - @test is_bipartite(g) - @test is_bipartite(g, 2) + @test @inferred(is_bipartite(g)) + @test @inferred(is_bipartite(g, 2)) end @@ -51,10 +51,10 @@ import LightGraphs: TreeBFSVisitorVector, bfs_tree!, tree for g in testgraphs(g6) n = nv(g) - visitor = TreeBFSVisitorVector(n) + visitor = @inferred(TreeBFSVisitorVector(n)) @test length(visitor.tree) == n parents = visitor.tree - bfs_tree!(visitor, g, 1) + @inferred(bfs_tree!(visitor, g, 1)) @test istree(parents, n, n) t = tree(parents) @@ -64,12 +64,12 @@ import LightGraphs: TreeBFSVisitorVector, bfs_tree!, tree # test Dict{Int,Int}() colormap - visitor = TreeBFSVisitorVector(n) + visitor = @inferred(TreeBFSVisitorVector(n)) @test length(visitor.tree) == n parents = visitor.tree - bfs_tree!(visitor, g, 1, vertexcolormap = Dict{Int,Int}()) + @inferred(bfs_tree!(visitor, g, 1, vertexcolormap = Dict{Int,Int}())) - @test istree(parents, n, n) + @test @inferred(istree(parents, n, n)) t = tree(parents) @test is_directed(t) @test typeof(t) <: AbstractGraph @@ -78,15 +78,15 @@ import LightGraphs: TreeBFSVisitorVector, bfs_tree!, tree g10 = CompleteGraph(10) for g in testgraphs(g10) - @test bipartite_map(g) == Vector{eltype(g)}() + @test @inferred(bipartite_map(g)) == Vector{eltype(g)}() end g10 = CompleteBipartiteGraph(10,10) for g in testgraphs(g10) T = eltype(g) - @test bipartite_map(g10) == Vector{T}([ones(T, 10); 2*ones(T, 10)]) + @test @inferred(bipartite_map(g10)) == Vector{T}([ones(T, 10); 2*ones(T, 10)]) h = blkdiag(g,g) - @test bipartite_map(h) == Vector{T}([ones(T, 10); 2*ones(T, 10); ones(T, 10); 2*ones(T, 10)]) + @test @inferred(bipartite_map(h)) == Vector{T}([ones(T, 10); 2*ones(T, 10); ones(T, 10); 2*ones(T, 10)]) end end diff --git a/test/traversals/dfs.jl b/test/traversals/dfs.jl index d2dcb2ff8..a3d684fb0 100644 --- a/test/traversals/dfs.jl +++ b/test/traversals/dfs.jl @@ -2,16 +2,16 @@ g5 = DiGraph(4) add_edge!(g5,1,2); add_edge!(g5,2,3); add_edge!(g5,1,3); add_edge!(g5,3,4) for g in testdigraphs(g5) - z = dfs_tree(g,1) + z = @inferred(dfs_tree(g,1)) @test ne(z) == 3 && nv(z) == 4 @test !has_edge(z, 1, 3) - @test topological_sort_by_dfs(g) == [1, 2, 3, 4] + @test @inferred(topological_sort_by_dfs(g)) == [1, 2, 3, 4] @test !is_cyclic(g) end gx = CycleDiGraph(3) for g in testdigraphs(gx) - @test is_cyclic(g) + @test @inferred(is_cyclic(g)) end end diff --git a/test/traversals/graphvisit.jl b/test/traversals/graphvisit.jl index eb218212d..d6819a2fe 100644 --- a/test/traversals/graphvisit.jl +++ b/test/traversals/graphvisit.jl @@ -5,8 +5,8 @@ f = IOBuffer() - @test traverse_graph_withlog(g, BreadthFirst(), [1;], f) == nothing - @test visited_vertices(g, BreadthFirst(), [1;]) == [1, 2, 3, 4, 5] + @test @inferred(traverse_graph_withlog(g, BreadthFirst(), [1;], f)) == nothing + @test @inferred(visited_vertices(g, BreadthFirst(), [1;])) == [1, 2, 3, 4, 5] function trivialgraphvisit( @@ -15,17 +15,17 @@ sources ) visitor = TrivialGraphVisitor() - traverse_graph!(g, alg, sources, visitor) + @inferred(traverse_graph!(g, alg, sources, visitor)) end @test trivialgraphvisit(g, BreadthFirst(), 1) == nothing # this just exercises some graph visitors - @test traverse_graph!(g, BreadthFirst(), 1, TrivialGraphVisitor()) == nothing - @test traverse_graph!(g, BreadthFirst(), 1, LogGraphVisitor(IOBuffer())) == nothing + @test @inferred(traverse_graph!(g, BreadthFirst(), 1, TrivialGraphVisitor())) == nothing + @test @inferred(traverse_graph!(g, BreadthFirst(), 1, LogGraphVisitor(IOBuffer()))) == nothing end # dummy edge map test - d = LightGraphs.DummyEdgeMap() + d = @inferred(LightGraphs.DummyEdgeMap()) e = Edge(1,2) @test d[e] == 0 end diff --git a/test/traversals/maxadjvisit.jl b/test/traversals/maxadjvisit.jl index 2755419bf..1d15f8863 100644 --- a/test/traversals/maxadjvisit.jl +++ b/test/traversals/maxadjvisit.jl @@ -32,20 +32,19 @@ @test nv(g) == 8 @test ne(g) == m - parity, bestcut = mincut(g, eweights) + parity, bestcut = @inferred(mincut(g, eweights)) @test length(parity) == 8 @test parity == [2, 2, 1, 1, 2, 2, 1, 1] @test bestcut == 4.0 - parity, bestcut = mincut(g) + parity, bestcut = @inferred(mincut(g)) @test length(parity) == 8 @test parity == [2, 1, 1, 1, 1, 1, 1, 1] @test bestcut == 2.0 - v = maximum_adjacency_visit(g) - + v = @inferred(maximum_adjacency_visit(g)) @test v == Vector{Int64}([1, 2, 5, 6, 3, 7, 4, 8]) end end diff --git a/test/traversals/randomwalks.jl b/test/traversals/randomwalks.jl index 56bb24b04..9f372e3ee 100644 --- a/test/traversals/randomwalks.jl +++ b/test/traversals/randomwalks.jl @@ -19,7 +19,7 @@ Used only for testing and debugging. """ function test_nbw(g, start, len) - w = non_backtracking_randomwalk(g, start, len) + w = @inferred(non_backtracking_randomwalk(g, start, len)) if is_nonbacktracking(w) return true else @@ -29,36 +29,36 @@ end gx = PathDiGraph(10) for g in testdigraphs(gx) - @test randomwalk(g, 1, 5) == [1:5;] - @test randomwalk(g, 2, 100) == [2:10;] + @test @inferred(randomwalk(g, 1, 5)) == [1:5;] + @test @inferred(randomwalk(g, 2, 100)) == [2:10;] @test_throws BoundsError randomwalk(g, 20, 20) - @test non_backtracking_randomwalk(g, 10, 20) == [10] - @test non_backtracking_randomwalk(g, 1, 20) == [1:10;] + @test @inferred(non_backtracking_randomwalk(g, 10, 20)) == [10] + @test @inferred(non_backtracking_randomwalk(g, 1, 20)) == [1:10;] end gx = PathGraph(10) for g in testgraphs(gx) - @test saw(g, 1, 20) == [1:10;] + @test @inferred(saw(g, 1, 20)) == [1:10;] @test_throws BoundsError saw(g, 20, 20) - @test non_backtracking_randomwalk(g, 1, 20) == [1:10;] + @test @inferred(non_backtracking_randomwalk(g, 1, 20)) == [1:10;] @test_throws BoundsError non_backtracking_randomwalk(g, 20, 20) end gx = DiGraph(PathGraph(10)) for g in testdigraphs(gx) - @test non_backtracking_randomwalk(g, 1, 20) == [1:10;] + @test @inferred(non_backtracking_randomwalk(g, 1, 20)) == [1:10;] @test_throws BoundsError non_backtracking_randomwalk(g, 20, 20) end gx = CycleGraph(10) for g in testgraphs(gx) - visited = non_backtracking_randomwalk(g, 1, 20) + visited = @inferred(non_backtracking_randomwalk(g, 1, 20)) @test visited == [1:10; 1:10;] || visited == [1; 10:-1:1; 10:-1:2;] end gx = CycleDiGraph(10) for g in testdigraphs(gx) - @test non_backtracking_randomwalk(g, 1, 20) == [1:10; 1:10;] + @test @inferred(non_backtracking_randomwalk(g, 1, 20)) == [1:10; 1:10;] end n = 10 diff --git a/test/utils.jl b/test/utils.jl index 283278207..ae8a4f117 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -1,17 +1,17 @@ @testset "Utils" begin - s = LightGraphs.sample!([1:10;], 3) + s = @inferred(LightGraphs.sample!([1:10;], 3)) @test length(s) == 3 for e in s @test 1 <= e <= 10 end - s = LightGraphs.sample!([1:10;], 6, exclude=[1,2]) + s = @inferred(LightGraphs.sample!([1:10;], 6, exclude=[1,2])) @test length(s) == 6 for e in s @test 3 <= e <= 10 end - s = LightGraphs.sample(1:10, 6, exclude=[1,2]) + s = @inferred(LightGraphs.sample(1:10, 6, exclude=[1,2])) @test length(s) == 6 for e in s @test 3 <= e <= 10 From e6ce33d8142a61c0c1ece2021fbca06b8e6bfc21 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Wed, 22 Mar 2017 11:08:51 -0700 Subject: [PATCH 39/56] redo graphmatrices tests --- test/linalg/graphmatrices.jl | 53 ++++++++++++------------------------ 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/test/linalg/graphmatrices.jl b/test/linalg/graphmatrices.jl index 05baf912d..d747cac1e 100644 --- a/test/linalg/graphmatrices.jl +++ b/test/linalg/graphmatrices.jl @@ -166,36 +166,20 @@ export test_adjacency, test_laplacian, test_accessors, test_arithmetic, test_oth @test norm(z) ≈ 0.0 atol=1e-8 end - begin - n = 10 - mat = sparse(spones(sprand(n,n,0.3))) - begin - test_adjacency(mat) - end - - begin - test_laplacian(mat) - end - begin - test_accessors(mat, n) - end - end + n = 10 + mat = sparse(spones(sprand(n,n,0.3))) + test_adjacency(mat) + test_laplacian(mat) + test_accessors(mat, n) - begin - n = 10 - mat = symmetrize(sparse(spones(sprand(n,n,0.3)))) - test_arithmetic(mat, n) - end + mat = symmetrize(sparse(spones(sprand(n,n,0.3)))) + test_arithmetic(mat, n) + test_other(mat, n) + test_symmetry(mat, n) + test_punchedmatrix(mat, n) - begin - n = 10 - mat = symmetrize(sparse(spones(sprand(n,n,0.3)))) - test_other(mat, n) - test_symmetry(mat, n) - test_punchedmatrix(mat, n) - end """Computes the stationary distribution of a random walk""" @@ -218,14 +202,11 @@ export test_adjacency, test_laplacian, test_accessors, test_arithmetic, test_oth end # Random walk demo - begin - n = 100 - p = 16/n - M = sprand(n,n, p) - M.nzval[:] = 1.0 - A = CombinatorialAdjacency(M) - sd = stationarydistribution(A; ncv=10) - @test all(sd.>=0) - end - end + n = 100 + p = 16/n + M = sprand(n,n, p) + M.nzval[:] = 1.0 + A = CombinatorialAdjacency(M) + sd = stationarydistribution(A; ncv=10) + @test all(sd.>=0) end From 9df52ad1dd089203dd3733c8237ce8e0551dffe7 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Wed, 22 Mar 2017 11:15:28 -0700 Subject: [PATCH 40/56] linalg test fix --- test/linalg/graphmatrices.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/linalg/graphmatrices.jl b/test/linalg/graphmatrices.jl index d747cac1e..7c9cbc154 100644 --- a/test/linalg/graphmatrices.jl +++ b/test/linalg/graphmatrices.jl @@ -210,3 +210,4 @@ export test_adjacency, test_laplacian, test_accessors, test_arithmetic, test_oth sd = stationarydistribution(A; ncv=10) @test all(sd.>=0) end +end From 91df2ab7cad70c0a9d2239195a2d33fc3d00ef2e Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Wed, 22 Mar 2017 11:39:48 -0700 Subject: [PATCH 41/56] loosen type restrictions in randgraphs functions --- src/generators/randgraphs.jl | 58 ++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 5444f7668..70dd15cb3 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -260,7 +260,7 @@ end """ -static_fitness_model{T<:Real}(m::Int, fitness::Vector{T}; seed::Int=-1) +static_fitness_model{T<:Real}(m::Integer, fitness::Vector{T}; seed::Int=-1) Generates a random graph with `length(fitness)` nodes and `m` edges, in which the probability of the existence of edge `(i, j)` is proportional @@ -271,7 +271,7 @@ Reference: * 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::Int, fitness::Vector{T}; seed::Int=-1) where T<:Real +function static_fitness_model(m::Integer, fitness::Vector{T}; seed::Int=-1) where T<:Real @assert(m >= 0, "invalid number of edges") n = length(fitness) m == 0 && return Graph(n) @@ -291,7 +291,7 @@ function static_fitness_model(m::Int, fitness::Vector{T}; seed::Int=-1) where T< return g end -function static_fitness_model(m::Int, fitness_out::Vector{T}, fitness_in::Vector{S}; seed::Int=-1) where T<:Real where S<:Real +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) @assert(length(fitness_in) == n, "fitness_in must have the same size as fitness_out") @@ -315,7 +315,7 @@ function static_fitness_model(m::Int, fitness_out::Vector{T}, fitness_in::Vector return g end -function _create_static_fitness_graph!(g::AbstractGraph, m::Int, cum_fitness_out::Vector{T}, cum_fitness_in::Vector{S}, seed::Int) where T<:Real where S<:Real +function _create_static_fitness_graph!(g::AbstractGraph, m::Integer, cum_fitness_out::Vector{T}, cum_fitness_in::Vector{S}, seed::Int) where T<:Real where S<:Real rng = getRNG(seed) max_out = cum_fitness_out[end] max_in = cum_fitness_in[end] @@ -332,7 +332,7 @@ function _create_static_fitness_graph!(g::AbstractGraph, m::Int, cum_fitness_out end """ -function static_scale_free(n::Int, m::Int, α::Float64; seed::Int=-1, finite_size_correction::Bool=true) +function static_scale_free(n::Integer, m::Integer, α::Real; seed::Int=-1, finite_size_correction::Bool=true) Generates a random graph with `n` vertices, `m` edges and expected power-law degree distribution with exponent `α`. `finite_size_correction` determines @@ -348,14 +348,14 @@ References: * 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::Int, m::Int, α::Float64; seed::Int=-1, finite_size_correction::Bool=true) +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(α >= 2, "out-degree exponent must be >= 2") fitness = _construct_fitness(n, α, finite_size_correction) static_fitness_model(m, fitness, seed=seed) end -function static_scale_free(n::Int, m::Int, α_out::Float64, α_in::Float64; seed::Int=-1, finite_size_correction::Bool=true) +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") @assert(α_in >= 2, "in-degree exponent must be >= 2") @@ -367,7 +367,7 @@ function static_scale_free(n::Int, m::Int, α_out::Float64, α_in::Float64; seed static_fitness_model(m, fitness_out, fitness_in, seed=seed) end -function _construct_fitness(n::Int, α::Float64, finite_size_correction::Bool) +function _construct_fitness(n::Integer, α::Real, finite_size_correction::Bool) α = -1/(α-1) fitness = zeros(n) j = float(n) @@ -394,7 +394,7 @@ 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. """ -function random_regular_graph(n::Int, k::Int; seed::Int=-1) +function random_regular_graph(n::Integer, k::Integer; seed::Int=-1) @assert(iseven(n*k), "n * k must be even") @assert(0 <= k < n, "the 0 <= k < n inequality must be satisfied") if k == 0 @@ -420,7 +420,7 @@ function random_regular_graph(n::Int, k::Int; seed::Int=-1) end doc""" -random_configuration_model(n::Int, k::Array{Int}; seed=-1, check_graphical=false) +random_configuration_model(n::Integer, k::Array{Integer}; seed=-1, check_graphical=false) Creates a random undirected graph according to the [configuration model] (http://tuvalu.santafe.edu/~aaronc/courses/5352/fall2013/csci5352_2013_L11.pdf). @@ -432,7 +432,7 @@ approximately $nc^2$ time. If `check_graphical=true` makes sure that `k` is a graphical sequence (see `isgraphical`). """ -function random_configuration_model(n::Integer, k::Array{Int}; seed::Int=-1, check_graphical::Bool=false) +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") m = sum(k) @assert(iseven(m), "sum(k) must be even") @@ -456,7 +456,7 @@ function random_configuration_model(n::Integer, k::Array{Int}; seed::Int=-1, che end doc""" -random_regular_digraph(n::Integer, k::Int; dir::Symbol=:out, seed=-1) +random_regular_digraph(n::Integer, k::Integer; dir::Symbol=:out, seed=-1) Creates a random directed [regular graph](https://en.wikipedia.org/wiki/Regular_graph) with `n` vertices, @@ -466,7 +466,7 @@ specified using `dir=:in` or `dir=:out`. The default is `dir=:out`. For directed graphs, 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::Int, k::Int; dir::Symbol=:out, seed::Int=-1) +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 # of a local rng @assert(0 <= k < n, "the 0 <= k < n inequality must be satisfied") @@ -497,8 +497,8 @@ function random_regular_digraph(n::Int, k::Int; dir::Symbol=:out, seed::Int=-1) end doc""" -stochastic_block_model(c::Matrix{Float64}, n::Vector{Int}; seed::Int = -1) -stochastic_block_model(cin::Float64, coff::Float64, n::Vector{Int}; seed::Int = -1) +stochastic_block_model(c::Matrix{Real}, n::Vector{Integer}; seed::Int = -1) +stochastic_block_model(cin::Real, coff::Float64, n::Vector{Integer}; seed::Int = -1) Returns a Graph generated according to the Stochastic Block Model (SBM). @@ -512,7 +512,7 @@ The second form samples from a SBM with `c[a,a]=cin`, and `c[a,b]=coff`. For a dynamic version of the SBM see the `StochasticBlockModel` type and related functions. """ -function stochastic_block_model(c::Matrix{T}, n::Vector{Int}; seed::Int = -1) where T<:Real +function stochastic_block_model(c::Matrix{T}, n::Vector{U}; seed::Int = -1) where T<:Real where U<:Integer @assert size(c,1) == length(n) @assert size(c,2) == length(n) # init dsfmt generator without altering GLOBAL_RNG @@ -548,7 +548,7 @@ function stochastic_block_model(c::Matrix{T}, n::Vector{Int}; seed::Int = -1) wh return g end -function stochastic_block_model(cint::T, cext::T, n::Vector{Int}; seed::Int=-1) where T<:Real +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] stochastic_block_model(c, n, seed=seed) @@ -606,25 +606,25 @@ end """Produce the sbm affinity matrix where the external probabilities are the same the internal probabilities and sizes differ by blocks. """ -function sbmaffinity(internalp::Vector{Float64}, externalp::Float64, sizes::Vector{Int}) +function sbmaffinity(internalp::Vector{T}, externalp::Real, sizes::Vector{U}) where T<:Real where U<:Integer numblocks = length(sizes) numblocks == length(internalp) || error("Inconsistent input dimensions: internalp, sizes") B = diagm(internalp) + externalp*(ones(numblocks, numblocks)-I) return B end -function StochasticBlockModel(internalp::Float64, - externalp::Float64, - size::Int, - numblocks::Int; +function StochasticBlockModel(internalp::Real, + externalp::Real, + size::Integer, + numblocks::Integer; seed::Int = -1) sizes = fill(size, numblocks) B = sbmaffinity(fill(internalp, numblocks), externalp, sizes) StochasticBlockModel(sizes, B, seed=seed) end -function StochasticBlockModel(internalp::Vector{Float64}, externalp::Float64 - , sizes::Vector{Int}; seed::Int = -1) +function StochasticBlockModel(internalp::Vector{T}, externalp::Real + , sizes::Vector{U}; seed::Int = -1) where T<:Real where U<:Integer B = sbmaffinity(internalp, externalp, sizes) return StochasticBlockModel(sizes, B, seed=seed) end @@ -640,13 +640,13 @@ 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` The blocks are connected with probability `between`. """ -function nearbipartiteaffinity(sizes::Vector{Int}, between::Float64, intra::Float64) +function nearbipartiteaffinity(sizes::Vector{T}, between::Real, intra::Real) where T<:Integer numblocks = div(length(sizes), 2) return kron(between*eye(numblocks), biclique) + eye(2numblocks)*intra end """Return a generator for edges from a stochastic block model near-bipartite graph.""" -function nearbipartiteaffinity(sizes::Vector{Int}, between::Float64, inter::Float64, noise::Real) +function nearbipartiteaffinity(sizes::Vector{T}, between::Real, inter::Real, noise::Real) where T<:Integer B = nearbipartiteaffinity(sizes, between, inter) + noise # info("Affinities are:\n$B")#, file=stderr) return B @@ -658,7 +658,7 @@ end """Generates a stream of random pairs in 1:n""" -function random_pair(rng::AbstractRNG, n::Int) +function random_pair(rng::AbstractRNG, n::Integer) f(ch) = begin while true put!(ch, Edge(rand(rng, 1:n), rand(rng, 1:n))) @@ -691,7 +691,7 @@ function make_edgestream(sbm::StochasticBlockModel) return Channel(edges, ctype=Edge, csize=32) end -function Graph(nvg::Int, neg::Int, edgestream::Channel) +function Graph(nvg::Integer, neg::Integer, edgestream::Channel) g = Graph(nvg) # println(g) for e in edgestream @@ -703,7 +703,7 @@ function Graph(nvg::Int, neg::Int, edgestream::Channel) return g end -Graph(nvg::Int, neg::Int, sbm::StochasticBlockModel) = +Graph(nvg::Integer, neg::Integer, sbm::StochasticBlockModel) = Graph(nvg, neg, make_edgestream(sbm)) """counts the number of edges that go between each block""" From ce00535bda0f4378e818b491a6b50693ef0c55da Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Wed, 22 Mar 2017 19:07:39 -0700 Subject: [PATCH 42/56] readall -> readstring, and comment rationalization in randgraphs --- src/generators/randgraphs.jl | 33 ++++++++++++++++++--------------- src/persistence/dot.jl | 4 ++-- src/persistence/gml.jl | 4 ++-- src/persistence/graphml.jl | 4 ++-- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 70dd15cb3..244655bf9 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -35,7 +35,7 @@ end erdos_renyi(n::Integer, p::Real; is_directed=false, seed=-1) erdos_renyi(n::Integer, ne::Integer; is_directed=false, seed=-1) -Creates an [Erdős–Rényi](http://en.wikipedia.org/wiki/Erdős–Rényi_model) +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. @@ -59,7 +59,8 @@ function erdos_renyi(n::Integer, ne::Integer; is_directed=false, seed::Integer=- end -"""Creates a [Watts-Strogatz](https://en.wikipedia.org/wiki/Watts_and_Strogatz_model) +""" +Create 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. @@ -152,7 +153,7 @@ end """ barabasi_albert(n::Integer, k::Integer; is_directed::Bool = false, complete::Bool = false, seed::Int = -1) -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 `k` vertices. Each new vertex is attached with `k` edges to `k` different vertices already present in the system by preferential attachment. @@ -186,7 +187,7 @@ end """ barabasi_albert!(g::AbstractGraph, n::Integer, k::Integer; seed::Int = -1) -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 `g`. Each new vertex is attached with `k` edges to `k` different vertices already present in the system by preferential attachment. @@ -262,7 +263,7 @@ end """ static_fitness_model{T<:Real}(m::Integer, fitness::Vector{T}; seed::Int=-1) -Generates a random graph with `length(fitness)` nodes and `m` edges, +Generate a random graph with `length(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|). @@ -334,7 +335,7 @@ end """ function static_scale_free(n::Integer, m::Integer, α::Real; seed::Int=-1, finite_size_correction::Bool=true) -Generates a random graph with `n` vertices, `m` edges and expected power-law +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`. @@ -386,7 +387,7 @@ end doc""" random_regular_graph(n::Int, k::Int; seed=-1) -Creates a random undirected +Create a random undirected [regular graph](https://en.wikipedia.org/wiki/Regular_graph) with `n` vertices, each with degree `k`. @@ -422,7 +423,7 @@ end doc""" random_configuration_model(n::Integer, k::Array{Integer}; seed=-1, check_graphical=false) -Creates a random undirected graph according to the [configuration model] +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]`. @@ -458,7 +459,7 @@ end doc""" random_regular_digraph(n::Integer, k::Integer; dir::Symbol=:out, seed=-1) -Creates a random directed +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`. @@ -500,7 +501,7 @@ doc""" stochastic_block_model(c::Matrix{Real}, n::Vector{Integer}; seed::Int = -1) stochastic_block_model(cin::Real, coff::Float64, n::Vector{Integer}; seed::Int = -1) -Returns a Graph generated according to the Stochastic Block Model (SBM). +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 @@ -603,7 +604,8 @@ function StochasticBlockModel(sizes::AbstractVector, affinities::AbstractMatrix; end -"""Produce the sbm affinity matrix where the external probabilities are the same +""" +Produce the sbm affinity matrix where the external probabilities are the same the internal probabilities and sizes differ by blocks. """ function sbmaffinity(internalp::Vector{T}, externalp::Real, sizes::Vector{U}) where T<:Real where U<:Integer @@ -632,7 +634,8 @@ end const biclique = ones(2,2) - eye(2) -"""Construct the affinity matrix for a near bipartite SBM. +""" +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. @@ -645,7 +648,7 @@ function nearbipartiteaffinity(sizes::Vector{T}, between::Real, intra::Real) whe return kron(between*eye(numblocks), biclique) + eye(2numblocks)*intra end -"""Return a generator for edges from a stochastic block model near-bipartite graph.""" +#Return a generator for edges from a stochastic block model near-bipartite graph. function nearbipartiteaffinity(sizes::Vector{T}, between::Real, inter::Real, noise::Real) where T<:Integer B = nearbipartiteaffinity(sizes, between, inter) + noise # info("Affinities are:\n$B")#, file=stderr) @@ -657,7 +660,7 @@ function nearbipartiteSBM(sizes, between, inter, noise; seed::Int = -1) end -"""Generates a stream of random pairs in 1:n""" +"""Generate a stream of random pairs in 1:n""" function random_pair(rng::AbstractRNG, n::Integer) f(ch) = begin while true @@ -706,7 +709,7 @@ end Graph(nvg::Integer, neg::Integer, sbm::StochasticBlockModel) = Graph(nvg, neg, make_edgestream(sbm)) -"""counts 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") I = collect(1:sbm.n) diff --git a/src/persistence/dot.jl b/src/persistence/dot.jl index 993a9c4cd..184dba829 100644 --- a/src/persistence/dot.jl +++ b/src/persistence/dot.jl @@ -20,7 +20,7 @@ function _dot_read_one_graph(pg::DOT.Graph) end function loaddot(io::IO, gname::String) - p = DOT.parse_dot(readall(io)) + p = DOT.parse_dot(readstring(io)) for pg in p isdir = pg.directed possname = isdir? DOT.StringID("digraph") : DOT.StringID("graph") @@ -31,7 +31,7 @@ function loaddot(io::IO, gname::String) end function loaddot_mult(io::IO) - p = DOT.parse_dot(readall(io)) + p = DOT.parse_dot(readstring(io)) graphs = Dict{String, AbstractGraph}() diff --git a/src/persistence/gml.jl b/src/persistence/gml.jl index 9ff0a5b2d..0ea706995 100644 --- a/src/persistence/gml.jl +++ b/src/persistence/gml.jl @@ -16,7 +16,7 @@ function _gml_read_one_graph(gs, dir) return g end function loadgml(io::IO, gname::String) - p = GML.parse_dict(readall(io)) + p = GML.parse_dict(readstring(io)) for gs in p[:graph] dir = Bool(get(gs, :directed, 0)) graphname = get(gs, :label, dir ? "digraph" : "graph") @@ -27,7 +27,7 @@ function loadgml(io::IO, gname::String) end function loadgml_mult(io::IO) - p = GML.parse_dict(readall(io)) + p = GML.parse_dict(readstring(io)) graphs = Dict{String, AbstractGraph}() for gs in p[:graph] dir = Bool(get(gs, :directed, 0)) diff --git a/src/persistence/graphml.jl b/src/persistence/graphml.jl index 28222046e..2fde28fb7 100644 --- a/src/persistence/graphml.jl +++ b/src/persistence/graphml.jl @@ -26,7 +26,7 @@ function _graphml_read_one_graph(el::EzXML.Node, isdirected::Bool) end function loadgraphml(io::IO, gname::String) - xdoc = parsexml(readall(io)) + xdoc = parsexml(readstring(io)) xroot = root(xdoc) # an instance of XMLElement name(xroot) == "graphml" || error("Not a GraphML file") @@ -51,7 +51,7 @@ function loadgraphml(io::IO, gname::String) end function loadgraphml_mult(io::IO) - xdoc = parsexml(readall(io)) + xdoc = parsexml(readstring(io)) xroot = root(xdoc) # an instance of XMLElement name(xroot) == "graphml" || error("Not a GraphML file") From 3d770d56202004a4f2d0a564dc552b96507187d9 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 22 Mar 2017 22:30:23 -0400 Subject: [PATCH 43/56] Fixes #555: graphmatrices convert incorrect on CA (#560) CombinatorialAdjacency(CombinatorialAdjacency(g)) was returning the backing storage. Fix includes tests. --- src/linalg/graphmatrices.jl | 1 + test/linalg/graphmatrices.jl | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/linalg/graphmatrices.jl b/src/linalg/graphmatrices.jl index 0c2091833..064cd252e 100644 --- a/src/linalg/graphmatrices.jl +++ b/src/linalg/graphmatrices.jl @@ -178,6 +178,7 @@ adjacency(lapl::GraphMatrix) = lapl.A convert(::Type{Adjacency}, lapl::Laplacian) = lapl.A convert(::Type{CombinatorialAdjacency}, adjmat::Adjacency) = adjmat.A convert(::Type{SparseMatrix}, adjmat::CombinatorialAdjacency) = adjmat.A +convert(::Type{CombinatorialAdjacency}, adjmat::CombinatorialAdjacency) = adjmat function sparse(lapl::M) where M<:Laplacian diff --git a/test/linalg/graphmatrices.jl b/test/linalg/graphmatrices.jl index 7c9cbc154..6d22bc26b 100644 --- a/test/linalg/graphmatrices.jl +++ b/test/linalg/graphmatrices.jl @@ -28,6 +28,8 @@ export test_adjacency, test_laplacian, test_accessors, test_arithmetic, test_oth converttest(SparseMatrix{Float64},stochmat) converttest(SparseMatrix{Float64},adjhat) converttest(SparseMatrix{Float64},avgmat) + @test isa(CombinatorialAdjacency(adjmat), CombinatorialAdjacency) + @test isa(CombinatorialAdjacency(avgmat), CombinatorialAdjacency) @test prescalefactor(adjhat) == postscalefactor(adjhat) @test postscalefactor(stochmat) == prescalefactor(avgmat) @test prescalefactor(adjhat) == postscalefactor(adjhat) From ab176b826fcd8da077466ccabb713b44828c0752 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Wed, 22 Mar 2017 20:17:08 -0700 Subject: [PATCH 44/56] fixes #564 --- src/linalg/graphmatrices.jl | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/linalg/graphmatrices.jl b/src/linalg/graphmatrices.jl index 064cd252e..6f3b78a99 100644 --- a/src/linalg/graphmatrices.jl +++ b/src/linalg/graphmatrices.jl @@ -116,9 +116,6 @@ Base.broadcast(::typeof(*), ::Noop, x) = x Diagonal(::Noop) = Noop() -A_mul_B!(Y, A::Noop, B) = copy!(Y, B) - - ==(g::GraphMatrix, h::GraphMatrix) = typeof(g) == typeof(h) && (g.A == h.A) @@ -175,9 +172,8 @@ adjacency(lapl::Laplacian) = lapl.A adjacency(lapl::GraphMatrix) = lapl.A -convert(::Type{Adjacency}, lapl::Laplacian) = lapl.A + convert(::Type{CombinatorialAdjacency}, adjmat::Adjacency) = adjmat.A -convert(::Type{SparseMatrix}, adjmat::CombinatorialAdjacency) = adjmat.A convert(::Type{CombinatorialAdjacency}, adjmat::CombinatorialAdjacency) = adjmat @@ -295,5 +291,13 @@ Only works on Adjacency because the normalizations don't commute with symmetriza symmetrize(adjmat::CombinatorialAdjacency, which=:or) = CombinatorialAdjacency(symmetrize(adjmat.A, which)) + +# per #564 +@deprecate A_mul_B!(Y, A::Noop, B) None +@deprecate convert(::Type{Adjacency}, lapl::Laplacian) None +@deprecate convert(::Type{SparseMatrix}, adjmat::CombinatorialAdjacency) sparse(adjmat) + + + """A package for using the type system to check types of graph matrices.""" LinAlg From 49b160ceff7fc9dbba3f43b90ee10d721ef094c8 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Wed, 22 Mar 2017 20:40:31 -0700 Subject: [PATCH 45/56] one more test --- test/interface.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/interface.jl b/test/interface.jl index 074b63552..3ecf45fbc 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -7,6 +7,8 @@ mutable struct DummyEdge <: AbstractEdge end dummydigraph = DummyDiGraph() dummyedge = DummyEdge() + @test_throws ErrorException is_directed(DummyGraph) + for edgefun in [src, dst, Pair, Tuple, reverse] @test_throws ErrorException edgefun(dummyedge) end From dfe72cb39c3ece89eb9f5bf6d0d7f8107d029781 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Wed, 22 Mar 2017 21:35:18 -0700 Subject: [PATCH 46/56] removed nv() and vertices() (#565) --- src/centrality/betweenness.jl | 2 +- src/graphtypes/simplegraphs/SimpleGraphs.jl | 9 ++++++--- src/graphtypes/simplegraphs/simpledigraph.jl | 12 ++++-------- src/graphtypes/simplegraphs/simplegraph.jl | 12 ++++-------- src/operators.jl | 2 -- src/persistence/jld.jl | 7 ++++--- 6 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/centrality/betweenness.jl b/src/centrality/betweenness.jl index caf1e882c..4fb998d21 100644 --- a/src/centrality/betweenness.jl +++ b/src/centrality/betweenness.jl @@ -125,7 +125,7 @@ function _accumulate_endpoints!( v1 = [1:n_v;] v2 = state.dists S = sortperm(state.dists, rev=true) - s = g.vertices[si] + s = vertices(g)[si] betweenness[s] += length(S) - 1 # 289 for w in S diff --git a/src/graphtypes/simplegraphs/SimpleGraphs.jl b/src/graphtypes/simplegraphs/SimpleGraphs.jl index e9e266262..f4ffc0e51 100644 --- a/src/graphtypes/simplegraphs/SimpleGraphs.jl +++ b/src/graphtypes/simplegraphs/SimpleGraphs.jl @@ -37,9 +37,12 @@ function show(io::IO, g::AbstractSimpleGraph) end end -vertices(g::AbstractSimpleGraph) = g.vertices +nv(g::AbstractSimpleGraph) = eltype(g)(length(fadj(g))) +vertices(g::AbstractSimpleGraph) = one(eltype(g)):nv(g) + + edges(g::AbstractSimpleGraph) = SimpleEdgeIter(g) -nv(g::AbstractSimpleGraph) = last(vertices(g)) + fadj(g::AbstractSimpleGraph) = g.fadjlist fadj(g::AbstractSimpleGraph, v::Integer) = g.fadjlist[v] @@ -66,6 +69,7 @@ end has_vertex(g::AbstractSimpleGraph, v::Integer) = v in vertices(g) ne(g::AbstractSimpleGraph) = g.ne + function rem_edge!(g::AbstractSimpleGraph, u::Integer, v::Integer) T = eltype(g) rem_edge!(g, edgetype(g)(T(u), T(v))) @@ -120,7 +124,6 @@ function rem_vertex!(g::AbstractSimpleGraph, v::Integer) end end - g.vertices = 1:n-1 pop!(g.fadjlist) if is_directed(g) pop!(g.badjlist) diff --git a/src/graphtypes/simplegraphs/simpledigraph.jl b/src/graphtypes/simplegraphs/simpledigraph.jl index f42f082a1..9721c871d 100644 --- a/src/graphtypes/simplegraphs/simpledigraph.jl +++ b/src/graphtypes/simplegraphs/simpledigraph.jl @@ -2,14 +2,13 @@ const SimpleDiGraphEdge = SimpleEdge """A type representing a directed graph.""" mutable struct SimpleDiGraph{T<:Integer} <: AbstractSimpleGraph - vertices::UnitRange{T} ne::Int fadjlist::Vector{Vector{T}} # [src]: (dst, dst, dst) badjlist::Vector{Vector{T}} # [dst]: (src, src, src) end -eltype(x::SimpleDiGraph{T}) where T = T +eltype(x::SimpleDiGraph{T}) where T = T # DiGraph{UInt8}(6), DiGraph{Int16}(7), DiGraph{Int8}() function (::Type{SimpleDiGraph{T}})(n::Integer = 0) where T<:Integer @@ -19,8 +18,7 @@ function (::Type{SimpleDiGraph{T}})(n::Integer = 0) where T<:Integer push!(badjlist, Vector{T}()) push!(fadjlist, Vector{T}()) end - vertices = one(T):T(n) - return SimpleDiGraph(vertices, 0, fadjlist, badjlist) + return SimpleDiGraph(0, fadjlist, badjlist) end # DiGraph() @@ -69,10 +67,9 @@ SimpleDiGraph(adjmx::AbstractMatrix) = SimpleDiGraph{Int}(adjmx) # converts DiGraph{Int} to DiGraph{Int32} function (::Type{SimpleDiGraph{T}})(g::SimpleDiGraph) where T<:Integer - h_vertices = one(T):T(nv(g)) h_fadj = [Vector{T}(x) for x in fadj(g)] h_badj = [Vector{T}(x) for x in badj(g)] - return SimpleDiGraph(h_vertices, ne(g), h_fadj, h_badj) + return SimpleDiGraph(ne(g), h_fadj, h_badj) end @@ -93,7 +90,7 @@ badj(g::SimpleDiGraph, v::Integer) = badj(g)[v] copy(g::SimpleDiGraph{T}) where T<:Integer = -SimpleDiGraph{T}(g.vertices, g.ne, deepcopy(g.fadjlist), deepcopy(g.badjlist)) +SimpleDiGraph{T}(g.ne, deepcopy(g.fadjlist), deepcopy(g.badjlist)) ==(g::SimpleDiGraph, h::SimpleDiGraph) = @@ -132,7 +129,6 @@ end function add_vertex!(g::SimpleDiGraph) T = eltype(g) (nv(g) + one(T) <= nv(g)) && return false # test for overflow - g.vertices = 1:nv(g)+1 push!(g.badjlist, Vector{T}()) push!(g.fadjlist, Vector{T}()) diff --git a/src/graphtypes/simplegraphs/simplegraph.jl b/src/graphtypes/simplegraphs/simplegraph.jl index fdb451275..831ff5454 100644 --- a/src/graphtypes/simplegraphs/simplegraph.jl +++ b/src/graphtypes/simplegraphs/simplegraph.jl @@ -2,7 +2,6 @@ const SimpleGraphEdge = SimpleEdge """A type representing an undirected graph.""" mutable struct SimpleGraph{T<:Integer} <: AbstractSimpleGraph - vertices::UnitRange{T} ne::Int fadjlist::Vector{Vector{T}} # [src]: (dst, dst, dst) end @@ -17,7 +16,7 @@ function (::Type{SimpleGraph{T}})(n::Integer = 0) where T<:Integer push!(fadjlist, Vector{T}()) end vertices = one(T):T(n) - return SimpleGraph{T}(vertices, 0, fadjlist) + return SimpleGraph{T}(0, fadjlist) end # Graph() @@ -45,9 +44,8 @@ end # converts Graph{Int} to Graph{Int32} function (::Type{SimpleGraph{T}})(g::SimpleGraph) where T<:Integer - h_vertices = one(T):T(nv(g)) h_fadj = [Vector{T}(x) for x in fadj(g)] - return SimpleGraph(h_vertices, ne(g), h_fadj) + return SimpleGraph(ne(g), h_fadj) end @@ -72,7 +70,7 @@ function SimpleGraph(g::SimpleDiGraph) end end iseven(edgect) || throw(AssertionError("invalid edgect in graph creation - please file bug report")) - return SimpleGraph(vertices(g), edgect ÷ 2, newfadj) + return SimpleGraph(edgect ÷ 2, newfadj) end edgetype(::SimpleGraph{T}) where T<:Integer = SimpleGraphEdge{T} @@ -95,7 +93,7 @@ NOTE: returns a reference, not a copy. Do not modify result. adj(g::SimpleGraph) = fadj(g) adj(g::SimpleGraph, v::Integer) = fadj(g, v) -copy(g::SimpleGraph) = SimpleGraph(g.vertices, g.ne, deepcopy(g.fadjlist)) +copy(g::SimpleGraph) = SimpleGraph(g.ne, deepcopy(g.fadjlist)) ==(g::SimpleGraph, h::SimpleGraph) = vertices(g) == vertices(h) && @@ -149,8 +147,6 @@ end function add_vertex!(g::SimpleGraph) T = eltype(g) (nv(g) + one(T) <= nv(g)) && return false # test for overflow - g.vertices = one(T):nv(g)+one(T) push!(g.fadjlist, Vector{T}()) - return true end diff --git a/src/operators.jl b/src/operators.jl index 1d0f5af4c..603b62a68 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -43,8 +43,6 @@ function reverse(g::DiGraph) h.fadjlist = deepcopy(g.badjlist) h.badjlist = deepcopy(g.fadjlist) h.ne = gne - h.vertices = g.vertices - return h end diff --git a/src/persistence/jld.jl b/src/persistence/jld.jl index 7dabc260b..9fdb2d9cd 100644 --- a/src/persistence/jld.jl +++ b/src/persistence/jld.jl @@ -1,5 +1,6 @@ using JLD -"""GraphSerializer is a type for custom serialization into JLD files. +""" +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 of the adjacency matrix of a graph g. @@ -47,7 +48,7 @@ function JLD.writeas(g::Graph) packed_adjlist[k+=1] = v end end - GraphSerializer(g.vertices, g.ne, packed_adjlist, n_adjlist) + GraphSerializer(vertices(g), ne(g), packed_adjlist, n_adjlist) end function JLD.readas(gs::GraphSerializer) @@ -64,7 +65,7 @@ function JLD.readas(gs::GraphSerializer) adj[i] = gs.packed_adjlist[posbegin:posend] end @assert sum(map(length, adj)) == 2gs.ne - g = Graph(1:n, gs.ne, adj) + g = Graph(gs.ne, adj) return g end From 4345a3ef184f36e3df4f32983f1d7a67ffd40b0e Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 26 Mar 2017 09:33:10 -0700 Subject: [PATCH 47/56] simpleedge tests --- test/graphtypes/simplegraphs/simpleedge.jl | 27 ++++++++++++++++++++++ test/runtests.jl | 1 + 2 files changed, 28 insertions(+) create mode 100644 test/graphtypes/simplegraphs/simpleedge.jl diff --git a/test/graphtypes/simplegraphs/simpleedge.jl b/test/graphtypes/simplegraphs/simpleedge.jl new file mode 100644 index 000000000..3f234aa94 --- /dev/null +++ b/test/graphtypes/simplegraphs/simpleedge.jl @@ -0,0 +1,27 @@ +import LightGraphs.SimpleGraphs.SimpleEdge +@testset "SimpleEdge" begin + e = SimpleEdge(1,2) + re = SimpleEdge(2,1) + + for s in [0x01, UInt16(1), 1] + T = typeof(s) + d = s+one(T) + p = Pair(s, d) + + ep = SimpleEdge(p) + t1 = (s, d) + t2 = (s, d, "foo") + + @test src(ep) == s + @test dst(ep) == s + one(T) + + @test eltype(p) == typeof(s) + @test SimpleEdge(p) == e + @test SimpleEdge(t1) == SimpleEdge(t2) == e + + @test Pair(e) == p + @test Tuple(e) == t1 + @test reverse(ep) == re + @test sprint(show, ep) == "Edge 1 => 2" + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 4a82c0992..83b3b8a7e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -48,6 +48,7 @@ tests = [ "interface", "core", "graphtypes/simplegraphs/simplegraphs", + "graphtypes/simplegraphs/simpleedge", "graphtypes/simplegraphs/simpleedgeiter", "operators", # "graphdigraph", From 23e712323f2a149a19e2b5a6a4bd8df7cf121527 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 26 Mar 2017 10:27:48 -0700 Subject: [PATCH 48/56] test coverage --- test/graphtypes/simplegraphs/simpleedge.jl | 2 ++ test/graphtypes/simplegraphs/simplegraphs.jl | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/test/graphtypes/simplegraphs/simpleedge.jl b/test/graphtypes/simplegraphs/simpleedge.jl index 3f234aa94..6d3601064 100644 --- a/test/graphtypes/simplegraphs/simpleedge.jl +++ b/test/graphtypes/simplegraphs/simpleedge.jl @@ -19,6 +19,8 @@ import LightGraphs.SimpleGraphs.SimpleEdge @test SimpleEdge(p) == e @test SimpleEdge(t1) == SimpleEdge(t2) == e + @test SimpleEdge{Int64}(ep) == e + @test Pair(e) == p @test Tuple(e) == t1 @test reverse(ep) == re diff --git a/test/graphtypes/simplegraphs/simplegraphs.jl b/test/graphtypes/simplegraphs/simplegraphs.jl index 57d228740..f51d20099 100644 --- a/test/graphtypes/simplegraphs/simplegraphs.jl +++ b/test/graphtypes/simplegraphs/simplegraphs.jl @@ -9,6 +9,8 @@ struct DummySimpleGraph <: AbstractSimpleGraph end @test @inferred(eltype(SimpleGraph(adjmx1))) == Int @test_throws ErrorException SimpleGraph(adjmx2) + @test_throws ErrorException badj(DummySimpleGraph()) + @test @inferred(ne(SimpleGraph(PathDiGraph(5)))) == 4 @test @inferred(!is_directed(SimpleGraph)) @@ -146,4 +148,12 @@ struct DummySimpleGraph <: AbstractSimpleGraph end e = first(@inferred(edges(g))) @test @inferred(has_edge(g, e)) end + + gdx = CompleteDiGraph(4) + for g in testdigraphs(gdx) + @test rem_vertex!(g, 2) + @test nv(g) == 3 && ne(g) == 6 + end + + end From 0e21aa35ae601b291006537745058ce9d9dccf3c Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 26 Mar 2017 10:53:16 -0700 Subject: [PATCH 49/56] short circuit B-F negative cycles, plus tests --- src/shortestpaths/bellman-ford.jl | 6 ++++-- test/shortestpaths/bellman-ford.jl | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/shortestpaths/bellman-ford.jl b/src/shortestpaths/bellman-ford.jl index 9d8c9c97c..e7ef5ab18 100644 --- a/src/shortestpaths/bellman-ford.jl +++ b/src/shortestpaths/bellman-ford.jl @@ -75,9 +75,11 @@ bellman_ford_shortest_paths( distmx::AbstractMatrix = DefaultDistance() ) = bellman_ford_shortest_paths(graph, [v], distmx) -function has_negative_edge_cycle(graph::AbstractGraph) +has_negative_edge_cycle(g::AbstractGraph) = false + +function has_negative_edge_cycle(g::AbstractGraph, distmx::AbstractMatrix) try - bellman_ford_shortest_paths(graph, vertices(graph)) + bellman_ford_shortest_paths(g, vertices(g), distmx) catch e isa(e, NegativeCycleError) && return true end diff --git a/test/shortestpaths/bellman-ford.jl b/test/shortestpaths/bellman-ford.jl index 5da0ac112..0c0c91a21 100644 --- a/test/shortestpaths/bellman-ford.jl +++ b/test/shortestpaths/bellman-ford.jl @@ -11,6 +11,7 @@ @test @inferred(enumerate_paths(z))[4] == enumerate_paths(z,4) == [2,3,4] @test @inferred(!has_negative_edge_cycle(g)) + y = @inferred(bellman_ford_shortest_paths(g, 2, d1)) z = @inferred(bellman_ford_shortest_paths(g, 2, d2)) @test y.dists == z.dists == [Inf, 0, 6, 17, 33] @@ -26,9 +27,11 @@ for g in testgraphs(gx) d = [1 -3 1; -3 1 1; 1 1 1] @test_throws LightGraphs.NegativeCycleError bellman_ford_shortest_paths(g, 1, d) + @test has_negative_edge_cycle(g, d) d = [1 -1 1; -1 1 1; 1 1 1] @test_throws LightGraphs.NegativeCycleError bellman_ford_shortest_paths(g, 1, d) + @test has_negative_edge_cycle(g, d) end # Negative cycle of length 3 in graph of diameter 4 @@ -36,5 +39,6 @@ d = [1 -1 1 1; 1 1 1 -1; 1 1 1 1; 1 1 1 1] for g in testgraphs(gx) @test_throws LightGraphs.NegativeCycleError bellman_ford_shortest_paths(g, 1, d) + @test has_negative_edge_cycle(g, d) end end From bcbd873d3c8df0fb3c322e6444bd564c641077a3 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 26 Mar 2017 11:02:31 -0700 Subject: [PATCH 50/56] more test coverage --- test/traversals/graphvisit.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/traversals/graphvisit.jl b/test/traversals/graphvisit.jl index d6819a2fe..f658faa0d 100644 --- a/test/traversals/graphvisit.jl +++ b/test/traversals/graphvisit.jl @@ -9,6 +9,12 @@ @test @inferred(visited_vertices(g, BreadthFirst(), [1;])) == [1, 2, 3, 4, 5] + vis = TrivialGraphVisitor() + @test discover_vertex!(vis, 1) + @test open_vertex!(vis, 1) + @test examine_neighbor!(vis, 1, 1, 0, 0, 0) + @test close_vertex!(vis, 1) + function trivialgraphvisit( g::AbstractGraph, alg::LightGraphs.AbstractGraphVisitAlgorithm, @@ -26,6 +32,8 @@ end # dummy edge map test d = @inferred(LightGraphs.DummyEdgeMap()) + e = Edge(1,2) @test d[e] == 0 + @test getindex(d, e) == 0 end From e733cfedf8ddd588226ef0a65eb99613b4e0ec4c Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sun, 26 Mar 2017 11:17:37 -0700 Subject: [PATCH 51/56] more test coverage --- test/shortestpaths/bellman-ford.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/shortestpaths/bellman-ford.jl b/test/shortestpaths/bellman-ford.jl index 0c0c91a21..cdf92dc21 100644 --- a/test/shortestpaths/bellman-ford.jl +++ b/test/shortestpaths/bellman-ford.jl @@ -10,6 +10,7 @@ @test @inferred(enumerate_paths(z))[2] == [] @test @inferred(enumerate_paths(z))[4] == enumerate_paths(z,4) == [2,3,4] @test @inferred(!has_negative_edge_cycle(g)) + @test @inferred(!has_negative_edge_cycle(g, d1)) y = @inferred(bellman_ford_shortest_paths(g, 2, d1)) From 727fe47079ee74d8e6aeea10c28128c33bcb923d Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Mon, 27 Mar 2017 13:13:58 -0700 Subject: [PATCH 52/56] Docs (#567) * docs, plus some various fixes (see blocking_flow). * nodes -> vertices, node -> vertex, and more doc consistency --- README.md | 125 ++------ src/LightGraphs.jl | 9 +- src/biconnectivity/articulation.jl | 39 ++- src/biconnectivity/biconnect.jl | 54 ++-- src/centrality/betweenness.jl | 64 ++-- src/centrality/closeness.jl | 12 +- src/centrality/degree.jl | 18 +- src/centrality/katz.jl | 10 +- src/centrality/pagerank.jl | 14 +- src/community/cliques.jl | 13 +- src/community/clustering.jl | 74 ++--- src/community/core-periphery.jl | 9 +- src/community/label_propagation.jl | 47 ++- src/community/modularity.jl | 6 +- src/connectivity.jl | 128 +++++--- src/core.jl | 137 ++++++-- src/digraph-transitivity.jl | 53 ++-- src/distance.jl | 68 ++-- src/edit_distance.jl | 59 ++-- src/flow/boykov_kolmogorov.jl | 26 +- src/flow/dinic.jl | 39 +-- src/flow/edmonds_karp.jl | 104 +++--- src/flow/ext_multiroute_flow.jl | 93 +++--- src/flow/kishimoto.jl | 18 +- src/flow/maximum_flow.jl | 102 +++--- src/flow/multiroute_flow.jl | 109 ++++--- src/flow/push_relabel.jl | 84 ++--- src/generators/euclideangraphs.jl | 46 +-- src/generators/randgraphs.jl | 318 ++++++++++++------- src/generators/smallgraphs.jl | 6 +- src/generators/staticgraphs.jl | 131 ++++++-- src/graphtypes/simplegraphs/SimpleGraphs.jl | 23 +- src/graphtypes/simplegraphs/simpledigraph.jl | 6 +- src/graphtypes/simplegraphs/simplegraph.jl | 37 ++- src/interface.jl | 131 ++++++-- src/linalg/graphmatrices.jl | 95 ++++-- src/linalg/nonbacktracking.jl | 43 ++- src/linalg/spectral.jl | 75 +++-- src/matching/README.md | 2 - src/operators.jl | 148 ++++++--- 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 | 41 +-- src/spanningtrees/prim.jl | 20 +- src/traversals/bfs.jl | 71 +++-- src/traversals/dfs.jl | 23 +- src/traversals/maxadjvisit.jl | 13 +- src/traversals/randomwalks.jl | 21 +- src/utils.jl | 19 +- test/flow/dinic.jl | 8 +- test/shortestpaths/dijkstra.jl | 4 +- 60 files changed, 1788 insertions(+), 1240 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 9bd0ee7cb..9e8524a35 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`](@ref) 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..ab9da8ed7 100644 --- a/src/centrality/betweenness.jl +++ b/src/centrality/betweenness.jl @@ -2,60 +2,42 @@ # TODO - weighted, separate unweighted, edge betweenness -doc""" -betweenness_centrality(g, k=0; normalize=true, endpoints=false) +@doc_str """ + 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 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`. -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}} +``. -$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** - -[1] Brandes 2001 & Brandes 2008 +### References +- 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 @@ -94,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] @@ -109,8 +91,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..2bd734525 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 vertices reachable +from node `n`. """ function closeness_centrality( g::AbstractGraph; diff --git a/src/centrality/degree.jl b/src/centrality/degree.jl index 812106e93..b0b267612 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..df860ce77 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 `ϵ`. 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..30a72f16f 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) @@ -56,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 @@ -87,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 @@ -107,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 83b369d90..ef31fb250 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 vertices `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) -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`. +@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 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. + +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,35 @@ 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 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. """ -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) + global_clustering_coefficient(g) -Computes the [global clustering coefficient](https://en.wikipedia.org/wiki/Clustering_coefficient). +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..7831a85ac 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..ebcba9ae7 100644 --- a/src/community/label_propagation.jl +++ b/src/community/label_propagation.jl @@ -1,25 +1,30 @@ """ -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) - 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) @@ -29,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 @@ -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..cc703db7a 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 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 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) + +Compute 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 vertices 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 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`. +""" 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 +Return true if 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..a22ecc297 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`](@ref) 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`](@ref) 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`](@ref) of vertices in `g`. +""" +Δin(g) = noallocextreme(indegree,(>), typemin(Int), g) + +""" + δin(g) + +Return the minimum [`indegree`](ref) of vertices in `g`. +""" +δin(g) = noallocextreme(indegree,(<), typemax(Int), g) + +""" + Δ(g) + +Return the maximum [`degree`](@ref) of vertices in `g`. +""" +Δ(g) = noallocextreme(degree,(>), typemin(Int), g) + +""" + δ(g) +Return the minimum [`degree`](@ref) 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. +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. """ 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. """ 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..bcea8fb73 100644 --- a/src/digraph-transitivity.jl +++ b/src/digraph-transitivity.jl @@ -1,51 +1,44 @@ -""" -```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. +### Performance +Time complexity is \\mathcal{O}(|V|^3). -# 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`. +### Implementation Notes +This version of the function modifies the original graph. """ -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 representing 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..d20746048 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 @@ -18,24 +24,21 @@ 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. -A custom heuristic can be 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: - -RIESEN, K., 2015. Structural Pattern Recognition with Graph Edit -Distance: Approximation Algorithms and Applications. (Chapter 2) +### References +- 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 +103,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..c42772758 100644 --- a/src/flow/boykov_kolmogorov.jl +++ b/src/flow/boykov_kolmogorov.jl @@ -1,26 +1,18 @@ """ -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 +### References +- 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 - -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/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..4d13a0817 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 + +### References +- [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( @@ -52,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) @@ -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..9dc5ef931 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 """ -Forces the maximum_flow function to use Dinic\'s algorithm. + 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.) +Return 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`](@ref) 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`](@ref). +- 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..90449150b 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 """ -Forces the multiroute_flow function to use the Kishimoto algorithm. + KishimotoAlgorithm + +Used to specify the Kishimoto algorithm. """ -struct KishimotoAlgorithm <: AbstractMultirouteFlowAlgorithm -end +struct KishimotoAlgorithm <: AbstractMultirouteFlowAlgorithm end """ -Forces the multiroute_flow function to use the Extended Multiroute Flow algorithm. + ExtendedMultirouteFlowAlgorithm + +Used to specify 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,90 +95,95 @@ 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. 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) -```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..803af8f98 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,11 @@ 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 +105,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 vertices above a cutoff height. +Reduce the number of relabels required. Requires arguments: @@ -161,20 +142,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 +173,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..58e12adbe 100644 --- a/src/generators/euclideangraphs.jl +++ b/src/generators/euclideangraphs.jl @@ -1,28 +1,10 @@ -""" -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. - - -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. +@doc_str """ + euclidean_graph(N, d; seed=-1, L=1., p=2., cutoff=-1., bc=:open) -Returns 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 end - function euclidean_graph(N::Int, d::Int; L=1., seed = -1, kws...) rng = LightGraphs.getRNG(seed) @@ -30,6 +12,24 @@ function euclidean_graph(N::Int, d::Int; return (euclidean_graph(points; L=L, kws...)..., points) end +""" + 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. + +### 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``. +""" 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..70536433c 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. +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` vertices 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,18 +206,21 @@ 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) 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 @@ -212,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 @@ -235,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 @@ -250,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 @@ -260,30 +284,34 @@ 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 ``|fitness|`` vertices and `m` edges, +in which the probability of the existence of ``Edge_{ij}`` is proportional +to ``fitness_i × fitness_j`. -Generate a random graph with `length(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|). +### 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") 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) @@ -292,21 +320,37 @@ 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|`` 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 +- `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) @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) @@ -332,32 +376,54 @@ 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) - @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) 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(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 @@ -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) 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]`. - -Defining `c = mean(k)`, it allocates an array of `nc` `Int`s, and takes -approximately $nc^2$ time. - - -If `check_graphical=true` makes sure that `k` is a graphical sequence (see `isgraphical`). +(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 +- `seed=-1`: set the RNG seed. +- `check_graphical=false`: if true, ensure that `k` is a graphical sequence +(see [`isgraphical`](@ref)). + +### 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 × 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,20 +576,20 @@ 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) -stochastic_block_model(cin::Real, coff::Float64, n::Vector{Integer}; seed::Int = -1) +@doc_str """ + stochastic_block_model(c, n) 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 +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 @@ -549,6 +628,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 +645,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)` @@ -571,9 +655,10 @@ 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`. -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 +671,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 +689,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,12 +722,15 @@ 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. +`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`. """ @@ -660,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 @@ -672,10 +767,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 +804,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..d1865e2a6 100644 --- a/src/generators/smallgraphs.jl +++ b/src/generators/smallgraphs.jl @@ -19,10 +19,10 @@ 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: +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 951683eb8..40bebcaf8 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..e064a6fef 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,26 @@ 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]) -NOTE: returns a reference, not a copy. Do not modify result. +Return the backwards 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. """ 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 +112,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 +158,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/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 6f3b78a99..cf24eb783 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) + +Return the degrees of a graph represented by the [CombinatorialAdjacency](@ref) `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) + +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) if which==:or @@ -286,7 +332,14 @@ function symmetrize(A::SparseMatrix, which=:or) end """ -Only works on Adjacency because the normalizations don't commute with symmetrization. + symmetrize(adjmat, which=: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](@ref) because the normalizations don't commute with symmetrization. """ symmetrize(adjmat::CombinatorialAdjacency, which=:or) = CombinatorialAdjacency(symmetrize(adjmat.A, which)) @@ -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..131b4c513 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 +@doc_str """ + Nonbacktracking{G} - g: the underlying graph - edgeidmap: the association between oriented edges and index into the NBT matrix +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..4cb8a23fa 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 -`laplacian_matrix`. +@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`](@ref). + +### Performance +Converts the matrix to dense with ``nv^2`` memory usage. + +### Implementation Notes +Use `eigs(laplacian_matrix(g); kwargs...)` to compute some of the +eigenvalues/eigenvectors. """ laplacian_spectrum(g::AbstractGraph, dir::Symbol=:unspec, T::DataType=Int) = eigvals(full(laplacian_matrix(g, dir, T))) -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 -`adjacency_matrix`. +@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`](@ref). + +### Performance +Converts the matrix to dense with ``nv^2`` memory usage. + +### Implementation Notes +Use `eigs(adjacency_matrix(g); kwargs...)` to compute some of the +eigenvalues/eigenvectors. """ function adjacency_spectrum(g::AbstractGraph, dir::Symbol=:unspec, T::DataType=Int) 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/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/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..1b29ef790 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..6725e0c26 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`](@ref) 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..9e468851a 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`](@ref) 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..f20a59b53 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`](@ref) 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 vertices. +Return a [`DijkstraState`](@ref) 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`](@ref) 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..b7ddcb287 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`](@ref) 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`](@ref) 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..8cde79b8b 100644 --- a/src/spanningtrees/kruskal.jl +++ b/src/spanningtrees/kruskal.jl @@ -6,34 +6,37 @@ 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!(vs, p, q) + +Perform [Quick-Find algorithm](https://en.wikipedia.org/wiki/Disjoint-set_data_structure) +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 -"""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}}() 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)) @@ -47,8 +50,8 @@ function kruskal_mst( 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/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..6682c0bdb 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 directed graph. +""" function tree(parents::AbstractVector{T}) where T<:Integer n = T(length(parents)) t = DiGraph(n) @@ -139,10 +145,14 @@ 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!`](@ref); use that function for more performance. """ function bfs_tree(g::AbstractGraph, s::Integer) nvg = nv(g) @@ -154,7 +164,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 +206,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 +235,12 @@ 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 +258,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 +292,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..723c3fec7 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`. +Return `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. +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) 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/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/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)) 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]] From e20781cf97b8e4b7245ea26d5be7d543eca74b0e Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Mon, 27 Mar 2017 13:40:53 -0700 Subject: [PATCH 53/56] doc fixes --- README.md | 4 ++-- src/centrality/katz.jl | 1 - src/shortestpaths/dijkstra.jl | 5 ++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 15b207dc1..4c0ecd0c2 100644 --- a/README.md +++ b/README.md @@ -133,8 +133,8 @@ These functions are defined as the public contract of the `LightGraphs.AbstractG ### Non-Core APIs These functions can be constructed from the Core API functions but can be given specialized implementations in order to improve performance. -- adjacency_matrix -- degree +- `adjacency_matrix` +- `degree` This can be computed from neighbors by default `degree(g,v) = length(neighbors(g,v))` so you don't need to implement this unless your type can compute degree faster than this method. diff --git a/src/centrality/katz.jl b/src/centrality/katz.jl index 3497fd1ce..720226df0 100644 --- a/src/centrality/katz.jl +++ b/src/centrality/katz.jl @@ -23,7 +23,6 @@ """ 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`. diff --git a/src/shortestpaths/dijkstra.jl b/src/shortestpaths/dijkstra.jl index f20a59b53..a851dd081 100644 --- a/src/shortestpaths/dijkstra.jl +++ b/src/shortestpaths/dijkstra.jl @@ -18,9 +18,8 @@ struct DijkstraState{T, U<:Integer}<: AbstractPathState end """ -dijkstra_shortest_paths(g, srcs, distmx=DefaultDistance()); - allpaths=false - ) + dijkstra_shortest_paths(g, srcs, distmx=DefaultDistance()); + Perform [Dijkstra's algorithm](http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) on a graph, computing shortest distances between `srcs` and all other vertices. Return a [`DijkstraState`](@ref) that contains various traversal information. From d8d12ce74dced91db6f71f4e6ff0b0073115f1c0 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Mon, 27 Mar 2017 20:27:56 -0700 Subject: [PATCH 54/56] 1.0 -> 0.8 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c0ecd0c2..48ebf697b 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ 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: LightGraphs versions in the 1.0 series are designed to work with Julia 0.6. +* Julia 0.6: LightGraphs versions in the 0.8 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 From 969b6b2a80a4226221dbcf1590a474c642845467 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Mon, 27 Mar 2017 23:33:36 -0700 Subject: [PATCH 55/56] docfix and benchmarks --- benchmark/max-flow.jl | 2 +- src/generators/randgraphs.jl | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/benchmark/max-flow.jl b/benchmark/max-flow.jl index bfb5fee58..46a0f10cd 100644 --- a/benchmark/max-flow.jl +++ b/benchmark/max-flow.jl @@ -4,7 +4,7 @@ p = 8.0 / n A = sprand(n,n,p) g = DiGraph(A) - cap = round(A*100) + cap = round.(A*100) @bench "n = $n" LightGraphs.maximum_flow($g, 1, $n, $cap) end end # max-flow diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 70536433c..05c6f38db 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -285,7 +285,7 @@ end @doc_str """ -static_fitness_model(m, fitness) + static_fitness_model(m, fitness) Generate a random graph with ``|fitness|`` vertices and `m` edges, in which the probability of the existence of ``Edge_{ij}`` is proportional @@ -391,9 +391,9 @@ 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, α::Real; seed::Int=-1, finite_size_correction::Bool=true) @assert(n >= 0, "Invalid number of vertices") From 2e1ae20c128ec463b0a3977676100bdffbb19049 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Tue, 28 Mar 2017 08:14:50 -0700 Subject: [PATCH 56/56] doc fixes --- src/flow/ext_multiroute_flow.jl | 4 +--- src/generators/randgraphs.jl | 8 ++------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/flow/ext_multiroute_flow.jl b/src/flow/ext_multiroute_flow.jl index 4d13a0817..4dcd65ebb 100644 --- a/src/flow/ext_multiroute_flow.jl +++ b/src/flow/ext_multiroute_flow.jl @@ -199,8 +199,6 @@ Requires argument: 2) - points::Vector{Tuple{T, T, Int}} # vector of points with T<:AbstractFloat - k::R<:Real # number of routes (slope of the line) """ - -# Compute the (expected) intersection of two lines function intersection( x1::T, # x coordinate of point 1 y1::T, # y coordinate of point 1 @@ -217,7 +215,7 @@ function intersection( y = a1 * x + b1 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) diff --git a/src/generators/randgraphs.jl b/src/generators/randgraphs.jl index 05c6f38db..d6b12536c 100644 --- a/src/generators/randgraphs.jl +++ b/src/generators/randgraphs.jl @@ -631,12 +631,8 @@ 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. +Return a Graph generated according to the Stochastic Block Model (SBM), sampling +from an SBM with ``c_{a,a}=cint``, and ``c_{a,b}=cext``. """ function stochastic_block_model(cint::T, cext::T, n::Vector{U}; seed::Int=-1) where T<:Real where U<:Integer K = length(n)