You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: page/representation.md
+14-11Lines changed: 14 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,25 +1,28 @@
1
1
## Term representation and simplification
2
2
3
-
### Preliminary representation
3
+
Performance of symbolic simplification depends on the datastructures used to represent them. Efficient datastructures often have the advantage of automatic simplification, and of efficient storage.
4
4
5
-
An expression is stored as a tree where the nodes store their children in some efficient data structure. A node `n` is a non-leaf node if `istree(n)` is true. For such nodes, `operation` and `arguments` must be defined. These return the operation and arguments of the term represented by the subtree `n`.
5
+
The most basic term representation simply holds a function call and stores the function and the arguments it is called with. This is done by the `Term` type in Symbolics. Functions that aren't commutative or associative, such as `sin` or `hypot` are stored as `Term`s. Commutatative and associative operations like `+` (and `-`), `*`, `/` and `^`, when applied to terms of type `<:Number`, stand to gain from the use of more efficient datastrucutres.
6
6
7
-
A generic term is represented with the `Term` type. It simply holds the function `f` being called and a vector of arguments in the order they were passed to `f`. The operators `+` (and `-`), `*`, `/` and `^` create terms with special storage.
7
+
All term representations must support `operation` and `arguments` functions. And define `istree` to return `true` on their type. Generic term-manipulation programs such as the rule-based rewriter make use of this interface to inspect expressions. In this way, the interface wins back the generality lost by having a zoo of term representations.
8
8
9
-
Linear combinations such as $\alpha_1 x_1 + \alpha_2 x_2 +...+ \alpha_n x_n$
10
-
are represented by `Add(Dict(x₁ => α₁, x₂ => α₂, ..., xₙ => αₙ))`. Now $x_n$ may themselves be other types mentioned in this section, but may not be an `Add`. When an `Add` is added to an `Add`, we merge their dictionaries and add up matching coefficients to create a single Add.
9
+
10
+
### Representation of arithmetic
11
+
12
+
Linear combinations such as $\alpha_1 x_1 + \alpha_2 x_2 +...+ \alpha_n x_n$ are represented by `Add(Dict(x₁ => α₁, x₂ => α₂, ..., xₙ => αₙ))`. Now $x_n$ may themselves be other types mentioned here, except for `Add`. When an `Add` is added to an `Add`, we merge their dictionaries and add up matching coefficients to create a single Add.
11
13
12
14
Similarly, $x_1^{m_1}x_2^{m_2}...x_{m_n}$ is represented by
13
15
`Mul(Dict(x₁ => m₁, x₂ => m₂, ..., xₙ => mₙ))`. $x_i$ may not themselves be `Mul`, multiplying a Mul with another Mul returns a flattened Mul.
14
16
15
17
$p / q$ is represented by `Div(p, q)`. The result of `*` on `Div` is maintainted as a `Div`. For example, `Div(p_1, q_1) * Div(p_2, q_2)` results in `Div(p_1 * p_2, q_1 * q_2)` and so on. The effect is, in `Div(p, q)`, `p` or `q` or, if they are Mul, any of their multiplicands is not a Div. So `Mul`s must always be nested inside a `Div` and can never show up immediately wrapping it.
16
18
19
+
Note that this storage performs a preliminary simplification which suffices to simplify numeric expressions to a large extent already during construction.
17
20
18
21
### Polynomial representation
19
22
20
-
Packages like DynamicPolynomials.jl provide even more efficient representations for polynomials. They also have efficient operations such as multi-variate polynomial GCD. However, DynamicPolynomials can only represent flat polynomials. For example, `(x-3)*(x+5)` can only be represented as `(x^2) + 15 - 8x`. Moreover, DynamicPolynomials does not have ways to represent generic Terms such as `sin(x-y)` in the tree.
23
+
Packages like DynamicPolynomials.jl provide representations that are even more efficient than the `Add` and `Mul` types mentioned above, and are designed specifically for multi-variate polynomials. They also provide common efficient algorithms such as multi-variate polynomial GCD. However, DynamicPolynomials can only represent flat polynomials. For example, `(x-3)*(x+5)` can only be represented as `(x^2) + 15 - 8x`. Moreover, DynamicPolynomials does not have ways to represent generic Terms such as `sin(x-y)` in the tree.
21
24
22
-
To reconcile these differences while being able to use the efficient representation of DynamicPolynomials we have the `PolyForm` type. This type holds a polynomial and the mappings necessary to present them as a SymbolicUtils expression. The mappings constructed for the conversion are 1) a bijection from DynamicPolynomials PolyVar type to a Symbolics `Sym`, and 2) a mapping from `Sym`s to non-polynomial terms that the `Sym`s stand-in for. These terms may themselves contain PolyForm if there are polynomials inside them. The mappings are transiently global, that is, when all references to the mappings go out of scope, they are released and re-created.
25
+
To reconcile these differences while being able to use the efficient representation of DynamicPolynomials we have the `PolyForm` type. This type holds a polynomial and the mappings necessary to present the polynomial as a SymbolicUtils expression (i.e. by defining `operation` and `arguments`). The mappings constructed for the conversion are 1) a bijection from DynamicPolynomials PolyVar type to a Symbolics `Sym`, and 2) a mapping from `Sym`s to non-polynomial terms that the `Sym`s stand-in for. These terms may themselves contain PolyForm if there are polynomials inside them. The mappings are transiently global, that is, when all references to the mappings go out of scope, they are released and re-created.
23
26
24
27
```julia
25
28
julia>@syms x y
@@ -29,7 +32,7 @@ julia> PolyForm((x-3)*(y-5))
29
32
x*y +15-5x -3y
30
33
```
31
34
32
-
Further, generic terms such as `sin`or `cos` terms are replaced with a symbol when representing the polynomial.
35
+
Terms for which the `operation` is not `+`, `*`, or `^`are replaced with a generated symbol when representing the polynomial, and a mapping from this new symbol to the original expression it stands-in for is maintained as stated above.
33
36
34
37
```julia
35
38
julia> p =PolyForm((sin(x) +cos(x))^2)
@@ -39,20 +42,20 @@ julia> p.p # this is the actual DynamicPolynomial stored
By default, polynomials inside generic terms are not also converted to PolyForm. For example,
45
+
By default, polynomials inside non-polynomial terms are not also converted to PolyForm. For example,
43
46
44
47
```julia
45
48
julia>PolyForm(sin((x-3)*(y-5)))
46
49
sin((x -3)*(y -5))
47
50
```
48
-
But you can pass in the `recurse=true` keyword argument to get this going.
51
+
But you can pass in the `recurse=true` keyword argument to do this.
49
52
50
53
```julia
51
54
julia>PolyForm(sin((x-3)*(y-5)), recurse=true)
52
55
sin(x*y +15-5x -3y)
53
56
```
54
57
55
-
Polynomials are constructed by first turning symbols and generic terms into Polynomial variables and then applying the `+`, `*`, `^` operations on these variables. You can control which of these operations are actually constructed with the `Fs` keyword argument. It is a Union type of the functions to apply. For example, let's say you want to turn terms into polynomials by only applying the `+` and `^` operations, and want to preserve `*` operations as-is, you could pass in `Fs=Union{typeof(+), typeof(^)}`
58
+
Polynomials are constructed by first turning symbols and non-polynomial terms into DynamicPolynomials-style variables and then applying the `+`, `*`, `^` operations on these variables. You can control the list of the polynomial operations with the `Fs` keyword argument. It is a `Union` type of the functions to apply. For example, let's say you want to turn terms into polynomials by only applying the `+` and `^` operations, and want to preserve `*` operations as-is, you could pass in `Fs=Union{typeof(+), typeof(^)}`
0 commit comments