diff --git a/Manifest.toml b/Manifest.toml index bd7120126..e48015ca6 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -16,10 +16,10 @@ uuid = "9e28174c-4ba2-5203-b857-d8d62c4213ee" version = "0.8.10" [[BinaryProvider]] -deps = ["Libdl", "Pkg", "SHA", "Test"] -git-tree-sha1 = "055eb2690182ebc31087859c3dd8598371d3ef9e" +deps = ["Libdl", "SHA"] +git-tree-sha1 = "c7361ce8a2129f20b0e05a89f7070820cfed6648" uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232" -version = "0.5.3" +version = "0.5.4" [[Combinatorics]] deps = ["LinearAlgebra", "Polynomials", "Test"] @@ -79,10 +79,10 @@ deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[Missings]] -deps = ["Dates", "InteractiveUtils", "SparseArrays", "Test"] -git-tree-sha1 = "d1d2585677f2bd93a97cfeb8faa7a0de0f982042" +deps = ["SparseArrays", "Test"] +git-tree-sha1 = "f0719736664b4358aa9ec173077d4285775f8007" uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "0.4.0" +version = "0.4.1" [[Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" @@ -95,9 +95,9 @@ version = "1.1.0" [[PDMats]] deps = ["Arpack", "LinearAlgebra", "SparseArrays", "SuiteSparse", "Test"] -git-tree-sha1 = "b6c91fc0ab970c0563cbbe69af18d741a49ce551" +git-tree-sha1 = "8b68513175b2dc4023a564cb0e917ce90e74fd69" uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" -version = "0.9.6" +version = "0.9.7" [[Pkg]] deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] diff --git a/Project.toml b/Project.toml index dea7a45d0..644ec5d63 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,7 @@ Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Requires = "ae029012-a4dd-5104-9daa-d747884805df" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/docs/make.jl b/docs/make.jl index d2aa81dbf..5d7fb7540 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -26,8 +26,9 @@ makedocs( "Centrality and paths" => "properties/paths.md", "Overlap and similarity" => "properties/overlap.md", "Null models" => "properties/nullmodels.md", + "Beta-diversity" => "properties/betadiversity.md", + "Resilience" => "properties/resilience.md" "Information theory" => "properties/information.md", - "Beta-diversity" => "properties/betadiversity.md" ] ] ) diff --git a/docs/src/properties/resilience.md b/docs/src/properties/resilience.md new file mode 100644 index 000000000..9e69e3199 --- /dev/null +++ b/docs/src/properties/resilience.md @@ -0,0 +1,49 @@ +# Resilience + +We provide the metrics proposed by Gao et al (2016) which summarize the global +behaviour of complex unipartite networks. The dynamics of a system of N components +(nodes/species) can follow the coupled nonlinear differential equation + +$$ +\frac{\text{d}x_i}{\text{d}t} = F(x_i) + \sum_{j=1}^N A_{ij}G(x_i, x_j) +$$ + +where the adjacency matrix $$A$$ captures the interaction between the components. + +This system can be descibed in 1-D using an effective term + +$$ +\frac{\text{d}x_\text{eff}}{\text{d}t} = F(x_\text{eff}) + \beta_\text{eff}G(x_\text{eff}, x_\text{eff}) +$$ + +with $$\beta_\text{eff}$$ a single resilience parameter which can capture +the effect of perturbating the system (node/link removal, weight change...). +This resience parameter can be computed from an `AbstractUnipartiteNetwork` +using the functions `βeff` or `resilience`. + +It can be shown that + +$$ +\beta_\text{eff} = \langle s \rangle + \mathcal{S} \mathcal{H}\,, +$$ + +with + +- $$\langle s \rangle$$ the average weighted degree (computed using `s_mean`), +- $$\mathcal{S}$$ the symmetry(computed using `symmetry`), +- $$\mathcal{H}$$ the heterogeneity (computed using `heterogeneity`). + + +> Goa, J., Barzael, B. and Barabási 2016. Universal resilience patterns in complex networks. +> Nature 530(7590), 307-312. doi:10.1038/nature16948 + +## Available functions + +```@docs +s +σ_in +σ_out +symmetry +heterogeneity +resilience +``` diff --git a/src/EcologicalNetworks.jl b/src/EcologicalNetworks.jl index 29bad9c4e..4d7263425 100644 --- a/src/EcologicalNetworks.jl +++ b/src/EcologicalNetworks.jl @@ -108,6 +108,13 @@ include(joinpath(".", "community/overlap.jl")) export overlap export AJS, EAJS +# Overlap +include(joinpath(".", "community/resilience.jl")) +export resilience +export symmetry, heterogeneity +export s +export σ_in, σ_out + # Modularity include(joinpath(".", "modularity/utilities.jl")) export Q, Qr diff --git a/src/community/resilience.jl b/src/community/resilience.jl new file mode 100644 index 000000000..627b5e1e7 --- /dev/null +++ b/src/community/resilience.jl @@ -0,0 +1,103 @@ +#= +Some functions from *Universal resilience patterns in complex networks* +by Gao et al. (2016) +=# + +using Statistics: mean, std + +""" + s(N::AbstractUnipartiteNetwork; dims::Union{Nothing,Integer}=nothing) + +Computes the average weighted degree. This is proportional to the (weighted) +density of interactions. + +If dims is provided, the incoming (`dims=1`) or outgoing (`dims=2`) is computed. +""" +function s(N::AbstractUnipartiteNetwork; dims::Union{Nothing,Integer}=nothing) + dims == 1 && return sum(N.A, dims=2) + dims == 2 && return sum(N.A, dims=1)' + if dims == nothing + return sum(N.A) / size(N)[1] + end +end +#= +""" + s(N::AbstractUnipartiteNetwork; dims) + +Computes the vector of incoming weighted degrees of an unipartite network. +""" +s_in(N::AbstractUnipartiteNetwork) = sum(N.A, dims=2) + +""" + s_out(N::AbstractUnipartiteNetwork) + +Computes the vector of outgoing weighted degrees of an unipartite network. +""" +s_out(N::AbstractUnipartiteNetwork) = sum(N.A, dims=1)' + +=# + +""" + σ_in(N::AbstractUnipartiteNetwork) + +Computes the standard deviation of the ingoing weighted degree of an unipartite +network. +""" +σ_in(N::AbstractUnipartiteNetwork) = std(s(N, dims=1), corrected=false) + +""" + σ_out(N::AbstractUnipartiteNetwork) + +Computes the standard deviation of the outgoing weighted degree of an unipartite +network. +""" +σ_out(N::AbstractUnipartiteNetwork) = std(s(N, dims=2), corrected=false) + +""" + symmetry(N::AbstractUnipartiteNetwork) + +Computes the symmetry between s^in and s^out (the in- and outgoing weighted +degree of an unipartite network). This is computed as the Pearson correlation +between the s^in and s^out. It is hence a value between -1 and 1, where high +positive values indicate that species with many outgoing degrees tend to have +many ingoing degrees and negative values mean the opposite. An undirected network +is perfectly symmetric but, for example, a food web where predators are less likely +to be prey would have a negative symmetry. + +> Goa, J., Barzael, B. and Barabási 2016. Universal resilience patterns in complex networks. +> Nature 530(7590), 307-312. doi:10.1038/nature16948 + +""" +symmetry(N::AbstractUnipartiteNetwork) = (mean(s(N, dims=1) .* s(N, dims=2)) - mean(s(N, dims=1)) * mean(s(N, dims=2))) / (σ_in(N) * σ_out(N)) + +""" + heterogeneity(N::AbstractUnipartiteNetwork) + +Computes the heterogeneity for an unipartite network, a topological characteristic +which quantifies the difference in in- and outgoing degrees between species. It +is computed as σ_in * σ_out / s_mean. A value of 0 indicates that all species +have the same (weighted) in- and outdegrees. + +> Goa, J., Barzael, B. and Barabási 2016. Universal resilience patterns in complex networks. +> Nature 530(7590), 307-312. doi:10.1038/nature16948 + +""" +heterogeneity(N::AbstractUnipartiteNetwork) = (σ_in(N) * σ_out(N)) / s(N) + +""" + resilience(N::AbstractUnipartiteNetwork) + +A resilience parameters described by Gao et al. (2016). It is a global parameters +describing the dynamics of an unipartite network as an effective 1D equation of +the form + +f(xeff) = F(xeff) + βeff G(xeff, xeff) + +i.e. describing a second-order term representing the effect of the network on the +dynamics of the 'effective state' xeff of the system. + +> Goa, J., Barzael, B. and Barabási 2016. Universal resilience patterns in complex networks. +> Nature 530(7590), 307-312. doi:10.1038/nature16948 + +""" +resilience(N::AbstractUnipartiteNetwork) = dot(s(N, dims=1), s(N, dims=2)) / sum(N) diff --git a/test/community/resilience.jl b/test/community/resilience.jl new file mode 100644 index 000000000..861c746d9 --- /dev/null +++ b/test/community/resilience.jl @@ -0,0 +1,42 @@ +module TestResilience + using Test, EcologicalNetworks + + A = rand([0, 1, 0.5], 10, 10) + N = UnipartiteQuantitativeNetwork(A) + + @assert s(N) ≈ sum(A) / 10 + @assert s(N, dims=1) ≈ sum(A, dims=2) + @assert s(N, dims=2) ≈ sum(A, dims=1)' + + @assert resilience(N) ≈ s(N) + symmetry(N) * heterogeneity(N) + + # species only interact with themselves + Nid = UnipartiteNetwork([true false false false; + false true false false; + false false true false; + false false false true]) + + @assert heterogeneity(Nid) ≈ 0.0 + # @assert symmetry(Nid) NAN + + # symetric network + Nsym = UnipartiteNetwork([false true true true; + true false false false; + true false false false; + true false false false]) + @assert symmetry(Nsym) ≈ 1.0 # in- and out degree is same + @assert heterogeneity(Nsym) ≈ 0.5 + + + # asymetric network + Nasym = UnipartiteNetwork([false true true true; + false false false false; + false false false false; + false false false false]) + @assert symmetry(Nasym) ≈ -1.0 # in- and out degree is same + @assert heterogeneity(Nasym) ≈ 0.75 + + v = ones(10) / 10 + Nhomo = UnipartiteProbabilisticNetwork(v * v') + @assert isapprox(heterogeneity(Nhomo), 0.0, atol=1e-10) +end diff --git a/test/runtests.jl b/test/runtests.jl index f40afcfd2..55922882d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -25,6 +25,7 @@ my_tests = [ "community/centrality.jl", "community/motifs.jl", "community/foodwebs.jl", + "community/resilience.jl", "betadiversity/operations.jl", "betadiversity/partitions.jl", "modularity/utilities.jl",