module Data.IGraph.Internal where
import qualified Data.HashMap.Strict as Map
import qualified Data.HashSet as Set
import qualified Data.Foldable as F
import qualified Data.Map as M
import Control.Monad
import Data.List as L
import Data.IORef
import Data.Maybe
import Foreign hiding (unsafePerformIO)
import Foreign.C
import System.IO.Unsafe (unsafePerformIO)
import Data.IGraph.Types
import Data.IGraph.Internal.Constants
nodeToId'' :: Graph d a -> a -> Int
nodeToId'' (G g) n = case Map.lookup n (graphNodeToId g) of
Just i -> i
Nothing -> error "nodeToId': Graph node/ID mismatch."
idToNode'' :: Graph d a -> Int -> a
idToNode'' (G g) i = case Map.lookup i (graphIdToNode g) of
Just n -> n
Nothing -> error ("idToNode': Graph ID/node mismatch, ID = " ++ show i)
edgeToEdgeId :: Graph d a -> Edge d a -> Int
edgeToEdgeId g@(G _) e = case elemIndex e (edges g) of
Just i -> i
_ -> error ("edgeToEdgeId: Edge not in graph.")
edgeIdToEdge :: Graph d a -> Int -> Edge d a
edgeIdToEdge g i
| i < 0 || i >= length es = error ("edgeIdToEdge: Index " ++ show i ++ " out of bound.")
| otherwise = es !! i
where
es = edges g
getNeiMode :: Integral i => Graph d a -> i
getNeiMode (G g) = fromIntegral $ fromEnum (graphNeiMode g)
foreign import ccall "igraphhaskell_graph_set_vertex_ids"
c_igraphhaskell_graph_set_vertex_ids :: GraphPtr -> IO Int
setVertexIds :: GraphPtr -> IO ()
setVertexIds gp = do
e <- c_igraphhaskell_graph_set_vertex_ids gp
case e of
1 -> error "setVertexIds: igraph C attributes not initialized. Try compiling your program with GHC instead of using GHCi. See GHC ticket #781."
_ -> return ()
foreign import ccall "igraphhaskell_graph_get_vertex_ids"
c_igraphhaskell_graph_get_vertex_ids :: GraphPtr -> VectorPtr -> IO CInt
getVertexIds :: GraphPtr -> IO (Maybe [Double])
getVertexIds gp = do
v <- newVector 0
s <- withVector v $ \vp ->
c_igraphhaskell_graph_get_vertex_ids
gp
vp
case s of
1 -> return Nothing
1 -> error "getVertexIds: igraph C attributes not initialized. Try compiling your program with GHC instead of using GHCi. See GHC ticket #781."
_ -> Just `fmap` vectorToList v
subgraphFromPtr :: Graph d a -> GraphPtr -> IO (Graph d a)
subgraphFromPtr g@(G _) gp' = do
Just is <- getVertexIds gp'
[l1,l2] <- vectorPtrToList =<< newVectorPtr' =<< c_igraph_edges gp'
let lookupM = M.fromList $ zip [0..] (map round is)
orgId :: Int -> Int
orgId i = case M.lookup i lookupM of
Just o -> o
_ -> error $ "subgraphFromPtr: Invalid ID " ++ show i
getNodes = map (idToNode'' g . orgId . round)
ls = zip (getNodes l1) (getNodes l2)
subg@(G sg) = fromListWithCtxt g ls
wes' = [ setWeight (toEdge a b) w
| e <- intersectBy (eqOnEdgeNodes g)
(edges g)
(edges subg)
, let a = edgeFrom e
b = edgeTo e
w = fromMaybe 0 (edgeWeight e)
]
subgWithWeights = G sg{ graphEdges = Set.fromList wes' }
return $
if isWeighted g
then subgWithWeights
else subg
where
fromListWithCtxt :: Graph d a -> [(a,a)] -> Graph d a
fromListWithCtxt (G _) l = fromList l
eqOnEdgeNodes :: Eq a => Graph d a -> Edge d a -> Edge d a -> Bool
eqOnEdgeNodes (G _) e1 e2 =
(==) (edgeFrom e1, edgeTo e1)
(edgeFrom e2, edgeTo e2)
foreign import ccall "igraphhaskell_initialize"
c_igraphhaskell_initialize :: IO CInt
initialize :: IO ()
initialize = do
_ <- c_igraphhaskell_initialize
return ()
foreign import ccall "c_igraph_create"
c_igraph_create :: VectorPtr -> CInt -> IO GraphPtr
foreign import ccall "c_arpack_create"
c_arpack_create :: IO ArpackPtr
foreign import ccall "&c_arpack_destroy"
c_arpack_destroy :: FunPtr (ArpackPtr -> IO ())
buildForeignGraph :: Graph d a -> Graph d a
buildForeignGraph g@(G gr) = G
(gr { graphForeignPtr = unsafePerformIO io
, graphArpackOptions = unsafePerformIO arpack
})
where
io :: IO (ForeignPtr Grph)
io = do
initialize
v <- edgesToVector g
withVector v $ \vp -> do
gp <- c_igraph_create vp (if isDirected g then 1 else 0)
newForeignPtr c_igraph_destroy gp
arpack :: IO (ForeignPtr Arpack)
arpack = do
p <- c_arpack_create
newForeignPtr c_arpack_destroy p
withGraph :: Graph d a -> (GraphPtr -> IO res) -> IO res
withGraph (G gr) = withForeignPtr (graphForeignPtr gr)
setGraphPointer :: Graph d a -> GraphPtr -> IO (Graph d a)
setGraphPointer (G g) gp = do
fp <- newForeignPtr c_igraph_destroy gp
return $ G g{ graphForeignPtr = fp }
withWeights :: Graph (Weighted d) a -> (VectorPtr -> IO res) -> IO res
withWeights g io = do
v <- listToVector $ map getWeight (edges g)
withVector v io
withOptionalWeights :: Graph d a -> (VectorPtr -> IO res) -> IO res
withOptionalWeights g@(G _) io = do
let mws = getWeights g
case mws of
Nothing -> io nullPtr
Just ws -> listToVector ws >>= flip withVector io
foreign import ccall "edges"
c_igraph_edges :: GraphPtr -> IO VectorPtrPtr
instance Show a => Show (Graph U a) where
show (G g) = show (graphEdges g)
instance Show a => Show (Graph D a) where
show (G g) = show (graphEdges g)
instance Eq (Graph U a) where
(G g1) == (G g2) = graphEdges g1 == graphEdges g2
instance Eq (Graph D a) where
(G g1) == (G g2) = graphEdges g1 == graphEdges g2
instance Show (Edge d a) => Show (Graph (Weighted d) a) where
show (G g) = show (graphEdges g)
instance Eq (Edge d a) => Eq (Graph (Weighted d) a) where
(G g1) == (G g2) = graphEdges g1 == graphEdges g2
foreign import ccall "c_igraph_vs_create"
c_igraph_vs_create :: IO VsPtr
foreign import ccall "&c_igraph_vs_destroy"
c_igraph_vs_destroy :: FunPtr (VsPtr -> IO ())
newVs :: IO VsForeignPtr
newVs = do
vsp <- c_igraph_vs_create
fvp <- newForeignPtr c_igraph_vs_destroy vsp
return $ VsF fvp
foreign import ccall "igraph_vs_all"
c_igraph_vs_all :: VsPtr -> IO CInt
foreign import ccall "igraph_vs_adj"
c_igraph_vs_adj :: VsPtr -> CInt -> CInt -> IO CInt
foreign import ccall "igraph_vs_nonadj"
c_igraph_vs_nonadj :: VsPtr -> CInt -> CInt -> IO CInt
foreign import ccall "igraph_vs_none"
c_igraph_vs_none :: VsPtr -> IO CInt
foreign import ccall "igraph_vs_1"
c_igraph_vs_1 :: VsPtr -> CInt -> IO CInt
foreign import ccall "igraph_vs_vector"
c_igraph_vs_vector :: VsPtr -> VectorPtr -> IO CInt
withVs :: VertexSelector a -> Graph d a -> (VsPtr -> IO res) -> IO res
withVs vs g f = do
fvs <- newVs
_e <- withVs' fvs $ \vsp ->
case vs of
VsAll -> c_igraph_vs_all vsp
VsNone -> c_igraph_vs_none vsp
VsAdj a -> c_igraph_vs_adj vsp (ident a) (fromIntegral $ fromEnum Out)
VsNonAdj a -> c_igraph_vs_nonadj vsp (ident a) (fromIntegral $ fromEnum Out)
Vs1 a -> c_igraph_vs_1 vsp (ident a)
VsList l -> do
v <- listToVector (map ident l)
withVector v $ c_igraph_vs_vector vsp
withVs' fvs f
where
ident a = fromIntegral (nodeToId'' g a) :: CInt
withVs' :: VsForeignPtr -> (VsPtr -> IO res) -> IO res
withVs' (VsF fp) = withForeignPtr fp
foreign import ccall "c_igraph_es_create"
c_igraph_es_create :: IO EsPtr
foreign import ccall "&c_igraph_es_destroy"
c_igraph_es_destroy :: FunPtr (EsPtr -> IO ())
newEs :: IO EsForeignPtr
newEs = do
esp <- c_igraph_es_create
fep <- newForeignPtr c_igraph_es_destroy esp
return $ EsF fep
foreign import ccall "igraph_es_all"
c_igraph_es_all :: EsPtr -> CInt -> IO CInt
foreign import ccall "igraph_es_none"
c_igraph_es_none :: EsPtr -> IO CInt
foreign import ccall "igraph_es_incident"
c_igraph_es_incident :: EsPtr -> CInt -> CInt -> IO CInt
foreign import ccall "igraph_es_1"
c_igraph_es_1 :: EsPtr -> CInt -> IO CInt
foreign import ccall "igraph_es_vector"
c_igraph_es_vector :: EsPtr -> VectorPtr -> IO CInt
foreign import ccall "es_fromto"
c_igraph_es_fromto :: EsPtr -> VsPtr -> VsPtr -> IO CInt
foreign import ccall "igraph_es_seq"
c_igraph_es_seq :: EsPtr -> CInt -> CInt -> IO CInt
withEs :: EdgeSelector d a -> Graph d a -> (EsPtr -> IO res) -> IO res
withEs es g f = do
fes <- newEs
_e <- withEs' fes $ \esp ->
case es of
EsAll -> c_igraph_es_all esp (fromIntegral $ fromEnum EdgeOrderId)
EsNone -> c_igraph_es_none esp
EsIncident a -> c_igraph_es_incident esp (ident a) (getNeiMode g)
EsSeq a b -> c_igraph_es_seq esp (ident a) (ident b)
Es1 e -> c_igraph_es_1 esp (ident' e)
EsFromTo vs1 vs2 -> withVs vs1 g $ \vsp1 -> withVs vs2 g $ \vsp2 ->
c_igraph_es_fromto esp vsp1 vsp2
EsList l -> do
v <- listToVector (map (edgeToEdgeId g) l)
withVector v $ \vp ->
c_igraph_es_vector esp vp
withEs' fes f
where
ident a = fromIntegral (nodeToId'' g a) :: CInt
ident' a = fromIntegral (edgeToEdgeId g a) :: CInt
withEs' :: EsForeignPtr -> (EsPtr -> IO res) -> IO res
withEs' (EsF fp) = withForeignPtr fp
withArpack :: Graph d a -> (ArpackPtr -> IO res) -> IO res
withArpack (G Graph{ graphArpackOptions = fp }) f =
withForeignPtr fp f
foreign import ccall "igraph_matrix_e"
c_igraph_matrix_get :: MatrixPtr -> CLong -> CLong -> IO CDouble
foreign import ccall "c_igraph_matrix_create"
c_igraph_matrix_create :: CLong -> CLong -> IO MatrixPtr
foreign import ccall "&c_igraph_matrix_destroy"
c_igraph_matrix_destroy :: FunPtr (MatrixPtr -> IO ())
foreign import ccall "igraph_matrix_set"
c_igraph_matrix_set :: MatrixPtr -> CLong -> CLong -> CDouble -> IO ()
foreign import ccall "igraph_matrix_nrow"
c_igraph_matrix_nrow :: MatrixPtr -> IO CLong
foreign import ccall "igraph_matrix_ncol"
c_igraph_matrix_ncol :: MatrixPtr -> IO CLong
foreign import ccall "igraph_matrix_get_row"
c_igraph_matrix_get_row :: MatrixPtr -> VectorPtr -> CLong -> IO CInt
newMatrix :: Int -> Int -> IO Matrix
newMatrix nrow ncol = do
mp <- c_igraph_matrix_create (fromIntegral nrow) (fromIntegral ncol)
fmp <- newForeignPtr c_igraph_matrix_destroy mp
return $ Matrix fmp
getMatrixValue :: Matrix -> Int -> Int -> IO Double
getMatrixValue (Matrix fmp) x y = withForeignPtr fmp $ \ mp -> do
cd <- c_igraph_matrix_get mp (fromIntegral x) (fromIntegral y)
return $ realToFrac cd
listToMatrix :: Integral a => [[a]] -> IO Matrix
listToMatrix l = do
m <- newMatrix nrow ncol
withMatrix m $ \mp ->
forListM_ (zip [0..] l) $ \(r,row) ->
forListM_ (zip [0..] row) $ \(c,val) ->
c_igraph_matrix_set mp r c (fromIntegral val)
return m
where
nrow = maximum (map length l)
ncol = length l
matrixToList :: Matrix -> IO [[Double]]
matrixToList m = withMatrix m $ \mp -> do
nrow <- c_igraph_matrix_nrow mp
ncol <- c_igraph_matrix_ncol mp
forM [0..nrow1] $ \r -> do
v <- newVector (fromIntegral ncol)
_e <- withVector v $ \vp ->
c_igraph_matrix_get_row mp vp r
vectorToList v
foreign import ccall "c_igraph_vector_create" c_igraph_vector_create :: CLong -> IO VectorPtr
foreign import ccall "&c_igraph_vector_destroy" c_igraph_vector_destroy :: FunPtr (VectorPtr -> IO ())
newVector :: Int -> IO Vector
newVector s = do
vp <- c_igraph_vector_create (fromIntegral s)
newVector' vp
newVector' :: VectorPtr -> IO Vector
newVector' vp = do
fvp <- newForeignPtr c_igraph_vector_destroy vp
return $ Vector fvp
foreign import ccall "igraph_vector_set" c_igraph_vector_set :: VectorPtr -> CLong -> CDouble -> IO ()
foreign import ccall "igraph_vector_e" c_igraph_vector_get :: VectorPtr -> CLong -> IO CDouble
foreign import ccall "igraph_vector_size" c_igraph_vector_length :: VectorPtr -> IO CLong
vectorToList :: Vector -> IO [Double]
vectorToList (Vector fvp) = withForeignPtr fvp vectorToList'
vectorToList' :: VectorPtr -> IO [Double]
vectorToList' vp = do
len <- c_igraph_vector_length vp
let go :: [Double] -> CLong -> IO [Double]
go acc 0 = return acc
go acc i = do e <- c_igraph_vector_get vp (i 1)
go (realToFrac e : acc) (i 1)
go [] len
listToVector :: (Integral a) => [a] -> IO Vector
listToVector as = do
v <- newVector (length as)
withVector v $ \vp -> do
sizeRef <- newIORef (0 :: Int)
forListM_ as $ \a -> do
size <- readIORef sizeRef
c_igraph_vector_set vp (fromIntegral size) (fromIntegral a)
modifyIORef sizeRef (+1)
return v
foreign import ccall "c_igraph_vector_ptr_create" c_igraph_vector_ptr_create :: CLong -> IO VectorPtrPtr
foreign import ccall "&c_igraph_vector_ptr_destroy" c_igraph_vector_ptr_destroy :: FunPtr (VectorPtrPtr -> IO ())
newVectorPtr :: Int -> IO VectorP
newVectorPtr s = do
vp <- c_igraph_vector_ptr_create (fromIntegral s)
newVectorPtr' vp
newVectorPtr' :: VectorPtrPtr -> IO VectorP
newVectorPtr' vp = do
fvp <- newForeignPtr c_igraph_vector_ptr_destroy vp
return $ VectorP fvp
foreign import ccall "&c_graph_vector_destroy"
c_graph_vector_destroy :: FunPtr (Ptr GraphVec -> IO ())
newGraphVector :: Int -> IO GraphVectorP
newGraphVector s = do
gvp <- c_igraph_vector_ptr_create (fromIntegral s)
fgvp <- newForeignPtr c_graph_vector_destroy (castPtr gvp)
return $ GraphVectorP fgvp
foreign import ccall "igraph_vector_ptr_e" c_igraph_vector_ptr_get :: VectorPtrPtr -> CLong -> IO VectorPtr
foreign import ccall "igraph_vector_ptr_size" c_igraph_vector_ptr_length :: VectorPtrPtr -> IO CLong
vectorPtrToList :: VectorP -> IO [[Double]]
vectorPtrToList vptr = do
vps <- vectorPtrToListOfVectorPtr vptr
mapM vectorToList' vps
vectorPtrToListOfVectorPtr
:: VectorP
-> IO [VectorPtr]
vectorPtrToListOfVectorPtr (VectorP fvp) = withForeignPtr fvp $ \vp -> do
len <- c_igraph_vector_ptr_length vp
let go :: [VectorPtr] -> CLong -> IO [VectorPtr]
go acc 0 = return acc
go acc i = do vp' <- c_igraph_vector_ptr_get vp (i1)
go (vp':acc) (i1)
go [] len
edgesToVector :: Graph d a -> IO Vector
edgesToVector g@(G g') =
listToVector $ foldr (\e r -> toId (edgeFrom e) : toId (edgeTo e) : r) [] (edges g)
where
toId n = case Map.lookup n (graphNodeToId g') of
Just i -> i
Nothing -> error "edgesToVector: Graph node/ID mismatch."
vectorToEdges :: Graph d a -> Vector -> IO [Edge d a]
vectorToEdges g@(G _) v = do
l <- vectorToList v
return $ map (edgeIdToEdge g . round) l
vectorToEdges' :: Graph d a -> VectorPtr -> IO [Edge d a]
vectorToEdges' g@(G _) vp = do
l <- vectorToList' vp
return $ map (edgeIdToEdge g . round) l
vectorToVertices :: Graph d a -> Vector -> IO [a]
vectorToVertices g@(G _) v = fmap (map (idToNode'' g . round)) (vectorToList v)
vectorToVertices' :: Graph d a -> VectorPtr -> IO [a]
vectorToVertices' g@(G _) vp = fmap (map (idToNode'' g . round)) (vectorToList' vp)
vectorPtrToVertices :: Graph d a -> VectorP -> IO [[a]]
vectorPtrToVertices g@(G _) v = fmap (map (map (idToNode'' g . round))) (vectorPtrToList v)
vectorPtrToEdges :: Graph d a -> VectorP -> IO [[Edge d a]]
vectorPtrToEdges g@(G _) v = do
l <- vectorPtrToList v
return $ map (map (edgeIdToEdge g . round)) l
graphVectorToSubgraphs :: GraphVectorP -> Graph d a -> IO [Graph d a]
graphVectorToSubgraphs (GraphVectorP fvp) g = withForeignPtr fvp $ \vp -> do
len <- c_igraph_vector_ptr_length (castPtr vp)
let go :: Graph d a -> [Graph d a] -> CLong -> IO [Graph d a]
go _ acc 0 = return acc
go ctxt acc i = do gp <- c_igraph_vector_ptr_get (castPtr vp) (i1)
g' <- subgraphFromPtr ctxt (castPtr gp)
go ctxt (g' : acc) (i1)
go g [] len
withMatrix :: Matrix -> (MatrixPtr -> IO a) -> IO a
withMatrix (Matrix fmp) = withForeignPtr fmp
withVector :: Vector -> (VectorPtr -> IO a) -> IO a
withVector (Vector fvp) = withForeignPtr fvp
withVectorPtr :: VectorP -> (VectorPtrPtr -> IO a) -> IO a
withVectorPtr (VectorP fvp) = withForeignPtr fvp
withGraphVector :: GraphVectorP -> (GraphVecPtr -> IO a) -> IO a
withGraphVector (GraphVectorP fgvp) = withForeignPtr fgvp
foreign import ccall "&c_igraph_destroy" c_igraph_destroy :: FunPtr (GraphPtr -> IO ())
forListM_ :: [a] -> (a -> IO b) -> IO ()
forListM_ [] _ = return ()
forListM_ (a : as) f = f a >> forListM_ as f
getWeight :: Edge (Weighted d) a -> Int
getWeight (W _ w) = w
toEdgeWeighted :: E d a => a -> a -> Int -> Edge (Weighted d) a
toEdgeWeighted a b w = W (toEdge a b) w
emptyGraph :: E d a => Graph d a
emptyGraph = buildForeignGraph $ G (Graph 0 0 Map.empty Map.empty Set.empty undefined undefined Out)
emptyWithCtxt :: Graph d a -> Graph d a
emptyWithCtxt (G _) = emptyGraph
fromList :: E d a => [(a,a)] -> Graph d a
fromList = foldl' (\g (a,b) -> insertEdge (toEdge a b) g) emptyGraph
fromListWeighted :: (E d a, IsUnweighted d) => [(a,a,Int)] -> Graph (Weighted d) a
fromListWeighted = foldl' (\g (a,b,w) -> insertEdge (W (toEdge a b) w) g) emptyGraph
numberOfNodes :: Graph d a -> Int
numberOfNodes (G g) = graphNodeNumber g
numberOfEdges :: Graph d a -> Int
numberOfEdges (G g) = graphEdgeNumber g
member :: a -> Graph d a -> Bool
member a (G g) = a `Map.member` graphNodeToId g
nodeToId :: Graph d a -> a -> Maybe Int
nodeToId (G g) n = Map.lookup n (graphNodeToId g)
idToNode :: Graph d a -> Int -> Maybe a
idToNode (G g) i = Map.lookup i (graphIdToNode g)
deleteNode :: a -> Graph d a -> Graph d a
deleteNode n (G g) = buildForeignGraph $ G $
case Map.lookup n (graphNodeToId g) of
Just i -> g { graphNodeNumber = graphNodeNumber g 1
, graphIdToNode = Map.delete i (graphIdToNode g)
, graphNodeToId = Map.delete n (graphNodeToId g)
, graphEdges = Set.filter (\e -> edgeFrom e /= n && edgeTo e /= n) (graphEdges g)
}
Nothing -> g
insertEdge :: Edge d a -> Graph d a -> Graph d a
insertEdge e (G g)
| e `elem` edges (G g) || f == t = G g
| otherwise = buildForeignGraph $ G $
case (Map.member f (graphNodeToId g), Map.member t (graphNodeToId g)) of
(True, True) -> insertEdge'' (G g)
(False, True) -> insertEdge'' (insertNode f i (G g))
(True, False) -> insertEdge'' (insertNode t i (G g))
(False, False) -> insertEdge'' (insertNode t (i+1) $ insertNode f i (G g))
where
(f,t) = (edgeFrom e, edgeTo e)
i = Map.size (graphIdToNode g)
insertEdge'' (G g') =
g' { graphEdgeNumber = graphEdgeNumber g' + 1
, graphEdges = Set.insert e (graphEdges g')
}
insertNode :: a -> Int -> Graph d a -> Graph d a
insertNode n ni (G g') = G $
g' { graphNodeNumber = graphNodeNumber g' + 1
, graphIdToNode = Map.insert ni n (graphIdToNode g')
, graphNodeToId = Map.insert n ni (graphNodeToId g') }
deleteEdge :: Edge d a -> Graph d a -> Graph d a
deleteEdge e (G g)
| Set.member e (graphEdges g) = buildForeignGraph $ deleteNodes $ G $
g { graphEdges = Set.delete e (graphEdges g)
, graphEdgeNumber = graphEdgeNumber g 1
}
| otherwise = G g
where
(f,t) = (edgeFrom e, edgeTo e)
deleteNodes g' =
let delF = if null (neighbours f g') then deleteNode f else id
delT = if null (neighbours t g') then deleteNode t else id
in delT . delF $ g'
nodes :: Graph d a -> [a]
nodes (G g) = Map.keys $ graphNodeToId g
edges :: Graph d a -> [Edge d a]
edges (G g) = F.toList $ graphEdges g
neighbours :: a -> Graph d a -> [a]
neighbours n g@(G _) =
foldr neighbours'' [] (edges g)
where
neighbours'' e r
| edgeFrom e == n = edgeTo e : r
| edgeTo e == n && not (isDirected g) = edgeFrom e : r
| otherwise = r
reverseGraphDirection :: Graph d a -> Graph d a
reverseGraphDirection (G g) = G g { graphNeiMode = reverse' (graphNeiMode g) }
where
reverse' Out = In
reverse' In = Out
reverse' _ = Out
toDirected :: (IsUndirected u, E (ToDirected u) a) => Graph u a -> Graph (ToDirected u) a
toDirected (G g) =
G g { graphEdges = Set.map undirectedToDirected (graphEdges g) }
toUndirected :: (IsDirected d, E (ToUndirected d) a) => Graph d a -> Graph (ToUndirected d) a
toUndirected (G g) =
G g { graphEdges = Set.map directedToUndirected (graphEdges g) }