packages feed

MagicHaskeller-0.8.6.2: MagicHaskeller/Analytical/Syntax.hs

-- 
-- (C) Susumu Katayama
--
module MagicHaskeller.Analytical.Syntax where

import Control.Monad -- hiding (guard)
import Data.List(nub)

import qualified MagicHaskeller.Types as Types

--
-- Datatypes
--

data IOPair  = IOP { numUniIDs :: Int  -- ^ number of variables quantified with forall
                   , inputs    :: [Expr] -- ^ input example for each argument. The last argument comes first.
                   , output    :: Expr}
             deriving (Show,Eq)

type TBS = [Bool]                 -- ^ the to-be-sought list
data Expr    = E Int -- ^ existential variable. When doing analytical synthesis, there is no functional variable. 
             | U Int -- ^ universal variable. When doing analytical synthesis, there is no functional variable. 
                     --   Int¤Ç¤Ï¤Ê¤¯TH.Name¤òľÀܻȤä¿Êý¤¬¤è¤¤¡©
             | C {sz :: Int, ctor :: Types.Typed Constr, fields :: [Expr]}
               deriving (Eq, Show)
type Constr  = Int
normalizeMkIOP :: [Expr] -> Expr -> IOPair
normalizeMkIOP ins out = let varIDs = nub $ concatMap vr (out : ins)
                             tup    = zip varIDs [0..]
                         in mapIOP (mapU (\tv -> case lookup tv tup of Just n  -> n)) IOP{numUniIDs = length varIDs, inputs = ins, output = out}
vr (U i)    = [i]
vr (C _ _ es) = concatMap vr es
mapU f (U i) = U $ f i
mapU f (C sz c xs) = C sz c $ map (mapU f) xs

maybeCtor :: Expr -> Maybe (Types.Typed Constr)
maybeCtor (C _ c _) = Just c
maybeCtor _       = Nothing

hasExistential (E _) = True
hasExistential (U _) = False
hasExistential (C _ _ es) = any hasExistential es

visibles tbs ins = [ i | (True,i) <- zip tbs ins ]

--
-- unification
--

type Subst = [(Int,Expr)]


unify (C _ i xs) (C _ j ys) | Types.typee i == Types.typee j = unifyList xs ys
                            | otherwise = mzero
unify e        f        | e==f      = return []
unify (E i)    e        = bind i e
unify e        (E i)    = bind i e
unify _        _        = mzero

unifyList []     []     = return []
unifyList (x:xs) (y:ys) = do s1 <- unify x y
                             s2 <- unifyList (map (apply s1) xs) (map (apply s1) ys)
                             return $ s2 `plusSubst` s1
unifyList _      _      = error "Partial application to a constructor." -- Can this happen?

bind i e | i `occursIn` e = mzero           -- I think permitting infinite data would break the unification algorithm.
         | otherwise      = return [(i,e)]

-- | 'apply' applies a substitution which replaces existential variables to an expression.
apply subst v@(E i)  = maybe v id $ lookup i subst
apply subst v@(U _)  = v
apply subst (C _ i xs) = cap i (map (apply subst) xs) -- ÃÙ¤¤¤«¤Í

i `occursIn` (E j)    = i==j
i `occursIn` (U _)    = False
i `occursIn` (C _ _ xs) = any (i `occursIn`) xs


plusSubst :: Subst -> Subst -> Subst
s0 `plusSubst` s1 = [(u, apply s0 t) | (u,t) <- s1] ++ s0

emptySubst = []


fresh f e@(E _)      = e
fresh f (U i)    = E $ f i
fresh f (C s c xs) = C s c (map (fresh f) xs)
-- | fusion of @apply s@ and @fresh f@
apfresh s e@(E _)      = e -- NB: this RHS is incorrect if apfresh is used for UniT (because s may include a replacement of e).
apfresh s (U i) = maybe (E i) id $ lookup i s
apfresh s (C _sz c xs) = cap c (map (apfresh s) xs)
mapE f e@(U _)    = e
mapE f (E i)      = E $ f i
mapE f (C s c xs) = C s c (map (mapE f) xs)


