module ToySolver.Combinatorial.HittingSet.GurvichKhachiyan1999
( findPrimeImplicateOrPrimeImplicant
, generateCNFAndDNF
, minimalHittingSets
, enumMinimalHittingSets
) where
import Control.Monad
import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet
import Data.Set (Set)
import qualified Data.Set as Set
import qualified ToySolver.Combinatorial.HittingSet.FredmanKhachiyan1996 as FredmanKhachiyan1996
findPrimeImplicateOrPrimeImplicant
:: IntSet
-> (IntSet -> Bool)
-> Set IntSet
-> Set IntSet
-> Maybe (Either IntSet IntSet)
findPrimeImplicateOrPrimeImplicant vs f cs ds = do
ret <- findImplicateOrImplicant vs f cs ds
case ret of
Left xs -> return $ Left $ minimizeImplicate vs f xs
Right xs -> return $ Right $ minimizeImplicant vs f xs
findImplicateOrImplicant
:: IntSet
-> (IntSet -> Bool)
-> Set IntSet
-> Set IntSet
-> Maybe (Either IntSet IntSet)
findImplicateOrImplicant vs f cs ds = do
xs <- FredmanKhachiyan1996.checkDuality ds cs
if f xs then
return $ Right xs
else
return $ Left (vs `IntSet.difference` xs)
minimizeImplicant :: IntSet -> (IntSet -> Bool) -> IntSet -> IntSet
minimizeImplicant _ f is = loop (IntSet.toList is) is
where
loop [] !ret = ret
loop (x:xs) !ret =
if f (IntSet.delete x ret)
then loop xs (IntSet.delete x ret)
else loop xs ret
minimizeImplicate :: IntSet -> (IntSet -> Bool) -> IntSet -> IntSet
minimizeImplicate vs f is = (vs `IntSet.difference`) $ loop (IntSet.toList is) (vs `IntSet.difference` is)
where
loop [] !ret = ret
loop (x:xs) !ret =
if not (f (IntSet.insert x ret))
then loop xs (IntSet.insert x ret)
else loop xs ret
generateCNFAndDNF
:: IntSet
-> (IntSet -> Bool)
-> Set IntSet
-> Set IntSet
-> (Set IntSet, Set IntSet)
generateCNFAndDNF vs f cs ds =
case findPrimeImplicateOrPrimeImplicant vs f cs ds of
Nothing -> (cs, ds)
Just (Left xs) -> generateCNFAndDNF vs f (Set.insert xs cs) ds
Just (Right xs) -> generateCNFAndDNF vs f cs (Set.insert xs ds)
minimalHittingSets :: Set IntSet -> Set IntSet
minimalHittingSets = Set.fromList . enumMinimalHittingSets
enumMinimalHittingSets :: Set IntSet -> [IntSet]
enumMinimalHittingSets dnf = loop Set.empty
where
vs = IntSet.unions $ Set.toList dnf
f = evalDNF dnf
loop :: Set IntSet -> [IntSet]
loop cs =
case findPrimeImplicateOrPrimeImplicant vs f cs dnf of
Nothing -> []
Just (Left c) -> c : loop (Set.insert c cs)
Just (Right d) -> error "GurvichKhachiyan1999.enumMinimalHittingSets: should not happen"
evalDNF :: Set IntSet -> IntSet -> Bool
evalDNF dnf xs = or [is `IntSet.isSubsetOf` xs | is <- Set.toList dnf]
evalCNF :: Set IntSet -> IntSet -> Bool
evalCNF cnf xs = and [not $ IntSet.null $ is `IntSet.intersection` xs | is <- Set.toList cnf]
f = Set.fromList $ map IntSet.fromList [[2,4,7], [7,8], [9]]
g = Set.fromList $ map IntSet.fromList [[7,9], [4,8,9], [2,8,9]]
testA1 = findPrimeImplicateOrPrimeImplicant (IntSet.fromList [2,4,7,8,9]) (evalDNF f) Set.empty f
testA2 = findPrimeImplicateOrPrimeImplicant (IntSet.fromList [2,4,7,8,9]) (evalDNF f) (Set.singleton (IntSet.fromList [2,8,9])) f
testA3 = findPrimeImplicateOrPrimeImplicant (IntSet.fromList [2,4,7,8,9]) (evalDNF f) (Set.fromList [IntSet.fromList [2,8,9], IntSet.fromList [4,8,9]]) f
testA4 = findPrimeImplicateOrPrimeImplicant (IntSet.fromList [2,4,7,8,9]) (evalDNF f) (Set.fromList [IntSet.fromList [2,8,9], IntSet.fromList [4,8,9], IntSet.fromList [7,9]]) f
testB1 = findPrimeImplicateOrPrimeImplicant (IntSet.fromList [2,4,7,8,9]) (evalDNF f) g Set.empty