packages feed

ideas-0.5.8: src/Service/SearchSpace.hs

-----------------------------------------------------------------------------
-- Copyright 2009, Open Universiteit Nederland. This file is distributed 
-- under the terms of the GNU General Public License. For more information, 
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Service.SearchSpace 
   ( searchSpace, searchSpaceWith, SearchSpaceConfig(..), defaultConfig
   ) where

import Common.Apply
import Common.Rewriting.TreeDiff
import Common.Transformation
import Common.Strategy hiding ((<||>), (<|>))
import Service.Progress
import qualified Data.Set as S

data SearchSpaceConfig = SSC 
   { costStrategy :: Rational
   , costRule     :: String -> Rational
   }
   
defaultConfig :: SearchSpaceConfig
defaultConfig = SSC 
   { costStrategy = 1
   , costRule     = const 1
   }

type Diffs a = a -> [(a, TreeDiff)] 

searchSpace :: (a -> a -> Ordering) -> Diffs a -> Maybe (Prefix a) -> [Rule a] -> a -> Progress Rational (a, Maybe (Prefix a), [Rule a])
searchSpace = searchSpaceWith defaultConfig

searchSpaceWith :: SearchSpaceConfig -> (a -> a -> Ordering) -> Diffs a -> Maybe (Prefix a) -> [Rule a] -> a -> Progress Rational (a, Maybe (Prefix a), [Rule a])
searchSpaceWith config ordering diffs mp rules q = rec (empty ordering) (success (q, [], mp))
 where
   rec history worklist =
      case extractFirst worklist of
         Nothing -> 
            emptyProgress
         Just (cost, (p, rs, mStrat), _, rest)
            | member p history ->
                 addScore cost failure <||> rec history rest
            | otherwise -> 
                 let new = newStrategy <|> newRule
                     newRule = mapProgress (\(a, r) -> (a, r:rs, Nothing)) (stepP p)
                     newStrategy = 
                        case mStrat of
                           Nothing -> emptyProgress
                           Just p1 -> addScore (costStrategy config) $ fromMaybeList $
                              flip map (runPrefixMajor p1 p) $ \(x, y) -> 
                                 case lastRuleInPrefix y of
                                    Just r | isMajorRule r && stepsToRules (prefixToSteps p1) /= stepsToRules (prefixToSteps y) -> 
                                       Just (x, r:rs, Just y)
                                    _ -> Nothing
                     newHistory  = insert p history
                     newWorklist = addScore cost new <|> rest
                 in addScore cost (success (p, mStrat, rs)) <||> rec newHistory newWorklist

   stepP a0 = do
      (r, a) <- scoreList 
         [ (cost , (r, a)) 
         | (a, td) <- diffs a0
         , r <- rules 
         , let cost = costRule config (name r) * scoreTreeDiff td
         ]
      fromMaybeList $ 
         case applyAll r a of
            [] -> [ Nothing ]
            bs -> [ Just (b, r) | b <- bs]

scoreTreeDiff :: TreeDiff -> Rational
scoreTreeDiff td =
   case td of
      Equal -> 1000 
      _ -> 1
      {- Equal     -> 20
      Inside    -> 10
      Different -> 1
      Top       -> 2 -}

-- History and X are a work-around, since we don't have an Ord instance for our type
data History a = History (a -> a -> Ordering) (S.Set (X a))

newtype X a = X (a -> a -> Ordering, a)

instance Eq (X a) where
   X (f, a) == X (_, b) = f a b == EQ

instance Ord (X a) where
   X (f, a) `compare` X (_, b) = f a b

empty :: (a -> a -> Ordering) -> History a 
empty f = History f S.empty

member :: a -> History a -> Bool
member a (History f s) = S.member (X (f, a)) s

insert :: a -> History a -> History a
insert a (History f s) = History f (S.insert (X (f, a)) s)

------------------------------------------------------
-- Example for logic domain
{- 
buggyRules     = map liftRuleToContext [buggyDeMorganOr, buggyDeMorganAnd, buggyAndOverOr, buggyOrOverAnd]
expensiveRules = map liftRuleToContext [ruleDefEquiv, ruleAndOverOr, ruleOrOverAnd]

buggyDeMorganOr :: LogicRule
buggyDeMorganOr = buggyRule $ makeRule "Buggy DeMorganOr" $
   (Not (x :||: y))  |-  (Not x :||: Not y)

buggyDeMorganAnd :: LogicRule
buggyDeMorganAnd = buggyRule $ makeRule "Buggy DeMorganAnd" $
   (Not (x :&&: y))  |-  (Not x :&&: Not y)
   
buggyAndOverOr :: LogicRule
buggyAndOverOr = buggyRule $ makeRuleList "Buggy AndOverOr"
   [ (x :&&: (y :||: z))  |-  ((x :||: y) :&&: (x :||: z))
   , ((x :||: y) :&&: z)  |-  ((x :||: z) :&&: (y :||: z))
   ]

buggyOrOverAnd :: LogicRule
buggyOrOverAnd = buggyRule $ makeRuleList "Buggy OrOverAnd"
   [ (x :||: (y :&&: z))  |-  ((x :&&: y) :||: (x :&&: z))
   , ((x :&&: y) :||: z)  |-  ((x :&&: z) :||: (y :&&: z))
   ]

ex = (T :->: Var "p") :&&: (Var "q" :||: T)

q = putStrLn $ unlines $ map f $ successes $ maxDepth 20 $ stepsP (inContext ex)
 where f (a, rs) = ppLogic (fromContext a) ++ "  " ++ show rs

w = map (\(a,b) -> (a, length b)) $ successesForScore $ maxDepth 25 $ stepsP (inContext ex)

ruleScore :: Rule (Context Logic) -> Int
ruleScore r
   | name r `elem` map name buggyRules     = fromInteger costBUGGY
   | name r `elem` map name expensiveRules = fromInteger costEXPENSIVE
   | otherwise                             = fromInteger costRULE

stepsP :: Context Logic -> Progress Int (Context Logic, [Rule (Context Logic)])
stepsP = searchSpace (emptyPrefix toDNF) rules
 where
   list  = map liftRuleToContext logicRules ++ buggyRules
   rules = zip (map ruleScore list) list -}