270 likes | 395 Views
This lecture by Tim Sheard from the Oregon Graduate Institute of Science & Technology introduces the Erwig style of functional programming applied to graphs, emphasizing depth-first search (DFS) algorithms. The talk covers the representation of graphs, types, and examples that highlight how to construct and decompose graphs inductively. It features references to influential works and illustrates operations on graphs, including mapping and graph reversal. Aimed at functional programming enthusiasts, this lecture serves as a practical introduction to graph theory in Haskell.
E N D
Advanced Functional Programming • Tim Sheard • Oregon Graduate Institute of Science & Technology • Lecture 17: • Erwig style - Functional Graphs • DFS – style of depth first search
References • The style of representing graphs, and most of the examples, come from the work of Martin Erwig • http://www.cs.orst.edu/~erwig/ • Functional Programming with Graphs • 2nd ACM SIGPLAN Int. Conf. on Functional Programming(ICFP'97), 52-65, 1997 • Inductive Graphs and Functional Graph Algorithms, Martin Erwig Journal of Functional Programming,
1 2 right a b left down up c 3 Graphs • Nodes = 1,2,3 • Edges = (1,2),(2,1),(3,1),(2,3) • Node Labels = a,b,c • Edge Labels = right,left,up,down
Types • type Node = Int • type LNode a = (Node,a) • type UNode = LNode () • type Edge = (Node,Node) • type LEdge b = (Node,Node,b) • type UEdge = LEdge ()
More Types • data Graph a b -- Abstract Type • --unlabled Graph • type UGraph = Graph () () • type Path = [Node] • type LPath a = [LNode a] • type UPath = [UNode]
1 right 2 a b left down up c 3 Making Graphs • ex1 = mkGraph [(1,"a"),(2,"b"),(3,"c")] • [(1,2,"right"),(2,1,"left"), • (2,3,"down"),(3,1,"up")] • Main> ex1 • 1:"a"->[("right",2)] • 2:"b"->[("left",1),("down",3)] • 3:"c"->[("up",1)]
Looking inside • noNodes :: Graph a b -> Int • nodes :: Graph a b -> [Node] • labNodes :: Graph a b -> [LNode a] • -- [(Node,a)] • edges :: Graph a b -> [Edge] • --[(Node,Node)] • labEdges :: Graph a b -> [LEdge b] • -– [(Node,Node,b)]
1 right 2 a b left down up c 3 Examples • Main> noNodes ex1 • 3 • Main> nodes ex1 • [1,2,3] • Main> labEdges ex1 • [(1,2,"right"),(2,1,"left"), • (2,3,"down"),(3,1,"up")]
Decomposing Graphs edge label • type Adj b = [(b,Node)] • type Context a b = (Adj b,Node,a,Adj b) • type MContext a b = Maybe (Context a b) • type Decomp a b = (MContext a b,Graph a b) • type GDecomp a b = (Context a b,Graph a b) • type UContext = ([Node],Node,[Node]) • type UDecomp = (Maybe UContext,UGraph) • match :: Node -> Graph a b -> Decomp a b Its predecessors The node Its succesors Its label
1 2 right a b left down up c 3 (Just(ps,n,lab,ss),ex2) = match 2 ex1 • Main> ps • [("right",1)] --predecessors • Main> ss --successors • [("left",1),("down",3)] • Main> n –- the node • 2 • Main> lab -- its label • "b" • Main> ex2 • 1:"a"->[] • 3:"c"->[("up",1)]
1 a up c 3 (Just(ps2,n2,lab2,ss2),ex3) = match 3 ex2 • Main> ps2 • [] • Main> n2 • 3 • Main> lab2 • "c" • Main> ss2 • [("up",1)] • Main> ex3 • 1:"a"->[]
Constructing Graphs Inductively • empty :: Graph a b • embed :: Context a b -> Graph a b -> Graph a b • infixr & • c & g = embed c g • ex4 = (ps2,n2,lab2,ss2) & ex3 • Main> ex4 • 1:"a"->[] • 3:"c"->[("up",1)] 1 a up c 3
Operations on graphs • -- maps on nodes • nmap :: (a->c) -> Graph a b -> Graph c b • -- maps on edges • emap :: (b->c) -> Graph a b -> Graph a c • -- graph reversal • grev :: Graph a b -> Graph a b
Example • ex5 = nmap (ord . head) ex1 • Main> ex5 • 1:97->[("right",2)] • 2:98->[("down",3),("left",1)] • 3:99->[("up",1)] • Main> ex1 • 1:"a"->[("right",2)] • 2:"b"->[("left",1),("down",3)] • 3:"c"->[("up",1)]
Example 2 • ex6 = emap length ex1 • Main> ex6 • 1:"a"->[(5,2)] • 2:"b"->[(4,3),(4,1)] • 3:"c"->[(2,1)] • Main> ex1 • 1:"a"->[("right",2)] • 2:"b"->[("left",1),("down",3)] • 3:"c"->[("up",1)]
Roll your own • mymap f g • | isEmpty g = empty • | otherwise = • let ns = nodes g • (Just(ps,n,lab,ss),g2) • = match (head ns) g • in (ps,n,f lab,ss) & (mymap f g2)
Depth First Search • data Tree a = Branch a [Tree a] deriving Show • df :: Node -> Graph b a -> (Tree b,Graph b a) • df root g = • let (Just(_,v,lab,ss), g') = match root g • (r,g1) = dff ss g' • in (Branch lab r,g1) • dff :: [(c,Node)] -> Graph b c -> ([Tree b],Graph b c) • dff [] g = ([],g) • dff ((lab,v):l) g = • let (x,g1) = df v g • (y,g2) = dff l g1 • in (x:y,g2)
Example • ex7 = fst (df 1 ex1) • Main> ex7 • Branch "c" [Branch "b" [Branch "a" []]]
Launchbury & King • “Structuring Depth-First Search Algorithms in Haskell” • David King and John Launchbury - POPL ’95 – pp 344--354 • Graph algorithms based upon Depth First Search. • Asymtotically efficient – uses lazy state
1 2 3 4 5 8 6 7 9 10 Representing Graphs • import ST • import qualified Array as A • type Vertex = Int • -- Representing graphs: • type Table a = A.Array Vertex a • type Graph = Table [Vertex] • -- Array Int [Int] Index for each node Edges (out of) that index
Functions on graphs • type Vertex = Int • type Edge = (Vertex,Vertex) • vertices :: Graph -> [Vertex] • indices :: Graph -> [Int] • edges :: Graph -> [Edge]
1 2 6 3 5 4 7 8 10 9 Building Graphs • buildG :: Bounds -> [Edge] -> Graph • graph = buildG (1,10) • [ (1, 2), (1, 6), (2, 3), • (2, 5), (3, 1), (3, 4), • (5, 4), (7, 8), (7, 10), • (8, 6), (8, 9), (8, 10) ]
1 2 6 3 5 4 7 8 10 9 DFS and Forests • data Tree a = Node a (Forest a) • type Forest a = [Tree a] • nodesTree (Node a f) ans = • nodesForest f (a:ans) • nodesForest [] ans = ans • nodesForest (t : f) ans = • nodesTree t (nodesForest f ans) • Note how any tree can be spanned • by a Forest. The Forest is not always • unique.
DFS • The DFS algorithm finds a spanning forest for a graph, from a set of roots. • dfs :: Graph -> [Vertex] -> Forest Vertex • dfs :: Graph -> [Vertex] -> Forest Vertex • dfs g vs = prune (A.bounds g) (map (generate g) vs) • generate :: Graph -> Vertex -> Tree Vertex • generate g v = Node v (map (generate g) (g `aat` v))
Sets of nodes already visited • type Set s = STArray s Vertex Bool • mkEmpty :: Bounds -> ST s (Set s) • mkEmpty bnds = newSTArray bnds False • contains :: Set s -> Vertex -> ST s Bool • contains m v = readSTArray m v • include :: Set s -> Vertex -> ST s () • include m v = writeSTArray m v True
Pruning already visited paths • prune :: Bounds -> Forest Vertex -> Forest Vertex • prune bnds ts = • runST (do { m <- mkEmpty bnds; chop m ts }) • chop :: Set s -> Forest Vertex -> ST s (Forest Vertex) • chop m [] = return [] • chop m (Node v ts : us) • do { visited <- contains m v • ; if visited • then chop m us • else do { include m v • ; as <- chop m ts • ; bs <- chop m us • ; return(Node v as : bs) • } • }
Topological Sort • postorder :: Tree a -> [a] • postorder (Node a ts) = postorderF ts ++ [a] • postorderF :: Forest a -> [a] • postorderF ts = concat (map postorder ts) • postOrd :: Graph -> [Vertex] • postOrd = postorderF . Dff • dff :: Graph -> Forest Vertex • dff g = dfs g (vertices g)