{-# LANGUAGE ScopedTypeVariables #-}
module Language.Fixpoint.Minimize ( minQuery, minQuals, minKvars ) where
import qualified Data.HashMap.Strict as M
import Control.Monad (filterM)
import Language.Fixpoint.Types.Visitor (mapKVars)
import Language.Fixpoint.Types.Config (Config (..), queryFile)
import Language.Fixpoint.Misc (safeHead)
import Language.Fixpoint.Utils.Files hiding (Result)
import Language.Fixpoint.Graph
import Language.Fixpoint.Types
import Control.DeepSeq
deltaDebug :: Bool -> Oracle a c -> Config -> Solver a -> FInfo a -> [c] -> [c] -> IO [c]
deltaDebug min testSet cfg solve finfo set r = do
let (s1, s2) = splitAt (length set `div` 2) set
if length set == 1
then deltaDebug1 min testSet cfg solve finfo set r
else do
test1 <- testSet cfg solve finfo (s1 ++ r)
if test1
then deltaDebug min testSet cfg solve finfo s1 r
else do
test2 <- testSet cfg solve finfo (s2 ++ r)
if test2
then deltaDebug min testSet cfg solve finfo s2 r
else do
d1 <- deltaDebug min testSet cfg solve finfo s1 (s2 ++ r)
d2 <- deltaDebug min testSet cfg solve finfo s2 (d1 ++ r)
return (d1 ++ d2)
deltaDebug1 :: Bool -> (a -> b -> c -> d -> IO Bool)
-> a -> b -> c -> [e] -> d
-> IO [e]
deltaDebug1 True _ _ _ _ set _ = return set
deltaDebug1 False testSet cfg solve finfo set r = do
test <- testSet cfg solve finfo r
if test then return [] else return set
type Oracle a c = (Config -> Solver a -> FInfo a -> [c] -> IO Bool)
commonDebug :: (NFData a, Fixpoint a) => (FInfo a -> [c])
-> (FInfo a -> [c] -> FInfo a)
-> (Result (Integer, a) -> Bool)
-> Bool
-> Config
-> Solver a
-> FInfo a
-> Ext
-> (FInfo a -> [c] -> String)
-> IO (Result (Integer, a))
commonDebug init updateFi checkRes min cfg solve fi ext formatter = do
let cs0 = init fi
let oracle = mkOracle updateFi checkRes
cs <- deltaDebug min oracle cfg solve fi cs0 []
let minFi = updateFi fi cs
saveQuery (addExt ext cfg) minFi
putStrLn $ formatter fi cs
return mempty
minQuery :: (NFData a, Fixpoint a) => Config -> Solver a -> FInfo a
-> IO (Result (Integer, a))
minQuery cfg solve fi = do
let cfg' = cfg { minimize = False }
let fis = partition' Nothing fi
failFis <- filterM (fmap (not . isSafe) . solve cfg') fis
let failFi = safeHead "--minimize can only be called on UNSAT fq" failFis
let format _ cs = "Minimized Constraints: " ++ show (fst <$> cs)
let update fi cs = fi { cm = M.fromList cs }
commonDebug (M.toList . cm) update (not . isSafe) True cfg' solve failFi Min format
minQuals :: (NFData a, Fixpoint a) => Config -> Solver a -> FInfo a
-> IO (Result (Integer, a))
minQuals cfg solve fi = do
let cfg' = cfg { minimizeQs = False }
let format fi qs = "Required Qualifiers: " ++ show (length qs)
++ "; Total Qualifiers: " ++ show (length $ quals fi)
let update fi qs = fi { quals = qs }
commonDebug quals update isSafe False cfg' solve fi MinQuals format
minKvars :: (NFData a, Fixpoint a) => Config -> Solver a -> FInfo a
-> IO (Result (Integer, a))
minKvars cfg solve fi = do
let cfg' = cfg { minimizeKs = False }
let format fi ks = "Required KVars: " ++ show (length ks)
++ "; Total KVars: " ++ show (length $ ws fi)
commonDebug (M.keys . ws) removeOtherKs isSafe False cfg' solve fi MinKVars format
removeOtherKs :: FInfo a -> [KVar] -> FInfo a
removeOtherKs fi0 ks = fi1 { ws = ws', cm = cm' }
where
fi1 = mapKVars go fi0
go k | k `elem` ks = Nothing
| otherwise = Just PTrue
ws' = M.filterWithKey (\k _ -> k `elem` ks) $ ws fi1
cm' = M.filter (isNonTrivial . srhs) $ cm fi1
addExt :: Ext -> Config -> Config
addExt ext cfg = cfg { srcFile = queryFile ext cfg }
mkOracle :: (NFData a, Fixpoint a) => (FInfo a -> [c] -> FInfo a)
-> (Result (Integer, a) -> Bool)
-> Oracle a c
mkOracle updateFi checkRes cfg solve fi qs = do
let fi' = updateFi fi qs
res <- solve cfg fi'
return $ checkRes res