Commit 52bc2f5f authored by Kai Zhang's avatar Kai Zhang

v0.4.0

parent f67af377
Revision history for haskell-igraph
===================================
v0.4.0 -- 2018-04-20
-------------------
* A new attribute interface written in C. The graph attributes are now directly serialized into bytestring using "cereal" (before we used the `Show` instance).
name: haskell-igraph
version: 0.4.0-dev
synopsis: Imcomplete igraph bindings
description: This is an attempt to create a complete bindings for the
igraph<"http://igraph.org/c/"> library.
version: 0.4.0
synopsis: Haskell interface of the igraph library.
description: igraph<"http://igraph.org/c/"> is a library for creating
and manipulating large graphs. This package provides the Haskell
interface of igraph.
license: MIT
license-file: LICENSE
author: Kai Zhang
......@@ -12,9 +13,11 @@ category: Math
build-type: Simple
cabal-version: >=1.24
extra-source-files:
cbits/haskell_igraph.h
cbits/bytestring.h
cbits/haskell_attributes.h
include/haskell_igraph.h
include/bytestring.h
include/haskell_attributes.h
README.md
ChangeLog.md
Flag graphics
Description: Enable graphics output
......@@ -22,8 +25,19 @@ Flag graphics
library
exposed-modules:
IGraph
IGraph.Types
IGraph.Mutable
IGraph.Clique
IGraph.Structure
IGraph.Isomorphism
IGraph.Community
IGraph.Read
IGraph.Motif
IGraph.Layout
IGraph.Generators
IGraph.Exporter.GEXF
IGraph.Internal.Initialization
IGraph.Internal.C2HS
IGraph.Internal.Constants
IGraph.Internal.Arpack
IGraph.Internal.Data
......@@ -36,18 +50,9 @@ library
IGraph.Internal.Clique
IGraph.Internal.Community
IGraph.Internal.Layout
IGraph
IGraph.Types
IGraph.Mutable
IGraph.Clique
IGraph.Structure
IGraph.Isomorphism
IGraph.Community
IGraph.Read
IGraph.Motif
IGraph.Layout
IGraph.Generators
IGraph.Exporter.GEXF
other-modules:
IGraph.Internal.C2HS
if flag(graphics)
exposed-modules: IGraph.Exporter.Graphics
......@@ -78,7 +83,7 @@ library
cbits/haskell_igraph.c
cbits/haskell_attributes.c
cbits/bytestring.c
include-dirs: cbits
include-dirs: include
test-suite tests
type: exitcode-stdio-1.0
......
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module IGraph
( LGraph(..)
( Graph(..)
, LGraph(..)
, U(..)
, D(..)
, Graph(..)
, decodeC
, empty
, mkGraph
......@@ -37,7 +37,6 @@ import Control.Monad.Primitive
import Control.Monad.ST (runST)
import qualified Data.ByteString as B
import Data.Conduit.Cereal
import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
import Data.Hashable (Hashable)
import qualified Data.HashMap.Strict as M
import qualified Data.HashSet as S
......@@ -45,7 +44,7 @@ import Data.List (sortBy)
import Data.Maybe
import Data.Ord (comparing)
import Data.Serialize
import Foreign (with, castPtr)
import Foreign (castPtr, with)
import System.IO.Unsafe (unsafePerformIO)
import IGraph.Internal.Attribute
......@@ -57,68 +56,63 @@ import IGraph.Mutable
import IGraph.Types
class MGraph d => Graph d where
-- | Graph is directed or not.
isDirected :: LGraph d v e -> Bool
isD :: d -> Bool
-- | Return the number of nodes in a graph.
nNodes :: LGraph d v e -> Int
nNodes (LGraph g _) = unsafePerformIO $ igraphVcount g
{-# INLINE nNodes #-}
nodes :: LGraph d v e -> [Int]
-- | Return all nodes. @nodes gr == [0 .. nNodes gr - 1]@.
nodes :: LGraph d v e -> [Node]
nodes gr = [0 .. nNodes gr - 1]
{-# INLINE nodes #-}
-- | Return the number of edges in a graph.
nEdges :: LGraph d v e -> Int
nEdges (LGraph g _) = unsafePerformIO $ igraphEcount g
{-# INLINE nEdges #-}
-- | Return all edges.
edges :: LGraph d v e -> [Edge]
edges gr@(LGraph g _) = unsafePerformIO $ mapM (igraphEdge g) [0..n-1]
where
n = nEdges gr
{-# INLINE edges #-}
-- | Whether a edge exists in the graph.
hasEdge :: LGraph d v e -> Edge -> Bool
hasEdge (LGraph g _) (fr, to) = unsafePerformIO $ do
i <- igraphGetEid g fr to True False
return $ i >= 0
{-# INLINE hasEdge #-}
-- | Return the label of given node.
nodeLab :: Serialize v => LGraph d v e -> Node -> v
nodeLab (LGraph g _) i = unsafePerformIO $
igraphHaskellAttributeVAS g vertexAttr i >>= fromBS
{-# INLINE nodeLab #-}
nodeLabMaybe :: Serialize v => LGraph d v e -> Node -> Maybe v
nodeLabMaybe gr@(LGraph g _) i = unsafePerformIO $ do
x <- igraphHaskellAttributeHasAttr g IgraphAttributeVertex vertexAttr
return $ if x
then Just $ nodeLab gr i
else Nothing
{-# INLINE nodeLabMaybe #-}
-- | Return all nodes that are associated with given label.
getNodes :: (Hashable v, Eq v) => LGraph d v e -> v -> [Node]
getNodes gr x = M.lookupDefault [] x $ _labelToNode gr
{-# INLINE getNodes #-}
-- | Return the label of given edge.
edgeLab :: Serialize e => LGraph d v e -> Edge -> e
edgeLab (LGraph g _) (fr,to) = unsafePerformIO $
igraphGetEid g fr to True True >>=
igraphHaskellAttributeEAS g edgeAttr >>= fromBS
{-# INLINE edgeLab #-}
edgeLabMaybe :: Serialize e => LGraph d v e -> Edge -> Maybe e
edgeLabMaybe gr@(LGraph g _) i = unsafePerformIO $ do
x <- igraphHaskellAttributeHasAttr g IgraphAttributeEdge edgeAttr
return $ if x
then Just $ edgeLab gr i
else Nothing
{-# INLINE edgeLabMaybe #-}
-- | Find the edge by edge ID.
getEdgeByEid :: LGraph d v e -> Int -> Edge
getEdgeByEid gr@(LGraph g _) i = unsafePerformIO $ igraphEdge g i
{-# INLINE getEdgeByEid #-}
-- | Find the edge label by edge ID.
edgeLabByEid :: Serialize e => LGraph d v e -> Int -> e
edgeLabByEid (LGraph g _) i = unsafePerformIO $
igraphHaskellAttributeEAS g edgeAttr i >>= fromBS
......@@ -132,6 +126,12 @@ instance Graph D where
isDirected = const True
isD = const True
-- | Graph with labeled nodes and edges.
data LGraph d v e = LGraph
{ _graph :: IGraph
, _labelToNode :: M.HashMap v [Node]
}
instance (Graph d, Serialize v, Serialize e, Hashable v, Eq v)
=> Serialize (LGraph d v e) where
put gr = do
......@@ -149,6 +149,8 @@ instance (Graph d, Serialize v, Serialize e, Hashable v, Eq v)
es <- replicateM ne get
return $ mkGraph nds es
-- | Decode a graph from a stream of inputs. This may be more memory efficient
-- than standard @decode@ function.
decodeC :: ( PrimMonad m, MonadThrow m, Graph d
, Serialize v, Serialize e, Hashable v, Eq v )
=> ConduitT B.ByteString o m (LGraph d v e)
......@@ -158,12 +160,16 @@ decodeC = do
ne <- sinkGet get
conduitGet2 get .| deserializeGraph nds ne
-- | Create a empty graph.
empty :: (Graph d, Hashable v, Serialize v, Eq v, Serialize e)
=> LGraph d v e
empty = runST $ new 0 >>= unsafeFreeze
-- | Create a graph.
mkGraph :: (Graph d, Hashable v, Serialize v, Eq v, Serialize e)
=> [v] -> [LEdge e] -> LGraph d v e
=> [v] -- ^ Nodes. Each will be assigned a ID from 0 to N.
-> [LEdge e] -- ^ Labeled edges.
-> LGraph d v e
mkGraph vattr es = runST $ do
g <- new 0
addLNodes vattr g
......@@ -172,6 +178,7 @@ mkGraph vattr es = runST $ do
where
n = length vattr
-- | Create a graph from labeled edges.
fromLabeledEdges :: (Graph d, Hashable v, Serialize v, Eq v, Serialize e)
=> [((v, v), e)] -> LGraph d v e
fromLabeledEdges es = mkGraph labels es'
......@@ -181,7 +188,7 @@ fromLabeledEdges es = mkGraph labels es'
labels = S.toList $ S.fromList $ concat [ [a,b] | ((a,b),_) <- es ]
labelToId = M.fromList $ zip labels [0..]
-- | Deserialize a graph.
-- | Create a graph from a stream of labeled edges.
fromLabeledEdges' :: (PrimMonad m, Graph d, Hashable v, Serialize v, Eq v, Serialize e)
=> a -- ^ Input, usually a file
-> (a -> ConduitT () ((v, v), e) m ()) -- ^ deserialize the input into a stream of edges
......@@ -213,8 +220,7 @@ deserializeGraph nds ne = do
let f i ((fr, to), attr) = unsafePrimToPrim $ do
igraphVectorSet evec (i*2) $ fromIntegral fr
igraphVectorSet evec (i*2+1) $ fromIntegral to
unsafeUseAsCStringLen (encode attr) $ \bs -> with (BSLen bs) $ \ptr ->
bsvectorSet bsvec i $ castPtr ptr
asBS attr $ \bs -> with bs $ \ptr -> bsvectorSet bsvec i $ castPtr ptr
return $ i + 1
foldMC f 0
gr@(MLGraph g) <- new 0
......@@ -225,6 +231,15 @@ deserializeGraph nds ne = do
unsafeFreeze gr
{-# INLINE deserializeGraph #-}
-- | Convert a mutable graph to immutable graph.
freeze :: (Hashable v, Eq v, Serialize v, PrimMonad m)
=> MLGraph (PrimState m) d v e -> m (LGraph d v e)
freeze (MLGraph g) = do
g' <- unsafePrimToPrim $ igraphCopy g
unsafeFreeze (MLGraph g')
-- | Convert a mutable graph to immutable graph. The original graph may not be
-- used afterwards.
unsafeFreeze :: (Hashable v, Eq v, Serialize v, PrimMonad m)
=> MLGraph (PrimState m) d v e -> m (LGraph d v e)
unsafeFreeze (MLGraph g) = unsafePrimToPrim $ do
......@@ -234,19 +249,15 @@ unsafeFreeze (MLGraph g) = unsafePrimToPrim $ do
return $ LGraph g $ M.fromListWith (++) $ zip labels $ map return [0..nV-1]
where
freeze :: (Hashable v, Eq v, Serialize v, PrimMonad m)
=> MLGraph (PrimState m) d v e -> m (LGraph d v e)
freeze (MLGraph g) = do
g' <- unsafePrimToPrim $ igraphCopy g
unsafeFreeze (MLGraph g')
-- | Create a mutable graph.
thaw :: (PrimMonad m, Graph d) => LGraph d v e -> m (MLGraph (PrimState m) d v e)
thaw (LGraph g _) = unsafePrimToPrim . liftM MLGraph . igraphCopy $ g
-- | Create a mutable graph. The original graph may not be used afterwards.
unsafeThaw :: PrimMonad m => LGraph d v e -> m (MLGraph (PrimState m) d v e)
unsafeThaw (LGraph g _) = return $ MLGraph g
thaw :: (PrimMonad m, Graph d) => LGraph d v e -> m (MLGraph (PrimState m) d v e)
thaw (LGraph g _) = unsafePrimToPrim . liftM MLGraph . igraphCopy $ g
-- | Find all neighbors of the given node
-- | Find all neighbors of the given node.
neighbors :: LGraph d v e -> Node -> [Node]
neighbors gr i = unsafePerformIO $ do
vs <- igraphVsAdj i IgraphAll
......@@ -296,8 +307,7 @@ mapEdges f gr = runST $ do
setEdgeAttr x (f e $ edgeLabByEid gr x) gr'
unsafeFreeze gr'
-- | Keep nodes that satisfy the constraint
-- | Keep nodes that satisfy the constraint.
filterEdges :: (Hashable v, Eq v, Serialize v, Graph d)
=> (LGraph d v e -> Edge -> Bool) -> LGraph d v e -> LGraph d v e
filterEdges f gr = runST $ do
......@@ -306,7 +316,7 @@ filterEdges f gr = runST $ do
delEdges deleted gr'
unsafeFreeze gr'
-- | Map a function over the node labels in a graph
-- | Map a function over the node labels in a graph.
nmap :: (Graph d, Serialize v, Hashable u, Serialize u, Eq u)
=> ((Node, v) -> u) -> LGraph d v e -> LGraph d u e
nmap fn gr = unsafePerformIO $ do
......@@ -317,7 +327,7 @@ nmap fn gr = unsafePerformIO $ do
with bs (igraphHaskellAttributeVASSet g vertexAttr i)
unsafeFreeze (MLGraph g)
-- | Map a function over the edge labels in a graph
-- | Map a function over the edge labels in a graph.
emap :: (Graph d, Serialize v, Hashable v, Eq v, Serialize e1, Serialize e2)
=> ((Edge, e1) -> e2) -> LGraph d v e1 -> LGraph d v e2
emap fn gr = unsafePerformIO $ do
......
......@@ -38,12 +38,20 @@ withEdgeAttr :: (CString -> IO a) -> IO a
withEdgeAttr = withCString edgeAttr
{-# INLINE withEdgeAttr #-}
-- | Mutable labeled graph.
newtype MLGraph m d v e = MLGraph IGraph
class MGraph d where
-- | Create a new graph.
new :: PrimMonad m => Int -> m (MLGraph (PrimState m) d v e)
addNodes :: PrimMonad m => Int -> MLGraph(PrimState m) d v e -> m ()
-- | Add nodes to the graph.
addNodes :: PrimMonad m
=> Int -- ^ The number of new nodes.
-> MLGraph(PrimState m) d v e -> m ()
addNodes n (MLGraph g) = unsafePrimToPrim $ igraphAddVertices g n nullPtr
-- | Add nodes with labels to the graph.
addLNodes :: (Serialize v, PrimMonad m)
=> [v] -- ^ vertices' labels
-> MLGraph (PrimState m) d v e -> m ()
......@@ -54,6 +62,7 @@ class MGraph d where
where
n = length labels
-- | Delete nodes from the graph.
delNodes :: PrimMonad m => [Int] -> MLGraph (PrimState m) d v e -> m ()
delNodes ns (MLGraph g) = unsafePrimToPrim $ do
vptr <- fromList $ map fromIntegral ns
......@@ -61,6 +70,7 @@ class MGraph d where
igraphDeleteVertices g vsptr
return ()
-- | Add edges to the graph.
addEdges :: PrimMonad m => [(Int, Int)] -> MLGraph (PrimState m) d v e -> m ()
addEdges es (MLGraph g) = unsafePrimToPrim $ do
vec <- fromList xs
......@@ -68,6 +78,7 @@ class MGraph d where
where
xs = concatMap ( \(a,b) -> [fromIntegral a, fromIntegral b] ) es
-- | Add edges with labels to the graph.
addLEdges :: (PrimMonad m, Serialize e) => [LEdge e] -> MLGraph (PrimState m) d v e -> m ()
addLEdges es (MLGraph g) = unsafePrimToPrim $ withEdgeAttr $ \eattr ->
asBSVector vs $ \bsvec -> with (mkStrRec eattr bsvec) $ \ptr -> do
......@@ -77,6 +88,7 @@ class MGraph d where
where
(xs, vs) = unzip $ map ( \((a,b),v) -> ([fromIntegral a, fromIntegral b], v) ) es
-- | Delete edges from the graph.
delEdges :: PrimMonad m => [(Int, Int)] -> MLGraph (PrimState m) d v e -> m ()
instance MGraph U where
......@@ -99,6 +111,7 @@ instance MGraph D where
igraphDeleteEdges g esptr
return ()
-- | Set node attribute.
setNodeAttr :: (PrimMonad m, Serialize v)
=> Int -- ^ Node id
-> v
......@@ -109,9 +122,10 @@ setNodeAttr nodeId x (MLGraph gr) = unsafePrimToPrim $ asBS x $ \bs ->
err <- igraphHaskellAttributeVASSet gr vertexAttr nodeId bsptr
when (err /= 0) $ error "Fail to set node attribute!"
setEdgeAttr :: (PrimMonad m, Serialize v)
-- | Set edge attribute.
setEdgeAttr :: (PrimMonad m, Serialize e)
=> Int -- ^ Edge id
-> v
-> e
-> MLGraph (PrimState m) d v e
-> m ()
setEdgeAttr edgeId x (MLGraph gr) = unsafePrimToPrim $ asBS x $ \bs ->
......
......@@ -35,7 +35,7 @@ inducedSubgraph gr vs = unsafePerformIO $ do
igraphHaskellAttributeVAS g' vertexAttr i >>= fromBS
return $ LGraph g' $ M.fromListWith (++) $ zip labels $ map return [0..nV-1]
-- | closeness centrality
-- | Closeness centrality
closeness :: [Int] -- ^ vertices
-> LGraph d v e
-> Maybe [Double] -- ^ optional edge weights
......@@ -52,7 +52,7 @@ closeness vs gr ws mode normal = unsafePerformIO $ do
igraphCloseness (_graph gr) vptr vsptr mode ws' normal
toList vptr
-- | betweenness centrality
-- | Betweenness centrality
betweenness :: [Int]
-> LGraph d v e
-> Maybe [Double]
......@@ -67,7 +67,7 @@ betweenness vs gr ws = unsafePerformIO $ do
igraphBetweenness (_graph gr) vptr vsptr True ws' False
toList vptr
-- | eigenvector centrality
-- | Eigenvector centrality
eigenvectorCentrality :: LGraph d v e
-> Maybe [Double]
-> [Double]
......@@ -103,6 +103,7 @@ pagerank gr ws d
n = nNodes gr
m = nEdges gr
-- | Personalized PageRank.
personalizedPagerank :: Graph d
=> LGraph d v e
-> [Double] -- ^ reset probability
......
......@@ -9,14 +9,8 @@ type Node = Int
type Edge = (Node, Node)
type LEdge a = (Edge, a)
data U = U
data D = D
-- | Undirected graph.
data U
-- | Mutable labeled graph
newtype MLGraph m d v e = MLGraph IGraph
-- | graph with labeled nodes and edges
data LGraph d v e = LGraph
{ _graph :: IGraph
, _labelToNode :: M.HashMap v [Node]
}
-- | Directed graph.
data D
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment