[Haskell-cafe] copy of boost graph library

Thomas Bereknyei tomberek at gmail.com
Tue Sep 14 17:12:12 EDT 2010


I was looking around and liked some of the ways that Boost organizes
its libraries.  So it got me thinking that it might be easy to use the
same for a Haskell graph library.  This IS NOT FGL, but does include
some elements of it at the end (InductiveGraph).

Mostly what I like, is that it presents a (somewhat) logical sequence
of operations for a graph writer to implement, getting a few freebies
along the way.  There aren't too many extensions or complications.
The most odd thing is the way I arranged the types.  A quick look at
some typesigs should clear up confusion, but:

Node g is the entire node, eg (Int,a)
NodeIndex is just the index eg Int
NodeLabel is just the label eg. a    the same for edge.

I'm just fishing for ideas and opinions, and whether or not this seems
simpler to use.

http://codepad.org/UXUL7LZv

{-# LANGUAGE   TypeFamilies
                ,FlexibleContexts

  #-}

  --TODO: Visitors? DFF searches

import qualified Data.IntMap as I
import Data.List (find,unfoldr,foldl')
import Data.Maybe (fromJust)
import Control.Arrow (second)

class Graph g where
    type NodeIndex g
    type EdgeIndex g
    type Node g --The entire node, including index, any labels and/or data.
    type Edge g --ditto
    node_index :: g -> Node g -> NodeIndex g
    edge_index :: g -> Edge g -> EdgeIndex g

    empty :: g
    isEmpty :: g -> Bool
    mkGraph :: [Node g] -> [Edge g] -> g

class Graph g => DirectionalGraph g where
    edges_out :: g -> NodeIndex g -> [Edge g]
    source , target :: g -> EdgeIndex g -> Node g

    degree_out :: g -> NodeIndex g -> Int
    degree_out = length ... edges_out

class DirectionalGraph g => BidirectionalGraph g where
    edges_in :: g -> NodeIndex g -> [Edge g]
    edges_both :: g -> NodeIndex g -> [Edge g]
    edges_both g n = edges_out g n ++ edges_in g n

    degree_in :: g -> NodeIndex g -> Int
    degree_in = length ... edges_in
    degree :: g -> NodeIndex g -> Int
    degree g n = degree_out g n + degree_in g n

class Graph g => AdjacencyGraph g where
    nodes_out,nodes_in,nodes_both :: g -> NodeIndex g -> [NodeIndex g]

class Graph g => VertexGraph g where
    nodes :: g -> [Node g]
    node :: g -> NodeIndex g -> Maybe (Node g)
    hasNode :: g -> NodeIndex g -> Bool
    hasNode g n = maybe False (const True) (node g n)
    order :: g -> Int
    order = length . nodes

class Graph g => EdgeGraph g where
    edges :: g -> [Edge g]
    edge :: g -> EdgeIndex g -> Maybe (Edge g)
    hasEdge :: g -> EdgeIndex g -> Bool
    hasEdge g e = maybe False (const True) (edge g e)
    size :: g -> Int
    size = length . edges

class Graph g => MutableGraph g where
    insert_node :: Node g -> g -> g --if preexists, update
    remove_node :: NodeIndex g -> g -> g
    insert_edge :: Edge g -> g -> g --if preexists, update
    remove_edge :: EdgeIndex g -> g -> g

class Graph g => PropertyGraph g where
    type NodeLabel g
    type EdgeLabel g

    node_label :: Node g -> NodeLabel g
    edge_label :: Edge g -> EdgeLabel g

    node_labelize :: NodeIndex g -> NodeLabel g -> Node g
    edge_labelize :: EdgeIndex g -> EdgeLabel g -> Edge g

    get_node_label :: g -> NodeIndex g -> NodeLabel g
    get_edge_label :: g -> EdgeIndex g -> EdgeLabel g

class (VertexGraph g,BidirectionalGraph g,MutableGraph g) =>
InductiveGraph g where
    data Context g
    edgesInC :: Context g -> [Edge g]
    nodeC :: Context g -> Node g
    edgesOutC :: Context g -> [Edge g]

    make_context :: [Edge g] -> Node g -> [Edge g] -> Context g

    --minimum definition is match or context, but default works too
    context :: g -> NodeIndex g -> Maybe (Context g)
    --context = fmap fst ... match
    context g n = do    foundNode <- node g n
                        return $ make_context (edges_in g n) foundNode
(edges_out g n)

    match :: g -> NodeIndex g -> Maybe (Context g,g)
    match g n = fmap (flip (,) $ remove_node n g) $ context g n

    insert :: Context g -> g -> g
    insert c g = foldr insert_edge g'' (edgesOutC c)
                    where
                        g' = insert_node (nodeC c) g
                        g'' = foldr insert_edge g' (edgesInC c)

    toContexts   :: g -> [Context g]
    toContexts g = unfoldr matchIt (g, map (node_index g) $ nodes g)
      where
        matchIt (_,  [])     = Nothing
        matchIt (g', (n:ns)) = fmap (second (flip (,) ns)) $ match g n

    fromContexts :: [Context g] -> g
    fromContexts = foldr insert empty

    adjust       :: (Context g -> Context g) -> NodeIndex g -> g -> g
    adjust f n g = maybe g (uncurry (insert . f)) $ match g n

    gfoldr     :: (Context g -> b -> b) -> b -> g -> b
    gfoldr f i = foldr f i . toContexts

    gfoldl'     :: (b -> Context g -> b) -> b -> g -> b
    gfoldl' f i = foldl' f i . toContexts

    gfilter   :: (Context g -> Bool) -> g -> g
    gfilter f = fromContexts . filter f . toContexts

class (InductiveGraph g) => MappableGraph g where

    gmap   :: InductiveGraph g' => (Context g -> Context g') -> g -> g'
    gmap f = fromContexts . map f . toContexts

    nmap   :: (InductiveGraph g,Edge g ~ Edge g) => (Node g -> Node g) -> g -> g
    nmap f = gmap f' where
        f' c = make_context (edgesInC c) (f $ nodeC c) (edgesOutC c)


    emap   :: ( InductiveGraph g', Node g ~ Node g') => (Edge g ->
Edge g') -> g -> g'
    emap f = gmap f' where
          f' c = make_context (map f $ edgesInC c) (nodeC c) (map f $
edgesOutC c)


More information about the Haskell-Cafe mailing list