packages feed

tpdb-0.6.0: TPDB/Compress.hs

{-# language TemplateHaskell #-}

module TPDB.Compress where

import TPDB.Data hiding ( trs, arity )
import TPDB.Plain.Write ()
import qualified TPDB.Plain.Read (trs) -- for testing
import TPDB.Pretty 

import Control.Monad ( guard ) 
import qualified Data.Set as S
import qualified Data.Map.Strict as M
import Data.List ( sortBy )
import Data.Ord ( comparing )


-- | compute a compressed version of the TRS.
-- Warning: in the output, the arities of fresh identifiers will be nonsensical
compress :: ( Ord s )
         => [s] -- ^ supply of function symbols. This can be any infinite list,
                -- the implementation will filter out those elements that
                -- occur in the original TRS's signature.
         -> TRS v s 
         -> ( TRS v s 
            , [ ((s, Int), Maybe (s,Int,s,Int) ) ] 
            ) -- ^  ((f, a), Just (g,i,h,a)) semantics: new function symbols f
              -- by substituting h in the i-th position (start at 0) of g.
              -- arity of child is a
              -- ((f, a), Nothing ) semantics: f of arity a is an "old" function symbol.
              -- output is in dependency order (will only refer to previously defined symbols).
compress pool sys = 
    let osig = ohsig sys
        forbidden = M.keysSet osig
        fresh = filter ( `S.notMember` forbidden ) pool
        con = make fresh sys
    in  ( trs con 
        ,    map ( \ fa -> ( fa, Nothing ) ) ( M.toList osig )
          ++ map ( \ (f, p) -> ( (f, arity p), Just (parent p, branch p, child p, child_arity p) ) ) 
             (  reverse $ defs con )
        )

ohsig sys = M.fromListWith ( \ o n -> if o == n then o else error "different arities" ) 
             $ do u <- rules sys ; t <- [ lhs u , rhs u ]
                  ( _ , Node f args ) <- positions t
                  return ( f, length args )

dont_compress pool sys = 
    let osig = ohsig sys
    in ( sys , map ( \ fa -> ( fa, Nothing ) ) ( M.toList osig ) )
                  

data Pattern s = Pattern
             { parent :: ! s
             , branch :: ! Int
             , child :: ! s
             , arity :: ! Int
             , child_arity :: ! Int
             , has_grand_child :: ! Bool 
             }
    deriving ( Eq, Ord )

data Container v s = Container 
               { trs :: TRS v s
               , defs :: [( s, Pattern s)] 
               }

make fresh trs = handle fresh $ Container trs []


handle free con = 
    case -- take 1 $ 
         disjoint $ best_patterns $ trs con of
        [] -> con
        ps -> 
            let ( pre, post ) 
                    = splitAt ( length ps ) free
                here = zip pre ps      
            in handle post
                 $ con { trs = apply_system here $ trs con
                       , defs = here ++ defs con
                       }

patterns_in_term t = 
    nonoverlapping t ++ overlapping t

overlapping t = do
    (pos, Node f xs) <- positions t
    ( k , x @ ( Node g ys ) ) <- zip [ 0 .. ] xs
    guard $ f == g
    let sub = case ys !! k of 
          Node h _ -> True
          _ -> False
    guard $ if sub then even $ length pos else True 
    let r = not $ null $ do Node {} <- ys ; return ()
    return $ Pattern { arity = length xs - 1 + length ys
                     , parent = f, branch = k
                     , child = g, child_arity = length ys
                     , has_grand_child = r 
                     }

nonoverlapping t = do
    Node f xs <- subterms t
    ( k , x @ ( Node g ys ) ) <- zip [ 0 .. ] xs
    guard $ f /= g
    let r = not $ null $ do Node {} <- ys ; return ()
    return $ Pattern { arity = length xs - 1 + length ys
                     , parent = f, branch = k
                     , child = g, child_arity = length ys
                     , has_grand_child = r 
                     }

patterns_in_rule u = do
    t <- [ lhs u, rhs u ]
    patterns_in_term t

patterns trs = do
    u <- rules trs
    patterns_in_rule u

disjoint ps =
   let h seen [] = []
       h seen (p:ps) = 
         if S.notMember (parent p) seen
            && S.notMember (child p) seen
         then p : h (S.insert (parent p)   
                     $ S.insert (child p) seen) ps
         else h seen ps     
   in  h S.empty ps 

best_patterns trs = do
    let pns = sortBy ( comparing ( negate . snd ))
            $  M.toList $ collect $ patterns trs
    let threshold = case pns of          
          [] -> 0
          (p,n) : _ -> div n 2
    (p,n) <- takeWhile ( \ (p,n) -> n >= threshold ) pns
    guard $ ( n > 1 ) || ( n == 1 && has_grand_child p )
    return p


apply_system fgps trs = do
    trs { rules = map ( apply_rule fgps ) $ rules trs }

apply_rule fgps u = 
    u { lhs = apply_term fgps $ lhs u
      , rhs = apply_term fgps $ rhs u
      }

apply_term _ ( Var v ) = Var v
apply_term fgps t @ (Node top args) = 
    let Node newtop newargs = multi_matches fgps t
    in  Node newtop $ map (apply_term fgps) newargs
        
multi_matches [] t = t
multi_matches ((fg, p@Pattern{parent=f,branch=i,child=g}) : fgps ) t@(Node top args) = 
    if matches p t
    then let ( pre, Node _ sub : post) = splitAt i args 
         in  Node fg ( pre ++ sub ++ post )    
    else multi_matches fgps t         

matches ( Pattern { parent = f, branch = i, child = g } ) 
        ( Node top args ) | top == f = 
    case args !! i of
         Node bot _ | bot == g -> True
         _ -> False
matches p _ = False



collect xs = M.fromListWith (+) $ do 
    x <- xs 
    return ( x, child_arity x )


invert :: ( Ord a, Ord b )
       => M.Map a b -> M.Map b [a]
invert fm = M.fromListWith (++) $ do
    ( k, v ) <- M.toList fm
    return ( v, [k] )