----------------------------------------------------------------------------- -- Copyright 2010, 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.Polynomial.CleanUp ( cleanUp, cleanUpRelation, cleanUpExpr, cleanUpExpr2 , cleanUpSimple, collectLikeTerms , normalizeSum, normalizeProduct ) where import Common.Uniplate import Common.View import Control.Monad import Data.List import Data.Maybe import Domain.Math.Data.OrList import Domain.Math.Data.Relation import Domain.Math.Data.SquareRoot (fromSquareRoot) import Domain.Math.Expr import Domain.Math.Numeric.Views import Domain.Math.Power.Views import Domain.Math.Simplification (smartConstructors) import Domain.Math.SquareRoot.Views import Prelude hiding ((^), recip) import qualified Prelude import Data.Ratio ---------------------------------------------------------------------- -- Root simplification simplerRoot :: Rational -> Integer -> Expr simplerRoot a b | b < 0 = 1 ./. simplerRoot a (abs b) | a < 0 && odd b = neg (simplerRoot (abs a) b) | otherwise = f (numerator a) b ./. f (denominator a) b where f x y | x == 0 = 0 | y == 0 || x <= 0 = root (fromIntegral x) (fromIntegral y) | a Prelude.^ y == x = fromIntegral a | otherwise = root (fromIntegral x) (fromIntegral y) where a = round (fromIntegral x ** (1 / fromIntegral y)) ---------------------------------------------------------------------- -- Expr normalization collectLikeTerms :: Expr -> Expr collectLikeTerms = simplifyWith f sumView where f = normalizeSum . map (simplifyWith (second normalizeProduct) productView) normalizeProduct :: [Expr] -> [Expr] normalizeProduct 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 normalizeSum :: [Expr] -> [Expr] normalizeSum 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):xs) = e:rec xs rec ((Just (r, a), e):xs) = new:rec rest where (js, rest) = partition (maybe False ((==a) . snd) . fst) xs rs = r:map fst (mapMaybe fst js) new | null js = e | otherwise = build rationalView (sum rs) .*. a ------------------------------------------------------------ -- Testing {- -- List with hard cases hardCases = map cleanUpExpr $ let x=Var "x" in [ -1/2*x*(x/1) , (x/(-3)) , (x/(-3))^2 , (0-x)*(-x)/(-5/2) , (x/(-1))^2 , (x/(-1))^2-(-7/2)*x/(-1) , (x^2+0)*3 , -(49/9*x^2+0^2)*(3/16) , (0*x-(-x^2))*(-3) , x^2 - x^2 , x^2-x^2-(x+x)*1 , x^2/(16/3)-x^2*(-1/3)-(x+(-26/3)-x^2)*1 , (-7+7*x)^2-(x*0)^2/(-3) , 1*(x+93)+4 , (1*(x+(-93/5))-(-4+x/19))/8-(x^2-x+(19/2-x)-34/3*(x*(-41/2)))/9 ] -} ------------------------------------------------------------ -- Cleaning up cleanUpSimple :: Expr -> Expr cleanUpSimple = transform (f4 . f2 . f1) where use v = simplifyWith (assoPlus v) sumView f1 = simplify rationalView f2 = use identity f4 = smartConstructors cleanUpRelation :: OrList (Relation Expr) -> OrList (Relation Expr) cleanUpRelation = simplifyWith cleanUp (switchView equationView) cleanUp :: OrList (Equation Expr) -> OrList (Equation Expr) cleanUp = idempotent . join . fmap (keepEquation . fmap cleanUpExpr) keepEquation :: Equation Expr -> OrList (Equation Expr) keepEquation eq@(a :==: b) | any falsity (universe a ++ universe b) = false | a == b = true | otherwise = case (match rationalView a, match rationalView b) of (Just r, Just s) | r == s -> true | otherwise -> false _ -> return eq where falsity (Sqrt e) = maybe False (<0) (match rationalView e) falsity (_ :/: e) = maybe False (==0) (match rationalView e) falsity _ = False -- also simplify square roots cleanUpExpr2 :: Expr -> Expr cleanUpExpr2 = cleanUpExpr . transform (simplify (squareRootViewWith rationalView)) cleanUpExpr :: Expr -> Expr cleanUpExpr = cleanUpBU2 {- e = if a1==a2 && a2==a3 && a3==a3 && a3==a4 then a1 else error $ "\n\n\n" ++ unlines (map show [e, a1, a2, a3, a4]) where a1 = cleanUpFix e a2 = cleanUpBU e a3 = cleanUpBU2 e a4 = cleanUpLattice e -} ------------------------------------------------------------ -- Technique 1: fixed points of views {- cleanUpFix :: Expr -> Expr cleanUpFix = fixpoint (f4 . f3 . f2 . f1) where use v = transform (simplifyWith (assoPlus v) sumView) f1 = use rationalView f2 = use (squareRootViewWith rationalView) f3 = use (powerFactorViewWith rationalView) f4 = smartConstructors -} assoPlus :: View Expr a -> [Expr] -> [Expr] assoPlus v = rec . map (simplify v) where rec (x:y:zs) = case canonical v (x+y) of Just a -> assoPlus v (a:zs) Nothing -> x:assoPlus v (y:zs) rec xs = xs ------------------------------------------------------------ -- Technique 2a: one bottom-up traversal {- cleanUpBU :: Expr -> Expr cleanUpBU = transform (f4 . f3 . f2 . f1) where use v = simplifyWith (assoPlus v) sumView f1 = simplify rationalView f2 = simplify (squareRootViewWith rationalView) f3 = use (powerFactorViewWith rationalView) f4 = smartConstructors -} ------------------------------------------------------------ -- Technique 2b: one bottom-up traversal cleanUpBU2 :: Expr -> Expr cleanUpBU2 = transform $ \e -> case ( canonical rationalView e , canonical specialSqrtOrder e , match sumView e ) of (Just a, _, _) -> a (_, Just a, _) -> -- Just simplify order of terms with square roots for now transform smart a (_, _, Just xs) | length xs > 1 -> build sumView (assoPlus (powerFactorViewWith rationalView) xs) _ -> case canonical (powerFactorViewWith rationalView) e of Just a -> a Nothing -> smart e specialSqrtOrder :: View Expr [Expr] specialSqrtOrder = sumView >>> makeView f id where make = match (squareRootViewWith rationalView) cmp (_, x) (_, y) = g x `compare` g y g = isNothing . fromSquareRoot f xs = do ys <- mapM make xs return $ map fst $ sortBy cmp $ zip xs ys smart :: Expr -> Expr smart (a :*: b) = a .*. b smart (a :/: b) = a ./. b smart expr@(Sym s [x, y]) | s == powerSymbol = x .^. y | s == rootSymbol = fromMaybe expr $ liftM2 simplerRoot (match rationalView x) (match integerView y) smart (Negate a) = neg a smart (a :+: b) = a .+. b smart (a :-: b) = a .-. b smart (Sqrt (Nat n)) = simplerRoot (fromIntegral n) 2 smart e = e ------------------------------------------------------------ -- Technique 3: lattice of views {- data T = R Rational | S (SquareRoot Rational) | P String Rational Int | E Expr deriving Show cleanUpLattice :: Expr -> Expr cleanUpLattice = fromT . toT fromT :: T -> Expr fromT (R r) = fromRational r fromT (S s) = build (squareRootViewWith rationalView) s fromT (P x r n) = build (powerFactorViewForWith x rationalView) (r, n) fromT (E e) = e toT :: Expr -> T toT (Nat n) = R (fromInteger n) toT (x :/: y) = divT (toT x) (toT y) toT (x :*: y) = mulT (toT x) (toT y) toT (Var x) = P x 1 1 toT (Sym s [x, y]) | s == powerSymbol = case (toT x, toT y) of (R x, R y) | denominator y == 1 -> R (x Prelude.^ fromInteger (numerator y)) (P x a n, R y) | denominator y == 1 -> P x (a Prelude.^ numerator y) (n*fromInteger (numerator y)) (x, y) -> E (fromT x .^. fromT y) toT e@(Sqrt _) = fromMaybe (E e) $ do -- Also here, too simplistic s <- match (squareRootViewWith rationalView) e return (S s) toT (Negate e) = negT (toT e) toT expr = case match sumView expr of Just xs | length xs > 1 -> sumT (map toT xs) _ -> error $ show expr negT :: T -> T negT (R r) = R (negate r) negT (S s) = S (negate s) negT (P x r n) = P x (negate r) n negT (E e) = E (neg e) sumT :: [T] -> T sumT = head . f (const True) . f (`elem` [1,2]) . f (==1) . concatMap g where g e@(E a) = case match sumView a of Just xs | length xs > 1 -> map (upgr . E) xs _ -> [e] g a = [a] f p (a:b:xs) | p (orderT a) && p (orderT b) = f p (plusT a b:xs) | otherwise = a:f p (b:xs) f _ xs = xs plusT :: T -> T -> T plusT (R 0) t = t -- ????? plusT t (R 0) = t -- ????? plusT (R x) (R y) = R (x+y) plusT (S x) (S y) = S (x+y) plusT t@(P _ _ _) b = plusT (E $ fromT t) b plusT (E a) (E b) = E (a .+. b) plusT a b = convTs plusT a b divT :: T -> T -> T divT t (R 1) = t -- ????? divT t (R (-1)) = negT t -- ????? divT (R x) (R y) | y /= 0 = R (x/y) divT t@(R _) b@(R _) = divT (E $ fromT t) b divT (S x) (S y) = S (x/y) divT t@(P _ _ _) b = divT (E $ fromT t) b divT (E a) (E b) = E (a ./. b) divT a b = convTs divT a b mulT :: T -> T -> T mulT (R 0) _ = R 0 -- ????? mulT _ (R 0) = R 0 -- ????? mulT t (R 1) = t -- ???? mulT (R 1) t = t -- ????? mulT (R a) (R b) = R (a*b) mulT (S a) (S b) = S (a*b) mulT (P x1 r1 n1) (P x2 r2 n2) | x1==x2 = P x1 (r1*r2) (n1+n2) | otherwise = error "" mulT (E a) (E b) = E (a .*. b) mulT a b = convTs mulT a b convTs :: (T -> T -> T) -> T -> T -> T convTs f (R a) t@(S _) = f (S (fromRational a)) t convTs f (R a) t@(P x _ _) = f (P x (fromRational a) 0) t convTs f t@(R _) e@(E _) = f (E $ fromT t) e convTs f t@(P _ _ _) e@(E _) = f (E $ fromT t) e convTs f a b | orderT a > orderT b = convTs (flip f) b a convTs _ x y = error $ "conv " ++ show (x, y) orderT :: T -> Int orderT (R _) = 1 orderT (S _) = 2 orderT (P _ _ _) = 3 orderT (E _) = 4 upgr :: T -> T upgr (E e) = case (match (squareRootViewWith rationalView) e, match (powerFactorViewWith rationalView) e) of (Just a, _) -> upgr (S a) (_, Just (x, a, n)) -> upgr (P x a n) _ -> E e upgr (S a) = maybe (S a) R (fromSquareRoot a) upgr (P _ a n) | n==0 = R a upgr t = t -}