|
| 1 | +## Adjacency list storage |
| 2 | + |
| 3 | +abstract type AbstractAdjListStorage{T,D} end |
| 4 | +Base.IteratorSize(::Type{<:AbstractAdjListStorage}) = Base.HasLength() |
| 5 | +Base.IteratorEltype(::Type{<:AbstractAdjListStorage}) = Base.HasEltype() |
| 6 | +Base.eltype(::Type{<:AbstractAdjListStorage{T}}) where T = Edge{T} |
| 7 | +function add_edges!(adjlist::AbstractAdjListStorage, edges; all::Bool=true) |
| 8 | + count = 0 |
| 9 | + for edge in edges |
| 10 | + if add_edge!(adjlist, edge) |
| 11 | + count += 1 |
| 12 | + elseif all |
| 13 | + return count |
| 14 | + end |
| 15 | + end |
| 16 | + return count |
| 17 | +end |
| 18 | + |
| 19 | +# Storage matching Graphs.SimpleGraph for high edge counts |
| 20 | +struct SimpleAdjListStorage{T,D} <: AbstractAdjListStorage{T,D} |
| 21 | + fadjlist::Vector{Vector{T}} |
| 22 | + badjlist::Vector{Vector{T}} |
| 23 | +end |
| 24 | +SimpleAdjListStorage{T,D}() where {T,D} = |
| 25 | + SimpleAdjListStorage{T,D}(Vector{Vector{T}}(), |
| 26 | + Vector{Vector{T}}()) |
| 27 | +Base.copy(adjlist::SimpleAdjListStorage{T,D}) where {T,D} = |
| 28 | + SimpleAdjListStorage{T,D}(copy(adjlist.fadjlist), |
| 29 | + copy(adjlist.badjlist)) |
| 30 | +Base.length(adjlist::SimpleAdjListStorage) = |
| 31 | + sum(length, adjlist.fadjlist; init=0) |
| 32 | +function Base.iterate(adjlist::SimpleAdjListStorage{T}) where T |
| 33 | + idx = findfirst(x->!isempty(x), adjlist.fadjlist) |
| 34 | + if idx === nothing |
| 35 | + return nothing |
| 36 | + end |
| 37 | + edge_idx = something(findfirst(x->!isempty(x), adjlist.fadjlist[idx])) |
| 38 | + state = (T(idx), T(edge_idx)) |
| 39 | + return Base.iterate(adjlist, state) |
| 40 | +end |
| 41 | +function Base.iterate(adjlist::SimpleAdjListStorage{T}, state::Tuple{T,T}) where T |
| 42 | + src, dst = state |
| 43 | + if src > length(adjlist.fadjlist) |
| 44 | + return nothing |
| 45 | + elseif dst > length(adjlist.fadjlist[src]) |
| 46 | + src += one(T) |
| 47 | + dst = one(T) |
| 48 | + head = src |
| 49 | + src = findfirst(x->!isempty(x), @view(adjlist.fadjlist[head:end])) |
| 50 | + if src === nothing |
| 51 | + return nothing |
| 52 | + end |
| 53 | + src = T(src) |
| 54 | + # Shift by offset from @view |
| 55 | + src += head - one(T) |
| 56 | + end |
| 57 | + value = (src, adjlist.fadjlist[src][dst]) |
| 58 | + dst += one(T) |
| 59 | + return (Edge(value), (src, dst)) |
| 60 | +end |
| 61 | +function Graphs.add_edge!(adjlist::SimpleAdjListStorage{T,D}, edge) where {T,D} |
| 62 | + src, dst = Tuple(edge) |
| 63 | + if !D |
| 64 | + src, dst = minmax(src, dst) |
| 65 | + end |
| 66 | + |
| 67 | + has_edge(adjlist, edge) && return false |
| 68 | + |
| 69 | + # If necessary, allocate more inner vectors |
| 70 | + nv = max(src, dst) |
| 71 | + if nv > length(adjlist.fadjlist) |
| 72 | + #= FIXME: Resize more efficiently, and use undef elements |
| 73 | + resize!(adjlist.fadjlist, nv) |
| 74 | + if D |
| 75 | + resize!(adjlist.badjlist, nv) |
| 76 | + end |
| 77 | + isassigned(adjlist.fadjlist, src) || (adjlist.fadjlist[src] = Vector{T}()) |
| 78 | + isassigned(adjlist.fadjlist, dst) || (adjlist.fadjlist[dst] = Vector{T}()) |
| 79 | + if D |
| 80 | + isassigned(adjlist.badjlist, src) || (adjlist.badjlist[src] = Vector{T}()) |
| 81 | + isassigned(adjlist.badjlist, dst) || (adjlist.badjlist[dst] = Vector{T}()) |
| 82 | + end |
| 83 | + =# |
| 84 | + idx = length(adjlist.fadjlist)+1 |
| 85 | + for _ in idx:nv |
| 86 | + push!(adjlist.fadjlist, Vector{T}()) |
| 87 | + if D |
| 88 | + push!(adjlist.badjlist, Vector{T}()) |
| 89 | + end |
| 90 | + end |
| 91 | + end |
| 92 | + |
| 93 | + # Add edges |
| 94 | + if D |
| 95 | + # Directed graphs have only forward edges |
| 96 | + push!(adjlist.fadjlist[src], dst) |
| 97 | + push!(adjlist.badjlist[dst], src) |
| 98 | + else |
| 99 | + # Undirected graphs have both forward and backward edges |
| 100 | + push!(adjlist.fadjlist[src], dst) |
| 101 | + push!(adjlist.fadjlist[dst], src) |
| 102 | + end |
| 103 | + |
| 104 | + return true |
| 105 | +end |
| 106 | +function Graphs.has_edge(adjlist::SimpleAdjListStorage{T,D}, edge) where {T,D} |
| 107 | + src, dst = Tuple(edge) |
| 108 | + if !D |
| 109 | + src, dst = (min(src, dst), max(src, dst)) |
| 110 | + end |
| 111 | + if length(adjlist.fadjlist) >= src && dst in adjlist.fadjlist[src] |
| 112 | + return true |
| 113 | + end |
| 114 | + return false |
| 115 | +end |
| 116 | + |
| 117 | +# Storage for sparse background graphs |
| 118 | +struct SparseAdjListStorage{T,D} <: AbstractAdjListStorage{T,D} |
| 119 | + adjlist::Vector{Tuple{T,T}} |
| 120 | +end |
| 121 | +SparseAdjListStorage{T,D}() where {T,D} = |
| 122 | + SparseAdjListStorage{T,D}(Vector{Tuple{T,T}}()) |
| 123 | +Base.copy(adjlist::SparseAdjListStorage{T,D}) where {T,D} = |
| 124 | + SparseAdjListStorage{T,D}(copy(adjlist.adjlist)) |
| 125 | +Base.length(adjlist::SparseAdjListStorage) = length(adjlist.adjlist) |
| 126 | +function Base.iterate(adjlist::SparseAdjListStorage{T}, state=one(T)) where T |
| 127 | + if state > length(adjlist.adjlist) |
| 128 | + return nothing |
| 129 | + end |
| 130 | + value = adjlist.adjlist[state] |
| 131 | + return (Edge(value), state+one(T)) |
| 132 | +end |
| 133 | +function Graphs.add_edge!(adjlist::SparseAdjListStorage, edge) |
| 134 | + if findfirst(==(Tuple(edge)), adjlist.adjlist) !== nothing |
| 135 | + return false |
| 136 | + end |
| 137 | + push!(adjlist.adjlist, Tuple(edge)) |
| 138 | + return true |
| 139 | +end |
| 140 | +function add_edges!(adjlist::SparseAdjListStorage, edges; all::Bool=true) |
| 141 | + # FIXME: Account for non-directedness |
| 142 | + edge_set = Set(map(Tuple, edges)) |
| 143 | + for edge in adjlist.adjlist |
| 144 | + if edge in edge_set |
| 145 | + if all |
| 146 | + return 0 |
| 147 | + else |
| 148 | + pop!(edge_set, edge) |
| 149 | + end |
| 150 | + end |
| 151 | + end |
| 152 | + append!(adjlist.adjlist, collect(edge_set)) |
| 153 | + return length(edge_set) |
| 154 | +end |
| 155 | +function Graphs.has_edge(adjlist::SparseAdjListStorage{T,D}, edge) where {T,D} |
| 156 | + src, dst = Tuple(edge) |
| 157 | + if !D |
| 158 | + src, dst = (min(src, dst), max(src, dst)) |
| 159 | + end |
| 160 | + return findfirst(x->x==(src, dst), adjlist.adjlist) !== nothing |
| 161 | +end |
| 162 | + |
| 163 | +## Adjacency list implementation |
| 164 | + |
| 165 | +struct AdjList{T,D,A<:AbstractAdjListStorage{T,D}} |
| 166 | + data::A |
| 167 | +end |
| 168 | +AdjList{T,D}(adjlist::AbstractAdjListStorage{T,D}) where {T,D} = |
| 169 | + AdjList{T,D,typeof(adjlist)}(adjlist) |
| 170 | +#AdjList{T,D}() where {T,D} = AdjList{T,D}(SimpleAdjListStorage{T,D}()) |
| 171 | +AdjList{T,D}() where {T,D} = AdjList{T,D}(SparseAdjListStorage{T,D}()) |
| 172 | +AdjList() = AdjList{Int,true}() |
| 173 | +Base.copy(adj::AdjList{T,D,A}) where {T,D,A} = AdjList{T,D,A}(copy(adj.data)) |
| 174 | +Graphs.ne(adj::AdjList) = length(adj.data) # TODO: Use ne() |
| 175 | +Graphs.has_edge(adj::AdjList{T}, src::Integer, dst::Integer) where T = |
| 176 | + has_edge(adj.data, Edge{T}(src, dst)) |
| 177 | +Graphs.has_edge(adj::AdjList{T,D}, edge) where {T,D} = has_edge(adj.data, edge) |
| 178 | +Graphs.add_edge!(adj::AdjList{T}, src::Integer, dst::Integer) where T = |
| 179 | + add_edge!(adj, Edge{T}(src, dst)) |
| 180 | +Graphs.add_edge!(adj::AdjList, edge) = add_edge!(adj.data, edge) |
| 181 | +add_edges!(adj::AdjList, edges; all::Bool=true) = add_edges!(adj.data, edges; all) |
| 182 | +Graphs.edges(adj::AdjList) = copy(adj.data) |
| 183 | +function Graphs.inneighbors(adj::AdjList{T,D}, v::Integer) where {T,D} |
| 184 | + neighbors = Int[] |
| 185 | + for edge in adj.data |
| 186 | + src, dst = Tuple(edge) |
| 187 | + if dst == v |
| 188 | + push!(neighbors, src) |
| 189 | + elseif !D && src == v |
| 190 | + push!(neighbors, dst) |
| 191 | + end |
| 192 | + end |
| 193 | + sort!(neighbors) |
| 194 | + unique!(neighbors) |
| 195 | + return neighbors |
| 196 | +end |
| 197 | +function Graphs.inneighbors(adj::AdjList{T,D,SimpleAdjListStorage{T,D}}, v::Integer) where {T,D} |
| 198 | + if D |
| 199 | + return length(adj.data.badjlist) >= v ? copy(adj.data.badjlist[v]) : T[] |
| 200 | + else |
| 201 | + if length(adj.data.fadjlist) >= v |
| 202 | + neighbors = copy(adj.data.fadjlist[v]) |
| 203 | + sort!(neighbors) |
| 204 | + unique!(neighbors) |
| 205 | + return neighbors |
| 206 | + else |
| 207 | + return T[] |
| 208 | + end |
| 209 | + end |
| 210 | +end |
| 211 | +function Graphs.outneighbors(adj::AdjList{T,D}, v::Integer) where {T,D} |
| 212 | + neighbors = Int[] |
| 213 | + for edge in adj.data |
| 214 | + src, dst = Tuple(edge) |
| 215 | + if src == v |
| 216 | + push!(neighbors, dst) |
| 217 | + elseif !D && dst == v |
| 218 | + push!(neighbors, src) |
| 219 | + end |
| 220 | + end |
| 221 | + sort!(neighbors) |
| 222 | + unique!(neighbors) |
| 223 | + return neighbors |
| 224 | +end |
| 225 | +function Graphs.outneighbors(adj::AdjList{T,SimpleAdjListStorage{T,D}}, v::Integer) where {T,D} |
| 226 | + if D |
| 227 | + return length(adj.data.fadjlist) >= v ? copy(adj.data.fadjlist[v]) : T[] |
| 228 | + else |
| 229 | + if length(adj.data.fadjlist) >= v |
| 230 | + neighbors = copy(adj.data.fadjlist[v]) |
| 231 | + sort!(neighbors) |
| 232 | + unique!(neighbors) |
| 233 | + return neighbors |
| 234 | + else |
| 235 | + return T[] |
| 236 | + end |
| 237 | + end |
| 238 | +end |
0 commit comments