# Function Building and Compilation (build_function)

At any time, callable functions can be generated from Symbolics IR by using `Symbolics.toexpr`

. This performs some cleaning to return an expression without extraneous pieces that commonly matches expressions one would write in functions like those for differential equation solvers and optimization libraries. These functions can be automatically parallelized and specialize on Julia types like static arrays and sparse matrices.

The core compilation process of Symbolics IR is `build_function`

. `build_function`

takes an operation or an `AbstractArray`

of operations and generates a compilable version of the model for numerical solvers. The form of this output is dependent on the `target`

. By default, the target outputs Julia code, but other formats, such as C, Stan, and MATLAB are available. These can be generated as expressions which can then be evaluated into a callable function, or the compilers for the respective targets can be invoked to directly give back the function handle.

## build_function

`Symbolics.build_function`

— Function`build_function`

Generates a numerically-usable function from a Symbolics `Num`

.

```
build_function(ex, args...;
expression = Val{true},
target = JuliaTarget(),
parallel=nothing,
kwargs...)
```

Arguments:

`ex`

: The`Num`

to compile`args`

: The arguments of the function`expression`

: Whether to generate code or whether to generate the compiled form. By default,`expression = Val{true}`

, which means that the code for the function is returned. If`Val{false}`

, then the returned value is compiled.

Keyword Arguments:

`target`

: The output target of the compilation process. Possible options are:`JuliaTarget`

: Generates a Julia function`CTarget`

: Generates a C function`StanTarget`

: Generates a function for compiling with the Stan probabilistic programming language`MATLABTarget`

: Generates an anonymous function for use in MATLAB and Octave environments

`parallel`

: The kind of parallelism to use in the generated function. Defaults to`SerialForm()`

, i.e. no parallelism, if`ex`

is a single expression or an array containing <= 1500 non-zero expressions. If`ex`

is an array of > 1500 non-zero expressions, then`ShardedForm(80, 4)`

is used. See below for more on`ShardedForm`

. Note that the parallel forms are not exported and thus need to be chosen like`Symbolics.SerialForm()`

. The choices are:`SerialForm()`

: Serial execution.`ShardedForm(cutoff, ncalls)`

: splits the output function into sub-functions which contain at most`cutoff`

number of output`rhss`

. These sub-functions are called by the top-level function that*build*function returns. This helps in reducing the compile time of the generated function.`MultithreadedForm()`

: Multithreaded execution with a static split, evenly splitting the number of expressions per thread.

`fname`

: Used by some targets for the name of the function in the target space.

Note that not all build targets support the full compilation interface. Check the individual target documentation for details.

## Target-Specific Definitions

`Symbolics._build_function`

— Method```
_build_function(target::JuliaTarget, rhss::AbstractArray, args...;
conv=toexpr,
expression = Val{true},
expression_module = @__MODULE__(),
checkbounds = false,
postprocess_fbody=ex -> ex,
linenumbers = false,
outputidxs=nothing,
skipzeros = false,
force_SA = false,
wrap_code = (nothing, nothing),
fillzeros = skipzeros && !(rhss isa SparseMatrixCSC),
states = LazyState(),
iip_config = (true, true),
parallel=nothing, cse = false, kwargs...)
```

Build function target: `JuliaTarget`

```
function _build_function(target::JuliaTarget, rhss, args...;
conv = toexpr,
expression = Val{true},
checkbounds = false,
linenumbers = false,
headerfun = addheader, outputidxs=nothing,
convert_oop = true, force_SA = false,
skipzeros = outputidxs===nothing,
fillzeros = skipzeros && !(typeof(rhss)<:SparseMatrixCSC),
parallel=SerialForm(), kwargs...)
```

Generates a Julia function which can then be utilized for further evaluations. If expression=Val{false}, the return is a Julia function which utilizes RuntimeGeneratedFunctions.jl to be free of world-age issues.

If the `rhss`

is a scalar, the generated function is a function with a scalar output. Otherwise, if it's an `AbstractArray`

, the output is two functions, one for out-of-place AbstractArray output and a second which is a mutating function. The outputted functions match the given argument order, i.e., f(u,p,args...) for the out-of-place and scalar functions and `f!(du,u,p,args..)`

for the in-place version.

Special Keyword Arguments:

`parallel`

: The kind of parallelism to use in the generated function. Defaults to`SerialForm()`

, i.e. no parallelism. Note that the parallel forms are not exported and thus need to be chosen like`Symbolics.SerialForm()`

. The choices are:`SerialForm()`

: Serial execution.`ShardedForm(cutoff, ncalls)`

: splits the output function into sub-functions which contain at most`cutoff`

number of output`rhss`

. These sub-functions are called by the top-level function that*build*function returns.`MultithreadedForm()`

: Multithreaded execution with a static split, evenly splitting the number of expressions per thread.

`conv`

: The conversion function of symbolic types to Expr. By default, this uses the`toexpr`

function.`checkbounds`

: For whether to enable bounds checking inside the generated function. Defaults to false, meaning that`@inbounds`

is applied.`linenumbers`

: Determines whether the generated function expression retains the line numbers. Defaults to true.`convert_oop`

: Determines whether the OOP version should try to convert the output to match the type of the first input. This is useful for cases like LabelledArrays or other array types that carry extra information. Defaults to true.`force_SA`

: Forces the output of the OOP version to be a StaticArray. Defaults to`false`

, and outputs a static array when the first argument is a static array.`skipzeros`

: Whether to skip filling zeros in the in-place version if the filling function is 0.`fillzeros`

: Whether to perform`fill(out,0)`

before the calculations to ensure safety with`skipzeros`

.

`Symbolics._build_function`

— MethodBuild function target: `CTarget`

```
function _build_function(target::CTarget, eqs::Array{<:Equation}, args...;
conv = toexpr, expression = Val{true},
fname = :diffeqf,
lhsname=:du,rhsnames=[Symbol("RHS$i") for i in 1:length(args)],
libpath=tempname(),compiler=:gcc)
```

This builds an in-place C function. Only works on arrays of equations. If `expression == Val{false}`

, then this builds a function in C, compiles it, and returns a lambda to that compiled function. These special keyword arguments control the compilation:

- libpath: the path to store the binary. Defaults to a temporary path.
- compiler: which C compiler to use. Defaults to :gcc, which is currently the only available option.

`Symbolics._build_function`

— MethodBuild function target: `StanTarget`

```
function _build_function(target::StanTarget, eqs::Array{<:Equation}, vs, ps, iv;
conv = toexpr, expression = Val{true},
fname = :diffeqf, lhsname=:internal_var___du,
rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])
```

This builds an in-place Stan function compatible with the Stan differential equation solvers. Unlike other build targets, this one requires (vs, ps, iv) as the function arguments. Only allowed on arrays of equations.

`Symbolics._build_function`

— MethodBuild function target: `MATLABTarget`

```
function _build_function(target::MATLABTarget, eqs::Array{<:Equation}, args...;
conv = toexpr, expression = Val{true},
lhsname=:internal_var___du,
rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])
```

This builds an out of place anonymous function @(t,rhsnames[1]) to be used in MATLAB. Compatible with the MATLAB differential equation solvers. Only allowed on expressions, and arrays of expressions.

## Limitations

`build_function`