ideas-math-1.1: src/Domain/Math/Simplification.hs
{-# LANGUAGE DeriveDataTypeable #-}
-----------------------------------------------------------------------------
-- Copyright 2014, 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)
--
-----------------------------------------------------------------------------
-- $Id: Simplification.hs 6548 2014-05-16 10:34:18Z bastiaan $
module Domain.Math.Simplification
( Simplify(..), SimplifyConfig(..)
, simplifyConfig
, Simplified, simplified, liftS, liftS2
, simplifyRule
, collectLikeTerms, mergeAlike, distribution, constantFolding
, mergeAlikeSum, mergeAlikeProduct
) where
import Control.Monad
import Data.List
import Data.Maybe
import Data.Typeable
import Domain.Math.CleanUp (smart)
import Domain.Math.Data.Relation
import Domain.Math.Expr
import Domain.Math.Numeric.Views
import Domain.Math.SquareRoot.Views
import Ideas.Common.Library hiding (simplify, simplifyWith)
import Ideas.Common.Utils.Uniplate
import qualified Ideas.Common.View as View
data SimplifyConfig = SimplifyConfig
{ withSmartConstructors :: Bool
, withMergeAlike :: Bool
, withDistribution :: Bool
, withSimplifySquareRoot :: Bool
, withConstantFolding :: Bool
}
class Simplify a where
simplifyWith :: SimplifyConfig -> a -> a
simplify :: a -> a
simplify = simplifyWith simplifyConfig
simplifyConfig :: SimplifyConfig
simplifyConfig = SimplifyConfig True True True True True
instance Simplify a => Simplify (Context a) where
simplifyWith cfg = changeInContext $ simplifyWith cfg
instance Simplify a => Simplify (Equation a) where
simplifyWith cfg = fmap $ simplifyWith cfg
instance Simplify a => Simplify (Relation a) where
simplifyWith cfg = fmap $ simplifyWith cfg
instance Simplify a => Simplify [a] where
simplifyWith cfg = fmap $ simplifyWith cfg
instance Simplify Expr where
simplifyWith cfg = let optional p f = if p then f else id in
optional (withSmartConstructors cfg) (transform smart)
. optional (withMergeAlike cfg) mergeAlike
. optional (withDistribution cfg) distribution
. optional (withSimplifySquareRoot cfg) (View.simplify
(squareRootViewWith rationalView))
. optional (withConstantFolding cfg) constantFolding
instance Simplify a => Simplify (Rule a) where
simplifyWith cfg = doAfter (simplifyWith cfg) -- by default, simplify afterwards
data Simplified a = S a deriving (Eq, Ord, Typeable)
instance Show a => Show (Simplified a) where
show (S x) = show x
instance (Read a, Simplify a) => Read (Simplified a) where
readsPrec n = map (mapFirst simplified) . readsPrec n
instance (Num a, Simplify a) => Num (Simplified a) where
(+) = liftS2 (+)
(*) = liftS2 (*)
(-) = liftS2 (-)
negate = liftS negate
abs = liftS abs
signum = liftS signum
fromInteger = simplified . fromInteger
instance (Fractional a, Simplify a) => Fractional (Simplified a) where
(/) = liftS2 (/)
recip = liftS recip
fromRational = simplified . fromRational
instance (Floating a, Simplify a) => Floating (Simplified a) where
pi = simplified pi
sqrt = liftS sqrt
(**) = liftS2 (**)
logBase = liftS2 logBase
exp = liftS exp
log = liftS log
sin = liftS sin
tan = liftS tan
cos = liftS cos
asin = liftS asin
atan = liftS atan
acos = liftS acos
sinh = liftS sinh
tanh = liftS tanh
cosh = liftS cosh
asinh = liftS asinh
atanh = liftS atanh
acosh = liftS acosh
instance (Simplify a, IsTerm a) => IsTerm (Simplified a) where
toTerm (S x) = toTerm x
fromTerm = liftM simplified . fromTerm
instance (Reference a, Simplify a) => Reference (Simplified a)
simplified :: Simplify a => a -> Simplified a
simplified = S . simplify
liftS :: Simplify a => (a -> a) -> Simplified a -> Simplified a
liftS f (S x) = simplified (f x)
liftS2 :: Simplify a => (a -> a -> a) -> Simplified a -> Simplified a -> Simplified a
liftS2 f (S x) (S y) = simplified (f x y)
simplifyRule :: Simplify a => Rule a
simplifyRule = simplify (idRule "simplify")
-------------------------------------------------------------
-- Distribution of constants
distribution :: Expr -> Expr
distribution = descend distribution . f
where
f expr =
fromMaybe expr $
case expr of
a :*: b -> do
(x, y) <- match plusView a
r <- match rationalView b
return $ (fromRational r .*. x) .+. (fromRational r .*. y)
`mplus` do
r <- match rationalView a
(x, y) <- match plusView b
return $ (fromRational r .*. x) .+. (fromRational r .*. y)
a :/: b -> do
xs <- match sumView a
guard (length xs > 1)
return $ build sumView $ map (./. b) xs
_ -> Nothing
-------------------------------------------------------------
-- Constant folding
-- Not an efficient implementation: could be improved if necessary
constantFolding :: Expr -> Expr
constantFolding expr =
case match rationalView expr of
Just r -> fromRational r
Nothing -> descend constantFolding expr
----------------------------------------------------------------------
-- merge alike for sums and products
-- Todo: combine with mergeAlike (subtle differences)
collectLikeTerms :: Expr -> Expr
collectLikeTerms = View.simplifyWith f sumView
where
f = mergeAlikeSum . map (View.simplifyWith (second mergeAlikeProduct) productView)
mergeAlike :: Expr -> Expr
mergeAlike a =
case (match sumView a, match productView a) of
(Just xs, _) | length xs > 1 ->
build sumView (sort $ mergeAlikeSum $ map mergeAlike xs)
(_, Just (b, ys)) | length (filter (/= 1) ys) > 1 ->
build productView (b, sort $ mergeAlikeProduct $ map mergeAlike ys)
_ -> a
mergeAlikeProduct :: [Expr] -> [Expr]
mergeAlikeProduct ys = f [ (match rationalView y, y) | y <- ys ]
where
f [] = []
f ((Nothing , e):xs) = e:f xs
f ((Just r , _):xs) =
let cs = r : [ c | (Just c, _) <- xs ]
rest = [ x | (Nothing, x) <- xs ]
in build rationalView (product cs):rest
mergeAlikeSum :: [Expr] -> [Expr]
mergeAlikeSum xs = rec [ (Just $ pm 1 x, x) | x <- xs ]
where
pm :: Rational -> Expr -> (Rational, Expr)
pm r (e1 :*: e2) = case (match rationalView e1, match rationalView e2) of
(Just r1, _) -> pm (r*r1) e2
(_, Just r1) -> pm (r*r1) e1
_ -> (r, e1 .*. e2)
pm r (Negate e) = pm (negate r) e
pm r e = case match rationalView e of
Just r1 -> (r*r1, Nat 1)
Nothing -> (r, e)
rec [] = []
rec ((Nothing, e):ys) = e:rec ys
rec ((Just (r, a), e):ys) = new:rec rest
where
(js, rest) = partition (maybe False ((==a) . snd) . fst) ys
rs = r:map fst (mapMaybe fst js)
new | null js = e
| otherwise = build rationalView (sum rs) .*. a