Commit 6ce9fdfd authored by Kai Zhang's avatar Kai Zhang

use dependent types

parent 9877beeb
...@@ -65,6 +65,7 @@ library ...@@ -65,6 +65,7 @@ library
, hashable , hashable
, hxt , hxt
, split , split
, singletons
extra-libraries: igraph extra-libraries: igraph
hs-source-dirs: src hs-source-dirs: src
......
This diff is collapsed.
...@@ -18,7 +18,7 @@ import IGraph.Internal.C2HS ...@@ -18,7 +18,7 @@ import IGraph.Internal.C2HS
#include "haskell_igraph.h" #include "haskell_igraph.h"
cliques :: LGraph d v e cliques :: Graph d v e
-> (Int, Int) -- ^ Minimum and maximum size of the cliques to be returned. -> (Int, Int) -- ^ Minimum and maximum size of the cliques to be returned.
-- No bound will be used if negative or zero -- No bound will be used if negative or zero
-> [[Int]] -- ^ cliques represented by node ids -> [[Int]] -- ^ cliques represented by node ids
...@@ -27,13 +27,13 @@ cliques gr (lo, hi) = unsafePerformIO $ allocaVectorPtr $ \vptr -> do ...@@ -27,13 +27,13 @@ cliques gr (lo, hi) = unsafePerformIO $ allocaVectorPtr $ \vptr -> do
(map.map) truncate <$> toLists vptr (map.map) truncate <$> toLists vptr
{#fun igraph_cliques as ^ { `IGraph', castPtr `Ptr VectorPtr', `Int', `Int' } -> `CInt' void- #} {#fun igraph_cliques as ^ { `IGraph', castPtr `Ptr VectorPtr', `Int', `Int' } -> `CInt' void- #}
largestCliques :: LGraph d v e -> [[Int]] largestCliques :: Graph d v e -> [[Int]]
largestCliques gr = unsafePerformIO $ allocaVectorPtr $ \vptr -> do largestCliques gr = unsafePerformIO $ allocaVectorPtr $ \vptr -> do
igraphLargestCliques (_graph gr) vptr igraphLargestCliques (_graph gr) vptr
(map.map) truncate <$> toLists vptr (map.map) truncate <$> toLists vptr
{#fun igraph_largest_cliques as ^ { `IGraph', castPtr `Ptr VectorPtr' } -> `CInt' void- #} {#fun igraph_largest_cliques as ^ { `IGraph', castPtr `Ptr VectorPtr' } -> `CInt' void- #}
maximalCliques :: LGraph d v e maximalCliques :: Graph d v e
-> (Int, Int) -- ^ Minimum and maximum size of the cliques to be returned. -> (Int, Int) -- ^ Minimum and maximum size of the cliques to be returned.
-- No bound will be used if negative or zero -- No bound will be used if negative or zero
-> [[Int]] -- ^ cliques represented by node ids -> [[Int]] -- ^ cliques represented by node ids
...@@ -42,7 +42,7 @@ maximalCliques gr (lo, hi) = unsafePerformIO $ allocaVectorPtr $ \vpptr -> do ...@@ -42,7 +42,7 @@ maximalCliques gr (lo, hi) = unsafePerformIO $ allocaVectorPtr $ \vpptr -> do
(map.map) truncate <$> toLists vpptr (map.map) truncate <$> toLists vpptr
{#fun igraph_maximal_cliques as ^ { `IGraph', castPtr `Ptr VectorPtr', `Int', `Int' } -> `CInt' void- #} {#fun igraph_maximal_cliques as ^ { `IGraph', castPtr `Ptr VectorPtr', `Int', `Int' } -> `CInt' void- #}
cliqueNumber :: LGraph d v e -> Int cliqueNumber :: Graph d v e -> Int
cliqueNumber gr = unsafePerformIO $ igraphCliqueNumber $ _graph gr cliqueNumber gr = unsafePerformIO $ igraphCliqueNumber $ _graph gr
{#fun igraph_clique_number as ^ {#fun igraph_clique_number as ^
{ `IGraph' { `IGraph'
......
{-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE DataKinds #-}
module IGraph.Community module IGraph.Community
( modularity ( modularity
, findCommunity , findCommunity
...@@ -25,8 +26,7 @@ import IGraph.Internal.C2HS ...@@ -25,8 +26,7 @@ import IGraph.Internal.C2HS
#include "haskell_igraph.h" #include "haskell_igraph.h"
modularity :: Graph d modularity :: Graph d v e
=> LGraph d v e
-> [[Int]] -- ^ Communities. -> [[Int]] -- ^ Communities.
-> Maybe [Double] -- ^ Weights -> Maybe [Double] -- ^ Weights
-> Double -> Double
...@@ -70,7 +70,7 @@ defaultSpinglass = Spinglass ...@@ -70,7 +70,7 @@ defaultSpinglass = Spinglass
, _coolFact = 0.99 , _coolFact = 0.99
, _gamma = 1.0 } , _gamma = 1.0 }
findCommunity :: LGraph U v e findCommunity :: Graph 'U v e
-> Maybe [Double] -- ^ node weights -> Maybe [Double] -- ^ node weights
-> CommunityMethod -- ^ Community finding algorithms -> CommunityMethod -- ^ Community finding algorithms
-> [[Int]] -> [[Int]]
......
...@@ -9,12 +9,13 @@ module IGraph.Exporter.GEXF ...@@ -9,12 +9,13 @@ module IGraph.Exporter.GEXF
, writeGEXF , writeGEXF
) where ) where
import Data.Colour (AlphaColour, alphaChannel, black, import Data.Colour (AlphaColour, alphaChannel, black, opaque,
opaque, over) over)
import Data.Colour.SRGB (channelBlue, channelGreen, import Data.Colour.SRGB (channelBlue, channelGreen, channelRed,
channelRed, toSRGB24) toSRGB24)
import Data.Hashable import Data.Hashable
import Data.Serialize import Data.Serialize
import Data.Singletons (SingI)
import GHC.Generics import GHC.Generics
import IGraph import IGraph
import Text.XML.HXT.Core import Text.XML.HXT.Core
...@@ -71,7 +72,7 @@ defaultEdgeAttributes = EdgeAttr ...@@ -71,7 +72,7 @@ defaultEdgeAttributes = EdgeAttr
, _edgeZindex = 2 , _edgeZindex = 2
} }
genXMLTree :: (ArrowXml a, Graph d) => LGraph d NodeAttr EdgeAttr -> a XmlTree XmlTree genXMLTree :: (SingI d, ArrowXml a) => Graph d NodeAttr EdgeAttr -> a XmlTree XmlTree
genXMLTree gr = root [] [gexf] genXMLTree gr = root [] [gexf]
where where
gexf = mkelem "gexf" [ attr "version" $ txt "1.2" gexf = mkelem "gexf" [ attr "version" $ txt "1.2"
...@@ -124,7 +125,7 @@ genXMLTree gr = root [] [gexf] ...@@ -124,7 +125,7 @@ genXMLTree gr = root [] [gexf]
a = show $ alphaChannel $ _edgeColour at a = show $ alphaChannel $ _edgeColour at
{-# INLINE genXMLTree #-} {-# INLINE genXMLTree #-}
writeGEXF :: Graph d => FilePath -> LGraph d NodeAttr EdgeAttr -> IO () writeGEXF :: SingI d => FilePath -> Graph d NodeAttr EdgeAttr -> IO ()
writeGEXF fl gr = runX (genXMLTree gr >>> writeDocument config fl) >> return () writeGEXF fl gr = runX (genXMLTree gr >>> writeDocument config fl) >> return ()
where where
config = [withIndent yes] config = [withIndent yes]
{-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
module IGraph.Generators module IGraph.Generators
( full ( full
, ErdosRenyiModel(..) , ErdosRenyiModel(..)
...@@ -10,6 +12,7 @@ module IGraph.Generators ...@@ -10,6 +12,7 @@ module IGraph.Generators
import Control.Monad (when) import Control.Monad (when)
import Data.Hashable (Hashable) import Data.Hashable (Hashable)
import Data.Serialize (Serialize) import Data.Serialize (Serialize)
import Data.Singletons (SingI, Sing, sing, fromSing)
import System.IO.Unsafe (unsafePerformIO) import System.IO.Unsafe (unsafePerformIO)
import qualified Foreign.Ptr as C2HSImp import qualified Foreign.Ptr as C2HSImp
...@@ -23,14 +26,17 @@ import IGraph.Mutable ...@@ -23,14 +26,17 @@ import IGraph.Mutable
#include "haskell_igraph.h" #include "haskell_igraph.h"
full :: Graph d full :: forall d. SingI d
=> Int -- ^ The number of vertices in the graph. => Int -- ^ The number of vertices in the graph.
-> Bool -- ^ Whether to include self-edges (loops) -> Bool -- ^ Whether to include self-edges (loops)
-> d -- ^ U or D -> Graph d () ()
-> LGraph d () () full n hasLoop = unsafePerformIO $ do
full n hasLoop d = unsafePerformIO $ do gr <- igraphFull n directed hasLoop
gr <- igraphFull n (isD d) hasLoop unsafeFreeze $ MGraph gr
unsafeFreeze $ MLGraph gr where
directed = case fromSing (sing :: Sing d) of
D -> True
U -> False
{#fun igraph_full as ^ {#fun igraph_full as ^
{ allocaIGraph- `IGraph' addIGraphFinalizer* { allocaIGraph- `IGraph' addIGraphFinalizer*
, `Int', `Bool', `Bool' , `Int', `Bool', `Bool'
...@@ -39,18 +45,21 @@ full n hasLoop d = unsafePerformIO $ do ...@@ -39,18 +45,21 @@ full n hasLoop d = unsafePerformIO $ do
data ErdosRenyiModel = GNP Int Double data ErdosRenyiModel = GNP Int Double
| GNM Int Int | GNM Int Int
erdosRenyiGame :: Graph d erdosRenyiGame :: forall d. SingI d
=> ErdosRenyiModel => ErdosRenyiModel
-> d -- ^ directed
-> Bool -- ^ self-loop -> Bool -- ^ self-loop
-> IO (LGraph d () ()) -> IO (Graph d () ())
erdosRenyiGame (GNP n p) d self = do erdosRenyiGame model self = do
gp <- igraphInit >> igraphErdosRenyiGame IgraphErdosRenyiGnp n p (isD d) self igraphInit
unsafeFreeze $ MLGraph gp gr <- case model of
erdosRenyiGame (GNM n m) d self = do GNP n p -> igraphErdosRenyiGame IgraphErdosRenyiGnp n p directed self
gp <- igraphInit >> igraphErdosRenyiGame IgraphErdosRenyiGnm n GNM n m -> igraphErdosRenyiGame IgraphErdosRenyiGnm n (fromIntegral m)
(fromIntegral m) (isD d) self directed self
unsafeFreeze $ MLGraph gp unsafeFreeze $ MGraph gr
where
directed = case fromSing (sing :: Sing d) of
D -> True
U -> False
{#fun igraph_erdos_renyi_game as ^ {#fun igraph_erdos_renyi_game as ^
{ allocaIGraph- `IGraph' addIGraphFinalizer* { allocaIGraph- `IGraph' addIGraphFinalizer*
, `ErdosRenyi', `Int', `Double', `Bool', `Bool' , `ErdosRenyi', `Int', `Double', `Bool', `Bool'
...@@ -59,24 +68,24 @@ erdosRenyiGame (GNM n m) d self = do ...@@ -59,24 +68,24 @@ erdosRenyiGame (GNM n m) d self = do
-- | Generates a random graph with a given degree sequence. -- | Generates a random graph with a given degree sequence.
degreeSequenceGame :: [Int] -- ^ Out degree degreeSequenceGame :: [Int] -- ^ Out degree
-> [Int] -- ^ In degree -> [Int] -- ^ In degree
-> IO (LGraph D () ()) -> IO (Graph 'D () ())
degreeSequenceGame out_deg in_deg = withList out_deg $ \out_deg' -> degreeSequenceGame out_deg in_deg = withList out_deg $ \out_deg' ->
withList in_deg $ \in_deg' -> do withList in_deg $ \in_deg' -> do
gp <- igraphDegreeSequenceGame out_deg' in_deg' IgraphDegseqSimple gp <- igraphDegreeSequenceGame out_deg' in_deg' IgraphDegseqSimple
unsafeFreeze $ MLGraph gp unsafeFreeze $ MGraph gp
{#fun igraph_degree_sequence_game as ^ {#fun igraph_degree_sequence_game as ^
{ allocaIGraph- `IGraph' addIGraphFinalizer* { allocaIGraph- `IGraph' addIGraphFinalizer*
, castPtr `Ptr Vector', castPtr `Ptr Vector', `Degseq' , castPtr `Ptr Vector', castPtr `Ptr Vector', `Degseq'
} -> `CInt' void- #} } -> `CInt' void- #}
-- | Randomly rewires a graph while preserving the degree distribution. -- | Randomly rewires a graph while preserving the degree distribution.
rewire :: (Graph d, Hashable v, Serialize v, Eq v, Serialize e) rewire :: (Hashable v, Serialize v, Eq v, Serialize e)
=> Int -- ^ Number of rewiring trials to perform. => Int -- ^ Number of rewiring trials to perform.
-> LGraph d v e -> Graph d v e
-> IO (LGraph d v e) -> IO (Graph d v e)
rewire n gr = do rewire n gr = do
(MLGraph gptr) <- thaw gr (MGraph gptr) <- thaw gr
err <- igraphRewire gptr n IgraphRewiringSimple err <- igraphRewire gptr n IgraphRewiringSimple
when (err /= 0) $ error "failed to rewire graph!" when (err /= 0) $ error "failed to rewire graph!"
unsafeFreeze $ MLGraph gptr unsafeFreeze $ MGraph gptr
{#fun igraph_rewire as ^ { `IGraph', `Int', `Rewiring' } -> `Int' #} {#fun igraph_rewire as ^ { `IGraph', `Int', `Rewiring' } -> `Int' #}
{-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE ScopedTypeVariables #-}
module IGraph.Isomorphism module IGraph.Isomorphism
( getSubisomorphisms ( getSubisomorphisms
, isomorphic , isomorphic
...@@ -8,6 +9,7 @@ module IGraph.Isomorphism ...@@ -8,6 +9,7 @@ module IGraph.Isomorphism
) where ) where
import System.IO.Unsafe (unsafePerformIO) import System.IO.Unsafe (unsafePerformIO)
import Data.Singletons (SingI, Sing, sing, fromSing)
import Foreign import Foreign
import Foreign.C.Types import Foreign.C.Types
...@@ -19,9 +21,8 @@ import IGraph.Mutable ...@@ -19,9 +21,8 @@ import IGraph.Mutable
#include "haskell_igraph.h" #include "haskell_igraph.h"
getSubisomorphisms :: Graph d getSubisomorphisms :: Graph d v1 e1 -- ^ graph to be searched in
=> LGraph d v1 e1 -- ^ graph to be searched in -> Graph d v2 e2 -- ^ smaller graph
-> LGraph d v2 e2 -- ^ smaller graph
-> [[Int]] -> [[Int]]
getSubisomorphisms g1 g2 = unsafePerformIO $ allocaVectorPtr $ \vpptr -> do getSubisomorphisms g1 g2 = unsafePerformIO $ allocaVectorPtr $ \vpptr -> do
igraphGetSubisomorphismsVf2 gptr1 gptr2 nullPtr nullPtr nullPtr nullPtr vpptr igraphGetSubisomorphismsVf2 gptr1 gptr2 nullPtr nullPtr nullPtr nullPtr vpptr
...@@ -45,9 +46,8 @@ getSubisomorphisms g1 g2 = unsafePerformIO $ allocaVectorPtr $ \vpptr -> do ...@@ -45,9 +46,8 @@ getSubisomorphisms g1 g2 = unsafePerformIO $ allocaVectorPtr $ \vpptr -> do
} -> `CInt' void- #} } -> `CInt' void- #}
-- | Determine whether two graphs are isomorphic. -- | Determine whether two graphs are isomorphic.
isomorphic :: Graph d isomorphic :: Graph d v1 e1
=> LGraph d v1 e1 -> Graph d v2 e2
-> LGraph d v2 e2
-> Bool -> Bool
isomorphic g1 g2 = unsafePerformIO $ alloca $ \ptr -> do isomorphic g1 g2 = unsafePerformIO $ alloca $ \ptr -> do
_ <- igraphIsomorphic (_graph g1) (_graph g2) ptr _ <- igraphIsomorphic (_graph g1) (_graph g2) ptr
...@@ -57,27 +57,32 @@ isomorphic g1 g2 = unsafePerformIO $ alloca $ \ptr -> do ...@@ -57,27 +57,32 @@ isomorphic g1 g2 = unsafePerformIO $ alloca $ \ptr -> do
-- | Creates a graph from the given isomorphism class. -- | Creates a graph from the given isomorphism class.
-- This function is implemented only for graphs with three or four vertices. -- This function is implemented only for graphs with three or four vertices.
isoclassCreate :: Graph d isoclassCreate :: forall d. SingI d
=> Int -- ^ The number of vertices to add to the graph. => Int -- ^ The number of vertices to add to the graph.
-> Int -- ^ The isomorphism class -> Int -- ^ The isomorphism class
-> d -> Graph d () ()
-> LGraph d () () isoclassCreate size idx = unsafePerformIO $ do
isoclassCreate size idx d = unsafePerformIO $ do gp <- igraphInit >> igraphIsoclassCreate size idx directed
gp <- igraphInit >> igraphIsoclassCreate size idx (isD d) unsafeFreeze $ MGraph gp
unsafeFreeze $ MLGraph gp where
directed = case fromSing (sing :: Sing d) of
D -> True
U -> False
{#fun igraph_isoclass_create as ^ {#fun igraph_isoclass_create as ^
{ allocaIGraph- `IGraph' addIGraphFinalizer* { allocaIGraph- `IGraph' addIGraphFinalizer*
, `Int', `Int', `Bool' , `Int', `Int', `Bool'
} -> `CInt' void- #} } -> `CInt' void- #}
isoclass3 :: Graph d => d -> [LGraph d () ()] isoclass3 :: forall d. SingI d => [Graph d () ()]
isoclass3 d = map (flip (isoclassCreate 3) d) n isoclass3 = map (isoclassCreate 3) (if directed then [0..15] else [0..3])
where where
n | isD d = [0..15] directed = case fromSing (sing :: Sing d) of
| otherwise = [0..3] D -> True
U -> False
isoclass4 :: Graph d => d -> [LGraph d () ()] isoclass4 :: forall d. SingI d => [Graph d () ()]
isoclass4 d = map (flip (isoclassCreate 4) d) n isoclass4 = map (isoclassCreate 4) (if directed then [0..217] else [0..10])
where where
n | isD d = [0..217] directed = case fromSing (sing :: Sing d) of
| otherwise = [0..10] D -> True
U -> False
...@@ -61,7 +61,7 @@ defaultLGL = LGL ...@@ -61,7 +61,7 @@ defaultLGL = LGL
where where
area x = fromIntegral $ x^2 area x = fromIntegral $ x^2
getLayout :: Graph d => LGraph d v e -> LayoutMethod -> IO [(Double, Double)] getLayout :: Graph d v e -> LayoutMethod -> IO [(Double, Double)]
getLayout gr method = case method of getLayout gr method = case method of
KamadaKawai seed niter sigma initemp coolexp kkconst -> case seed of KamadaKawai seed niter sigma initemp coolexp kkconst -> case seed of
Nothing -> allocaMatrix $ \mat -> do Nothing -> allocaMatrix $ \mat -> do
......
{-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE DataKinds #-}
module IGraph.Motif module IGraph.Motif
( triad ( triad
, triadCensus , triadCensus
...@@ -32,7 +33,7 @@ import IGraph ...@@ -32,7 +33,7 @@ import IGraph
-- 120C: A->B->C, A<->C. -- 120C: A->B->C, A<->C.
-- 210: A->B<->C, A<->C. -- 210: A->B<->C, A<->C.
-- 300: A<->B<->C, A<->C, the complete graph. -- 300: A<->B<->C, A<->C, the complete graph.
triad :: [LGraph D () ()] triad :: [Graph 'D () ()]
triad = map make edgeList triad = map make edgeList
where where
edgeList = edgeList =
...@@ -53,10 +54,10 @@ triad = map make edgeList ...@@ -53,10 +54,10 @@ triad = map make edgeList
, [(0,1), (1,2), (2,1), (0,2), (2,0)] , [(0,1), (1,2), (2,1), (0,2), (2,0)]
, [(0,1), (1,0), (1,2), (2,1), (0,2), (2,0)] , [(0,1), (1,0), (1,2), (2,1), (0,2), (2,0)]
] ]
make :: [(Int, Int)] -> LGraph D () () make :: [(Int, Int)] -> Graph 'D () ()
make xs = mkGraph (replicate 3 ()) $ zip xs $ repeat () make xs = mkGraph (replicate 3 ()) $ zip xs $ repeat ()
triadCensus :: (Hashable v, Eq v, Read v) => LGraph d v e -> [Int] triadCensus :: (Hashable v, Eq v, Read v) => Graph d v e -> [Int]
triadCensus gr = unsafePerformIO $ allocaVector $ \result -> do triadCensus gr = unsafePerformIO $ allocaVector $ \result -> do
igraphTriadCensus (_graph gr) result igraphTriadCensus (_graph gr) result
map truncate <$> toList result map truncate <$> toList result
......
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
module IGraph.Mutable module IGraph.Mutable
( MGraph(..) ( MGraph(..)
, MLGraph(..) , new
, addNodes
, addLNodes
, delNodes
, addEdges
, addLEdges
, delEdges
, setEdgeAttr , setEdgeAttr
, setNodeAttr , setNodeAttr
)where )where
import Control.Monad (when, forM) import Control.Monad (forM, when)
import Control.Monad.Primitive import Control.Monad.Primitive
import Data.Serialize (Serialize, encode) import Data.Serialize (Serialize, encode)
import Foreign import Data.Singletons.Prelude (Sing, SingI, fromSing, sing)
import Foreign hiding (new)
import IGraph.Internal import IGraph.Internal
import IGraph.Internal.Initialization import IGraph.Internal.Initialization
import IGraph.Types import IGraph.Types
-- | Mutable labeled graph. -- | Mutable labeled graph.
newtype MLGraph m d v e = MLGraph IGraph newtype MGraph m (d :: EdgeType) v e = MGraph IGraph
class MGraph d where -- | Create a new graph.
-- | Create a new graph. new :: forall m d v e. (SingI d, PrimMonad m)
new :: PrimMonad m => Int -> m (MLGraph (PrimState m) d v e) => Int -> m (MGraph (PrimState m) d v e)
new n = unsafePrimToPrim $ igraphInit >>= igraphNew n directed >>= return . MGraph
-- | Add nodes to the graph. where
addNodes :: PrimMonad m directed = case fromSing (sing :: Sing d) of
=> Int -- ^ The number of new nodes. D -> True
-> MLGraph(PrimState m) d v e -> m () U -> False
addNodes n (MLGraph g) = unsafePrimToPrim $ igraphAddVertices g n nullPtr
-- | Add nodes to the graph.
-- | Add nodes with labels to the graph. addNodes :: PrimMonad m
addLNodes :: (Serialize v, PrimMonad m) => Int -- ^ The number of new nodes.
=> [v] -- ^ vertices' labels -> MGraph(PrimState m) d v e -> m ()
-> MLGraph (PrimState m) d v e -> m () addNodes n (MGraph g) = unsafePrimToPrim $ igraphAddVertices g n nullPtr
addLNodes labels (MLGraph g) = unsafePrimToPrim $
withAttr vertexAttr labels $ \attr -> -- | Add nodes with labels to the graph.
withPtrs [attr] (igraphAddVertices g n . castPtr) addLNodes :: (Serialize v, PrimMonad m)
where => [v] -- ^ vertices' labels
n = length labels -> MGraph (PrimState m) d v e -> m ()
addLNodes labels (MGraph g) = unsafePrimToPrim $
-- | Delete nodes from the graph. withAttr vertexAttr labels $ \attr ->
delNodes :: PrimMonad m => [Int] -> MLGraph (PrimState m) d v e -> m () withPtrs [attr] (igraphAddVertices g n . castPtr)
delNodes ns (MLGraph g) = unsafePrimToPrim $ withVerticesList ns $ \vs -> where
igraphDeleteVertices g vs n = length labels
-- | Add edges to the graph. -- | Delete nodes from the graph.
addEdges :: PrimMonad m => [(Int, Int)] -> MLGraph (PrimState m) d v e -> m () delNodes :: PrimMonad m => [Int] -> MGraph (PrimState m) d v e -> m ()
addEdges es (MLGraph g) = unsafePrimToPrim $ withList xs $ \vec -> delNodes ns (MGraph g) = unsafePrimToPrim $ withVerticesList ns $ \vs ->
igraphAddEdges g vec nullPtr igraphDeleteVertices g vs
where
xs = concatMap ( \(a,b) -> [a, b] ) es -- | Add edges to the graph.
addEdges :: PrimMonad m => [(Int, Int)] -> MGraph (PrimState m) d v e -> m ()
-- | Add edges with labels to the graph. addEdges es (MGraph g) = unsafePrimToPrim $ withList xs $ \vec ->
addLEdges :: (PrimMonad m, Serialize e) => [LEdge e] -> MLGraph (PrimState m) d v e -> m () igraphAddEdges g vec nullPtr
addLEdges es (MLGraph g) = unsafePrimToPrim $ where
withAttr edgeAttr vs $ \attr -> withList (concat xs) $ \vec -> xs = concatMap ( \(a,b) -> [a, b] ) es
withPtrs [attr] (igraphAddEdges g vec . castPtr)
where -- | Add edges with labels to the graph.
(xs, vs) = unzip $ map ( \((a,b),v) -> ([a, b], v) ) es addLEdges :: (PrimMonad m, Serialize e)
=> [LEdge e] -> MGraph (PrimState m) d v e -> m ()
-- | Delete edges from the graph. addLEdges es (MGraph g) = unsafePrimToPrim $
delEdges :: PrimMonad m => [(Int, Int)] -> MLGraph (PrimState m) d v e -> m () withAttr edgeAttr vs $ \attr -> withList (concat xs) $ \vec ->
withPtrs [attr] (igraphAddEdges g vec . castPtr)
instance MGraph U where where
new n = unsafePrimToPrim $ igraphInit >>= igraphNew n False >>= return . MLGraph (xs, vs) = unzip $ map ( \((a,b),v) -> ([a, b], v) ) es
delEdges es (MLGraph g) = unsafePrimToPrim $ do -- | Delete edges from the graph.
eids <- forM es $ \(fr, to) -> igraphGetEid g fr to False True delEdges :: forall m d v e. (SingI d, PrimMonad m)
withEdgeIdsList eids (igraphDeleteEdges g) => [(Int, Int)] -> MGraph (PrimState m) d v e -> m ()
delEdges es (MGraph g) = unsafePrimToPrim $ do
instance MGraph D where eids <- forM es $ \(fr, to) -> igraphGetEid g fr to directed True
new n = unsafePrimToPrim $ igraphInit >>= igraphNew n True >>= return . MLGraph withEdgeIdsList eids (igraphDeleteEdges g)
where
delEdges es (MLGraph g) = unsafePrimToPrim $ do directed = case fromSing (sing :: Sing d) of
eids <- forM es $ \(fr, to) -> igraphGetEid g fr to True True D -> True
withEdgeIdsList eids (igraphDeleteEdges g) U -> False
-- | Set node attribute. -- | Set node attribute.
setNodeAttr :: (PrimMonad m, Serialize v) setNodeAttr :: (PrimMonad m, Serialize v)
=> Int -- ^ Node id => Int -- ^ Node id
-> v -> v
-> MLGraph (PrimState m) d v e -> MGraph (PrimState m) d v e
-> m () -> m ()
setNodeAttr nodeId x (MLGraph gr) = unsafePrimToPrim $ setNodeAttr nodeId x (MGraph gr) = unsafePrimToPrim $
withByteString (encode x) $ \bs -> do withByteString (encode x) $ \bs -> do
err <- igraphHaskellAttributeVASSet gr vertexAttr nodeId bs err <- igraphHaskellAttributeVASSet gr vertexAttr nodeId bs
when (err /= 0) $ error "Fail to set node attribute!" when (err /= 0) $ error "Fail to set node attribute!"
...@@ -90,9 +100,9 @@ setNodeAttr nodeId x (MLGraph gr) = unsafePrimToPrim $ ...@@ -90,9 +100,9 @@ setNodeAttr nodeId x (MLGraph gr) = unsafePrimToPrim $
setEdgeAttr :: (PrimMonad m, Serialize e) setEdgeAttr :: (PrimMonad m, Serialize e)
=> Int -- ^ Edge id => Int -- ^ Edge id
-> e -> e
-> MLGraph (PrimState m) d v e -> MGraph (PrimState m) d v e
-> m () -> m ()
setEdgeAttr edgeId x (MLGraph gr) = unsafePrimToPrim $ setEdgeAttr edgeId x (MGraph gr) = unsafePrimToPrim $
withByteString (encode x) $ \bs -> do withByteString (encode x) $ \bs -> do
err <- igraphHaskellAttributeEASSet gr edgeAttr edgeId bs err <- igraphHaskellAttributeEASSet gr edgeAttr edgeId bs
when (err /= 0) $ error "Fail to set edge attribute!" when (err /= 0) $ error "Fail to set edge attribute!"
...@@ -4,20 +4,21 @@ module IGraph.Read ...@@ -4,20 +4,21 @@ module IGraph.Read
, readAdjMatrixWeighted , readAdjMatrixWeighted
) where ) where
import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Char8 as B
import Data.ByteString.Lex.Fractional (readSigned, readExponential) import Data.ByteString.Lex.Fractional (readExponential, readSigned)
import Data.Maybe (fromJust) import Data.Maybe (fromJust)
import Data.Singletons (SingI)
import IGraph import IGraph
readDouble :: B.ByteString -> Double readDouble :: B.ByteString -> Double
readDouble = fst . fromJust . readSigned readExponential readDouble = fst . fromJust . readSigned readExponential
{-# INLINE readDouble #-} {-# INLINE readDouble #-}
readAdjMatrix :: Graph d => FilePath -> IO (LGraph d B.ByteString ()) readAdjMatrix :: SingI d => FilePath -> IO (Graph d B.ByteString ())
readAdjMatrix = fmap fromAdjMatrix . B.readFile readAdjMatrix = fmap fromAdjMatrix . B.readFile
fromAdjMatrix :: Graph d => B.ByteString -> LGraph d B.ByteString () fromAdjMatrix :: SingI d => B.ByteString -> Graph d B.ByteString ()
fromAdjMatrix bs = fromAdjMatrix bs =
let (header:xs) = B.lines bs let (header:xs) = B.lines bs
mat = map (map readDouble . B.words) xs mat = map (map readDouble . B.words) xs
...@@ -31,7 +32,7 @@ fromAdjMatrix bs = ...@@ -31,7 +32,7 @@ fromAdjMatrix bs =
f ((i,j),v) = i < j && v /= 0 f ((i,j),v) = i < j && v /= 0
{-# INLINE fromAdjMatrix #-} {-# INLINE fromAdjMatrix #-}
readAdjMatrixWeighted :: Graph d => FilePath -> IO (LGraph d B.ByteString Double) readAdjMatrixWeighted :: SingI d => FilePath -> IO (Graph d B.ByteString Double)
readAdjMatrixWeighted fl = do readAdjMatrixWeighted fl = do
c <- B.readFile fl c <- B.readFile fl
let (header:xs) = B.lines c let (header:xs) = B.lines c
......
...@@ -15,6 +15,7 @@ import qualified Data.HashMap.Strict as M ...@@ -15,6 +15,7 @@ import qualified Data.HashMap.Strict as M
import Data.Serialize (Serialize, decode) import Data.Serialize (Serialize, decode)
import System.IO.Unsafe (unsafePerformIO) import System.IO.Unsafe (unsafePerformIO)
import Data.Maybe import Data.Maybe
import Data.Singletons (SingI)
import Foreign import Foreign
import Foreign.C.Types import Foreign.C.Types
...@@ -26,14 +27,14 @@ import IGraph.Mutable ...@@ -26,14 +27,14 @@ import IGraph.Mutable
#include "igraph/igraph.h" #include "igraph/igraph.h"
inducedSubgraph :: (Hashable v, Eq v, Serialize v) => LGraph d v e -> [Int] -> LGraph d v e inducedSubgraph :: (Hashable v, Eq v, Serialize v) => Graph d v e -> [Int] -> Graph d v e
inducedSubgraph gr nds = unsafePerformIO $ withVerticesList nds $ \vs -> inducedSubgraph gr nds = unsafePerformIO $ withVerticesList nds $ \vs ->
igraphInducedSubgraph (_graph gr) vs IgraphSubgraphCreateFromScratch >>= igraphInducedSubgraph (_graph gr) vs IgraphSubgraphCreateFromScratch >>=
unsafeFreeze . MLGraph unsafeFreeze . MGraph
-- | Closeness centrality -- | Closeness centrality
closeness :: [Int] -- ^ vertices closeness :: [Int] -- ^ vertices
-> LGraph d v e -> Graph d v e
-> Maybe [Double] -- ^ optional edge weights -> Maybe [Double] -- ^ optional edge weights
-> Neimode -> Neimode
-> Bool -- ^ whether to normalize -> Bool -- ^ whether to normalize
...@@ -45,7 +46,7 @@ closeness nds gr ws mode normal = unsafePerformIO $ allocaVector $ \result -> ...@@ -45,7 +46,7 @@ closeness nds gr ws mode normal = unsafePerformIO $ allocaVector $ \result ->
-- | Betweenness centrality -- | Betweenness centrality
betweenness :: [Int] betweenness :: [Int]
-> LGraph d v e -> Graph d v e
-> Maybe [Double] -> Maybe [Double]
-> [Double] -> [Double]
betweenness nds gr ws = unsafePerformIO $ allocaVector $ \result -> betweenness nds gr ws = unsafePerformIO $ allocaVector $ \result ->
...@@ -54,7 +55,7 @@ betweenness nds gr ws = unsafePerformIO $ allocaVector $ \result -> ...@@ -54,7 +55,7 @@ betweenness nds gr ws = unsafePerformIO $ allocaVector $ \result ->
toList result toList result
-- | Eigenvector centrality -- | Eigenvector centrality
eigenvectorCentrality :: LGraph d v e eigenvectorCentrality :: Graph d v e
-> Maybe [Double] -> Maybe [Double]
-> [Double] -> [Double]
eigenvectorCentrality gr ws = unsafePerformIO $ allocaArpackOpt $ \arparck -> eigenvectorCentrality gr ws = unsafePerformIO $ allocaArpackOpt $ \arparck ->
...@@ -63,8 +64,8 @@ eigenvectorCentrality gr ws = unsafePerformIO $ allocaArpackOpt $ \arparck -> ...@@ -63,8 +64,8 @@ eigenvectorCentrality gr ws = unsafePerformIO $ allocaArpackOpt $ \arparck ->
toList result toList result
-- | Google's PageRank -- | Google's PageRank
pagerank :: Graph d pagerank :: SingI d
=> LGraph d v e => Graph d v e
-> Maybe [Double] -- ^ edge weights -> Maybe [Double] -- ^ edge weights
-> Double -- ^ damping factor, usually around 0.85 -> Double -- ^ damping factor, usually around 0.85
-> [Double] -> [Double]
...@@ -81,8 +82,8 @@ pagerank gr ws d ...@@ -81,8 +82,8 @@ pagerank gr ws d
m = nEdges gr m = nEdges gr
-- | Personalized PageRank. -- | Personalized PageRank.
personalizedPagerank :: Graph d personalizedPagerank :: SingI d
=> LGraph d v e => Graph d v e
-> [Double] -- ^ reset probability -> [Double] -- ^ reset probability
-> Maybe [Double] -> Maybe [Double]
-> Double -> Double
......
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module IGraph.Types where module IGraph.Types where
import Data.Singletons.Prelude
import Data.Singletons.TH
$(singletons [d|
data EdgeType = D
| U
deriving (Show, Read, Eq)
|])
type Node = Int type Node = Int
type LNode a = (Node, a) type LNode a = (Node, a)
type Edge = (Node, Node) type Edge = (Node, Node)
type LEdge a = (Edge, a) type LEdge a = (Edge, a)
-- | Undirected graph.
data U
-- | Directed graph.
data D
vertexAttr :: String vertexAttr :: String
vertexAttr = "vertex_attribute" vertexAttr = "vertex_attribute"
......
{-# LANGUAGE DataKinds #-}
module Test.Attributes module Test.Attributes
( tests ( tests
) where ) where
...@@ -31,14 +32,14 @@ tests = testGroup "Attribute tests" ...@@ -31,14 +32,14 @@ tests = testGroup "Attribute tests"
nodeLabelTest :: TestTree nodeLabelTest :: TestTree
nodeLabelTest = testCase "node label test" $ do nodeLabelTest = testCase "node label test" $ do
let ns = sort $ map show [38..7000] let ns = sort $ map show [38..7000]
gr = mkGraph ns [] :: LGraph D String () gr = mkGraph ns [] :: Graph 'D String ()
assertBool "" $ sort (map (nodeLab gr) $ nodes gr) == ns assertBool "" $ sort (map (nodeLab gr) $ nodes gr) == ns
labelTest :: TestTree labelTest :: TestTree
labelTest = testCase "edge label test" $ do labelTest = testCase "edge label test" $ do
dat <- randEdges 1000 10000 dat <- randEdges 1000 10000
let es = sort $ zipWith (\a b -> (a,b)) dat $ map show [1..] let es = sort $ zipWith (\a b -> (a,b)) dat $ map show [1..]
gr = fromLabeledEdges es :: LGraph D Int String gr = fromLabeledEdges es :: Graph 'D Int String
es' = sort $ map (\(a,b) -> ((nodeLab gr a, nodeLab gr b), edgeLab gr (a,b))) $ edges gr es' = sort $ map (\(a,b) -> ((nodeLab gr a, nodeLab gr b), edgeLab gr (a,b))) $ edges gr
assertBool "" $ es == es' assertBool "" $ es == es'
...@@ -48,8 +49,8 @@ serializeTest = testCase "serialize test" $ do ...@@ -48,8 +49,8 @@ serializeTest = testCase "serialize test" $ do
let es = map ( \(a, b) -> ( let es = map ( \(a, b) -> (
( defaultNodeAttributes{_nodeZindex=a} ( defaultNodeAttributes{_nodeZindex=a}
, defaultNodeAttributes{_nodeZindex=b}), defaultEdgeAttributes) ) dat , defaultNodeAttributes{_nodeZindex=b}), defaultEdgeAttributes) ) dat
gr = fromLabeledEdges es :: LGraph D NodeAttr EdgeAttr gr = fromLabeledEdges es :: Graph 'D NodeAttr EdgeAttr
gr' :: LGraph D NodeAttr EdgeAttr gr' :: Graph 'D NodeAttr EdgeAttr
gr' = case decode $ encode gr of gr' = case decode $ encode gr of
Left msg -> error msg Left msg -> error msg
Right r -> r Right r -> r
......
{-# LANGUAGE DataKinds #-}
module Test.Basic module Test.Basic
( tests ( tests
) where ) where
...@@ -33,8 +34,8 @@ graphCreation = testGroup "Graph creation" ...@@ -33,8 +34,8 @@ graphCreation = testGroup "Graph creation"
where where
edgeList = sort $ unsafePerformIO $ randEdges 1000 100 edgeList = sort $ unsafePerformIO $ randEdges 1000 100
m = length edgeList m = length edgeList
gr = mkGraph (replicate 100 ()) $ zip edgeList $ repeat () :: LGraph D () () gr = mkGraph (replicate 100 ()) $ zip edgeList $ repeat () :: Graph 'D () ()
simple = mkGraph (replicate 3 ()) $ zip [(0,1),(1,2),(2,0)] $ repeat () :: LGraph D () () simple = mkGraph (replicate 3 ()) $ zip [(0,1),(1,2),(2,0)] $ repeat () :: Graph 'D () ()
graphCreationLabeled :: TestTree graphCreationLabeled :: TestTree
graphCreationLabeled = testGroup "Graph creation -- with labels" graphCreationLabeled = testGroup "Graph creation -- with labels"
...@@ -49,14 +50,14 @@ graphCreationLabeled = testGroup "Graph creation -- with labels" ...@@ -49,14 +50,14 @@ graphCreationLabeled = testGroup "Graph creation -- with labels"
randEdges 10000 1000) $ repeat 1 randEdges 10000 1000) $ repeat 1
n = length $ nubSort $ concatMap (\((a,b),_) -> [a,b]) edgeList n = length $ nubSort $ concatMap (\((a,b),_) -> [a,b]) edgeList
m = length edgeList m = length edgeList
gr = fromLabeledEdges edgeList :: LGraph D String Int gr = fromLabeledEdges edgeList :: Graph 'D String Int
gr' = unsafePerformIO $ fromLabeledEdges' edgeList yieldMany :: LGraph D String Int gr' = unsafePerformIO $ fromLabeledEdges' edgeList yieldMany :: Graph 'D String Int
graphEdit :: TestTree graphEdit :: TestTree
graphEdit = testGroup "Graph editing" graphEdit = testGroup "Graph editing"
[ testCase "" $ [(1,2)] @=? (sort $ edges simple') ] [ testCase "" $ [(1,2)] @=? (sort $ edges simple') ]
where where
simple = mkGraph (replicate 3 ()) $ zip [(0,1),(1,2),(2,0)] $ repeat () :: LGraph U () () simple = mkGraph (replicate 3 ()) $ zip [(0,1),(1,2),(2,0)] $ repeat () :: Graph 'U () ()
simple' = runST $ do simple' = runST $ do
g <- thaw simple g <- thaw simple
delEdges [(0,1),(0,2)] g delEdges [(0,1),(0,2)] g
......
{-# LANGUAGE DataKinds #-}
module Test.Structure module Test.Structure
( tests ( tests
) where ) where
...@@ -27,7 +28,7 @@ subGraphs = testGroup "generate induced subgraphs" ...@@ -27,7 +28,7 @@ subGraphs = testGroup "generate induced subgraphs"
, ["a","c"], [("a","c"), ("c","a")] ) , ["a","c"], [("a","c"), ("c","a")] )
test (ori,ns,expect) = sort expect @=? sort result test (ori,ns,expect) = sort expect @=? sort result
where where
gr = fromLabeledEdges $ zip ori $ repeat () :: LGraph D String () gr = fromLabeledEdges $ zip ori $ repeat () :: Graph 'D String ()
ns' = map (head . getNodes gr) ns ns' = map (head . getNodes gr) ns
gr' = inducedSubgraph gr ns' gr' = inducedSubgraph gr ns'
result = map (nodeLab gr' *** nodeLab gr') $ edges gr' result = map (nodeLab gr' *** nodeLab gr') $ edges gr'
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