symtegration-0.6.1: src/Symtegration/Symbolic/Simplify/AlgebraicRingOrder.hs
-- |
-- Module: Symtegration.Symbolic.Simplify.AlgebraicRingOrder
-- Description: Order terms in a mathematical expression according to a deterministic order.
-- Copyright: Copyright 2024 Yoo Chung
-- License: Apache-2.0
-- Maintainer: dev@chungyc.org
module Symtegration.Symbolic.Simplify.AlgebraicRingOrder (order) where
import Data.List (sortBy)
import Data.Set qualified as Set
import Data.Text (Text)
import Symtegration.Symbolic
-- $setup
-- >>> import Symtegration.Symbolic.Haskell
-- | Order terms in an mathematical expression.
--
-- Terms will be ordered according to a deterministic set of rules.
-- The re-ordering aims to make it easier to identify common factors and terms.
-- Terms with higher integral powers of the variable are sorted later.
-- Addition and multiplication will be re-arranged to associate to the left.
--
-- >>> toHaskell $ order "x" $ "x" + 1
-- "1 + x"
-- >>> toHaskell $ order "x" $ 2 + 3 * "x"**2 + "x"
-- "2 + x + 3 * x ** 2"
order ::
-- | Symbol representing the variable.
Text ->
-- | Expression to be ordered.
Expression ->
-- | Expression with terms re-ordered.
Expression
order _ e@(Number _) = e
order _ e@(Symbol _) = e
order v (UnaryApply func x) = UnaryApply func $ order v x
order v (x :/: y) = order v x :/: order v y
order v (x :**: y) = order v x :**: order v y
order v (LogBase' x y) = LogBase' (order v x) (order v y)
order v e = fromAddList $ sortBy (compareExpressions v) orderedAddTerms
where
terms = toAddMultiplyList v e
orderedAddTerms = map (fromMultiplyList . sortBy (compareExpressions v)) terms
-- | Gather additive terms formed out of multiplicative terms.
-- No particular ordering should be expected.
toAddMultiplyList :: Text -> Expression -> [[Expression]]
toAddMultiplyList v (x@(_ :+: _) :+: y@(_ :+: _)) = toAddMultiplyList v x ++ toAddMultiplyList v y
toAddMultiplyList v (x@(_ :+: _) :+: y) = toMultiplyList v y : toAddMultiplyList v x
toAddMultiplyList v (x :+: y@(_ :+: _)) = toMultiplyList v x : toAddMultiplyList v y
toAddMultiplyList v (x :+: y) = map (toMultiplyList v) [x, y]
toAddMultiplyList v (x :-: y) = toAddMultiplyList v (x :+: (Number (-1) :*: y))
toAddMultiplyList v x = [toMultiplyList v x]
-- | Gather multiplicative terms.
-- No particular ordering should be expected.
toMultiplyList :: Text -> Expression -> [Expression]
toMultiplyList v (x@(_ :*: _) :*: y@(_ :*: _)) = toMultiplyList v x ++ toMultiplyList v y
toMultiplyList v (x@(_ :*: _) :*: y) = y : toMultiplyList v x
toMultiplyList v (x :*: y@(_ :*: _)) = x : toMultiplyList v y
toMultiplyList v (x :*: y) = [order v x, order v y]
toMultiplyList _ x@(Number _) = [x]
toMultiplyList _ x@(Symbol _) = [x]
toMultiplyList v (Negate' x) = Number (-1) : toMultiplyList v x
toMultiplyList v (UnaryApply func x) = [UnaryApply func $ order v x]
toMultiplyList v (BinaryApply func x y) = [BinaryApply func (order v x) (order v y)]
-- | Convert a list of sub-expressions for a multiplicative term into a single expression.
fromMultiplyList :: [Expression] -> Expression
fromMultiplyList [] = Number 1
fromMultiplyList [x] = x
fromMultiplyList (x : xs) = x :*: fromMultiplyList xs
-- | Convert a list of sub-expressions for an additive term into a single expression.
fromAddList :: [Expression] -> Expression
fromAddList [] = Number 0
fromAddList [x] = x
fromAddList (x : xs) = x :+: fromAddList xs
-- | Defines a total order among expressions.
-- In particular, higher integral powers of the variable are ordered later.
compareExpressions :: Text -> Expression -> Expression -> Ordering
compareExpressions v x y
| (Just LT) <- compareDegree = LT
| (Just GT) <- compareDegree = GT
| LT <- comparePseudoDegree = LT
| GT <- comparePseudoDegree = GT
| LT <- compareSymbolCount = LT
| GT <- compareSymbolCount = GT
| LT <- compareOp = LT
| GT <- compareOp = GT
| Number n <- x, Number m <- y = compare n m
| Symbol s <- x, Symbol r <- y = compare s r
| UnaryApply _ x' <- x, UnaryApply _ y' <- y = compareExpressions v x' y'
| BinaryApply _ x' x'' <- x,
BinaryApply _ y' y'' <- y =
case compareExpressions v x' y' of
EQ -> compareExpressions v x'' y''
c -> c
| otherwise = EQ
where
compareDegree = do
xd <- degree v x
yd <- degree v y
case (xd, yd) of
(0, 0) -> return EQ
(0, _) -> return LT
(_, 0) -> return GT
_ -> return $ compare xd yd
comparePseudoDegree = compare (pseudoDegree v x) (pseudoDegree v y)
compareSymbolCount = compare (symbolCount x) (symbolCount y)
compareOp = compare (expressionOrder v x) (expressionOrder v y)
-- | The integral power of the variable for a particular expression.
degree :: Text -> Expression -> Maybe Integer
degree _ (Number _) = Just 0
degree v (Symbol s) | v == s = Just 1 | otherwise = Just 0
degree v (Negate' x) = degree v x
degree v (x :+: y) = max <$> degree v x <*> degree v y
degree v (x :-: y) = max <$> degree v x <*> degree v y
degree v (x :*: y) = (+) <$> degree v x <*> degree v y
degree v (x :/: y) = (-) <$> degree v x <*> degree v y
degree v (x :**: (Number n)) = (n *) <$> degree v x
degree v (x :**: Negate' y) = degree v $ x :**: y
degree _ _ = Nothing
-- | The number of times the variable appears in an expression.
-- Used as part of a somewhat arbitrary ordering.
pseudoDegree :: Text -> Expression -> Integer
pseudoDegree _ (Number _) = 0
pseudoDegree v (Symbol s) | v == s = 1 | otherwise = 0
pseudoDegree v (Negate' x) = pseudoDegree v x
pseudoDegree v (UnaryApply _ x) = pseudoDegree v x
pseudoDegree v (BinaryApply _ x y) = pseudoDegree v x + pseudoDegree v y
symbolCount :: Expression -> Int
symbolCount x = Set.size $ collect x
where
collect (Number _) = Set.empty
collect (Symbol s) = Set.singleton s
collect (UnaryApply _ u) = collect u
collect (BinaryApply _ u v) = Set.union (collect u) (collect v)
-- | A fixed order between functions and operators.
-- Ignores the actual values the functins and operators are given.
expressionOrder :: Text -> Expression -> Int
expressionOrder _ (Number _) = 0
-- constant symbol has expressionOrder 1
expressionOrder _ (UnaryApply Negate _) = 2
expressionOrder _ (UnaryApply Signum _) = 3
expressionOrder _ (UnaryApply Abs _) = 4
expressionOrder _ (BinaryApply Add _ _) = 5
expressionOrder _ (BinaryApply Subtract _ _) = 6
expressionOrder _ (BinaryApply Multiply _ _) = 7
expressionOrder _ (BinaryApply Divide _ _) = 8
expressionOrder _ (BinaryApply Power _ _) = 9
expressionOrder _ (UnaryApply Sqrt _) = 10
expressionOrder _ (UnaryApply Exp _) = 11
expressionOrder _ (UnaryApply Log _) = 12
expressionOrder _ (BinaryApply LogBase _ _) = 13
expressionOrder _ (UnaryApply Sin _) = 14
expressionOrder _ (UnaryApply Cos _) = 15
expressionOrder _ (UnaryApply Tan _) = 16
expressionOrder _ (UnaryApply Asin _) = 17
expressionOrder _ (UnaryApply Acos _) = 18
expressionOrder _ (UnaryApply Atan _) = 19
expressionOrder _ (UnaryApply Sinh _) = 20
expressionOrder _ (UnaryApply Cosh _) = 21
expressionOrder _ (UnaryApply Tanh _) = 22
expressionOrder _ (UnaryApply Asinh _) = 23
expressionOrder _ (UnaryApply Acosh _) = 24
expressionOrder _ (UnaryApply Atanh _) = 25
expressionOrder v (Symbol s)
| v == s = 26
| otherwise = 1