Create edges using -> operator, to make direction of edge explicit

This commit is contained in:
Mark Spanbroek 2021-08-02 13:47:02 +02:00
parent d15e23cf13
commit 0a604491ee
4 changed files with 44 additions and 42 deletions

View File

@ -6,18 +6,18 @@ type
# invariant: (∀ x->y ∈ outgoing: y<-x ∈ incoming)
outgoing: Table[Vertex, HashSet[Vertex]]
incoming: Table[Vertex, HashSet[Vertex]]
Edge*[Vertex] = (Vertex, Vertex)
Edge*[Vertex] = object
x*, y* : Vertex
func init*[V](_: type EdgeSet[V]): EdgeSet[V] =
discard
func incl*[V](edges: var EdgeSet[V], edge: Edge[V]) =
let (x, y) = edge
edges.outgoing.mgetOrPut(x, HashSet[V].default).incl(y)
edges.incoming.mgetOrPut(y, HashSet[V].default).incl(x)
edges.outgoing.mgetOrPut(edge.x, HashSet[V].default).incl(edge.y)
edges.incoming.mgetOrPut(edge.y, HashSet[V].default).incl(edge.x)
func contains*[V](edges: EdgeSet[V], edge: Edge[V]): bool =
edge[1] in edges.outgoing.getOrDefault(edge[0])
edge.y in edges.outgoing.getOrDefault(edge.x)
iterator outgoing*[V](edges: EdgeSet[V], vertex: V): V =
for v in edges.outgoing.getOrDefault(vertex).items:
@ -26,3 +26,6 @@ iterator outgoing*[V](edges: EdgeSet[V], vertex: V): V =
iterator incoming*[V](edges: EdgeSet[V], vertex: V): V =
for v in edges.incoming.getOrDefault(vertex).items:
yield v
func `->`*[V](x, y: V): Edge[V] =
Edge[V](x: x, y: y)

View File

@ -13,6 +13,8 @@ import ./merge
## Uses the dynamic topological sort algorithm by
## [Pearce and Kelly](https://www.doc.ic.ac.uk/~phjk/Publications/DynamicTopoSortAlg-JEA-07.pdf).
export `->`
type
SortedDag*[Vertex] = ref object
## A DAG whose vertices are kept in topological order
@ -92,7 +94,7 @@ func add[V](dag: SortedDag[V], vertex: V) =
if vertex notin dag:
dag.order[vertex] = -(dag.order.len)
func add*[V](dag: SortedDag[V], edge: tuple[x, y: V]) =
func add*[V](dag: SortedDag[V], edge: Edge[V]) =
## Adds an edge x -> y to the DAG
dag.add(edge.y)
dag.add(edge.x)

View File

@ -11,15 +11,15 @@ suite "DAG edge set":
edges = EdgeSet[string].init()
test "contains edges":
edges.incl( ("a", "b") )
check ("a", "b") in edges
check not ( ("a", "c") in edges )
check not ( ("b", "a") in edges )
edges.incl("a"->"b")
check ("a"->"b") in edges
check ("a"->"c") notin edges
check ("b"->"a") notin edges
test "iterates over outgoing edges":
edges.incl( ("a", "b") )
edges.incl( ("b", "c") )
edges.incl( ("a", "c") )
edges.incl("a"->"b")
edges.incl("b"->"c")
edges.incl("a"->"c")
let neighboursA = toSeq(edges.outgoing("a"))
let neighboursB = toSeq(edges.outgoing("b"))
let neighboursC = toSeq(edges.outgoing("c"))
@ -30,9 +30,9 @@ suite "DAG edge set":
check "c" in neighboursB
test "iterates over incoming edges":
edges.incl( ("a", "b") )
edges.incl( ("b", "c") )
edges.incl( ("a", "c") )
edges.incl("a"->"b")
edges.incl("b"->"c")
edges.incl("a"->"c")
let neighboursA = toSeq(edges.incoming("a"))
let neighboursB = toSeq(edges.incoming("b"))
let neighboursC = toSeq(edges.incoming("c"))
@ -48,7 +48,7 @@ suite "DAG edge set":
let x, y = rand(100)
if x != y:
let (x, y) = (min(x,y), max(x,y))
large.incl((x,y))
check (x, y) in large
large.incl(x->y)
check (x->y) in large
check y in toSeq large.outgoing(x)
check x in toSeq large.incoming(y)

View File

@ -8,20 +8,20 @@ suite "Sorted DAG":
test "contains vertices":
var dag = SortedDag[int].new
dag.add( (1, 2) )
dag.add(1->2)
check 1 in dag
check 2 in dag
check 42 notin dag
dag.add( (2, 42) )
dag.add(2->42)
check 42 in dag
test "contains edges":
var dag = SortedDag[int].new
dag.add( (1, 2) )
check (1, 2) in dag
check (2, 3) notin dag
dag.add( (2, 3) )
check (2, 3) in dag
dag.add(1->2)
check (1->2) in dag
check (2->3) notin dag
dag.add(2->3)
check (2->3) in dag
test "visits reachable vertices, nearest first":
@ -30,7 +30,7 @@ suite "Sorted DAG":
# ②
var dag = SortedDag[int].new
for edge in [ (0, 1), (1, 2), (0, 2) ]:
for edge in [0->1, 1->2, 0->2]:
dag.add(edge)
check toSeq(dag.visit(0)) == @[1, 2]
@ -46,7 +46,7 @@ suite "Sorted DAG":
# ③
var dag = SortedDag[int].new
for edge in [ (5, 2), (5, 0), (4, 0), (4, 1), (2, 3), (3, 1) ]:
for edge in [5->2, 5->0, 4->0, 4->1, 2->3, 3->1]:
dag.add(edge)
let reachableFrom5 = toSeq(dag.visit(5))
@ -65,11 +65,11 @@ suite "Sorted DAG":
# gain ← spend
var dag = SortedDag[string].new
for edge in [("acks", "ack1"),
("acks", "ack2"),
("ack1", "gain"),
("ack2", "spend"),
("spend", "gain")]:
for edge in ["acks"->"ack1",
"acks"->"ack2",
"ack1"->"gain",
"ack2"->"spend",
"spend"->"gain"]:
dag.add(edge)
let walk = toSeq dag.visit("acks")
@ -91,28 +91,25 @@ suite "Sorted DAG":
var dag = SortedDag[int].new
for vertex in [1,6]:
dag.add((0, vertex))
dag.add(0->vertex)
for vertex in 1..<5:
dag.add((vertex, vertex + 1))
dag.add(vertex->vertex + 1)
for vertex in 6..<10:
dag.add((vertex, vertex + 1))
dag.add(vertex->vertex + 1)
for vertex in [1, 3, 5]:
dag.add((vertex, vertex + 5))
dag.add(vertex->vertex + 5)
for vertex in [2, 4]:
dag.add((vertex+5, vertex))
dag.add(vertex+5->vertex)
check toSeq(dag.visit(0)) == @[1, 6, 7, 2, 3, 8, 9, 4, 5, 10]
test "handles DAGs with many edges":
var dag = SortedDag[int].new
var vertices: seq[int]
for _ in 0..10_000:
let x, y = rand(100)
if x != y:
dag.add((min(x,y), max(x,y)))
dag.add(min(x,y)->max(x,y))
var latest = -1
for vertex in dag.visit(0):
@ -125,7 +122,7 @@ suite "Sorted DAG":
var dag = SortedDag[int].new
for i in 1..10_000:
dag.add((i, i-1))
dag.add(i->i-1)
var latest = 10_000
for vertex in dag.visit(10_000):