packages feed

ideas-0.5.8: src/Domain/Math/Equation/CoverUpRules.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 Domain.Math.Equation.CoverUpRules 
   ( coverUpRules, coverUpRulesOr
   , coverUpPower, coverUpPlus, coverUpMinusLeft, coverUpMinusRight 
   , coverUpTimes, coverUpNegate
   , coverUpNumerator, coverUpDenominator, coverUpSqrt 
     -- parameterized rules
   , ConfigCoverUp, configName, predicateCovered, predicateCombined
   , coverLHS, coverRHS, configCoverUp, varConfig
   , coverUpPowerWith, coverUpTimesWith, coverUpNegateWith
   , coverUpPlusWith, coverUpMinusLeftWith, coverUpMinusRightWith
   , coverUpNumeratorWith, coverUpDenominatorWith, coverUpSqrtWith
   ) where

import Common.View
import Domain.Math.Expr
import Domain.Math.Data.Equation
import Control.Monad.Identity
import Common.Transformation
import Domain.Math.Expr.Symbols
import Domain.Math.Data.OrList
import Common.Traversable
import Domain.Math.Expr.Symbolic

---------------------------------------------------------------------
-- Constructors for cover-up rules

coverUpBinary2Rule :: (OnceJoin f, Switch f) => String -> (Expr -> [(Expr, Expr)]) 
                   -> (Expr -> Expr -> [f Expr])
                   -> ConfigCoverUp -> Rule (f (Equation Expr))
coverUpBinary2Rule opName fm fb cfg = 
   makeSimpleRuleList name $ onceJoinM $ \eq -> 
      (guard (coverLHS cfg) >> coverLeft eq) ++ 
      (guard (coverRHS cfg) >> coverRight eq)
 where
   name       = coverUpRuleName opName (configName cfg)
   coverRight = map (fmap flipSides) . coverLeft . flipSides
   
   coverLeft (lhs :==: rhs) = do
      (e1, e2) <- fm lhs
      guard (predicateCovered  cfg e1)
      new <- fb rhs e2
      switch $ fmap (guard . predicateCombined cfg) new
      return (fmap (e1 :==:) new)

coverUpBinaryRule :: String -> (Expr -> [(Expr, Expr)]) -> (Expr -> Expr -> Expr) 
                  -> ConfigCoverUp -> Rule (Equation Expr)
coverUpBinaryRule opName fm fb =
   let lp = makeLiftPair (return . Identity) (const . runIdentity) 
       fbi x y = [Identity (fb x y)]
   in lift lp . coverUpBinary2Rule opName fm fbi
      
coverUpUnaryRule :: String -> (Expr -> [Expr]) -> (Expr -> Expr) 
               -> ConfigCoverUp -> Rule (Equation Expr)
coverUpUnaryRule opName fm fb = 
   coverUpBinaryRule opName (map (\e -> (e, e)) . fm) (const . fb) 

coverUpRuleName :: String -> Maybe String -> String
coverUpRuleName opName viewName =
   "cover-up " ++ opName ++ maybe "" (\s -> " [" ++ s ++ "]") viewName

---------------------------------------------------------------------
-- Configuration for cover-up rules

data ConfigCoverUp = Config
   { configName        :: Maybe String
   , predicateCovered  :: Expr -> Bool
   , predicateCombined :: Expr -> Bool
   , coverLHS          :: Bool
   , coverRHS          :: Bool
   }

configCoverUp :: ConfigCoverUp
configCoverUp = Config
   { configName        = Nothing
   , predicateCovered  = const True
   , predicateCombined = const True
   , coverLHS          = True
   , coverRHS          = True
   }

-- default configuration
varConfig :: ConfigCoverUp 
varConfig = configCoverUp
   { configName        = Just "vars"
   , predicateCovered  = hasVars
   , predicateCombined = noVars
   }

---------------------------------------------------------------------
-- Parameterized cover-up rules

