module Camfort.Specification.Units.InferenceFrontend
( initInference, runCriticalVariables, runInferVariables, runCompileUnits, runInconsistentConstraints, getConstraint
, puName, puSrcName )
where
import Data.Data (Data)
import Data.List (nub, intercalate, partition)
import qualified Data.Map.Strict as M
import qualified Data.IntMap.Strict as IM
import qualified Data.Set as S
import Data.Maybe (isJust, fromMaybe, catMaybes)
import Data.Generics.Uniplate.Operations
import Control.Monad
import Control.Monad.State.Strict
import Control.Monad.Writer.Strict
import Control.Monad.Trans.Except
import Control.Monad.RWS.Strict
import qualified Language.Fortran.AST as F
import Language.Fortran.Parser.Utils (readReal, readInteger)
import Language.Fortran.Util.Position (getSpan)
import Language.Fortran.Util.ModFile
import qualified Language.Fortran.Analysis as FA
import Language.Fortran.Analysis (varName, srcName)
import Camfort.Analysis.CommentAnnotator (annotateComments)
import Camfort.Analysis.Annotations
import Camfort.Specification.Units.Environment
import Camfort.Specification.Units.Monad
import Camfort.Specification.Units.InferenceBackend
import qualified Camfort.Specification.Units.Parser as P
import qualified Debug.Trace as D
import qualified Numeric.LinearAlgebra as H
initInference :: UnitSolver ()
initInference = do
pf <- gets usProgramFile
let (linkedPF, parserReport) = runWriter $ annotateComments P.unitParser pf
modifyProgramFile $ const linkedPF
mapM_ tell parserReport
insertGivenUnits
insertParametricUnits
insertUndeterminedUnits
annotateAllVariables
annotateLiterals
propagateUnits
abstractCons <- extractConstraints
dumpConsM "***abstractCons" abstractCons
cons <- applyTemplates abstractCons
dumpConsM "***concreteCons" cons
modifyProgramFile cleanLinks
modify $ \ s -> s { usConstraints = cons }
debugLogging
cleanLinks :: F.ProgramFile UA -> F.ProgramFile UA
cleanLinks = transformBi (\ a -> a { unitPU = Nothing, unitBlock = Nothing, unitSpec = Nothing } :: UnitAnnotation A)
runCriticalVariables :: UnitSolver [UnitInfo]
runCriticalVariables = do
cons <- usConstraints `fmap` get
return $ criticalVariables cons
runInferVariables :: UnitSolver [(VV, UnitInfo)]
runInferVariables = do
cons <- usConstraints `fmap` get
return $ inferVariables cons
runInconsistentConstraints :: UnitSolver (Maybe Constraints)
runInconsistentConstraints = do
cons <- usConstraints `fmap` get
return $ inconsistentConstraints cons
runCompileUnits :: UnitSolver CompiledUnits
runCompileUnits = do
cons <- usConstraints `fmap` get
let unitAssigns = map (fmap flattenUnits) $ genUnitAssignments cons
let mulCons x = map (\ (UnitPow u k) -> UnitPow u (x * k))
let negateCons = mulCons (1)
let epsilon = 0.001
let approxEq a b = abs (b a) < epsilon
let uninvert ([UnitPow u k], rhs) | not (k `approxEq` 1) = ([UnitPow u 1], mulCons (1 / k) rhs)
uninvert (lhs, rhs) = (lhs, rhs)
let shiftTerms name pos (lhs, rhs) = (lhsOk ++ negateCons rhsShift, rhsOk ++ negateCons lhsShift)
where
(lhsOk, lhsShift) = partition isLHS lhs
(rhsOk, rhsShift) = partition (not . isLHS) rhs
isLHS (UnitParamPosAbs (n, i)) | n == name && i == pos = True
isLHS (UnitPow u _) = isLHS u
isLHS _ = False
let nameParams = M.fromList [ (NPKParam name pos, rhs) | assign <- unitAssigns
, UnitParamPosAbs (name, pos) <- universeBi assign
, let (_, rhs) = uninvert $ shiftTerms name pos assign ]
let variables = M.fromList [ (NPKVariable var, units) | ([UnitPow (UnitVar var) k], units) <- unitAssigns
, k `approxEq` 1 ]
tmap <- gets usTemplateMap
return $ CompiledUnits { cuTemplateMap = tmap, cuNameParamMap = nameParams `M.union` variables }
insertParametricUnits :: UnitSolver ()
insertParametricUnits = gets usProgramFile >>= (mapM_ paramPU . universeBi)
where
paramPU pu = do
forM_ (indexedParams pu) $ \ (i, param) -> do
modifyVarUnitMap $ M.insertWith (curry snd) param (UnitParamPosAbs (fname, i))
where
fname = puName pu
indexedParams :: F.ProgramUnit UA -> [(Int, VV)]
indexedParams pu
| F.PUFunction _ _ _ _ _ (Just paList) (Just r) _ _ <- pu = zip [0..] $ map toVV (r : F.aStrip paList)
| F.PUFunction _ _ _ _ _ (Just paList) _ _ _ <- pu = zip [0..] $ (fname, sfname) : map toVV (F.aStrip paList)
| F.PUSubroutine _ _ _ _ (Just paList) _ _ <- pu = zip [1..] $ map toVV (F.aStrip paList)
| otherwise = []
where
fname = puName pu
sfname = puSrcName pu
toVV e = (varName e, srcName e)
insertUndeterminedUnits :: UnitSolver ()
insertUndeterminedUnits = do
pf <- gets usProgramFile
dmap <- (M.union (extractDeclMap pf) . combinedDeclMap . M.elems) `fmap` asks uoModFiles
forM_ (universeBi pf :: [F.ProgramUnit UA]) $ \ pu ->
modifyPUBlocksM (transformBiM (insertUndeterminedUnitVar dmap)) pu
insertUndeterminedUnitVar :: DeclMap -> F.Expression UA -> UnitSolver (F.Expression UA)
insertUndeterminedUnitVar dmap v@(F.ExpValue _ _ (F.ValVariable _)) = do
let vname = varName v
let sname = srcName v
let unit = toUnitVar dmap (vname, sname)
modifyVarUnitMap $ M.insertWith (curry snd) (varName v, srcName v) unit
return v
insertUndeterminedUnitVar _ e = return e
toUnitVar :: DeclMap -> VV -> UnitInfo
toUnitVar dmap (vname, sname) = unit
where
unit = case fst `fmap` M.lookup vname dmap of
Just (DCFunction (F.Named fname)) -> UnitParamVarAbs (fname, (vname, sname))
Just (DCSubroutine (F.Named fname)) -> UnitParamVarAbs (fname, (vname, sname))
_ -> UnitVar (vname, sname)
transformExplicitPolymorphism :: Maybe F.ProgramUnitName -> UnitInfo -> UnitInfo
transformExplicitPolymorphism (Just (F.Named f)) (UnitName a@('\'':_)) = UnitParamEAPAbs (a, f ++ "_" ++ a)
transformExplicitPolymorphism _ u = u
insertGivenUnits :: UnitSolver ()
insertGivenUnits = do
pf <- gets usProgramFile
mapM_ checkPU (universeBi pf)
where
checkPU :: F.ProgramUnit UA -> UnitSolver ()
checkPU pu@(F.PUComment a _ _)
| Just (P.UnitAssignment (Just vars) unitsAST) <- mSpec
, Just pu <- mPU = insertPUUnitAssigns (toUnitInfo unitsAST) pu vars
| Just (P.UnitAlias name unitsAST) <- mSpec = modifyUnitAliasMap (M.insert name (toUnitInfo unitsAST))
| otherwise = return ()
where
mSpec = unitSpec (FA.prevAnnotation a)
mPU = unitPU (FA.prevAnnotation a)
checkPU pu = mapM_ (checkBlockComment (getName pu)) [ b | b@(F.BlComment {}) <- universeBi (F.programUnitBody pu) ]
where
getName pu = case pu of
F.PUFunction {} -> Just $ F.getName pu
F.PUSubroutine {} -> Just $ F.getName pu
_ -> Nothing
checkBlockComment :: Maybe F.ProgramUnitName -> F.Block UA -> UnitSolver ()
checkBlockComment pname (F.BlComment a _ _)
| Just (P.UnitAssignment (Just vars) unitsAST) <- mSpec
, Just b <- mBlock = insertBlockUnitAssigns pname (toUnitInfo unitsAST) b vars
| Just (P.UnitAlias name unitsAST) <- mSpec = modifyUnitAliasMap (M.insert name (toUnitInfo unitsAST))
| otherwise = return ()
where
mSpec = unitSpec (FA.prevAnnotation a)
mBlock = unitBlock (FA.prevAnnotation a)
insertBlockUnitAssigns :: Maybe F.ProgramUnitName -> UnitInfo -> F.Block UA -> [String] -> UnitSolver ()
insertBlockUnitAssigns pname info (F.BlStatement _ _ _ (F.StDeclaration _ _ _ _ decls)) varRealNames = do
let info' = transform (transformExplicitPolymorphism pname) info
let m = M.fromList [ ((varName e, srcName e), info')
| e@(F.ExpValue _ _ (F.ValVariable _)) <- universeBi decls :: [F.Expression UA]
, varRealName <- varRealNames
, varRealName == srcName e ]
modifyVarUnitMap $ M.unionWith const m
modifyGivenVarSet . S.union . S.fromList . map fst . M.keys $ m
insertPUUnitAssigns :: UnitInfo -> F.ProgramUnit UA -> [String] -> UnitSolver ()
insertPUUnitAssigns info pu@(F.PUFunction _ _ _ _ _ _ mret _ _) varRealNames
| (retUniq, retSrc) <- case mret of Just ret -> (FA.varName ret, FA.srcName ret)
Nothing -> (puName pu, puSrcName pu)
, retSrc `elem` varRealNames = do
let pname = Just $ F.getName pu
let info' = transform (transformExplicitPolymorphism pname) info
let m = M.fromList [ ((retUniq, retSrc), info') ]
modifyVarUnitMap $ M.unionWith const m
modifyGivenVarSet . S.union . S.fromList . map fst . M.keys $ m
checkPolymorphicAnnotation :: UnitSolver [String]
checkPolymorphicAnnotation = do
pf <- gets usProgramFile
checks <- mapM checkPU (universeBi pf)
return . map fst . filter (not . snd) $ checks
where
checkPU :: F.ProgramUnit UA -> UnitSolver (String, Bool)
checkPU pu = do
(argPolys, resPolys) <- foldM (checkBlockComment (getNameAndArgs pu)) ([], []) [ b | b@(F.BlComment {}) <- universeBi (F.programUnitBody pu) ]
return (puName pu, S.fromList resPolys `S.isSubsetOf` S.fromList argPolys)
where
getNameAndArgs :: F.ProgramUnit UA -> Maybe (String, [String], Maybe String)
getNameAndArgs pu = case pu of
F.PUFunction _ _ _ _ _ args Nothing _ _
| name <- puName pu -> Just (name, map varName (universeBi args :: [F.Expression UA]), Just name)
F.PUFunction _ _ _ _ _ args (Just res) _ _
| name <- puName pu -> Just (name, map varName (universeBi args :: [F.Expression UA]), Just (varName res))
F.PUSubroutine _ _ _ _ args _ _
| name <- puName pu -> Just (name, map varName (universeBi args :: [F.Expression UA]), Nothing)
_ -> Nothing
checkBlockComment :: Maybe (String, [String], Maybe String) -> ([String], [String]) -> F.Block UA -> UnitSolver ([String], [String])
checkBlockComment pinfo (argPolys, resPolys) (F.BlComment a _ _)
| Just (pname, args, mres) <- pinfo
, Just (P.UnitAssignment (Just vars) unitsAST) <- mSpec
, Just b <- mBlock =
let
annotVars = S.fromList [ varName e
| e@(F.ExpValue _ _ (F.ValVariable _)) <- universeBi b :: [F.Expression UA]
, varSrcName <- vars
, varSrcName == srcName e ]
extractPolys ast = [ v | P.UnitBasic (v@('\'':_)) <- universeBi ast ]
in case () of
() | any (`S.member` annotVars) args -> return (extractPolys unitsAST ++ argPolys, resPolys)
| Just res <- mres,
res `S.member` annotVars -> return (argPolys, extractPolys unitsAST ++ resPolys)
| otherwise -> return (argPolys, resPolys)
| otherwise = return (argPolys, resPolys)
where
mSpec = unitSpec (FA.prevAnnotation a)
mBlock = unitBlock (FA.prevAnnotation a)
annotateAllVariables :: UnitSolver ()
annotateAllVariables = modifyProgramFileM $ \ pf -> do
varUnitMap <- usVarUnitMap `fmap` get
let annotateExp e@(F.ExpValue _ _ (F.ValVariable _))
| Just info <- M.lookup (varName e, srcName e) varUnitMap = setUnitInfo info e
annotateExp e = e
return $ transformBi annotateExp pf
annotateLiterals :: UnitSolver ()
annotateLiterals = modifyProgramFileM (transformBiM annotateLiteralsPU)
annotateLiteralsPU :: F.ProgramUnit UA -> UnitSolver (F.ProgramUnit UA)
annotateLiteralsPU pu = do
mode <- asks uoLiterals
case mode of
LitUnitless -> modifyPUBlocksM (transformBiM expUnitless) pu
LitPoly -> modifyPUBlocksM (transformBiM (withLiterals genParamLit)) pu
LitMixed -> modifyPUBlocksM (transformBiM expMixed) pu
where
expMixed e = case e of
F.ExpValue _ _ (F.ValInteger i) | readInteger i == Just 0 -> withLiterals genParamLit e
| isPolyCtxt -> expUnitless e
| otherwise -> withLiterals genUnitLiteral e
F.ExpValue _ _ (F.ValReal i) | readReal i == Just 0 -> withLiterals genParamLit e
| isPolyCtxt -> expUnitless e
| otherwise -> withLiterals genUnitLiteral e
_ -> return e
expUnitless e
| isLiteral e = return $ setUnitInfo UnitlessLit e
| otherwise = return e
withLiterals m e
| isLiteral e = flip setUnitInfo e `fmap` m
| otherwise = return e
isPolyCtxt = case pu of F.PUFunction {} -> True; F.PUSubroutine {} -> True; _ -> False
isLiteral :: F.Expression UA -> Bool
isLiteral (F.ExpValue _ _ (F.ValReal _)) = True
isLiteral (F.ExpValue _ _ (F.ValInteger _)) = True
isLiteral _ = False
isLiteralZero :: F.Expression UA -> Bool
isLiteralZero (F.ExpValue _ _ (F.ValInteger i)) = readInteger i == Just 0
isLiteralZero (F.ExpValue _ _ (F.ValReal i)) = readReal i == Just 0
isLiteralZero _ = False
isLiteralNonZero :: F.Expression UA -> Bool
isLiteralNonZero (F.ExpValue _ _ (F.ValInteger i)) = readInteger i /= Just 0
isLiteralNonZero (F.ExpValue _ _ (F.ValReal i)) = readReal i /= Just 0
isLiteralNonZero _ = False
applyTemplates :: Constraints -> UnitSolver Constraints
applyTemplates cons = do
dumpConsM "applyTemplates" cons
let instances = nub [ (name, i) | UnitParamPosUse (name, _, i) <- universeBi cons ]
pf <- gets usProgramFile
dummies <- forM (topLevelFuncsAndSubs pf) $ \ pu -> do
id <- genCallId
return (puName pu, id)
whenDebug $ do
D.traceM ("instances: " ++ show instances ++ "\n")
D.traceM ("dummies: " ++ show dummies ++ "\n")
concreteCons <- liftM2 (++) (foldM (substInstance False []) [] instances)
(foldM (substInstance True []) [] dummies)
dumpConsM "applyTemplates: concreteCons" concreteCons
aliasMap <- usUnitAliasMap `fmap` get
let aliases = [ ConEq (UnitAlias name) def | (name, def) <- M.toList aliasMap ]
let transAlias (UnitName a) | a `M.member` aliasMap = UnitAlias a
transAlias u = u
dumpConsM "aliases" aliases
return . transformBi transAlias $ cons ++ concreteCons ++ aliases
substInstance :: Bool -> [F.Name] -> Constraints -> (F.Name, Int) -> UnitSolver Constraints
substInstance isDummy callStack output (name, callId) = do
tmap <- gets usTemplateMap
let npc = []
template <- transformBiM callIdRemap $ npc `fromMaybe` M.lookup name tmap
dumpConsM ("substInstance " ++ show isDummy ++ " " ++ show callStack ++ " " ++ show (name, callId) ++ " template lookup") template
modify $ \ s -> s { usCallIdRemap = IM.empty }
let instances = nub [ (name, i) | UnitParamPosUse (name, _, i) <- universeBi template ]
template' <- if name `elem` callStack then
return []
else
foldM (substInstance False (name:callStack)) [] instances
dumpConsM ("instantiating " ++ show (name, callId) ++ ": (output ++ template) is") (output ++ template)
dumpConsM ("instantiating " ++ show (name, callId) ++ ": (template') is") (template')
let filterForVars (NPKVariable _) _ = True; filterForVars _ _ = False
nmap <- M.filterWithKey filterForVars `fmap` gets usNameParamMap
let importedVariables = [ ConEq (UnitVar vv) (foldUnits units) | (NPKVariable vv, units) <- M.toList nmap ]
let output' =
(if isDummy then output ++ template
else instantiate callId (output ++ template)) ++
instantiate callId template' ++
importedVariables
dumpConsM ("final output for " ++ show (name, callId)) output'
return output'
foldUnits units
| null units = UnitlessVar
| otherwise = foldl1 UnitMul units
nameParamConstraints :: F.Name -> UnitSolver Constraints
nameParamConstraints fname = do
let filterForName (NPKParam n _) _ = n == fname
filterForName _ _ = False
nlst <- (M.toList . M.filterWithKey filterForName) `fmap` gets usNameParamMap
return [ ConEq (UnitParamPosAbs (fname, pos)) (foldUnits units) | (NPKParam _ pos, units) <- nlst ]
callIdRemap :: UnitInfo -> UnitSolver UnitInfo
callIdRemap info = modifyCallIdRemapM $ \ idMap -> case info of
UnitParamPosUse (n, p, i)
| Just i' <- IM.lookup i idMap -> return (UnitParamPosUse (n, p, i'), idMap)
| otherwise -> genCallId >>= \ i' ->
return (UnitParamPosUse (n, p, i'), IM.insert i i' idMap)
UnitParamVarUse (n, v, i)
| Just i' <- IM.lookup i idMap -> return (UnitParamVarUse (n, v, i'), idMap)
| otherwise -> genCallId >>= \ i' ->
return (UnitParamVarUse (n, v, i'), IM.insert i i' idMap)
UnitParamLitUse (l, i)
| Just i' <- IM.lookup i idMap -> return (UnitParamLitUse (l, i'), idMap)
| otherwise -> genCallId >>= \ i' ->
return (UnitParamLitUse (l, i'), IM.insert i i' idMap)
UnitParamEAPUse (v, i)
| Just i' <- IM.lookup i idMap -> return (UnitParamEAPUse (v, i'), idMap)
| otherwise -> genCallId >>= \ i' ->
return (UnitParamEAPUse (v, i'), IM.insert i i' idMap)
_ -> return (info, idMap)
instantiate :: Data a => Int -> a -> a
instantiate callId = transformBi $ \ info -> case info of
UnitParamPosAbs (name, position) -> UnitParamPosUse (name, position, callId)
UnitParamLitAbs litId -> UnitParamLitUse (litId, callId)
UnitParamVarAbs (fname, vname) -> UnitParamVarUse (fname, vname, callId)
UnitParamEAPAbs vname -> UnitParamEAPUse (vname, callId)
_ -> info
topLevelFuncsAndSubs :: F.ProgramFile a -> [F.ProgramUnit a]
topLevelFuncsAndSubs (F.ProgramFile _ pus) = topLevel =<< pus
where
topLevel (F.PUModule _ _ _ _ (Just contains)) = topLevel =<< contains
topLevel (F.PUMain _ _ _ _ (Just contains)) = topLevel =<< contains
topLevel f@(F.PUFunction {}) = return f
topLevel s@(F.PUSubroutine {}) = return s
topLevel _ = []
extractConstraints :: UnitSolver Constraints
extractConstraints = do
pf <- gets usProgramFile
dmap <- (M.union (extractDeclMap pf) . combinedDeclMap . M.elems) `fmap` asks uoModFiles
varUnitMap <- gets usVarUnitMap
return $ [ con | b <- mainBlocks pf, con@(ConEq {}) <- universeBi b ] ++
[ ConEq (toUnitVar dmap v) u | (v, u) <- M.toList varUnitMap ]
mainBlocks :: F.ProgramFile UA -> [F.Block UA]
mainBlocks = concatMap getBlocks . universeBi
where
getBlocks (F.PUMain _ _ _ bs _) = bs
getBlocks (F.PUModule _ _ _ bs _) = bs
getBlocks _ = []
isParametric :: Constraint -> Bool
isParametric info = not . null $ [ () | UnitParamPosAbs _ <- universeBi info ] ++
[ () | UnitParamVarAbs _ <- universeBi info ] ++
[ () | UnitParamLitAbs _ <- universeBi info ]
isAllParametric :: Constraint -> Bool
isAllParametric = all f . universeBi
where
f i = case i of
UnitParamPosAbs _ -> True
UnitParamVarAbs _ -> True
UnitParamLitAbs _ -> True
_ -> False
propagateUnits :: UnitSolver ()
propagateUnits = modifyProgramFileM $ transformBiM propagatePU <=<
transformBiM propagateStatement <=<
transformBiM propagateExp
propagateExp :: F.Expression UA -> UnitSolver (F.Expression UA)
propagateExp e = fmap uoLiterals ask >>= \ lm -> case e of
F.ExpValue _ _ _ -> return e
F.ExpBinary _ _ F.Multiplication e1 e2 -> setF2 UnitMul (getUnitInfoMul lm e1) (getUnitInfoMul lm e2)
F.ExpBinary _ _ F.Division e1 e2 -> setF2 UnitMul (getUnitInfoMul lm e1) (flip UnitPow (1) `fmap` (getUnitInfoMul lm e2))
F.ExpBinary _ _ F.Exponentiation e1 e2 -> setF2 UnitPow (getUnitInfo e1) (constantExpression e2)
F.ExpBinary _ _ o e1 e2 | isOp AddOp o -> setF2C ConEq (getUnitInfo e1) (getUnitInfo e2)
| isOp RelOp o -> setF2C ConEq (getUnitInfo e1) (getUnitInfo e2)
F.ExpFunctionCall {} -> propagateFunctionCall e
F.ExpSubscript _ _ e1 _ -> return $ maybeSetUnitInfo (getUnitInfo e1) e
F.ExpUnary _ _ _ e1 -> return $ maybeSetUnitInfo (getUnitInfo e1) e
_ -> do
whenDebug . tell $ "propagateExp: " ++ show (getSpan e) ++ " unhandled: " ++ show e
return e
where
setF2 f u1 u2 = return $ maybeSetUnitInfoF2 f u1 u2 e
setF2C f u1 u2 = return . maybeSetUnitInfo u1 $ maybeSetUnitConstraintF2 f u1 u2 e
propagateFunctionCall :: F.Expression UA -> UnitSolver (F.Expression UA)
propagateFunctionCall e@(F.ExpFunctionCall a s f Nothing) = do
(info, _) <- callHelper f []
let cons = intrinsicHelper info f []
return . setConstraint (ConConj cons) . setUnitInfo info $ F.ExpFunctionCall a s f Nothing
propagateFunctionCall e@(F.ExpFunctionCall a s f (Just (F.AList a' s' args))) = do
(info, args') <- callHelper f args
let cons = intrinsicHelper info f args'
return . setConstraint (ConConj cons) . setUnitInfo info $ F.ExpFunctionCall a s f (Just (F.AList a' s' args'))
propagateStatement :: F.Statement UA -> UnitSolver (F.Statement UA)
propagateStatement stmt = case stmt of
F.StExpressionAssign _ _ e1 e2 -> literalAssignmentSpecialCase e1 e2 stmt
F.StCall a s sub (Just (F.AList a' s' args)) -> do
(info, args') <- callHelper sub args
let cons = intrinsicHelper info sub args'
return . setConstraint (ConConj cons) $ F.StCall a s sub (Just (F.AList a' s' args'))
F.StDeclaration {} -> transformBiM propagateDeclarator stmt
_ -> return stmt
propagateDeclarator :: F.Declarator UA -> UnitSolver (F.Declarator UA)
propagateDeclarator decl = case decl of
F.DeclVariable _ _ e1 _ (Just e2) -> literalAssignmentSpecialCase e1 e2 decl
F.DeclArray _ _ e1 _ _ (Just e2) -> literalAssignmentSpecialCase e1 e2 decl
_ -> return decl
literalAssignmentSpecialCase e1 e2 ast
| u2@(Just (UnitLiteral _)) <- getUnitInfo e2 = do
return $ maybeSetUnitConstraintF2 ConEq (getUnitInfo e1) u2 ast
| isLiteralNonZero e2 = do
u2 <- genUnitLiteral
return $ maybeSetUnitConstraintF2 ConEq (getUnitInfo e1) (Just u2) ast
| otherwise = do
return $ maybeSetUnitConstraintF2 ConEq (getUnitInfo e1) (getUnitInfo e2) ast
propagatePU :: F.ProgramUnit UA -> UnitSolver (F.ProgramUnit UA)
propagatePU pu = do
let name = puName pu
let bodyCons = [ con | con@(ConEq {}) <- universeBi pu ]
varMap <- gets usVarUnitMap
givenCons <- forM (indexedParams pu) $ \ (i, param) -> do
case M.lookup param varMap of
Just (UnitParamPosAbs {}) -> return . ConEq (UnitParamVarAbs (name, param)) $ UnitParamPosAbs (name, i)
Just u -> return . ConEq u $ UnitParamPosAbs (name, i)
_ -> return . ConEq (UnitParamVarAbs (name, param)) $ UnitParamPosAbs (name, i)
let cons = givenCons ++ bodyCons
case pu of F.PUFunction {} -> modifyTemplateMap (M.insert name cons)
F.PUSubroutine {} -> modifyTemplateMap (M.insert name cons)
_ -> return ()
let pu' = case (pu, indexedParams pu) of
(F.PUFunction {}, (0, res):_) -> setUnitInfo (UnitParamPosAbs (name, 0) `fromMaybe` M.lookup res varMap) pu
_ -> pu
return (setConstraint (ConConj cons) pu')
containsParametric :: Data from => String -> from -> Bool
containsParametric name x = not . null $ [ () | UnitParamPosAbs (name', _) <- universeBi x, name == name' ] ++
[ () | UnitParamVarAbs (name', _) <- universeBi x, name == name' ]
callHelper :: F.Expression UA -> [F.Argument UA] -> UnitSolver (UnitInfo, [F.Argument UA])
callHelper nexp args = do
let name = varName nexp
callId <- genCallId
let eachArg i arg@(F.Argument _ _ _ e)
| Just u <- getUnitInfo e = setConstraint (ConEq u (UnitParamPosUse (name, i, callId))) arg
| otherwise = arg
let args' = zipWith eachArg [1..] args
let info = UnitParamPosUse (name, 0, callId)
return (info, args')
intrinsicHelper (UnitParamPosUse (_, _, callId)) f@(F.ExpValue _ _ (F.ValIntrinsic _)) args
| Just (retU, argUs) <- M.lookup sname intrinsicUnits = zipWith eachArg [0..numArgs] (retU:argUs)
where
numArgs = length args
sname = srcName f
vname = varName f
eachArg i u = ConEq (UnitParamPosUse (vname, i, callId)) (instantiate callId u)
intrinsicHelper _ _ _ = []
genCallId :: UnitSolver Int
genCallId = do
st <- get
let callId = usCallIds st
put $ st { usCallIds = callId + 1 }
return callId
genUnitLiteral :: UnitSolver UnitInfo
genUnitLiteral = do
s <- get
let i = usLitNums s
put $ s { usLitNums = i + 1 }
return $ UnitLiteral i
genParamLit :: UnitSolver UnitInfo
genParamLit = do
s <- get
let i = usLitNums s
put $ s { usLitNums = i + 1 }
return $ UnitParamLitAbs i
getUnitInfo :: F.Annotated f => f UA -> Maybe UnitInfo
getUnitInfo = unitInfo . FA.prevAnnotation . F.getAnnotation
getConstraint :: F.Annotated f => f UA -> Maybe Constraint
getConstraint = unitConstraint . FA.prevAnnotation . F.getAnnotation
getUnitInfoMul :: LiteralsOpt -> F.Expression UA -> Maybe UnitInfo
getUnitInfoMul LitPoly e = getUnitInfo e
getUnitInfoMul _ e
| isJust (constantExpression e) = Just UnitlessLit
| otherwise = getUnitInfo e
setUnitInfo :: F.Annotated f => UnitInfo -> f UA -> f UA
setUnitInfo info = modifyAnnotation (onPrev (\ ua -> ua { unitInfo = Just info }))
setConstraint :: F.Annotated f => Constraint -> f UA -> f UA
setConstraint (ConConj []) = id
setConstraint c = modifyAnnotation (onPrev (\ ua -> ua { unitConstraint = Just c }))
maybeSetUnitInfo :: F.Annotated f => Maybe UnitInfo -> f UA -> f UA
maybeSetUnitInfo Nothing e = e
maybeSetUnitInfo (Just u) e = setUnitInfo u e
maybeSetUnitInfoF2 :: F.Annotated f => (a -> b -> UnitInfo) -> Maybe a -> Maybe b -> f UA -> f UA
maybeSetUnitInfoF2 f (Just u1) (Just u2) e = setUnitInfo (f u1 u2) e
maybeSetUnitInfoF2 _ _ _ e = e
maybeSetUnitConstraintF2 :: F.Annotated f => (a -> b -> Constraint) -> Maybe a -> Maybe b -> f UA -> f UA
maybeSetUnitConstraintF2 f (Just u1) (Just u2) e = setConstraint (f u1 u2) e
maybeSetUnitConstraintF2 _ _ _ e = e
fmapUnitInfo :: F.Annotated f => (UnitInfo -> UnitInfo) -> f UA -> f UA
fmapUnitInfo f x
| Just u <- getUnitInfo x = setUnitInfo (f u) x
| otherwise = x
modifyPUBlocksM :: Monad m => ([F.Block a] -> m [F.Block a]) -> F.ProgramUnit a -> m (F.ProgramUnit a)
modifyPUBlocksM f pu = case pu of
F.PUMain a s n b pus -> flip fmap (f b) $ \ b' -> F.PUMain a s n b' pus
F.PUModule a s n b pus -> flip fmap (f b) $ \ b' -> F.PUModule a s n b' pus
F.PUSubroutine a s r n p b subs -> flip fmap (f b) $ \ b' -> F.PUSubroutine a s r n p b' subs
F.PUFunction a s r rec n p res b subs -> flip fmap (f b) $ \ b' -> F.PUFunction a s r rec n p res b' subs
F.PUBlockData a s n b -> flip fmap (f b) $ \ b' -> F.PUBlockData a s n b'
F.PUComment {} -> return pu
data FNum = FReal Double | FInt Integer
fnumToDouble (FReal x) = x
fnumToDouble (FInt x) = fromIntegral x
fAdd, fSub, fMul, fDiv :: FNum -> FNum -> FNum
fAdd (FReal x) fy = FReal $ x + fnumToDouble fy
fAdd fx (FReal y) = FReal $ fnumToDouble fx + y
fAdd (FInt x) (FInt y) = FInt $ x + y
fSub (FReal x) fy = FReal $ x fnumToDouble fy
fSub fx (FReal y) = FReal $ fnumToDouble fx y
fSub (FInt x) (FInt y) = FInt $ x y
fMul (FReal x) fy = FReal $ x * fnumToDouble fy
fMul fx (FReal y) = FReal $ fnumToDouble fx * y
fMul (FInt x) (FInt y) = FInt $ x * y
fDiv (FReal x) fy = FReal $ x / fnumToDouble fy
fDiv fx (FReal y) = FReal $ fnumToDouble fx / y
fDiv (FInt x) (FInt y) = FInt $ x `quot` y
fPow (FReal x) fy = FReal $ x ** fnumToDouble fy
fPow fx (FReal y) = FReal $ fnumToDouble fx ** y
fPow (FInt x) (FInt y)
| y >= 0 = FInt $ x ^ y
| otherwise = FReal $ fromIntegral x ^^ y
fDivMaybe mx my
| Just y <- my,
fnumToDouble y == 0.0 = Nothing
| otherwise = liftM2 fDiv mx my
constantExpression :: F.Expression a -> Maybe Double
constantExpression e = fnumToDouble `fmap` ce e
where
ce e = case e of
(F.ExpValue _ _ (F.ValInteger i)) -> FInt `fmap` readInteger i
(F.ExpValue _ _ (F.ValReal r)) -> FReal `fmap` readReal r
(F.ExpBinary _ _ F.Addition e1 e2) -> liftM2 fAdd (ce e1) (ce e2)
(F.ExpBinary _ _ F.Subtraction e1 e2) -> liftM2 fSub (ce e1) (ce e2)
(F.ExpBinary _ _ F.Multiplication e1 e2) -> liftM2 fMul (ce e1) (ce e2)
(F.ExpBinary _ _ F.Division e1 e2) -> fDivMaybe (ce e1) (ce e2)
(F.ExpBinary _ _ F.Exponentiation e1 e2) -> liftM2 fPow (ce e1) (ce e2)
_ -> Nothing
isOp :: BinOpKind -> F.BinaryOp -> Bool
isOp cat = (== cat) . binOpKind
data BinOpKind = AddOp | MulOp | DivOp | PowerOp | LogicOp | RelOp deriving Eq
binOpKind :: F.BinaryOp -> BinOpKind
binOpKind F.Addition = AddOp
binOpKind F.Subtraction = AddOp
binOpKind F.Multiplication = MulOp
binOpKind F.Division = DivOp
binOpKind F.Exponentiation = PowerOp
binOpKind F.Concatenation = AddOp
binOpKind F.GT = RelOp
binOpKind F.GTE = RelOp
binOpKind F.LT = RelOp
binOpKind F.LTE = RelOp
binOpKind F.EQ = RelOp
binOpKind F.NE = RelOp
binOpKind F.Or = LogicOp
binOpKind F.And = LogicOp
binOpKind F.Equivalent = RelOp
binOpKind F.NotEquivalent = RelOp
binOpKind (F.BinCustom _) = RelOp
dumpConsM str = whenDebug . D.traceM . unlines . ([replicate 50 '-', str ++ ":"]++) . (++[replicate 50 '^']) . map f
where
f (ConEq u1 u2) = show (flattenUnits u1) ++ " === " ++ show (flattenUnits u2)
f (ConConj cons) = intercalate " && " (map f cons)
debugLogging :: UnitSolver ()
debugLogging = whenDebug $ do
(tell . unlines . map (\ (ConEq u1 u2) -> " ***AbsConstraint: " ++ show (flattenUnits u1) ++ " === " ++ show (flattenUnits u2) ++ "\n")) =<< extractConstraints
pf <- gets usProgramFile
cons <- usConstraints `fmap` get
vum <- usVarUnitMap `fmap` get
tell . unlines $ [ " " ++ show info ++ " :: " ++ n | ((n, _), info) <- M.toList vum ]
tell "\n\n"
uam <- usUnitAliasMap `fmap` get
tell . unlines $ [ " " ++ n ++ " = " ++ show info | (n, info) <- M.toList uam ]
tell . unlines $ map (\ (ConEq u1 u2) -> " ***Constraint: " ++ show (flattenUnits u1) ++ " === " ++ show (flattenUnits u2) ++ "\n") cons
tell $ show cons ++ "\n\n"
forM_ (universeBi pf) $ \ pu -> case pu of
F.PUFunction {}
| Just (ConConj cons) <- getConstraint pu ->
tell . unlines $ (puName pu ++ ":"):map (\ (ConEq u1 u2) -> " constraint: " ++ show (flattenUnits u1) ++ " === " ++ show (flattenUnits u2)) cons
F.PUSubroutine {}
| Just (ConConj cons) <- getConstraint pu ->
tell . unlines $ (puName pu ++ ":"):map (\ (ConEq u1 u2) -> " constraint: " ++ show (flattenUnits u1) ++ " === " ++ show (flattenUnits u2)) cons
_ -> return ()
let (lhsM, rhsM, _, lhsColA, rhsColA) = constraintsToMatrices cons
tell "\n--------------------------------------------------\nLHS Cols:\n"
tell $ show lhsColA
tell "\n--------------------------------------------------\nRHS Cols:\n"
tell $ show rhsColA
tell "\n--------------------------------------------------\nLHS M:\n"
tell $ show lhsM
tell "\n--------------------------------------------------\nRHS M:\n"
tell $ show rhsM
tell "\n--------------------------------------------------\nSolved (RREF) M:\n"
let augM = if H.rows rhsM == 0 || H.cols rhsM == 0 then lhsM else H.fromBlocks [[lhsM, rhsM]]
tell . show . rref $ augM
tell "\n--------------------------------------------------\n"
tell $ "Rank LHS: " ++ show (H.rank lhsM) ++ "\n"
tell "\n--------------------------------------------------\n"
let augA = if H.rows rhsM == 0 || H.cols rhsM == 0 then lhsM else H.fromBlocks [[lhsM, rhsM]]
tell $ "Rank Augmented: " ++ show (H.rank augA) ++ "\n"
tell "\n--------------------------------------------------\nGenUnitAssignments:\n"
let unitAssignments = genUnitAssignments cons
tell . unlines $ map (\ (u1s, u2) -> " ***UnitAssignment: " ++ show u1s ++ " === " ++ show (flattenUnits u2) ++ "\n") unitAssignments
tell "\n--------------------------------------------------\n"
puName :: F.ProgramUnit UA -> F.Name
puName pu
| F.Named n <- FA.puName pu = n
| otherwise = "_nameless"
puSrcName :: F.ProgramUnit UA -> F.Name
puSrcName pu
| F.Named n <- FA.puSrcName pu = n
| otherwise = "_nameless"
intrinsicUnits :: M.Map F.Name (UnitInfo, [UnitInfo])
intrinsicUnits =
M.fromList
[ ("abs", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("iabs", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("dabs", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("cabs", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("aimag", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("aint", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("dint", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("anint", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("dnint", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("cmplx", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("conjg", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("dble", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("dim", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a"), UnitParamEAPAbs ("'a", "'a")]))
, ("idim", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a"), UnitParamEAPAbs ("'a", "'a")]))
, ("ddim", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a"), UnitParamEAPAbs ("'a", "'a")]))
, ("dprod", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("ceiling", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("floor", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("int", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("ifix", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("idint", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("max", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("min", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("min0", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("amin1", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("dmin1", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("amin0", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("min1", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("mod", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a"), UnitParamEAPAbs ("'b", "'b")]))
, ("modulo", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a"), UnitParamEAPAbs ("'b", "'b")]))
, ("amod", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a"), UnitParamEAPAbs ("'b", "'b")]))
, ("dmod", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a"), UnitParamEAPAbs ("'b", "'b")]))
, ("nint", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("real", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("float", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("sngl", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a")]))
, ("sign", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a"), UnitParamEAPAbs ("'b", "'b")]))
, ("isign", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a"), UnitParamEAPAbs ("'b", "'b")]))
, ("dsign", (UnitParamEAPAbs ("'a", "'a"), [UnitParamEAPAbs ("'a", "'a"), UnitParamEAPAbs ("'b", "'b")]))
, ("present", (UnitParamEAPAbs ("'a", "'a"), [UnitlessVar]))
, ("sqrt", (UnitParamEAPAbs ("'a", "'a"), [UnitPow (UnitParamEAPAbs ("'a", "'a")) 2]))
, ("dsqrt", (UnitParamEAPAbs ("'a", "'a"), [UnitPow (UnitParamEAPAbs ("'a", "'a")) 2]))
, ("csqrt", (UnitParamEAPAbs ("'a", "'a"), [UnitPow (UnitParamEAPAbs ("'a", "'a")) 2]))
, ("exp", (UnitlessVar, [UnitlessVar]))
, ("dexp", (UnitlessVar, [UnitlessVar]))
, ("cexp", (UnitlessVar, [UnitlessVar]))
, ("alog", (UnitlessVar, [UnitlessVar]))
, ("dlog", (UnitlessVar, [UnitlessVar]))
, ("clog", (UnitlessVar, [UnitlessVar]))
, ("alog10", (UnitlessVar, [UnitlessVar]))
, ("dlog10", (UnitlessVar, [UnitlessVar]))
, ("sin", (UnitlessVar, [UnitlessVar]))
, ("dsin", (UnitlessVar, [UnitlessVar]))
, ("csin", (UnitlessVar, [UnitlessVar]))
, ("cos", (UnitlessVar, [UnitlessVar]))
, ("dcos", (UnitlessVar, [UnitlessVar]))
, ("ccos", (UnitlessVar, [UnitlessVar]))
, ("tan", (UnitlessVar, [UnitlessVar]))
, ("dtan", (UnitlessVar, [UnitlessVar]))
, ("asin", (UnitlessVar, [UnitlessVar]))
, ("dasin", (UnitlessVar, [UnitlessVar]))
, ("acos", (UnitlessVar, [UnitlessVar]))
, ("dacos", (UnitlessVar, [UnitlessVar]))
, ("atan", (UnitlessVar, [UnitlessVar]))
, ("datan", (UnitlessVar, [UnitlessVar]))
, ("atan2", (UnitlessVar, [UnitParamEAPAbs ("'a", "'a"), UnitParamEAPAbs ("'a", "'a")]))
, ("datan2", (UnitlessVar, [UnitParamEAPAbs ("'a", "'a"), UnitParamEAPAbs ("'a", "'a")]))
, ("sinh", (UnitlessVar, [UnitlessVar]))
, ("dsinh", (UnitlessVar, [UnitlessVar]))
, ("cosh", (UnitlessVar, [UnitlessVar]))
, ("dcosh", (UnitlessVar, [UnitlessVar]))
, ("tanh", (UnitlessVar, [UnitlessVar]))
, ("dtanh", (UnitlessVar, [UnitlessVar])) ]