| Bytes | Lang | Time | Link |
|---|---|---|---|
| 036 | APLDyalog Unicode 20 | 250617T141126Z | Mat_rdv |
| 309 | Python3 | 250616T171044Z | Ajax1234 |
| 024 | Jelly | 210123T175102Z | PurkkaKo |
| 066 | Charcoal | 210123T001117Z | Neil |
| 7158 | J | 210122T081929Z | xash |
APL(Dyalog Unicode 20), 36 bytes SBCS
{∪⍛≡∨.∧⍨⍛∨⍣≡¨⍛,⍉⍛∨¨⍛,=/¨∘⍳∘⍴⍛∨¨⍛,⊂⍵}
The graph representation is an adjacency matrix.
Explanation
r ← =/¨∘⍳∘⍴⍛∨ ⍝ reflexive closure
s ← ⍉⍛∨ ⍝ symmetric closure
t ← ∨.∧⍨⍛∨⍣≡ ⍝ transitive closure
{∪⍛≡ t¨⍛, s¨⍛, r¨⍛, ⊂⍵} solution
⍵ the right argument (adjacency matrix)
⊂ Enclose: consider the argument as one element
, Concatenate
⍛ Behind: f⍛g Y ←→ (f Y) g Y
¨ Each (apply the function on the left to each cell of array
or to an enclosed value)
r¨⍛, apply r to each and prepend to the original vector
total: (r⍵) ⍵
s¨⍛, total: (s r⍵) (s⍵) (r⍵) ⍵
t¨⍛, total: (t s r⍵) (t s⍵) (t r⍵) (t⍵) (s r⍵) (s⍵) (r⍵) ⍵
≡ Match (array equality)
⍛
∪ Unique
∪⍛≡ whether all elements of a vector are different
Reflexive closure
∨ Logical OR
⍛
⍴ Shape: n×n matrix → 2-element vector (n n)
∘ Compose: f∘g Y ←→ f (g Y)
⍳ Index Generator: ⍳ n n → matrix of pairs (i j) in order, where 1≤i≤n, 1≤j≤n
∘
¨ Each
/ Reduce
=
=/ =/i j ←→ i=j
=/¨∘⍳∘⍴ identity matrix of same shape as original
=/¨∘⍳∘⍴⍛∨
This is golfed reflexive closure, practical one would be ∘.=⍨∘⍳∘≢⍛∨.
Symmetric closure
∨
⍛
⍉ Transpose
⍉⍛∨
Transitive closure
≡
⍣ Power operator
⍣≡ fixpoint (do until the argument stop changing)
∨
⍛
⍨ Selfie: f⍨ Y ←→ Y f Y
∧ Logical AND
. Inner product (generalized matrix product)
∨
∨.∧⍨ for relation table S← R ∨.∧ R:
S[i;j] ←→ ∃k: R[i;k] and R[k;j]
∨.∧⍨⍛∨⍣≡
Python3, 309 bytes
def f(v,e):
l=[lambda v,e:{*e,*{(x,x)for x in v}},lambda v,e:{*e,*{(y,x)for x,y in e}},lambda v,e:{*e,*{(x,Z)for x,y in e for Y,Z in e if y==Y}}]
U=[e]+[(T:=lambda x,v,e:e if[]==x else T(x[1:],v,l[x[0]](v,e)))(i,v,e)for i in[[0],[1],[2],[0,1],[1,2],[0,2],[0,1,2]]]
return len({str(sorted(j))for j in U})==8
Jelly, 31 29 27 26 25 24 bytes
,|Z$æ*ⱮLS,Ʋ€Ẏæ*0|,Ʋ€Ẏ¬QƑ
Try it online! (comes with a test harness that runs all test inputs)
Takes the adjacency matrix and returns 1 or 0.
I'm very rusty in code golf (especially in Jelly) and this solution tries to stay very safe regarding the specification, so there is very likely room for improvement. (Edit 7 bytes later: yes, there was. For example, just found out about Ƒ when re-reading the docs.)
Explanation
- The main link gets the adjacency matrix.
Ztransposes the matrix and|bitwise-ORs it with the original. This gives the symmetric closure.,makes the pair[orig, SC].€does the following for each of the two matrices:Lfinds the edge length N of the matrix.æ*Ɱraises the matrix to each power 1…N to find routes of length 1 to N.Ssums the resulting matrices together to find all routes. This gives the transitive closure, but the matrix can have values greater than one.,makes the pair[TC, orig].
Ẏflattens the outermost array, giving[TC, orig, STC, SC].€does the following for each of the four matrices:æ*0gets the zeroth matrix power, i.e. the identity matrix.|bitwise-ORs it with the previous matrix, giving the reflexive closure.,makes the pair[RC, orig].
Ẏflattens the outermost array, giving[RTC, TC, RC, orig, RSTC, STC, RSC, SC].¬logical-NOTs all values in the matrices to turn 0 into 1 and anything else into 0. This makes all nonzero values equal.Qremoves duplicates from that list andƑsees if it stayed the same.
Correctness
The order of operations here is important. The transitive closure must be performed after the symmetric closure, but the reflexive closure can be performed at any point. I'll try to give a (very hand-wavy) proof of my algorithm's correctness.
For any path A1→…→An in SC(G), An→…→A1 will also exist, and thus TC(SC(G)) will be symmetric. SC(G) must be a subset of STC(G) (all "new edges" of SC(G) must also be in STC(G)) and TC(SC(G)) is the minimal transitive superset of SC(G). Thus TC(SC(G)) = STC(G).
RC(STC(G)) is always symmetric and transitive, since the edges added by RC will never break transitivity or symmetricity. STC(G) must be a subset of RSTC(G) (all "new edges" of STC(G) must also be in RSTC(G)) and RC(STC(G)) is the minimal reflexive superset of STC(G), so RC(STC(G)) = RSTC(G).1
Charcoal, 66 bytes
⊞υθ⊞υEθEι∨λ§§θμκF⮌υ⊞υEιEκ∨μ⁼νλFEυEιλ«FθUMιEλ∨ν⊙λ∧π§§ιρξ⊞υι»⬤υ⁼¹№υι
Try it online! Link is to verbose version of code. Takes input as an adjacency matrix. Explanation: Port of @xash's answer.
⊞υθ
Push the input to the predefined empty list.
⊞υEθEι∨λ§§θμκ
Logical Or the input with its transpose and push it to the list.
F⮌υ⊞υEιEκ∨μ⁼νλ
For each matrix in the (reversed) list, logical Or it with the identity matrix and push the result to the list. (The list is reversed because otherwise the For command will attempt to iterate over the new results.)
FEυEιλ«
For each shallow cloned matrix in the list...
Fθ
... for as many times as the size of the matrix...
UMιEλ∨ν⊙λ∧π§§ιρξ
... logical Or the matrix with its square, and...
⊞υι
... push the final result to the list.
»⬤υ⁼¹№υι
Check that all of the matrices are unique.
J, 71 58 bytes
Takes in the adjacency matrix.
*/@~:@(,([+./@,#)"#/~^:_"2)@(,s@r,(s=:+.|:),:r=:[+.=@i.@#)
How it works
First the original, s, r and r->s variants are computed, then all their transitive closures. Albeit the operations are not commutative, I believe calculating the transitive closure last will work here (r->t is equal to r->t->r as r just adds the diagonal, which was already set. s->t is equal to s->t->s as every transition was already symmetric. s->r is equal to r->s as every reflexion is symmetric.) Though this might be wrong, and if it is, I'm happy to slap some ^:_ on there. :-)
r=:+.=@i.@#: original matrix OR the diagonal=@i.@#.s=:+.|:: original matrix OR its transposition|:.,s@r,s,:r: original matrix and all its variants in a list.([+./@,#)"#/~^:_"2: until the result does not change^:_: for every node: take the neighboring rows#, append them to the current row[ ,and OR them together+./.*/@~::~:returns whether each element of the list is unique, then AND*/those booleans.