Translating Julia functions into C code
In this example, we will demonstrate one technique that allows you to generate C code from a function created in Julia.
Task description
Let's assume that we have code on Julia that we want to migrate to another platform for which it is inconvenient for us to compile code on the platform. For example, on a microcontroller. At the same time, it would be good to keep the ability to run the same code in the model loop inside Engee. What should I do?
Let's use the polymorphism mechanism. You can feed numbers into your code, and then it will execute and calculate a numerical result for you. You can submit matrices, then the result will be a matrix. Let's input symbolic variables and see what happens.
Pkg.add( "Symbolics" )
Code generation from a simple function
Consider generating code from a very simple function that returns us a single number.:
simple_fcn(x1, x2) = 2 .* x1 + sin( x2 )
simple_fcn(1, 2)
Let's declare, at the global level, two variables x1 and x2, with which symbolic operations can be performed.
using Symbolics
@variables x_1 x_2
If we substitute symbolic variables as arguments to our simple function, we won't see anything unusual (except that we'll see the equation embedded in the function):
simple_fcn( x_1, x_2 )
But we will get a well-structured object from which we can generate code.:
c_model_code = build_function( simple_fcn( x_1, x_2 ), [x_1, x_2]; target=Symbolics.CTarget(), fname="SIMPLE_FCN", lhsname=:c, rhsnames=[:x] )
println( c_model_code )
We have received a code optimized for a specific "target". CTarget.
This code may require various improvements. For example, converting to type float or changing function names (exp on expf) to meet the requirements of the standard used in your platform. Or adding a function main.
Then this code can be saved to a file. simple_function.c, compile with the command gcc -o out simple_function.c -lm.
c_code_standalone = """
#include <stdio.h>
$c_model_code
int main(int argc, char const *argv[]){
double out[1];
double in[] = {1, 2};
SIMPLE_FCN( out, in );
printf( "%f\\n", out[0] );
return 0;
}""";
# Сохраняем в файл доработанный код
open("$(@__DIR__)/simple_function.c", "w") do f
println( f, "$c_code_standalone" )
end
Compiling the resulting code:
;gcc -o out simple_function.c -lm
And check that the resulting binary file outputs the same value as the original function.:
;./out
simple_fcn(1, 2)
The result is the same as what we calculated at the beginning of the example.
Conclusion
We generated the code for a very simple function on Julia, compiled and executed it in Engee, or we could have put it in a block. C Function to simplify semi-natural tests.
It is worth noting that not every code is easily translated into C code by this method, often the translation procedure will need to be significantly changed so that it allows the code to accept matrices as input. But this way it is possible to generate a fairly large subclass of algorithms that are useful in a semi-natural simulation scenario.