packages feed

hgraph-1.10.0.0: src/HGraph/Directed/Connectivity/Basic.hs

module HGraph.Directed.Connectivity.Basic
       ( reach
       , reverseReach
       , reachable
       , allPaths
       , allLinkages
       , allMaximalPaths
       , strongComponents
       )
where

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

-- | Vertices that `s` can reach.
reach d s = metaBfs d s (\_ -> []) id
reverseReach d s = metaBfs d s id (\_ -> [])

-- | Vertices which can reach `s`.
reverseReach d s = metaBfs d s id (\_ -> [])

reachable d s t = t `elem` (reach d s)

allPaths d s0 t = allPaths' S.empty s0
  where
    allPaths' visited s
      | s == t = [[t]]
      | otherwise = do
        v <- filter (\u -> not $ u `S.member` visited) $ outneighbors d s
        fmap (s:) $ allPaths' (S.insert v visited) v

allLinkages
  :: (DirectedGraph t1, Adjacency t1, Eq b, Eq t2, Num t2)
  => t1 b -> t2 -> b -> b -> [[[b]]]
allLinkages d k s t = do
  s0 <- choose k (outneighbors di si)
  fmap (map ((s :) . map (iToV M.!))) $ allLinkages' s0 (S.fromList $ si : s0)
  where
    (di, itova) = linearizeVertices d
    Just si = fmap fst $ find ((==s) . snd) itova
    Just ti = fmap fst $ find ((==t) . snd) itova
    iToV = M.fromList itova
    allLinkages' sj visited
      | all (==ti) sj = return $ map (:[]) sj
      | otherwise = do
      (step, visited') <- linkageSteps di visited sj ti
      fmap (zipWith (:) sj) $ allLinkages' step visited'

linkageSteps _ visited [] _ = return ([], visited)
linkageSteps d visited (v:vs) t = do
  u <- if v == t then return v else filter (\u -> not $ S.member u visited) $ outneighbors d v
  fmap (\(ws, visited') -> (u:ws, visited')) $ linkageSteps d (if u /= t then S.insert u visited else visited) vs t

-- | All maximal paths on a digraph, represented as a list of vertices.
-- | Cycles are also considered as maximal paths and their corresponding lists contain the initial vertex twice.
allMaximalPaths d = map (map (iToV M.!)) $ allMaximalPaths' (vertices di) S.empty
  where
    (di, itova) = linearizeVertices d
    iToV = M.fromList itova
    allMaximalPaths' [] _ = []
    allMaximalPaths' (v:vs) blocked = vPaths ++ allMaximalPaths' vs (S.insert v blocked)
      where
        vPaths = concatMap inExtensions $ uniPaths True outneighbors blocked v
        uniPaths canClose neighborF visited u
          | null nu && (null $ filter (`S.member` blocked) $ neighborF di u) = [[u]]
          | null nu && null vCycle = []
          | null nu = [[u, v]]
          | otherwise = map (u:) $ vCycle ++ concatMap (uniPaths canClose neighborF (S.insert u visited)) nu
          where
            nu = filter (not . (`S.member` visited)) $ neighborF di u
            vCycle
              | not canClose = []
              | v `elem` (neighborF di u) = [[v]]
              | otherwise = []
        inExtensions p 
          | p0 == pn && (not $ null $ drop 1 p) = [p] -- p is already a cycle
          | otherwise = map combine $ uniPaths canClose inneighbors (foldr S.insert blocked p) v
          where
            canClose = null $ drop 1 p -- allow closing backwards cycles
            combine q
              | null q = []
              | arcExists di (pn, q0) = pn : q' ++ p
              | null q' = p 
              | otherwise = q' ++ p
              where
                q' = reverse $ tail q
                q0 = last q
            pn = last p
            p0 = head p

-- | All strongly connected components of a digraph, in an arbitrary order.
strongComponents d
  | numVertices d == 0 = []
  | otherwise = 
    let v = head $ vertices d
        component = (S.toList $ (S.fromList $ v : reach d v) `S.intersection` (S.fromList $ v : reverseReach d v))
        d' = foldr removeVertex d $ component
    in component : strongComponents d'