@@ -14,6 +14,9 @@ Regarding incident classes:
1414 - Foreign: the two classes differ.
1515 - Reflexive: source class and target class are the same class.
1616 - Symmetric: reflexive + edges are undirected ('a' points to 'b' => 'b' points to 'a').
17+ In this *bidirectional* situation: the number of edges
18+ is the number of conceptual *undirected* edges,
19+ And their order is the row-wise lower triangular only: (source <= target) pairs.
1720
1821Regarding edges density:
1922
@@ -94,10 +97,13 @@ Raise flag to skip over targets with no sources.
9497backward(:: Topology ; skip = false ) = throw(" unimplemented" )
9598export backward
9699
100+ Map = OrderedDict{Int,Int} # Used by the sparse variants.
101+ vecmap(n:: Int ) = [Map() for _ in 1 : n]
102+
97103# ==========================================================================================
98104struct SparseForeign <: Topology
99- forward:: Vector{OrderedDict{Int,Int} } # [source: {target: edge}]
100- backward:: Vector{OrderedDict{Int,Int} } # [target: {source: edge}]
105+ forward:: Vector{Map } # [source: {target: edge}]
106+ backward:: Vector{Map } # [target: {source: edge}]
101107 n_edges:: Int
102108end
103109export SparseForeign
@@ -114,11 +120,12 @@ n_targets(s::S, src::Int) = length(s.forward[src])
114120is_edge(s:: S , src:: Int , tgt:: Int ) = haskey(s. forward[src], tgt)
115121n_edges(s:: S ) = s. n_edges
116122edge(s:: S , src:: Int , tgt:: Int ) = s. forward[src][tgt]
117- edges(s:: S ) = I. flatten(I. map(enumerate(s. forward)) do (src, targets)
118- I. map(keys(targets)) do tgt
119- (src, tgt)
123+ edges(s:: S ) =
124+ I. flatmap(enumerate(s. forward)) do (src, targets)
125+ I. map(keys(targets)) do tgt
126+ (src, tgt)
127+ end
120128 end
121- end )
122129forward(s:: S ; skip = false ) =
123130 filter_map(enumerate(s. forward)) do (src, targets)
124131 (skip && isempty(targets)) ? nothing : Some((src, I. map(targets) do (tgt, edge)
@@ -140,29 +147,29 @@ Construct from non-empty entries in a sparse matrix (disregarding values).
140147"""
141148function SparseForeign(m:: AbstractSparseMatrix )
142149 n_sources, n_targets = size(m)
143- (sources, targets, _) = findnz(m)
144- n_edges = length(sources)
145- # Restore row-wise ordering.
146- o = sortperm(sources)
147- sources, targets = sources[o], targets[o]
148- D = OrderedDict{Int,Int}
149- forward = [D() for _ in 1 : n_sources]
150- backward = [D() for _ in 1 : n_targets]
150+ (sources, targets, n_edges) = rowwise(m)
151+ (forward, backward) = vecmap.((n_sources, n_targets))
151152 for (edge, (source, target)) in enumerate(zip(sources, targets))
152153 forward[source][target] = edge
153154 backward[target][source] = edge
154155 end
155156 SparseForeign(forward, backward, n_edges)
156157end
158+ # Extract data from sparse matrix with *row-wise* ordering.
159+ function rowwise(m:: AbstractSparseMatrix )
160+ (sources, targets, _) = findnz(m)
161+ n_edges = length(sources)
162+ o = sortperm(sources)
163+ (sources[o], targets[o], n_edges)
164+ end
165+
157166
158167"""
159168Construct from lit entries in a dense boolean matrix.
160169"""
161170function SparseForeign(m:: AbstractMatrix{Bool} )
162171 n_sources, n_targets = size(m)
163- D = OrderedDict{Int,Int}
164- forward = [D() for _ in 1 : n_sources]
165- backward = [D() for _ in 1 : n_targets]
172+ (forward, backward) = vecmap.((n_sources, n_targets))
166173 n_edges = 0
167174 for source in 1 : n_sources, target in 1 : n_targets
168175 m[source, target] || continue
176183# ==========================================================================================
177184struct SparseReflexive <: Topology
178185 # [node: ({source: edge}, {target: edge})]
179- nodes:: Vector{Tuple{OrderedDict{Int,Int},OrderedDict{Int,Int} }}
186+ nodes:: Vector{Tuple{Map,Map }}
180187 n_edges:: Int
181188end
182189export SparseReflexive
@@ -186,19 +193,20 @@ export SparseReflexive
186193S = SparseReflexive
187194targets(s:: S , src:: Int ) = I. map(p -> (first(p), last(p)), s. nodes[src][2 ])
188195sources(s:: S , tgt:: Int ) = I. map(p -> (first(p), last(p)), s. nodes[tgt][1 ])
189- n_sources(s:: S ) = length(s . nodes )
190- n_targets(s:: S ) = n_sources (s)
196+ n_sources(s:: S ) = n_nodes(s )
197+ n_targets(s:: S ) = n_nodes (s)
191198n_sources(s:: S , tgt:: Int ) = length(s. nodes[tgt][1 ])
192199n_targets(s:: S , src:: Int ) = length(s. nodes[src][2 ])
193200is_edge(s:: S , src:: Int , tgt:: Int ) = haskey(s. nodes[src][2 ], tgt)
194201n_edges(s:: S ) = s. n_edges
195202edge(s:: S , src:: Int , tgt:: Int ) = s. nodes[src][2 ][tgt]
196- edges(s:: S ) = I. flatten(I. map(enumerate(s. nodes)) do (src, neighbours)
197- (_, targets) = neighbours
198- I. map(keys(targets)) do tgt
199- (src, tgt)
203+ edges(s:: S ) =
204+ I. flatmap(enumerate(s. nodes)) do (src, neighbours)
205+ (_, targets) = neighbours
206+ I. map(keys(targets)) do tgt
207+ (src, tgt)
208+ end
200209 end
201- end )
202210forward(s:: S ; skip = false ) =
203211 filter_map(enumerate(s. nodes)) do (src, (_, targets))
204212 (skip && isempty(targets)) ? nothing : Some((src, I. map(targets) do (tgt, edge)
@@ -212,6 +220,11 @@ backward(s::S; skip = false) =
212220 end ))
213221 end
214222
223+ # -------------------------------------------------------------------------------------------
224+ # Extra dedicated interface.
225+ n_nodes(s:: S ) = length(s. nodes)
226+ export n_nodes
227+
215228# -------------------------------------------------------------------------------------------
216229# Construct.
217230
@@ -220,13 +233,8 @@ Construct from non-empty entries in a sparse matrix (disregarding values).
220233"""
221234function SparseReflexive(m:: AbstractSparseMatrix )
222235 n_nodes = check_square(m)
223- (sources, targets, _) = findnz(m)
224- n_edges = length(sources)
225- # Restore row-wise ordering.
226- o = sortperm(sources)
227- sources, targets = sources[o], targets[o]
228- D = OrderedDict{Int,Int}
229- nodes = [(D(), D()) for _ in 1 : n_nodes]
236+ (sources, targets, n_edges) = rowwise(m)
237+ nodes = [(Map(), Map()) for _ in 1 : n_nodes]
230238 for (edge, (source, target)) in enumerate(zip(sources, targets))
231239 nodes[source][2 ][target] = edge
232240 nodes[target][1 ][source] = edge
@@ -246,8 +254,7 @@ Construct from lit entries in a dense boolean matrix.
246254"""
247255function SparseReflexive(m:: AbstractMatrix{Bool} )
248256 n_nodes = check_square(m)
249- D = OrderedDict{Int,Int}
250- nodes = [(D(), D()) for _ in 1 : n_nodes]
257+ nodes = [(Map(), Map()) for _ in 1 : n_nodes]
251258 n_edges = 0
252259 for source in 1 : n_nodes, target in 1 : n_nodes
253260 m[source, target] || continue
@@ -257,3 +264,90 @@ function SparseReflexive(m::AbstractMatrix{Bool})
257264 end
258265 SparseReflexive(nodes, n_edges)
259266end
267+
268+ # ==========================================================================================
269+ struct SparseSymmetric <: Topology
270+ nodes:: Vector{Map} # [node: {neighbour: edge}]
271+ n_edges:: Int
272+ end
273+ export SparseSymmetric
274+
275+ # -------------------------------------------------------------------------------------------
276+ # Duties to Topology.
277+ S = SparseSymmetric # "Self"
278+ targets(s:: S , src:: Int ) = I. map(p -> (first(p), last(p)), s. nodes[src])
279+ sources(s:: S , tgt:: Int ) = targets(s, tgt)
280+ n_sources(s:: S ) = n_nodes(s)
281+ n_targets(s:: S ) = n_nodes(s)
282+ n_sources(s:: S , tgt:: Int ) = n_neighbours(s, tgt)
283+ n_targets(s:: S , src:: Int ) = n_neighbours(s, src)
284+ is_edge(s:: S , src:: Int , tgt:: Int ) = haskey(s. nodes[src], tgt) # Accept both directions.
285+ n_edges(s:: S ) = s. n_edges
286+ edge(s:: S , src:: Int , tgt:: Int ) = s. nodes[src][tgt]
287+ edges(s:: S ) =
288+ I. flatmap(enumerate(s. nodes)) do (src, targets)
289+ I. map(stopwhen(> (src), keys(targets))) do tgt
290+ (src, tgt)
291+ end
292+ end
293+ forward(s:: S ; skip = false ) = adjacency(s; skip)
294+ backward(s:: S ; skip = false ) = adjacency(s; skip)
295+
296+ # -------------------------------------------------------------------------------------------
297+ # Extra dedicated interface.
298+ n_nodes(s:: S ) = length(s. nodes)
299+ neighbours(s:: S , src:: Int ) = I. map(p -> (first(p), last(p)), s. nodes[src])
300+ neighbour_nodes(s:: S , src:: Int ) = I. map(first, neighbours(s, src))
301+ neighbour_edges(s:: S , src:: Int ) = I. map(last, neighbours(s, src))
302+ n_neighbours(s:: S , n:: Int ) = length(s. nodes[n])
303+ # Lower the 'upper' flag to only get lower triangle and have every edge yielded only once.
304+ adjacency(s:: S ; skip = false , upper = true ) =
305+ filter_map(enumerate(s. nodes)) do (src, neighbours)
306+ (skip && (isempty(neighbours) || ! upper && first(keys(neighbours)) > src)) ?
307+ nothing :
308+ Some((
309+ src,
310+ I. map(stopwhen(((tgt, _),) -> ! upper && tgt > src, neighbours)) do (ngb, edge)
311+ (ngb, edge)
312+ end ,
313+ ))
314+ end
315+ export neighbours, neighbour_nodes, neighbour_edges, n_neighbours, adjacency
316+
317+ # -------------------------------------------------------------------------------------------
318+ # Construct.
319+
320+ """
321+ Construct from non-empty entries in a lower-triangular sparse matrix
322+ (disregarding values and upper triangle).
323+ """
324+ function SparseSymmetric(m:: AbstractSparseMatrix )
325+ n_nodes = check_square(m)
326+ (sources, targets, _) = rowwise(m)
327+ nodes = vecmap(n_nodes)
328+ n_edges = 0
329+ for (source, target) in zip(sources, targets)
330+ source < target && continue # Dismiss upper triangle.
331+ n_edges += 1
332+ nodes[source][target] = n_edges
333+ nodes[target][source] = n_edges
334+ end
335+ SparseSymmetric(nodes, n_edges)
336+ end
337+
338+ """
339+ Construct from lit entries in a lower-triangular dense boolean matrix
340+ (disregarding upper triangle).
341+ """
342+ function SparseSymmetric(m:: AbstractMatrix{Bool} )
343+ n_nodes = check_square(m)
344+ nodes = vecmap(n_nodes)
345+ n_edges = 0
346+ for source in 1 : n_nodes, target in 1 : source # Triangular iteration.
347+ m[source, target] || continue
348+ n_edges += 1
349+ nodes[source][target] = n_edges
350+ nodes[target][source] = n_edges
351+ end
352+ SparseSymmetric(nodes, n_edges)
353+ end
0 commit comments