clafer-0.3.5: src/Language/Clafer/Optimizer/Optimizer.hs
{-# LANGUAGE FlexibleContexts #-}
{-
Copyright (C) 2012 Kacper Bak, Jimmy Liang <http://gsd.uwaterloo.ca>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-}
module Language.Clafer.Optimizer.Optimizer where
import Data.Maybe
import Data.List
import Control.Monad.State
import qualified Data.Map as Map
import Language.Clafer.Common
import Language.Clafer.ClaferArgs
import Language.Clafer.Intermediate.Intclafer
-- | Apply optimizations for unused abstract clafers and inheritance flattening
optimizeModule :: ClaferArgs -> (IModule, GEnv) -> IModule
optimizeModule args (imodule, genv) =
imodule{mDecls = em $ rm $ map (optimizeElement (1, 1)) $
markTopModule $ mDecls imodule}
where
rm = if keep_unused args then makeZeroUnusedAbs else remUnusedAbs
em = if flatten_inheritance args then flip (curry expModule) genv else id
optimizeElement :: Interval -> IElement -> IElement
optimizeElement interval' x = case x of
IEClafer c -> IEClafer $ optimizeClafer interval' c
IEConstraint _ _ -> x
IEGoal _ _ -> x
optimizeClafer :: Interval -> IClafer -> IClafer
optimizeClafer interval' c = c {glCard = glCard',
elements = map (optimizeElement glCard') $ elements c}
where
glCard' = multInt (fromJust $ card c) interval'
multInt :: Interval -> Interval -> Interval
multInt (m, n) (m', n') = (m * m', multExInt n n')
multExInt :: Integer -> Integer -> Integer
multExInt 0 _ = 0
multExInt _ 0 = 0
multExInt m n = if m == -1 || n == -1 then -1 else m * n
-- -----------------------------------------------------------------------------
makeZeroUnusedAbs :: [IElement] -> [IElement]
makeZeroUnusedAbs decls' = map (\x -> if (x `elem` unusedAbs) then IEClafer (getIClafer x){card = Just (0, 0)} else x) decls'
where
unusedAbs = map IEClafer $ findUnusedAbs clafers $ map uid $
filter (not.isAbstract) clafers
clafers = toClafers decls'
getIClafer (IEClafer c) = c
getIClafer _ = error "Function makeZeroUnusedAbs from Optimizer expected paramter of type IClafer got a differnt IElement" --This should never happen
remUnusedAbs :: [IElement] -> [IElement]
remUnusedAbs decls' = decls' \\ unusedAbs
where
unusedAbs = map IEClafer $ findUnusedAbs clafers $ map uid $
filter (not.isAbstract) clafers
clafers = toClafers decls'
findUnusedAbs :: [IClafer] -> [String] -> [IClafer]
findUnusedAbs maybeUsed [] = maybeUsed
findUnusedAbs [] _ = []
findUnusedAbs maybeUsed used = findUnusedAbs maybeUsed' $ getUniqExtended used'
where
(used', maybeUsed') = partition (\c -> uid c `elem` used) maybeUsed
getUniqExtended :: [IClafer] -> [String]
getUniqExtended used = nub $ used >>= getExtended
getExtended :: IClafer -> [String]
getExtended c =
sName ++ ((getSubclafers $ elements c) >>= getExtended)
where
sName = if not $ isOverlapping $ super c then [getSuper c] else []
-- -----------------------------------------------------------------------------
-- inheritance expansions
expModule :: ([IElement], GEnv) -> [IElement]
expModule (decls', genv) = evalState (mapM expElement decls') genv
expClafer :: MonadState GEnv m => IClafer -> m IClafer
expClafer claf = do
super' <- expSuper $ super claf
elements' <- mapM expElement $ elements claf
return $ claf {super = super', elements = elements'}
expSuper :: MonadState GEnv m => ISuper -> m ISuper
expSuper x = case x of
ISuper False _ -> return x
ISuper True pexps -> ISuper True `liftM` mapM expPExp pexps
expElement :: MonadState GEnv m => IElement -> m IElement
expElement x = case x of
IEClafer claf -> IEClafer `liftM` expClafer claf
IEConstraint isHard' constraint -> IEConstraint isHard' `liftM` expPExp constraint
IEGoal isMaximize' goal -> IEGoal isMaximize' `liftM` expPExp goal
expPExp :: MonadState GEnv m => PExp -> m PExp
expPExp (PExp t pid' pos' exp') = PExp t pid' pos' `liftM` expIExp exp'
expIExp :: MonadState GEnv m => IExp -> m IExp
expIExp x = case x of
IDeclPExp quant' decls' pexp -> do
decls'' <- mapM expDecl decls'
pexp' <- expPExp pexp
return $ IDeclPExp quant' decls'' pexp'
IFunExp op' exps' -> if op' == iJoin
then expNav x else IFunExp op' `liftM` mapM expPExp exps'
IClaferId _ _ _ -> expNav x
_ -> return x
expDecl :: MonadState GEnv m => IDecl -> m IDecl
expDecl x = case x of
IDecl disj locids pexp -> IDecl disj locids `liftM` expPExp pexp
expNav :: MonadState GEnv m => IExp -> m IExp
expNav x = do
xs <- split' x return
xs' <- mapM (expNav' "") xs
return $ mkIFunExp iUnion $ map fst xs'
expNav' :: MonadState GEnv m => String -> IExp -> m (IExp, String)
expNav' context (IFunExp _ (p0:p:_)) = do
(exp0', context') <- expNav' context $ Language.Clafer.Intermediate.Intclafer.exp p0
(exp', context'') <- expNav' context' $ Language.Clafer.Intermediate.Intclafer.exp p
return (IFunExp iJoin [ p0 {Language.Clafer.Intermediate.Intclafer.exp = exp0'}
, p {Language.Clafer.Intermediate.Intclafer.exp = exp'}], context'')
expNav' context x@(IClaferId modName' id' isTop') = do
st <- gets stable
if Map.member id' st
then do
let impls = (Map.!) st id'
let (impls', context') = maybe (impls, "")
(\y -> ([[head y]], head y)) $
find (\z -> context == (head.tail) z) impls
return (mkIFunExp iUnion $ map (\u -> IClaferId modName' u isTop') $
map head impls', context')
else do
return (x, id')
expNav' _ _ = error "Function expNav' from Optimizer expects an argument of type ClaferId or IFunExp but was given another IExp"
split' :: MonadState GEnv m => IExp -> (IExp -> m IExp) -> m [IExp]
split'(IFunExp _ (p:pexp:_)) f =
split' (Language.Clafer.Intermediate.Intclafer.exp p) (\s -> f $ IFunExp iJoin
[p {Language.Clafer.Intermediate.Intclafer.exp = s}, pexp])
split' (IClaferId modName' id' isTop') f = do
st <- gets stable
mapM f $ map (\y -> IClaferId modName' y isTop') $ maybe [id'] (map head) $ Map.lookup id' st
split' _ _ = error "Function split' from Optimizer expects an argument of type ClaferId or IFunExp but was given another IExp"
-- -----------------------------------------------------------------------------
-- checking if all clafers have unique names and don't extend other clafers
allUnique :: IModule -> Bool
allUnique imodule = and un && (null $
filter (\xs -> 1 < length xs) $ group $ sort $ concat idents) && identsOk
where
(un, idents) = unzip $ map allUniqueElement $ mDecls imodule
identsOk = and $ map (checkConstraintElement (concat idents)) $ mDecls imodule
allUniqueClafer :: IClafer -> (Bool, [String])
allUniqueClafer claf =
(getSuper claf `elem` "clafer" : primitiveTypes && and un,
ident claf : concat idents)
where
(un, idents) = unzip $ map allUniqueElement $ elements claf
allUniqueElement :: IElement -> (Bool, [String])
allUniqueElement x = case x of
IEClafer claf -> allUniqueClafer claf
IEConstraint _ _ -> (True, [])
IEGoal _ _ -> (True, [])
checkConstraintElement :: [String] -> IElement -> Bool
checkConstraintElement idents x = case x of
IEClafer claf -> and $ map (checkConstraintElement idents) $ elements claf
IEConstraint _ pexp -> checkConstraintPExp idents pexp
IEGoal _ _ -> True
checkConstraintPExp :: [String] -> PExp -> Bool
checkConstraintPExp idents pexp = checkConstraintIExp idents $
Language.Clafer.Intermediate.Intclafer.exp pexp
checkConstraintIExp :: [String] -> IExp -> Bool
checkConstraintIExp idents x = case x of
IDeclPExp _ oDecls' pexp ->
checkConstraintPExp ((oDecls' >>= (checkConstraintIDecl idents)) ++ idents) pexp
IClaferId _ ident' _ -> if ident' `elem` (specialNames ++ idents) then True
else error $ "optimizer: " ++ ident' ++ " not found"
_ -> True
checkConstraintIDecl :: [String] -> IDecl -> [String]
checkConstraintIDecl idents (IDecl _ decls' pexp)
| checkConstraintPExp idents pexp = decls'
| otherwise = []
-- -----------------------------------------------------------------------------
findDupModule :: ClaferArgs -> IModule -> IModule
findDupModule args imodule = imodule{mDecls = decls'}
where
decls'' = mDecls imodule
decls'
| check_duplicates args = findDupModule' decls''
| otherwise = decls''
findDupModule' :: [IElement] -> [IElement]
findDupModule' declarations
| null dups = map findDupElement declarations
| otherwise = error $ show dups
where
dups = findDuplicates $ toClafers declarations
findDupClafer :: IClafer -> IClafer
findDupClafer claf = if null dups
then claf{elements = map findDupElement $ elements claf}
else error $ (show $ ident claf) ++ show dups
where
dups = findDuplicates $ getSubclafers $ elements claf
findDupElement :: IElement -> IElement
findDupElement x = case x of
IEClafer claf -> IEClafer $ findDupClafer claf
IEConstraint _ _ -> x
IEGoal _ _ -> x
findDuplicates :: [IClafer] -> [String]
findDuplicates clafers =
map head $ filter (\xs -> 1 < length xs) $ group $ sort $ map ident clafers
-- -----------------------------------------------------------------------------
-- marks top clafers
markTopModule :: [IElement] -> [IElement]
markTopModule decls' = map (markTopElement (
[this, parent, children, strType, intType, integerType] ++
(map uid $ toClafers decls'))) decls'
markTopClafer :: [String] -> IClafer -> IClafer
markTopClafer clafers c =
c {super = markTopSuper clafers $ super c,
elements = map (markTopElement clafers) $ elements c}
markTopSuper :: [String] -> ISuper -> ISuper
markTopSuper clafers x = x{supers = map (markTopPExp clafers) $ supers x}
markTopElement :: [String] -> IElement -> IElement
markTopElement clafers x = case x of
IEClafer c -> IEClafer $ markTopClafer clafers c
IEConstraint isHard' pexp -> IEConstraint isHard' $ markTopPExp clafers pexp
IEGoal isMaximize' pexp -> IEGoal isMaximize' $ markTopPExp clafers pexp
markTopPExp :: [String] -> PExp -> PExp
markTopPExp clafers pexp =
pexp {Language.Clafer.Intermediate.Intclafer.exp = markTopIExp clafers $
Language.Clafer.Intermediate.Intclafer.exp pexp}
markTopIExp :: [String] -> IExp -> IExp
markTopIExp clafers x = case x of
IDeclPExp quant' decl pexp -> IDeclPExp quant' (map (markTopDecl clafers) decl)
(markTopPExp ((decl >>= decls) ++ clafers) pexp)
IFunExp op' exps' -> IFunExp op' $ map (markTopPExp clafers) exps'
IClaferId modName' sident' _ ->
IClaferId modName' sident' $ sident' `elem` clafers
_ -> x
markTopDecl :: [String] -> IDecl -> IDecl
markTopDecl clafers x = case x of
IDecl disj locids pexp -> IDecl disj locids $ markTopPExp clafers pexp