Transformers

A transformer can be viewed as a function that, when called, transform an expression that it gets as an argument into another expression. It is similar to a computation graph. The reasons for transformers in Symbolica are twofold:

The delayed execution is explained in the pattern matching where the right-hand side should be transformed, but only after substitution of the wildcard.

High-performance execution in Symbolica can also be achieved even when running from Python. As is well known, Python generally does not achieve performance similar to Rust as it is not a compiled language. However, we can use transformers to build a program that is run entirely in Rust.

A simple example of a transformer is:

from symbolica import Transformer
t = Transformer().expand()
Tip

Transformer().expand() is similar to lambda x: x.expand()

A transformer can be executed by calling execute() on it. For this, the input expression must be known. One way to give input is to call transform() on an Expression:

from symbolica import Transformer
e = Expression.parse('(x+1)^2')
e.transform().expand().execute()

Some functions take an unbound transformer as an input (with no expression set). An example is the map() function uses for term streaming and the replace_all() function.

Below is an example in Python that uses the repeat transformer, which takes a list of transformers that it will repeat until there are no more changes between the input and output expression.

x_ = Expression.var('x_')
f = Expression.fun('f')
e = Expression.parse('f(100)')

e = e.transform().repeat(
    Transformer().expand(),
    Transformer().replace_all(f(x_), f(x_ - 1) + f(x_ - 2), x_.req_gt(1))
).execute()

Transformers can be chained, so the program above can also be rewritten

from symbolica import Expression, Transformer
x_ = Expression.var('x_')
f = Expression.fun('f')
e = Expression.parse('f(100)')

e = e.transform().repeat(
    Transformer().expand().replace_all(f(x_), f(x_ - 1) + f(x_ - 2), x_.req_gt(1))
).execute()