Skip to content

Commit 2249f8a

Browse files
committed
🚧 ✨ FullForeign.
1 parent 9ffdeb6 commit 2249f8a

File tree

2 files changed

+114
-11
lines changed

2 files changed

+114
-11
lines changed

‎src/Networks/topologies.jl‎

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Regarding incident classes:
1616
- Symmetric: reflexive + edges are undirected ('a' points to 'b' => 'b' points to 'a').
1717
In this *bidirectional* situation: the number of edges
1818
is the number of conceptual *undirected* edges,
19-
And their order is the row-wise lower triangular only: (source <= target) pairs.
19+
And their order is the row-wise lower triangular only: (source >= target) pairs.
2020
2121
Regarding edges density:
2222
@@ -82,7 +82,7 @@ export edges
8282
"""
8383
Obtain a nested iterable over edges:
8484
first level are sources, second level are their incident targets and edges.
85-
Collecting the two levels yields [(source, [(target, edge), ..]), ..]
85+
Collecting the two levels yields [(source, [(target, edge), ..]), ..].
8686
Raise flag to skip over sources with no targets.
8787
"""
8888
forward(::Topology; skip = false) = throw("unimplemented")
@@ -91,12 +91,47 @@ export forward
9191
"""
9292
Obtain a nested iterable over edges:
9393
first level are targets, second level are their incident sources and edges.
94-
Collecting the two levels yields [(target, [(source, edge), ..]), ..]
94+
Collecting the two levels yields [(target, [(source, edge), ..]), ..].
9595
Raise flag to skip over targets with no sources.
9696
"""
9797
backward(::Topology; skip = false) = throw("unimplemented")
9898
export backward
9999

100+
#-------------------------------------------------------------------------------------------
101+
# Extend interface for constrained topologies.
102+
"""
103+
A 'square' topology within one class, both source and target of the web.
104+
"""
105+
abstract type ReflexiveTopology <: Topology end
106+
"""
107+
A 'bidirectional' reflexive topology:
108+
if a targets b then b targets a, and this only counts for one edge.
109+
"""
110+
abstract type SymmetricTopology <: ReflexiveTopology end
111+
112+
"""
113+
Obtain the number of nodes in the topology.
114+
"""
115+
n_nodes(::ReflexiveTopology) = throw("unimplemented")
116+
export n_nodes
117+
118+
"""
119+
Obtain neighbours in a symmetric topology, neither/both targets or/and sources.
120+
"""
121+
neighbours(::SymmetricTopology, ::Int) = throw("unimplemented")
122+
neighbours_nodes(::SymmetricTopology, ::Int) = throw("unimplemented")
123+
neighbours_edges(::SymmetricTopology, ::Int) = throw("unimplemented")
124+
n_neighbours(::SymmetricTopology, ::Int) = throw("unimplemented")
125+
export neighbours, neighbour_nodes, neighbour_edges, n_neighbours
126+
127+
"""
128+
Obtain a nested iterable over neighbours and edges like 'forward' or 'backward'.
129+
Lower 'upper' flag to skip over duplicate edges and yield only lower-triangular ones,
130+
with (source >= target).
131+
"""
132+
adjacency(::SymmetricTopology; skip = false, upper = true) = throw("unimplemented")
133+
export adjacency
134+
100135
Map = OrderedDict{Int,Int} # Used by the sparse variants.
101136
vecmap(n::Int) = [Map() for _ in 1:n]
102137

@@ -181,7 +216,7 @@ function SparseForeign(m::AbstractMatrix{Bool})
181216
end
182217

183218
# ==========================================================================================
184-
struct SparseReflexive <: Topology
219+
struct SparseReflexive <: ReflexiveTopology
185220
# [node: ({source: edge}, {target: edge})]
186221
nodes::Vector{Tuple{Map,Map}}
187222
n_edges::Int
@@ -220,10 +255,8 @@ backward(s::S; skip = false) =
220255
end))
221256
end
222257

223-
#-------------------------------------------------------------------------------------------
224-
# Extra dedicated interface.
258+
# Duties to ReflexiveTopology.
225259
n_nodes(s::S) = length(s.nodes)
226-
export n_nodes
227260

228261
#-------------------------------------------------------------------------------------------
229262
# Construct.
@@ -266,7 +299,7 @@ function SparseReflexive(m::AbstractMatrix{Bool})
266299
end
267300

268301
# ==========================================================================================
269-
struct SparseSymmetric <: Topology
302+
struct SparseSymmetric <: SymmetricTopology
270303
nodes::Vector{Map} # [node: {neighbour: edge}]
271304
n_edges::Int
272305
end
@@ -293,9 +326,10 @@ edges(s::S) =
293326
forward(s::S; skip = false) = adjacency(s; skip)
294327
backward(s::S; skip = false) = adjacency(s; skip)
295328

