{- | Emping 0.6 (provisional) Tue 19 May 2009 05:53:47 PM CEST Module Abduce contains functions to find implications of reduced rules with the same consequent. Each reduced rule stands for one or more originals. Rule red1 implies red2 if the originals of red1 are a subset of the originals of red2. Reduced rules, which imply the same original rule, form an equivalence class. So the reduced rules for the same consequent are in a poset of equivalence classes. The function abduceReds gets the graph of this poset from the lists of reduced and original rules. Sun 24 May 2009 07:10:00 PM CEST -} module Abduce ( abduceReds, graphHasImps,graphMLGen ) where import Data.Set (Set) import qualified Data.Set as Abduce import Data.List ( findIndices ) import Control.Monad.State import Data.Maybe (isJust, fromJust) import Data.Graph.Inductive import DefRules (Rule) ---------------------- redefined set functions ----------------------- isSub :: Ord a => Set a -> Set a -> Bool isSub = Abduce.isSubsetOf set_fromList :: Ord a => [a] -> Set a set_fromList = Abduce.fromList ---------------------------------------------------------------------- -- find indices of original rule(s) denoted by the same reduced rule -- antecedents of reduction are subsets (including equal) of the original redIOrgsOne :: Rule -> [Rule] -> Set Int redIOrgsOne red rules = set_fromList $ findIndices (\x -> (fst red) `isSub` (fst x)) rules -- tuples of the indices of originals for each reduced rule -- first in tuple is the reduced rule index, second the indices of originals redIOrgsAll :: [Rule] -> [Rule] -> [(Int, Set Int)] redIOrgsAll reds rules = zip [0..] orixls where orixls = map (flip redIOrgsOne rules) reds -- reduced rules may denote the same original rule (equivalence) -- fst in the result is a list of equals, snd is the indices of originals getEqualsOne :: (Int,Set Int) -> [(Int,Set Int)] -> ([Int], Set Int) getEqualsOne (_, orig) ls = (eqls, orig ) where eqls = [ fst x | x <- ls , snd x == orig ] -- if a reduction is already in some equals list, ignore, otherwise get a new one -- now every reduction is in one equivalence list, no duplicate lists getEqsOneSt :: [(Int, Set Int)] -> (Int,Set Int) -> State [Int] (Maybe ([Int], Set Int) ) getEqsOneSt ls r = State (\s -> let res = getEqualsOne r ls in if (fst r) `elem` s then (Nothing, s) else (Just res, (fst res) ++ s) ) -- group all reduced rules, with originals, to equals with originals groupEquals :: [(Int, Set Int)] -> [([Int], Set Int)] groupEquals ls = [ fromJust x | x <- mbeqls, isJust x ] where mbeqls = evalState (mapM (getEqsOneSt ls) ls) [] type ReRu = ([Int], Set Int) -- | from the list of reductions and the list of rules (all same consequent) -- get equivalent reductions with their original rule indices) eqivRedRulS :: [Rule] -> [Rule] -> [ReRu] eqivRedRulS reds ruls = groupEquals $ redIOrgsAll reds ruls ------------------------------------------------------------------------------- {- get implications between equivalence classes of reduced rules the partial order (of the implication relation) is found through the originals red1 implies red2 if origs set of red1 is a subset of origs set of red2 the rule which implies less originals, implies the rule which implies more -} reruToNode :: [ReRu] -> [LNode ReRu] reruToNode reruls = zip [1..] reruls -- partial order (no equals, these are caught inthe equivalence lists) -- HI = high, LW = low, NT = not ordered data Porder = HI | LW | NT deriving (Eq) -- poset comparison: the smaller (LW) implies the larger (HI) -- isSubsetOf from from Data.Set (could also use isProperSubsetOf) pcomp :: LNode ReRu -> LNode ReRu -> Porder pcomp (_,(_, x1)) (_,(_,x2)) | isSub x1 x2 = LW | isSub x2 x1 = HI | otherwise = NT --------------- get a graph of the poset by implication ------------------------ {- add a possible node to an adjacency list. If it is a superset of any in the list, then ignore. If it is unconnected to all others , add it. If it is a subset of some members, replace those with it. -} newLub :: LNode ReRu -> [LNode ReRu] -> [LNode ReRu] newLub mb lubs | any (\x -> (pcomp x mb) == LW) lubs = lubs | all (\x -> (pcomp x mb) == NT) lubs = mb:lubs | otherwise = mb:newlubs where newlubs = filter (\x -> (pcomp x mb) /= HI) lubs -- if a ReRu implies another, it is a possible member of the adjacency list addLub :: LNode ReRu -> LNode ReRu -> State [LNode ReRu] (LNode ReRu) addLub nod mb = State (\s -> if (pcomp nod mb) == LW then (nod , newLub mb s) else (nod, s) ) -- N.B nod must NOT be a member of mbls! This is batch , starts from [] getAdj :: LNode ReRu -> [LNode ReRu] -> [LNode ReRu] getAdj nod mbls = execState (mapM (addLub nod) mbls) [] -- get a node with its nearest implications getAdjPair :: LNode ReRu -> [LNode ReRu] -> (LNode ReRu,[LNode ReRu]) getAdjPair nod nodls = (nod, adj) where adj = getAdj nod src src = filter (/= nod) nodls -- get the (node, adjacency list) tuples for all elements of a LNode ReRu list getAdjPairAll :: [LNode ReRu] -> [(LNode ReRu,[LNode ReRu])] getAdjPairAll reruls = map ((flip getAdjPair) reruls) reruls -- type synonym for just the indices of reductions type Re = [Int] -- in a node with its closest implications, we don't need the originals any more remOrigsFromNodes :: [(LNode ReRu, [LNode ReRu])] -> [(LNode Re, [LNode Re])] remOrigsFromNodes napls = map redsonly napls where redsonly (nd, adj) = (remfn nd, map remfn adj) remfn (x,(r, _)) = (x,r) -- get the list of nodes to build the graph (all types) -- if the adjacency list is empty, the node is unconnected, but still in the graph nodesFromNodeAdjList :: [(LNode a, [LNode a])] -> [LNode a] nodesFromNodeAdjList ndals = map fst ndals -- get the list of edges from a node adjacency list (all types of Eq (?)) -- if the adjacency list is empty, there are no edges for that node edgesFromNodeAdj :: Eq a => (LNode a, [LNode a]) -> [UEdge] edgesFromNodeAdj (nd, adj) | adj == [] = [] | otherwise = zip3 src tgt typ where tgt = map fst adj n = length tgt src = replicate n (fst nd) typ = replicate n () -- get the list of edges to build the graph (all types of Eq (?) ) -- N.B. concat takes care of any empty edge lists edgesFromNodeAdjList :: Eq a => [(LNode a,[LNode a])] -> [UEdge] edgesFromNodeAdjList = concatMap edgesFromNodeAdj -- get the reduced rules themselves from a node reToRed :: [Rule] -> LNode Re -> LNode [Rule] reToRed reds (nd,ils) = (nd, map (reds !!) ils) -- change all indices in a node list to the rules themselves reToRedAll :: [Rule] -> [LNode Re] -> [LNode [Rule]] reToRedAll reds ndls = map (reToRed reds) ndls -- from reductions and their originals, get a graph of reduced rules -- each edge represents an implication, the first implies the second abduceReds :: [Rule] -> [Rule] -> Gr [Rule] () abduceReds reds ruls = mkGraph nds eds where nds = reToRedAll reds (nodesFromNodeAdjList nda) eds = edgesFromNodeAdjList nda nda = (remOrigsFromNodes . getAdjPairAll . reruToNode . (eqivRedRulS reds)) ruls -------------------------------------------------------------------- ----------------------------- graph analysis ----------------------- -- | check if a graph has any rule implications at all graphHasImps :: Gr [Rule] () -> Bool graphHasImps gr | edges gr == [] = False | otherwise = True -- workaround gsel to get tops and bottoms of a graph lnodesMLGen :: Bool -> Gr [Rule] () -> [LNode [Rule]] lnodesMLGen msg ag = [ (n,l) | (n,l) <- labNodes ag, mldeg ag n == 0 ] where mldeg = if msg then outdeg else indeg graphMLGen :: Bool -> Gr [Rule] () -> Gr [Rule] () graphMLGen msg ag = mkGraph nds [] where nds = lnodesMLGen msg ag