diff --git a/Project.toml b/Project.toml index 669b455a..c1956e3b 100644 --- a/Project.toml +++ b/Project.toml @@ -9,6 +9,7 @@ Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" +Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Mangal = "b8b640a6-63d9-51e6-b784-5033db27bef2" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" diff --git a/src/SpeciesInteractionNetworks.jl b/src/SpeciesInteractionNetworks.jl index 97f3f491..b4494bd5 100644 --- a/src/SpeciesInteractionNetworks.jl +++ b/src/SpeciesInteractionNetworks.jl @@ -14,6 +14,7 @@ using StatsBase using TestItems import Tables import Mangal +import Graphs # Various utilities for probabilities # include(joinpath(".", "misc", "probabilities.jl")) @@ -45,6 +46,7 @@ include("interfaces/iteration.jl") include("interfaces/table.jl") include("interfaces/broadcast.jl") include("interfaces/linearalgebra.jl") +include("interfaces/graphs.jl") export svd, rank, diag export complexity, tsvd, rdpg diff --git a/src/interfaces/graphs.jl b/src/interfaces/graphs.jl new file mode 100644 index 00000000..02e09067 --- /dev/null +++ b/src/interfaces/graphs.jl @@ -0,0 +1,70 @@ +""" + Base.reverse(N::SpeciesInteractionNetwork{<:Partiteness, <:Interactions}) + +Returns a copy of the network, in which the interactions have been flipped. In +other words, an interaction A → B is now B → A. This maintains the nature of the +interaction, and works for loops, self-edges etc. +""" +function Base.reverse(N::SpeciesInteractionNetwork{<:Partiteness, <:Interactions}) + M = copy(N) + reverse!(M) +end + +""" + Base.reverse!(N::SpeciesInteractionNetwork{<:Partiteness, <:Interactions}) + +Modifies the network given as its argument so that the interactions are flipped. +See [`reverse`](@ref) for more information. +""" +function Base.reverse!(N::SpeciesInteractionNetwork{<:Partiteness, <:Interactions}) + for interaction in interactions(N) + N[interaction[2], interaction[1]], N[interaction[1], interaction[2]] = N[interaction[1], interaction[2]], N[interaction[2], interaction[1]] + end + return N +end + +@testitem "We can reverse a network" begin + edges = Binary(Bool[0 1 0 0; 0 0 1 0; 1 0 0 0; 0 1 1 1]) + nodes = Unipartite(edges) + N = SpeciesInteractionNetwork(nodes, edges) + R = reverse(N) + for interaction in interactions(R) + @test N[interaction[2], interaction[1]] == interaction[3] + end +end + +Graphs.has_vertex(N::T, v) where {T <: SpeciesInteractionNetwork} = v in species(N) + +@testitem "We can check the existence of a vertex" begin + import SpeciesInteractionNetworks.Graphs + edges = Binary(Bool[0 1 0 0; 0 0 1 0; 1 0 0 0; 0 1 1 1]) + nodes = Unipartite(edges) + N = SpeciesInteractionNetwork(nodes, edges) + @test Graphs.has_vertex(N, :node_1) + @test !Graphs.has_vertex(N, :node_1000) +end + +function Graphs.has_edge(N::T, s, d) where {T <: SpeciesInteractionNetwork} + if !(Graphs.has_vertex(N, s) && Graphs.has_vertex(N, d)) + return false + else + return !iszero(N[s,d]) + end +end + +@testitem "We can check the existence of an edge" begin + import SpeciesInteractionNetworks.Graphs + edges = Binary(Bool[0 1 0 0; 0 0 1 0; 1 0 0 0; 0 1 1 1]) + nodes = Unipartite(edges) + N = SpeciesInteractionNetwork(nodes, edges) + @test Graphs.has_edge(N, :node_1, :node_2) + @test !Graphs.has_edge(N, :node_2, :node_1) + @test !Graphs.has_edge(N, :node_2000, :node_1) + @test !Graphs.has_edge(N, :node_2, :node_1000) + @test !Graphs.has_edge(N, :node_20000, :node_1000) +end + +Graphs.inneighbors(N::T, v) where {T <: SpeciesInteractionNetwork} = predecessors(N, v) +Graphs.outneighbors(N::T, v) where {T <: SpeciesInteractionNetwork} = successors(N, v) + +Graphs.edgetype(N::T) where {T <: SpeciesInteractionNetwork} = eltype(N) \ No newline at end of file