packages feed

hgraph-1.2.0.1: src/HGraph/Directed/Connectivity/IntegralLinkage.hs

module HGraph.Directed.Connectivity.IntegralLinkage
       ( extendLinkage
       , linkage
       , linkageI
       , LinkageInstance(..)
       )
where

import HGraph.Directed.Connectivity.Flow
import HGraph.Utils
import HGraph.Directed
import qualified Data.Map as M
import Data.Maybe

data LinkageInstance a = 
  LinkageInstance
  { liTerminalPairs :: M.Map Int (a,a)
  , liLinkage       :: M.Map a Int
  , liPath          :: M.Map Int [a]
  }

extendLinkage d inst = 
  case extendLinkage' $ M.keys $ liTerminalPairs inst of
    Nothing -> Nothing
    Just [] -> Just inst
    Just ext ->
      let link' = M.union (foldr (\(v,i) -> 
                                   M.insert v i)
                                 M.empty ext)
                          (liLinkage inst)
          st' = M.union (M.fromList $ [ (i, (v, t))
                                    | (v,i) <- ext
                                    , let (s,t) = (liTerminalPairs inst) M.! i
                                    , v `elem` (outneighbors d s)
                                    ] ++
                                    [ (i, (s, v))
                                    | (v,i) <- ext
                                    , let (s,t) = (liTerminalPairs inst) M.! i
                                    , v `elem` (inneighbors d t)
                                    ]
                        )
                        (liTerminalPairs inst)
      in extendLinkage d inst{liTerminalPairs = st', liLinkage = link'}
  where
    extendLinkage' [] = Just []
    extendLinkage' (i:is)
      | s == t  = extendLinkage' is
      | null cut = Nothing
      | not $ null $ drop 1 cut = extendLinkage' is
      | maybe True (i/=) (cv `M.lookup` (liLinkage inst)) = Just [(cv,i)]
      where
        (s,t) = (liTerminalPairs inst) M.! i
        d' = foldr removeVertex d
                   [ v
                   | v <- vertices d
                   , maybe False (i ==) (v `M.lookup` (liLinkage inst))
                   ]
        cut = minCutI d' s t
        cv = head cut

-- | Finds an integral linkaged connecting the given terminal pairs, if one exists.
linkage :: (DirectedGraph t, Adjacency t, Mutable t, Eq a) => t a -> [(a,a)] -> Maybe [((a,a), [a])]
linkage d st = fmap (map convertResult) $ linkageI di sti
  where
    (di, itova) = linearizeVertices d
    sti = [ (si, ti)
          | (s,t) <- st
          , let si = fst $ head $ filter (\(_, v) -> v == s) itova
          , let ti = fst $ head $ filter (\(_, v) -> v == t) itova
          ]
    iToV = M.fromList itova
    convertResult ((v,u), ps) = ((iToV M.! v, iToV M.! u), map (iToV M.!) ps )

-- | Special case of `linkage` where vertices are of type `Int`.
-- | Faster than calling `linkage` if vertices of the digraph are already of type `Int`.
linkageI :: (DirectedGraph t, Adjacency t, Mutable t, Integral a, Ord a, Eq a) => t a -> [(a,a)] -> Maybe [((a,a), [a])]
linkageI d st = linkage' inst0
  where
    sti = zip [0..] st
    terminalPairs0 = M.fromList sti
    inst0 = LinkageInstance
            { liLinkage = M.fromList $ 
                concatMap (\(i, (s,t)) ->  [(s, i), (t, i)]) sti
            , liTerminalPairs = terminalPairs0
            , liPath = M.empty
            }
    -- linkage' :: (Eq a, Ord a, Num a) => LinkageInstance a -> Maybe [((a,a), [a])]
    linkage' inst
      | M.null $ liTerminalPairs inst = Just [ (terminalPairs0 M.! t, reverse ps) | (t, ps) <- M.assocs $ liPath inst]
      | otherwise = 
        let (i, (s,t)) = head $ M.assocs $ liTerminalPairs inst
            tries = do
              v <- filter (\u -> not $ isJust $ M.lookup u $ liLinkage inst) $ inneighbors d t
              let inst' = extendLinkage d $ inst
                          { liTerminalPairs = M.insert i (s,v) (liTerminalPairs inst)
                          , liPath = M.insertWith (++) i [v] $ liPath inst
                          , liLinkage = M.insert v i $ liLinkage inst
                          }
              case fmap linkage' inst' of
                Just (Just r) -> return r
                Nothing -> []
        in mhead tries