{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE ViewPatterns #-}
module Math.Grads.GenericGraph
( GenericGraph (..)
, addEdges
, addVertices
, applyG
, applyV
, getVertices
, getEdge
, isConnected
, removeEdges
, removeVertices
, safeAt
, safeIdx
, subgraph, subgraphWithReindex
, sumGraphs
, typeOfEdge
) where
import Control.Arrow (first)
import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions,
genericParseJSON, genericToJSON)
import Data.Array (Array)
import qualified Data.Array as A
import Data.Bimap (Bimap)
import qualified Data.Bimap as BM
import Data.List (find, groupBy, sortBy)
import Data.Map.Strict (Map, mapKeys, member, (!))
import qualified Data.Map.Strict as M
import Data.Maybe (fromJust, fromMaybe, isJust)
import qualified Data.Set as S
import GHC.Generics (Generic)
import Math.Grads.Graph (Graph (..))
data GenericGraph v e = GenericGraph { gIndex :: Array Int v
, gRevIndex :: Map v Int
, gAdjacency :: Array Int [(Int, e)]
}
deriving (Generic)
instance (Ord v, Eq e, ToJSON v, ToJSON e) => ToJSON (GenericGraph v e) where
toJSON (toList -> l) = genericToJSON defaultOptions l
instance (Ord v, Eq e, FromJSON v, FromJSON e) => FromJSON (GenericGraph v e) where
parseJSON v = fromList <$> genericParseJSON defaultOptions v
instance Graph GenericGraph where
fromList :: (Ord v, Eq v) => ([v], [(Int, Int, e)]) -> GenericGraph v e
fromList (vertices, edges) = GenericGraph idxArr revMap adjArr
where
count = length vertices
idxArr = A.listArray (0, count - 1) vertices
revMap = M.fromList $ zip vertices [0..]
indices = concatMap insertFunc edges
insertFunc (at, other, b) | at == other = [(at, (other, b))]
| otherwise = [(at, (other, b)), (other, (at, b))]
adjArr = A.accumArray (flip (:)) [] (0, count - 1) indices
toList :: (Ord v, Eq v) => GenericGraph v e -> ([v], [(Int, Int, e)])
toList (GenericGraph idxArr _ adjArr) = (snd <$> A.assocs idxArr, edges)
where
edges = distinct . concatMap toEdges . A.assocs $ adjArr
toEdges (k, v) = map (toAscending k) v
toAscending k (a, b) | k > a = (a, k, b)
| otherwise = (k, a, b)
compare3 (at1, other1, _) (at2, other2, _) = compare (at1, other1) (at2, other2)
eq3 v1 v2 = compare3 v1 v2 == EQ
distinct = map head . groupBy eq3 . sortBy compare3
vCount :: GenericGraph v e -> Int
vCount (GenericGraph idxArr _ _) = length idxArr
(!>) :: (Ord v, Eq v) => GenericGraph v e -> v -> [(v, e)]
(GenericGraph idxArr revMap adjArr) !> at = first (idxArr A.!) <$> adjacent
where
idx = revMap ! at
adjacent = adjArr A.! idx
(?>) :: (Ord v, Eq v) => GenericGraph v e -> v -> Maybe [(v, e)]
gr@(GenericGraph _ revMap _) ?> at | at `member` revMap = Just (gr !> at)
| otherwise = Nothing
(!.) :: GenericGraph v e -> Int -> [(Int, e)]
(!.) (GenericGraph _ _ adjArr) = (adjArr A.!)
(?.) :: GenericGraph v e -> Int -> Maybe [(Int, e)]
gr@(GenericGraph _ _ adjArr) ?. idx | idx `inBounds` A.bounds adjArr = Just (gr !. idx)
| otherwise = Nothing
where
inBounds :: Ord a => a -> (a, a) -> Bool
inBounds i (lo, hi) = (i >= lo) && (i <= hi)
instance (Ord v, Eq v, Show v, Show e) => Show (GenericGraph v e) where
show gr = unlines . map fancyShow . filter (\(a, b, _) -> a < b) . snd . toList $ gr
where
idxArr = gIndex gr
fancyShow (at, other, bond) = concat [show $ idxArr A.! at, "\t", show bond, "\t", show $ idxArr A.! other]
instance Functor (GenericGraph v) where
fmap f (GenericGraph idxArr revMap adjArr) = GenericGraph idxArr revMap (((f <$>) <$>) <$> adjArr)
instance Ord v => Semigroup (GenericGraph v e) where
(<>) = sumGraphs
instance (Ord v, Eq v) => Monoid (GenericGraph v e) where
mempty = fromList ([], [])
applyG :: ([(Int, e1)] -> [(Int, e2)]) -> GenericGraph v e1 -> GenericGraph v e2
applyG f (GenericGraph idxArr revMap adjArr) = GenericGraph idxArr revMap (f <$> adjArr)
applyV :: Ord v2 => (v1 -> v2) -> GenericGraph v1 e -> GenericGraph v2 e
applyV f (GenericGraph idxArr revMap adjArr) = GenericGraph (f <$> idxArr) (mapKeys f revMap) adjArr
getVertices :: GenericGraph v e -> [v]
getVertices (GenericGraph idxArr _ _) = map snd $ A.assocs idxArr
subgraph :: Ord v => GenericGraph v e -> [Int] -> GenericGraph v e
subgraph graph = snd . subgraphWithReindex graph
subgraphWithReindex :: Ord v => GenericGraph v e -> [Int] -> (Bimap Int Int, GenericGraph v e)
subgraphWithReindex graph toKeep = (vMap, fromList (newVertices, newEdges))
where
vSet :: S.Set Int
vSet = S.fromList toKeep
eRemain :: (Int, Int, e) -> Bool
eRemain (at, other, _) = (at `S.member` vSet) && (other `S.member` vSet)
(oldVertices, edges) = filter eRemain <$> toList graph
(newVertices, oldIdx) = unzip . filter (\(_, ix) -> ix `S.member` vSet) $ zip oldVertices [0..]
vMap :: Bimap Int Int
vMap = BM.fromList $ zip oldIdx [0 ..]
newEdges = map (\(at, other, bond) -> (vMap BM.! at, vMap BM.! other, bond)) edges
addVertices :: Ord v => GenericGraph v e -> [v] -> GenericGraph v e
addVertices graph toAdd = fromList (first (++ toAdd) (toList graph))
removeVertices :: Ord v => GenericGraph v e -> [Int] -> GenericGraph v e
removeVertices graph toRemove = fromList (newVertices, newEdges)
where
vSet :: S.Set Int
vSet = S.fromList toRemove
eRemove :: (Int, Int, e) -> Bool
eRemove (at, other, _) = (at `S.notMember` vSet) && (other `S.notMember` vSet)
(oldVertices, edges) = filter eRemove <$> toList graph
(newVertices, oldIdx) = unzip . filter ((`S.notMember` vSet) . snd) $ zip oldVertices [0..]
vMap :: Map Int Int
vMap = M.fromList $ zip oldIdx [0 ..]
newEdges = map (\(at, other, bond) -> (vMap ! at, vMap ! other, bond)) edges
removeEdges :: Ord v => GenericGraph v e -> [(Int, Int)] -> GenericGraph v e
removeEdges graph toRemove = fromList (vertices, edges)
where
eSet :: S.Set (Int, Int)
eSet = S.fromList toRemove
(vertices, edges) = filter eRemove <$> toList graph
eRemove (at, other, _) = ((at, other) `S.notMember` eSet) && ((other, at) `S.notMember` eSet)
addEdges :: Ord v => GenericGraph v e -> [(Int, Int, e)] -> GenericGraph v e
addEdges (GenericGraph inds rinds edges) toAdd = GenericGraph inds rinds res
where
accumList = foldl (\x (a, b, t) -> x ++ [(a, (b, t)), (b, (a, t))]) [] toAdd
res = A.accum (flip (:)) edges accumList
typeOfEdge :: Ord v => GenericGraph v e -> Int -> Int -> e
typeOfEdge graph fromInd toInd = res
where
neighbors = gAdjacency graph A.! fromInd
res = snd (fromJust (find ((== toInd) . fst) neighbors))
safeIdx :: GenericGraph v e -> Int -> [Int]
safeIdx graph = map fst . fromMaybe [] . (graph ?.)
safeAt :: GenericGraph v e -> Int -> [(Int, e)]
safeAt graph = fromMaybe [] . (graph ?.)
getEdge :: GenericGraph v e -> Int -> Int -> e
getEdge graph from to = found
where
neighbors = graph !. from
found = snd (fromJust (find ((== to) . fst) neighbors))
isConnected :: GenericGraph v e -> Int -> Int -> Bool
isConnected g fInd tInd = isJust $ find ((==) tInd . fst) $ safeAt g fInd
sumGraphs :: Ord v => GenericGraph v e -> GenericGraph v e -> GenericGraph v e
sumGraphs graphA graphB = res
where
(vertA, edgeA) = toList graphA
(vertB, edgeB) = toList graphB
renameMapB = M.fromList (zip [0..length vertB - 1] [length vertA..length vertA + length vertB - 1])
renameFunc = (renameMapB M.!)
newVertices = vertA ++ vertB
newEdges = edgeA ++ fmap (\(a, b, t) -> (renameFunc a, renameFunc b, t)) edgeB
res = fromList (newVertices, newEdges)