packages feed

Emping-0.2: src/Reduce.hs

module Reduce (isSub,f2Grp, redAll, ambOrg ) where

-- (c) 2007 Hans van Thiel
-- Version 0.2 License GPL

{- module: get the reduced normal form of a rule model

a fact is a list of attribute value pairs
a rule is a the same list of av pairs, interpreted with
init as antecedent and last as consequent (of course
reshuffled according to consequent attribute selection)

the reduction algorithm is implemented by redPos p n
redAll implements this on a group of rules, partitioned
by their consequent attribute. So redAll follows f2Grp!

-}

import Data.List (nub, (\\), nubBy, partition, delete )

-- some general purpose functions

isSub, isSuper :: Eq a => [a] -> [a] -> Bool
isSub [] y = True
isSub (x:xs) y | not (x `elem` y) = False
               | otherwise = isSub xs y

isSuper = flip isSub

isEq :: Eq a => [a] -> [a] -> Bool
isEq x y = isSub x y && isSub y x

-- minLs needs to take second value because of foldr in extrMin

minLs :: Eq a => [a] -> [a] -> [a]
minLs x y | x `isSub` y = x
          | otherwise = y

-- partitions a list according to an equivalence relation

partitionBy :: (a -> a -> Bool) -> [a] -> [[a]]
partitionBy eq [] = []
partitionBy eq ls = x:(partitionBy eq y)  where
                   (x,y) = partition ((head ls) `eq`) ls 

-- A,B and C: the reduction algorithm in its three steps

-- A: formulate hypothesis from original rules (positive)

hypot :: Eq a => [[a]] -> [a]
hypot = nub . concat

-- B: falsify the hypothesis
--    1. match with all the rules (negative)

match :: Eq a => [a] -> [[a]] -> [[a]]
match h  = map (h \\) 

--    2. transform the orlist of andlists to andlist of orlists

-- test if an attribute is in the list and get it

attElem :: (Eq a, Eq b) => (a,b) -> [(a,b)] -> Maybe (a,b)
attElem x [] = Nothing
attElem x (y:ys) = if fst x == fst y then Just y
                                     else attElem x ys
{- and a predicate to an andlist
     attributes with different values contradict
     attributes with the same value are equal -}

andP :: (Eq a,Eq b) => (a,b) -> [(a,b)] -> [(a,b)]
andP x ans = case attElem x ans of 
                   Nothing -> x:ans
                   Just y -> if snd x == snd y 
                                    then ans
                                    else []            

-- first: anding an orlist to an orlist of andlists
-- remove all the empty lists

repandPls :: (Eq a,Eq b) => [(a,b)] -> [[(a,b)]] -> [[(a,b)]]
repandPls ors als = 
    filter (/= []) [ andP x y | x <- ors, y <- als ]

-- then: extract the smallest sublists from an orlist of andlists

extrMin :: Eq a => [[a]] -> [[a]]
extrMin ls =  nubBy isEq [ getMinin x ls | x <-ls ] 
          where  getMinin x y = foldr minLs x y

-- B.2.1: anding an orlist to an orlist of andlists

andOrAnds :: (Eq a,Eq b) => [(a,b)] -> [[(a,b)]] -> [[(a,b)]]
andOrAnds x = extrMin . (repandPls x)

-- B.2.2: transform andlist of orlists to orlist of andlists in batch

trAndOr :: (Eq a,Eq b) => [[(a,b)]] -> [[(a,b)]]
trAndOr x = foldr andOrAnds (raise (last x)) (init x)
                 where raise ls = [ [y] | y <- ls]

-- C: Verify the falsification result with the original positive rules

verify :: Eq a => [[a]] -> [[a]] -> [[a]]
verify flsd orig = [x | x <- flsd , x `isIn` orig ] where
                    isIn y ls = or (map (isSub y) ls)

-- A, B and C: reduce a list of positive original rules 
-- Note: redPos takes antecedents only!

redPos :: (Eq a,Eq b) => [[(a,b)]] -> [[(a,b)]] -> [[(a,b)]]
redPos p n = verify (trAndOr (match (hypot p) n)) p
-------------------------------------------------------

-- facts to rules by putting consequent attribute last

shuf :: (Eq a, Eq b) => a -> [(a,b)] -> [(a,b)]
shuf at avls = (fst z) ++ (snd z) where
                  z = partition ((at /=) . fst) avls

f2rules :: (Eq a, Eq b) => a -> [[(a,b)]] -> [[(a,b)]]
f2rules at facls =  map (shuf at) facls

-- group according to consequent attribute-values

f2Grp :: (Eq a, Eq b) => a -> [[(a,b)]] -> [[[(a,b)]]] 
f2Grp at facls = 
    partitionBy (\x y -> (last x) == (last y)) ruls where
                    ruls = f2rules at facls
                 
-- reduce one of a group of rules. Consequent is last in
-- each rule list..

redOne :: (Eq a, Eq b) => [[[(a,b)]]] -> [[(a,b)]] -> [[(a,b)]]
redOne grp rls  = map (++ [cns]) (redPos p n)  where
           p = map init rls
           n = map init (concat $ (delete rls grp))
           cns = (last . head) rls

-- reduce a rule model for all attribute-value pairs 
-- the consequents will be last in each AV-list
-- Note: facts are converted to grouped rules by f2rGrp!

redAll :: (Eq a,Eq b)=> [[[(a,b)]]] ->  [[[(a,b)]]]
redAll rlgrp = map (redOne rlgrp) rlgrp
--------------------------------------------------------

-- find ambiguities in rule group
-- Note: == works because rows have same av order

ambOrg :: (Eq a, Eq b) => [[[(a,b)]]] -> [[[(a,b)]]]
ambOrg grp = filter (\x -> (length x) > 1) anteqs where
   anteqs = partitionBy (\x y -> (init x) == (init y)) ols
   ols = concat grp