Skip to content

Commit fe106af

Browse files
authored
Clarify transitive closure function
And add other related functions.
2 parents 8aa3a31 + 0354c3a commit fe106af

File tree

3 files changed

+67
-23
lines changed

3 files changed

+67
-23
lines changed
Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,40 @@
11
module Data.Graph.Inductive.Query.TransClos(
2-
trc
2+
trc, rc, tc
33
) where
44

55
import Data.Graph.Inductive.Graph
6-
import Data.Graph.Inductive.Query.DFS (reachable)
7-
8-
9-
getNewEdges :: (DynGraph gr) => [LNode a] -> gr a b -> [LEdge ()]
10-
getNewEdges vs g = map (`toLEdge` ())
11-
. concatMap (\u -> map ((,) u) (reachable u g))
12-
$ map fst vs
6+
import Data.Graph.Inductive.Query.BFS (bfen)
137

148
{-|
159
Finds the transitive closure of a directed graph.
1610
Given a graph G=(V,E), its transitive closure is the graph:
1711
G* = (V,E*) where E*={(i,j): i,j in V and there is a path from i to j in G}
1812
-}
13+
tc :: (DynGraph gr) => gr a b -> gr a ()
14+
tc g = newEdges `insEdges` insNodes ln empty
15+
where
16+
ln = labNodes g
17+
newEdges = [ (u, v, ()) | (u, _) <- ln, (_, v) <- bfen (outU g u) g ]
18+
outU gr = map toEdge . out gr
19+
20+
{-|
21+
Finds the transitive, reflexive closure of a directed graph.
22+
Given a graph G=(V,E), its transitive closure is the graph:
23+
G* = (V,E*) where E*={(i,j): i,j in V and either i = j or there is a path from i to j in G}
24+
-}
1925
trc :: (DynGraph gr) => gr a b -> gr a ()
20-
trc g = insEdges (getNewEdges ln g) (insNodes ln empty)
21-
where ln = labNodes g
26+
trc g = newEdges `insEdges` insNodes ln empty
27+
where
28+
ln = labNodes g
29+
newEdges = [ (u, v, ()) | (u, _) <- ln, (_, v) <- bfen [(u, u)] g ]
30+
31+
{-|
32+
Finds the reflexive closure of a directed graph.
33+
Given a graph G=(V,E), its transitive closure is the graph:
34+
G* = (V,Er union E) where Er = {(i,i): i in V}
35+
-}
36+
rc :: (DynGraph gr) => gr a b -> gr a ()
37+
rc g = newEdges `insEdges` insNodes ln empty
38+
where
39+
ln = labNodes g
40+
newEdges = [ (u, u, ()) | (u, _) <- ln ]

test/Data/Graph/Inductive/Query/Properties.hs

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import Test.Hspec (Spec, describe, it, shouldBe, shouldMatchList,
2626
import Test.QuickCheck
2727

2828
import Control.Arrow (second)
29-
import Data.List (delete, sort, unfoldr)
29+
import Data.List (delete, sort, unfoldr, group, (\\))
3030
import qualified Data.Set as S
3131

3232
#if __GLASGOW_HASKELL__ < 710
@@ -327,18 +327,41 @@ test_sp _ cg = all test_p (map unLPath (msTree g))
327327
-- -----------------------------------------------------------------------------
328328
-- TransClos
329329

330-
test_trc :: (ArbGraph gr, Eq (BaseGraph gr a ())) => Proxy (gr a b)
331-
-> UConnected (SimpleGraph gr) a ()
332-
-> Bool
333-
test_trc _ cg = gReach == trc g
330+
-- | The transitive, reflexive closure of a graph means that every
331+
-- node is a successor of itself, and also that if (a, b) is an edge,
332+
-- and (b, c) is an edge, then (a, c) must also be an edge.
333+
test_trc :: DynGraph gr => Proxy (gr a b) -> (NoMultipleEdges gr) a b -> Bool
334+
test_trc _ nme = all valid $ nodes gTrans
334335
where
335-
g = connGraph cg
336-
337-
lns = labNodes g
338-
339-
gReach = (`asTypeOf` g)
340-
. insEdges [(v,w,()) | (v,_) <- lns, (w,_) <- lns]
341-
$ mkGraph lns []
336+
g = emap (const ()) (nmeGraph nme)
337+
gTrans = trc g
338+
valid n =
339+
-- For each node n, check that:
340+
-- the successors for n in gTrans are a superset of the successors for n in g.
341+
null (suc g n \\ suc gTrans n) &&
342+
-- the successors for n in gTrans are exactly equal to the reachable nodes for n in g, plus n.
343+
sort (suc gTrans n) == map head (group (sort (n:[ v | u <- suc g n, v <- reachable u g ])))
344+
345+
-- | The transitive closure of a graph means that if (a, b) is an
346+
-- edge, and (b, c) is an edge, then (a, c) must also be an edge.
347+
test_tc :: DynGraph gr => Proxy (gr a b) -> (NoMultipleEdges gr) a b -> Bool
348+
test_tc _ nme = all valid $ nodes gTrans
349+
where
350+
g = nmeGraph nme
351+
gTrans = tc g
352+
valid n =
353+
-- For each node n, check that:
354+
-- the successors for n in gTrans are a superset of the successors for n in g.
355+
null (suc g n \\ suc gTrans n) &&
356+
-- the successors for n in gTrans are exactly equal to the reachable nodes for n in g.
357+
sort (suc gTrans n) == map head (group (sort [ v | u <- suc g n, v <- reachable u g ]))
358+
359+
-- | The reflexive closure of a graph means that all nodes are a
360+
-- successor of themselves.
361+
test_rc :: DynGraph gr => Proxy (gr a b) -> gr a b -> Bool
362+
test_rc _ g = and [ n `elem` suc gRefl n | n <- nodes gRefl ]
363+
where
364+
gRefl = rc g
342365

343366
-- -----------------------------------------------------------------------------
344367
-- Utility functions

test/TestSuite.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,11 @@ queryTests = describe "Queries" $ do
117117
test_maxFlow
118118
propP "msTree" test_msTree
119119
propP "sp" test_sp
120-
keepSmall $
120+
keepSmall $ do
121121
-- Just producing the sample graph to compare against is O(|V|^2)
122122
propP "trc" test_trc
123+
propP "tc" test_tc
124+
propP "rc" test_rc
123125
where
124126
propP str = prop str . ($p)
125127

0 commit comments

Comments
 (0)