-- Note that numUniIDs will not be touched.
applyIOPs s iops = map (applyIOP s) iops
applyIOP s iop = mapIOP (apply s) iop
mapIOP f (IOP bvs ins out) = IOP bvs (map f ins) (f out)
mapTypee f (x Types.::: t) = f x Types.::: t


--
-- termination
--

newtype TermStat = TS {unTS :: [Bool]} deriving Show

initTS :: TermStat
initTS = TS $ replicate (length termCrit) True
updateTS :: [Expr] -> [Expr] -> TermStat -> TermStat
updateTS bkis is (TS bs) = TS $ zipWith (&&) bs [ bkis < is | (<) <- termCrit ]
evalTS :: TermStat -> Bool
evalTS (TS bs) = or bs

-- termination criteria. Enumerate anything that come to your mind. (Should this be an option?)
termCrit :: [[Expr]->[Expr]->Bool]
-- termCrit = [fullyLex, aWise, revFullyLex, revAWise ] -- , linear
--termCrit = [aWise,revAWise]
termCrit = [aWise]

fullyLex, revFullyLex, aWise, revAWise, linear :: [Expr]->[Expr]->Bool
fullyLex   = lessRevListsLex cmpExprs
revFullyLex= lessListsLex cmpExprs
aWise      = lessRevListsLex cmpExprSzs
revAWise   = lessListsLex cmpExprSzs
-- linear is really slow, so is not recommended.
linear ls rs = sum (map size ls) < sum (map size rs)
-- ¤Ç¤â¡¤case¤Ç¤Ö¤Ã¤¿Àڤ俤¢¤È¤Î¤¹¤Ù¤Æ¤Î°ú¿ô¤òÈæ³Ó¤·¤Æ¤¤¤ë¤«¤éÃÙ¤¤¤Î¤Ç¤¢¤Ã¤Æ¡¤°ìÈֺǽé¤ÎÃʳ¬¤Î°ú¿ô¤À¤±¤ÇÈæ³Ó¤¹¤ì¤Ð®¤¤¤Î¤Ç¤Ï¡©
-- ¤Ç¤â¡¤Ackermann's function¤Ç¹Í¤¨¤ë¤È¡¤¤ä¤Ã¤Ñ¤½¤ì¤Ç¤Ï¥À¥á¤Ã¤Ý¤¤¡¥

revArgs :: ([Expr]->[Expr]->Bool) -> [Expr]->[Expr]->Bool
revArgs cmp ls rs = cmp (reverse ls) (reverse rs)

lessRevListsLex cmp  = revArgs (lessListsLex cmp)
lessListsLex cmp []       _        = False -- In general, input arguments of BKs should be shorter, and we have to compare only this length.
lessListsLex cmp (e0:es0) (e1:es1) = case cmp e0 e1 of LT -> True
                                                       EQ -> lessListsLex cmp es0 es1
                                                       GT -> False
cmpExprss []       []       = EQ
cmpExprss []       _        = LT
cmpExprss _        []       = GT
cmpExprss (e0:es0) (e1:es1) = case cmpExprs e0 e1 of EQ -> cmpExprss es0 es1
                                                     c  -> c
cmpExprs (C _ _ fs) (C _ _ gs) = cmpExprss fs gs
cmpExprs _          (C _ _ _)  = LT
cmpExprs (C _ _ _)  _          = GT
cmpExprs _          _          = EQ

cmpExprSzs e0 e1 = compare (size e0) (size e1)
size (C sz _ fs) = sz
size _        = 1 -- questionable?
cap con fs = C (1 + sum (map size fs)) con fs

-- Q: Are existential variables always smaller than constructor applications? A: No, I'm afraid.
-- If we want to make sure the termination, we can always return GT when questionable;
-- if we want to save all questionable expressions, we can always return LT when questionable.