296-
#-------------------------------------------------------------------------------------------
297-
# Extra dedicated interface.
329+
# Duties to ReflexiveTopology.
298330
n_nodes(s::S) = length(s.nodes)
331+
332+
# Duties to SymmetricTopology.
299333
neighbours(s::S, src::Int) = I.map(p -> (first(p), last(p)), s.nodes[src])
300334
neighbour_nodes(s::S, src::Int) = I.map(first, neighbours(s, src))
301335
neighbour_edges(s::S, src::Int) = I.map(last, neighbours(s, src))
@@ -312,7 +346,6 @@ adjacency(s::S; skip = false, upper = true) =
312346
end,
313347
))
314348
end
315-
export neighbours, neighbour_nodes, neighbour_edges, n_neighbours, adjacency
316349

317350
#-------------------------------------------------------------------------------------------
318351
# Construct.
@@ -351,3 +384,34 @@ function SparseSymmetric(m::AbstractMatrix{Bool})
351384
end
352385
SparseSymmetric(nodes, n_edges)
353386
end
387+
388+
# ==========================================================================================
389+
struct FullForeign <: Topology
390+
n_sources::Int
391+
n_targets::Int
392+
end
393+
export FullForeign
394+
395+
S = FullForeign # "Self"
396+
n_sources(s::S) = s.n_sources
397+
n_targets(s::S) = s.n_targets
398+
targets(s::S, ::Int) = 1:n_targets(s)
399+
sources(s::S, ::Int) = 1:n_sources(s)
400+
n_sources(s::S, ::Int) = n_sources(s)
401+
n_targets(s::S, ::Int) = n_targets(s)
402+
is_edge(::S, ::Int, ::Int) = true # Assuming correct input.
403+
n_edges(s::S) = n_sources(s) * n_targets(s)
404+
edge(s::S, src::Int, tgt::Int) = (src - 1) * n_targets(s) + tgt
405+
edges(s::S) = ((src, tgt) for src in 1:n_sources(s) for tgt in 1:n_targets(s))
406+
forward(s::S; skip = false) =
407+
I.map(1:n_sources(s)) do src
408+
(src, I.map(1:n_targets(s)) do tgt
409+
(tgt, edge(s, src, tgt))
410+
end)
411+
end
412+
backward(s::S; skip = false) =
413+
I.map(1:n_targets(s)) do tgt
414+
(tgt, I.map(1:n_sources(s)) do src
415+
(src, edge(s, src, tgt))
416+
end)
417+
end

‎test/networks/topologies.jl‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ function check_adjacent(top, (nodes, nb), n, expected)
88
actual = collect(nodes(top, n))
99
@test length(actual) == nb(top, n)
1010
@test expected == actual
11+
true
1112
end
1213

1314
check_targets(top, src, exp) = check_adjacent(top, (target_nodes, n_targets), src, exp)
@@ -329,4 +330,42 @@ end
329330
end
330331
end
331332

333+
@testset "Full foreign topology." begin
334+
335+
ff = FullForeign(4, 5)
336+
@test n_sources(ff) == 4
337+
@test n_targets(ff) == 5
338+
@test n_edges(ff) == 4 * 5
339+
340+
# Uniform edges: all inside.
341+
@test all(check_targets(ff, s, 1:5) for s in 1:4)
342+
@test all(check_sources(ff, t, 1:4) for t in 1:5)
343+
@test all(is_edge(ff, s, t) for s in 1:4 for t in 1:5)
344+
@test collect(edges(ff)) == [(s, t) for s in 1:4 for t in 1:5]
345+
346+
# Nested iterators.
347+
for skip in [true, false] # (should be indifferent)
348+
check_nested(
349+
forward(ff; skip),
350+
[
351+
1 => [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)],
352+
2 => [(1, 6), (2, 7), (3, 8), (4, 9), (5, 10)],
353+
3 => [(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)],
354+
4 => [(1, 16), (2, 17), (3, 18), (4, 19), (5, 20)],
355+
],
356+
)
357+
check_nested(
358+
backward(ff; skip),
359+
[
360+
1 => [(1, 1), (2, 6), (3, 11), (4, 16)],
361+
2 => [(1, 2), (2, 7), (3, 12), (4, 17)],
362+
3 => [(1, 3), (2, 8), (3, 13), (4, 18)],
363+
4 => [(1, 4), (2, 9), (3, 14), (4, 19)],
364+
5 => [(1, 5), (2, 10), (3, 15), (4, 20)],
365+
],
366+
)
367+
end
368+
369+
end
370+
332371
end

0 commit comments

Comments
 (0)