coverUpPowerWith :: ConfigCoverUp -> Rule (OrList (Equation Expr))
coverUpPowerWith = coverUpBinary2Rule "power" (isBinary powerSymbol) fb
 where
   fb rhs e2 = do
      n <- isNat e2
      guard (n > 0)
      new1 <- canonicalM identity (makeRoot n rhs)
      new2 <- canonicalM identity (negate (makeRoot n rhs))
      return $ orList $ new1 : [ new2 | new1 /= new2, even n ]
      
coverUpPlusWith :: ConfigCoverUp -> Rule (Equation Expr)
coverUpPlusWith = coverUpBinaryRule "plus" (commOp . isPlus) (-)

coverUpMinusLeftWith :: ConfigCoverUp -> Rule (Equation Expr)
coverUpMinusLeftWith = coverUpBinaryRule "minus left" isMinus (+)

coverUpMinusRightWith :: ConfigCoverUp -> Rule (Equation Expr)
coverUpMinusRightWith = coverUpBinaryRule "minus right" (flipOp . isMinus) (flip (-))

-- | Negations are pushed inside
coverUpTimesWith :: ConfigCoverUp -> Rule (Equation Expr)
coverUpTimesWith = coverUpBinaryRule "times" (map signs . commOp . matchM timesView) (/)
 where
   signs (Negate x, y) = (x, neg y) -- puts negation at combined term
   signs (x, y) = (x, y)

coverUpNegateWith :: ConfigCoverUp -> Rule (Equation Expr)
coverUpNegateWith = coverUpUnaryRule "negate" isNegate negate

-- | Negations are pushed inside
coverUpNumeratorWith :: ConfigCoverUp -> Rule (Equation Expr)
coverUpNumeratorWith = coverUpBinaryRule "numerator" (matchM divView) (*)

-- | Negations are pushed inside
coverUpDenominatorWith :: ConfigCoverUp -> Rule (Equation Expr)
coverUpDenominatorWith = coverUpBinaryRule "denominator" (flipOp . matchM divView) (flip (/))

coverUpSqrtWith :: ConfigCoverUp -> Rule (Equation Expr)
coverUpSqrtWith = coverUpUnaryRule "square root" isSqrt (\x -> x*x)
 where
   isSqrt (Sqrt a) = return a
   isSqrt _        = []

---------------------------------------------------------------------
-- Cover-up rules for variables

coverUpRulesOr :: [Rule (OrList (Equation Expr))]
coverUpRulesOr = coverUpPower : map ruleOnce coverUpRules

coverUpRules :: [Rule (Equation Expr)]
coverUpRules = 
   [ coverUpPlus, coverUpMinusLeft, coverUpMinusRight, coverUpNegate
   , coverUpTimes, coverUpNumerator, coverUpDenominator, coverUpSqrt
   ]

coverUpPower :: Rule (OrList (Equation Expr))
coverUpPlus, coverUpMinusLeft, coverUpMinusRight, coverUpTimes, coverUpNegate, 
   coverUpNumerator, coverUpDenominator, coverUpSqrt :: Rule (Equation Expr)

coverUpPower       = coverUpPowerWith       varConfig
coverUpPlus        = coverUpPlusWith        varConfig
coverUpMinusLeft   = coverUpMinusLeftWith   varConfig
coverUpMinusRight  = coverUpMinusRightWith  varConfig
coverUpTimes       = coverUpTimesWith       varConfig
coverUpNegate      = coverUpNegateWith      varConfig
coverUpNumerator   = coverUpNumeratorWith   varConfig
coverUpDenominator = coverUpDenominatorWith varConfig
coverUpSqrt        = coverUpSqrtWith        varConfig

---------------------------------------------------------------------
-- Some helper-functions

commOp :: MonadPlus m => m (a, a) -> m (a, a)
commOp m = do 
   (a, b) <- m 
   return (a, b) `mplus` return (b, a)

flipOp :: Monad m => m (a, a) -> m (a, a)
flipOp = liftM (\(x, y) -> (y, x))

isNat :: MonadPlus m => Expr -> m Integer
isNat (Nat n) = return n
isNat _       = mzero

makeRoot :: Integer -> Expr -> Expr
makeRoot n a 
   | n == 1    = a
   | n == 2    = sqrt a
   | otherwise = root (fromInteger n) a