-
Notifications
You must be signed in to change notification settings - Fork 76
Compilation
We will consider ways to improve the performance of computations via compiling functions.
Here we will consider a bunch of methods, which all are overloads of one: Compile
.
This method compiles a given symbolic expression into a native delegate, which then
can be executed with a very high speed. This time, we first consider the way
you highly likely need it to be compiled.
Let us start with a simple example:
Entity expr = "sin(x)";
Func<float, float> mySin = expr.Compile<float, float>("x");
Console.WriteLine(mySin(4.2f));
Output:
-0.87157565
The variable mySin
has type Func<float, float>
. Its type parameters accurately correspond
to the type parameters of method Compile
. Let us consider a more complicated example:
Entity expr = "sqrt(sin(x) + 3)";
var f = expr.Compile<float, float>("x");
Console.WriteLine(f(4.2f));
Output:
1.458912
The number 3
, although integer, is upcasted to float
automatically. But if only you passed
an integer as type params, sin
will return an int
as well. Example:
Entity expr = "sqrt(sin(x) + 3)";
var f = expr.Compile<int, int>("x");
Console.WriteLine(f(4));
Output:
1
You can also pass multiple parameters, as well as multiple parameters of different types. Example:
Entity expr = "sin(x) + y";
var f = expr.Compile<float, double, float>("x", "y");
Console.WriteLine(f(4.3f, 7.8d));
Output:
6.883834
The supported types are: int
, long
, float
, double
, Complex
, BigInteger
. Let us consider
an example, where int
is upcasted to a Complex
:
Entity expr = "x + y";
var f = expr.Compile<Complex, int, Complex>("x", "y");
Console.WriteLine(f(new(4, 5), 5));
Output:
(9, 5)
You can pass up to 8 type parameters excluding the one for output.
Extensions include the same set of methods, but for string.
If needed to pass more than 8 parameters, or other types than the ones listed, or
custom rules for types, consider declaring your own CompilationProtocol
.
Since the way AM compiles an expression is through Linq.Expression, it requires provided rules for constants to be converted into a constant, unary/binary/n-ary nodes to be converted into calls or operators.
That is why AM has class CompilationProtocol
which defines a number of rules for every such
operation.
Its property ConstantConverter
is responsible for converting a given constant
expression of type Entity
into a Linq.Expression.
BinaryNodeConverter
takes two arguments of type Linq.Expression
and one argument of type
Entity
. Although allowed, it is not recommended to rely on the third argument's value. Instead,
we encourage to use it as a type holder and switch over it when looking for a good node. For example,
that is a fragment of code for this ruleset:
...
return typeHolder switch
{
Sumf => Expression.Add(left, right),
Minusf => Expression.Subtract(left, right),
Mulf => Expression.Multiply(left, right),
Divf => Expression.Divide(left, right),
...
Same way it is recommended to define UnaryNodeConverter
and AnyArgumentConverter
(the latter
for n-ary nodes).
Now, after you defined your own compilation protocol, you need to pass your delegate and parameters of the final function:
Entity expr = "x + y";
var f = expr.Compile<Func<float, float, float>>(
new(), typeof(float),
new[] { (typeof(float), Var("x")), (typeof(float), Var("y")) }
);
Console.WriteLine(f(4, 5));
Here we use new()
instead of an instance of CompilationProtocol
, but instead of it a custom
protocol can be passed.
It is highly recommend to compile a given expression the way it is described above.
FastExpression
is a special type. It contains instructions for every node and works as a stack
machine. When compiled into it, it translates a given expression into a number of instructions,
which are then sequentially executed.
It does not accept any arguments, as it unconditionally work with Complex
. Example:
Entity expr = "x + y";
var f = expr.Compile("x", "y");
Console.WriteLine(f.Call(4, 5));
Output:
(9, 0)
It is not as fast and flexible as Compile<>
.
If you did not find what you were looking for, feel free to create an issue raising your problem.