packages feed

hgraph-1.10.0.0: src/HGraph/Directed/Subgraph.hs

module HGraph.Directed.Subgraph
       ( contains
       , isSubgraphOf
       , subgraphIsomorphism
       , subgraphIsomorphismI
       , isSubgraphIsomorphism
       , enumerateSubgraphs
       , enumerateSubgraphsI
       )
where

import HGraph.Directed
import HGraph.Utils
import qualified Data.Map as M
import qualified Data.Set as S
import Data.Maybe
import Data.List

-- | Whether `d` contains `h` as a subgraph (the identity is used for the isomorphism).
contains d h = null $ 
  [ v
  | v <- vertices h
  , u <- outneighbors h v
  , not $ arcExists d (v,u)
  ] ++
  filter (not . isVertex d) (vertices h)
  

-- | Whether `h` is isomorphic to some subgraph of `d`.
isSubgraphOf h d = isJust $ subgraphIsomorphism d h

-- | Find an isomorphism from `h` to some subgraph of `d`, if it exists.
subgraphIsomorphism d h = fmap (M.mapKeys (iToV M.!)) $ subgraphIsomorphismI d hi
  where
    (hi, itova) = linearizeVertices h
    iToV = M.fromList itova

subgraphIsomorphismI d hi = findIso (vertices hi) M.empty candidates0
  where
    candidates0 = M.fromList
                  [ (v, S.fromList us)
                  | v <- vertices hi
                  , let ov = outdegree hi v
                  , let iv = indegree hi v
                  , let us = filter (\u -> outdegree d u >= ov && indegree d u >= iv) $ vertices d
                  ]
    findIso [] phi _ = Just phi
    findIso (v:vs) phi candidates = mhead $ map fromJust $ filter isJust $ do      
      u <- S.toList $ candidates M.! v
      let phi' = M.insert v u phi
      let candidates' = M.map (S.delete u) $ M.delete v $ 
              foldr (uncurry $ M.insertWith (\n o -> S.intersection n o) )
                    candidates $
                    [ (w, S.fromList $ outneighbors d u)
                    | w <- outneighbors hi v
                    ] ++
                    [ (w, S.fromList $ inneighbors d u)
                    | w <- inneighbors hi v
                    ]
      if null $ M.filter S.null candidates' then
        return $ findIso vs phi' candidates'
      else
        []

-- | Enumerate all subgraphs of `d` which are isomorphic to `h`
enumerateSubgraphs ::  (DirectedGraph t, Adjacency t, Mutable t) => t a -> t b -> [t a]
enumerateSubgraphs d h = map (renameVertices iToV (empty d)) $ enumerateSubgraphsI di hi
  where
    (hi, itovh) = linearizeVertices h
    (di, itovd) = linearizeVertices d
    iToV = M.fromList itovd

enumerateSubgraphsI d hi = 
  do
    u0 <- S.toList $ candidates0 M.! v0
    embed d M.empty S.empty candidates0 ((inneighbors hi v0) ++ (outneighbors hi v0)) (S.fromList $ vertices hi) v0 u0
  where
    (v0, deg0) = maximumBy (\v u -> compare (snd v) (snd u)) [(v, indegree hi v + outdegree hi v) | v <- vertices hi]
    candidates0 = M.fromList
                  [ (v, S.fromList us)
                  | v <- vertices hi
                  , let ov = outdegree hi v
                  , let iv = indegree hi v
                  , let us = filter (\u -> outdegree d u >= ov && indegree d u >= iv) $ vertices d
                  ]
    embed :: (DirectedGraph t, Adjacency t, Mutable t, Ord a) =>
             t a -> M.Map Int a -> S.Set a -> M.Map Int (S.Set a) -> [Int] -> (S.Set Int) -> Int -> a -> [t a]
    embed d phi blocked candidates queue missing v u
      | or $ map (\v' -> S.null $ candidates' M.! v' ) vN = []
      | otherwise = embeddings d (M.insert v u phi) (S.insert u blocked) candidates' queue missing
      where
        vN = filter (not . (`M.member` phi)) $ outneighbors hi v ++ inneighbors hi v
        candidates' = (foldr (\(v', n) c -> M.insertWith S.intersection v' n c) candidates $ 
                                             [ (v', S.fromList $ filter (not . (`S.member` blocked)) $
                                                                         outneighbors d u) 
                                             | v' <- outneighbors hi v ] ++
                                             [ (v', S.fromList $ filter (not . (`S.member` blocked)) $
                                                                         inneighbors d u)
                                             | v' <-  inneighbors hi v ]
                                   )
    embeddings :: (DirectedGraph t, Adjacency t, Mutable t, Ord a) =>
                  t a -> M.Map Int a -> S.Set a -> M.Map Int (S.Set a) -> [Int] -> (S.Set Int) -> [t a]
    embeddings d phi blocked candidates [] missing
      | S.null missing = return $ toSubgraph d phi
      | otherwise = 
        let v = head $ S.toList missing
        in embeddings d phi blocked candidates [v] (S.delete v missing)
    embeddings d phi blocked candidates (v:vs) missing
      | v `M.member` phi = embeddings d phi blocked candidates vs missing
      | otherwise = do
          u <- S.toList $ candidates M.! v
          embed d phi blocked candidates ((inneighbors hi v) ++ (outneighbors hi v) ++ vs) (S.delete v missing) v u
    toSubgraph :: (DirectedGraph t, Adjacency t, Mutable t, Ord a) => t a -> M.Map Int a -> t a
    toSubgraph d phi = 
      foldr addArc (foldr addVertex (empty d) $ M.elems phi) [(phi M.! v, phi M.! u) | (v,u) <- arcs hi]

-- | Whether `phi` is a subgraph isomorphism from `h` to some subgraph of `d`.
isSubgraphIsomorphism d h phi = null
  [ v
  | v <- vertices h
  , u <- outneighbors h v
  , isNothing $ do
      dv <- M.lookup v phi
      du <- M.lookup u phi
      if arcExists d (dv,du) then
        return ()
      else
        Nothing
  ]