Reputation: 305
Say we have a rank 2 vector that represents edges in a graph:
m←3 2⍴3 5 5 9 6 3
m
3 5
5 9
6 3
And we want to compute the transitive edges.
e.g. Since we have the edge from node 3 to 5 and the edge from node 5 to 9 then transitively we also have the edge from node 3 to 9.
I have implemented a function to do that like this:
transitive_edges_one_step←{idx←⍸⍵[;2]∘.=⍵[;1] ⋄ 0=⍴idx:⍵ ⋄ ∪⍵⍪↑⍵∘{⍺[1⌷⍵;1],⍺[2⌷⍵;2]}¨idx}
Running it 'till quiescence produces the desired result:
(transitive_edges_one_step⍣≡) m
3 5
5 9
6 3
3 9
6 5
6 9
What are some more terse solutions?
Does this problem require a recursive approach (here with the power operator)?
Also, for larger rank 2 vectors (20,000 rows) I get WS FULL
in Dyalog. Are there solutions that use less memory?
Upvotes: 3
Views: 94
Reputation: 53
Adam's solution showcases the classic transitive closure idiom in APL. I just want to share some more excellent write ups by Roger Hui around this, which also includes an implementation using lists instead of a matrix (more efficient if your graph is sparse)
https://forums.dyalog.com/viewtopic.php?f=13&t=1376&hilit=transitive+closure
https://forums.dyalog.com/viewtopic.php?f=30&t=1636&p=6468
Upvotes: 2
Reputation: 7671
Here's a shorter and more efficient version of B. Wilson's answer:
First, the adjacency matrix is computed simply by checking which of all possible edges exist in the list of edges:
⎕←a←(↓∊⍨∘⍳2⍴⌈⌿∘,)m
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1
0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
Then we compute the transitive closure of that using Boolean functions instead of arithmetic ones:
⎕←e←(∨.∧⍨∨⊢)⍣≡a
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 1
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1
0 0 1 0 1 0 0 0 1
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
Finally, as in the original, we get the indices of the edges:
↑⍸e
3 5
3 9
5 9
6 3
6 5
6 9
This is brief enough that we can put it all together in a single line:
↑⍸(∨.∧⍨∨⊢)⍣≡(↓∊⍨∘⍳2⍴⌈⌿∘,)m
3 5
3 9
5 9
6 3
6 5
6 9
We can even define this as a function:
TE←↑∘⍸(∨.∧⍨∨⊢)⍣≡∘(↓∊⍨∘⍳2⍴⌈⌿∘,)
TE m
3 5
3 9
5 9
6 3
6 5
6 9
Upvotes: 3
Reputation: 131
The power series of the adjacency matrix gives the transitive closure:
a←{(2⍴1+⌈⌿,⍵)↑⍸⍣¯1↓⍵}m ⍝ Adjacency matrix
↑⍸(×⊢+a+.×⊢)⍣≡a
3 5
3 9
5 9
6 3
6 5
6 9
Granted, memory usage is quadratic in node count.
Upvotes: 2
Reputation: 421
An only slightly better answer:
z←{∪⍵⍪↑⊃,/f,¨¨t((t←⊢/⍵)/⍨⊢)⍤=¨⊂f←⊣/⍵}
z⍣≡⊢ m
3 5
5 9
6 3
3 9
6 5
6 9
Upvotes: 1