Copyright | Copyright 2024 Yoo Chung |
---|---|
License | Apache-2.0 |
Maintainer | dev@chungyc.org |
Safe Haskell | None |
Language | GHC2021 |
Symtegration is a library for symbolic integration of mathematical expressions. For normal use, this is the only module which needs to be loaded. Other modules are used for finer control over what happens, or for supporting the work that yet other modules do.
For example, with \(\int (4x^3 + 1) \, dx = x^4 + x\):
>>>
import Symtegration
>>>
toHaskell <$> integrate "x" (4 * "x" ** 3 + 1)
Just "x + x ** 4"
For another example, with \(\int (xz+y) \, dz = \frac{xz^2}{2} + yz\):
>>>
import Symtegration
>>>
toHaskell <$> integrate "z" ("x" * "z" + "y")
Just "y * z + 1 / 2 * x * z ** 2"
Synopsis
- data Expression
- integrate :: Text -> Expression -> Maybe Expression
- differentiate :: Text -> Expression -> Expression
- evaluate :: Floating a => Expression -> (Text -> Maybe a) -> Maybe a
- fractionalEvaluate :: (Eq a, Fractional a) => Expression -> (Text -> Maybe a) -> Maybe a
- toFunction :: Floating b => Expression -> (Text -> a -> b) -> a -> b
- toHaskell :: Expression -> Text
- toLaTeX :: Expression -> Text
- simplify :: Expression -> Expression
- tidy :: Expression -> Expression
Symbolic representation
data Expression Source #
Symbolic representation of a mathematical expression.
It is an instance of the Num
, Fractional
, and Floating
type classes,
so normal Haskell expressions can be used, although the expressions
are limited to using the functions defined by these type classses.
The type is also an instance of the IsString
type class,
so symbols can be expressed as Haskell string with the OverloadedStrings
extension.
The structure of these values is intended to be visible.
>>>
2 :: Expression
Number 2>>>
"x" :: Expression
Symbol "x">>>
2 + sin "x" :: Expression
BinaryApply Add (Number 2) (UnaryApply Sin (Symbol "x"))
A somewhat more concise representation can be obtained using toHaskell
:
>>>
toHaskell $ 2 * "y" + sin "x"
"2 * y + sin x"
Instances
Integration
:: Text | The symbol representing the variable being integrated over. |
-> Expression | The mathematical expression being integrated. |
-> Maybe Expression | The indefinite integral, if derived. |
Returns the indefinite integral of a mathematical expression given
its symbolic representation. It will return Nothing
if it is
unable to derive an integral. The indefinite integral will be
simplified to a certain extent.
For example, with \(\int (4x^3 + 1) \, dx = x^4 + x\) where all the coefficients are numbers:
>>>
toHaskell <$> integrate "x" (4 * "x" ** 3 + 1)
Just "x + x ** 4"
It can also return indefinite integrals when the coefficients are symbolic, as with \(\int (xz+y) \, dz = \frac{xz^2}{2} + yz\):
>>>
toHaskell <$> integrate "z" ("x" * "z" + "y")
Just "y * z + 1 / 2 * x * z ** 2"
Differentiation
:: Text | Symbol representing the variable. |
-> Expression | Symbolic representation of the mathematical expression to differentiate. |
-> Expression | The derivative. |
Differentiates a mathematical expression.
>>>
toHaskell $ differentiate "x" $ "x" ** 2
"2 * x">>>
toHaskell $ differentiate "x" $ "a" * sin "x"
"a * cos x"
This uses Numeric.AD.
Computation
:: Floating a | |
=> Expression | Mathematical expression to evaluate. |
-> (Text -> Maybe a) | Maps symbols to concrete values. |
-> Maybe a | Evaluation result. |
Calculates the value for a mathematical expression for a given assignment of values to symbols.
For example, when \(x=5\), then \(2x+1=11\).
>>>
evaluate (2 * "x" + 1) (\case "x" -> Just 5)
Just 11.0
All symbols except for "pi"
in a mathematical expression must be assigned a value.
Otherwise, a value cannot be computed.
>>>
evaluate (2 * "x" + 1) (const Nothing)
Nothing
The symbol "pi"
is always used to represent \(\pi\),
and any assignment to "pi"
will be ignored.
For example, the following is \(\pi - \pi\), not \(100 - \pi\).
>>>
evaluate ("pi" - pi) (\case "x" -> Just 100)
Just 0.0
:: (Eq a, Fractional a) | |
=> Expression | Mathematical expression to evaluate. |
-> (Text -> Maybe a) | Maps symbols to concrete values. |
-> Maybe a | Evaluation result. |
Evaluates a mathematical expression with only operations available to Fractional
values.
In particular, this allows exact evaluations with Rational
values.
Nothing
will be returned if a function not supported by all Fractional
values
is used by the mathematical expression.
As an exception, the (**)
operator is allowed with constant integer exponents,
even though (**)
is not a function applicable to all Fractional
types.
For example,
>>>
let p = 1 / (3 * "x"**5 - 2 * "x" + 1) :: Expression
>>>
fractionalEvaluate p (\case "x" -> Just (2 / 7 :: Rational))
Just (16807 % 7299)
Compare against evaluate
, which cannot even use Rational
computations
because Rational
is not an instance of the Floating
type class:
>>>
evaluate p (\case "x" -> Just (2 / 7 :: Double))
Just 2.3026441978353196
:: Floating b | |
=> Expression | The expression to be converted into a function. |
-> (Text -> a -> b) | Maps how the argument to the function should be mapped to a value for a symbol. E.g., "x" could map the first element in a tuple as the value to use in its place. |
-> a | The function generated from the expression. |
-> b |
Returns a function based on a given expression. This requires a specification of how a symbol maps the argument to a value to be used in its place.
For example, the symbol "x" could use the argument as is as its value. I.e., "x" can be mapped to a function which maps the argument to itself.
>>>
let f = toFunction ("x" ** 2 + 1) (\case "x" -> id) :: Double -> Double
>>>
f 3 -- 3 ** 2 + 1
10.0>>>
f 10 -- 10 ** 2 + 1
101.0
For another example, "x" could map the first element from a tuple argument, and "y" could map the second element from the tuple argument. I.e., for a tuple argument to the function, the first element will be used as "x" and the second element will be used as "y".
>>>
let m = \case "x" -> (\(x,_) -> x); "y" -> (\(_,y) -> y)
>>>
let g = toFunction ("x" + 2 * "y") m :: (Double, Double) -> Double
>>>
g (3,4) -- 3 + 2 * 4
11.0>>>
g (7,1) -- 7 + 2 * 1
9.0
Conversion
toHaskell :: Expression -> Text Source #
Converts an Expression
into an equivalent Haskell expression.
>>>
toHaskell $ BinaryApply Add (Number 1) (Number 3)
"1 + 3">>>
toHaskell $ 1 + 3
"1 + 3"
Symbols are included without quotation.
>>>
toHaskell $ ("x" + "y") * 4
"(x + y) * 4"
toLaTeX :: Expression -> Text Source #
Converts an Expression
into an equivalent LaTeX expression.
>>>
toLaTeX $ exp 5
"e^{5}"
Symbols are included without quotation.
>>>
toLaTeX $ exp "x"
"e^{x}">>>
toLaTeX $ "x" + 4 * sin "y"
"x + 4 \\sin y"
Since the text for symbols are included as is, we can also include LaTeX symbols:
>>>
toLaTeX $ exp "\\delta_0"
"e^{\\delta_0}"
Simplification
When using only this module, explicitly simplifying mathematical expressions should usually not be necessary, since the exported functions automatically simplify results as appropriate. One may want to explicitly simplify mathematical expressions when used with other packages, however, such as when using Numeric.AD directly for differentiation.
simplify :: Expression -> Expression Source #
Simplifies symbolic representations of mathematical expressions.
All addition and multiplication will be associated to the left. The simplification is done with an eye towards making it easier to find common factors.
>>>
toHaskell $ simplify $ 4 - "x" + "a" * "x" ** 3 + 2 * "x" - 3
"1 + x + a * x ** 3"
tidy :: Expression -> Expression Source #
Tidies up expressions for nicer output.
Assumes that other simplifications have been applied first. In fact, it may undo changes that made other simplifications easier.
What is tidied up
This section shows examples of what this function tidies up.
>>>
toHaskell $ tidy $ "x" + negate "y"
"x - y"
>>>
toHaskell $ tidy $ "x" + Number (-2) * "y"
"x - 2 * y"
>>>
toHaskell $ tidy $ Number (-1) / Number 2
"negate (1 / 2)"
>>>
toHaskell $ tidy $ Number (-1) * "x"
"negate x"
>>>
toHaskell $ tidy $ (-"x") * "y"
"negate (x * y)"
>>>
toHaskell $ tidy $ "x" * (-"y")
"negate (x * y)"
>>>
toHaskell $ tidy $ (-"x") * (-"y")
"x * y"
>>>
toHaskell $ tidy $ "x" + ((-"y") + "z")
"x - y + z"