Code generation

Note: this feature is experimental and the API might change frequently

toexpr(ex) turns any expression (ex) into the equivalent Expr which is suitable for eval. The SymbolicUtils.Code module provides some combinators which provides the ability to construct more complex expressions than just function calls. These include:

Do using SymbolicUtils.Code to get the following bindings

toexpr

toexpr

toexpr(ex, [st,])

Convert a symbolic expression into an Expr, suitable to be passed into eval.

For example,

julia> @syms a b
(a, b)

julia> toexpr(a+b)
:((+)(a, b))

julia> toexpr(a+b) |> dump
Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: + (function of type typeof(+))
    2: Symbol a
    3: Symbol b

Note that the function is an actual function object.

For more complex expressions, see other code-related combinators,

Namely Assignment, Let, Func, SetArray, MakeArray, MakeSparseArray and MakeTuple.

To make your own type convertible to Expr using toexpr define toexpr(x, st) and forward the state st in internal calls to toexpr. st is state used to know when to leave something like y(t) as it is or when to make it var"y(t)". E.g. when y(t) is itself the argument of a function rather than y.

Code Combinators

These are all exported when you do using SymbolicUtils.Code

Assignment

Assignment(lhs, rhs)

An assignment expression. Shorthand lhs ← rhs (\leftarrow)

Let

Let(assignments, body)

A Let block.

  • assignments is a vector of Assignments

  • body is the body of the let block

Func

Func(args, kwargs, body)

A function.

  • args is a vector of expressions

  • kwargs is a vector of Assignments

  • body is the body of the function

Special features in args:

  • args can contain DestructuredArgs

  • call expressions

For example,

julia> @syms a b c t f(d) x(t) y(t) z(t)
(a, b, c, t, f(::Number)::Number, x(::Number)::Number, y(::Number)::Number, z(::Number)::Number)

julia> func = Func([a,x(t), DestructuredArgs([b, y(t)]), f], # args
                   [c ← 2, z(t) ← 42], # kwargs
                   f((a + b + c) / x(t) + y(t) + z(t)));

julia> toexpr(func)
:(function (a, var"x(t)", var"##arg#255", f; c = 2, var"z(t)" = 42)
      let b = var"##arg#255"[1], var"y(t)" = var"##arg#255"[2]
          f((+)(var"y(t)", var"z(t)", (*)((+)(a, b, c), (inv)(var"x(t)"))))
      end
  end)
  • the second argument is a DestructuredArgs, in the Expr form, it is given a random name, and is expected to receive a vector or tuple of size 2 containing the values of c and y(t). The let block that is automatically generated "destructures" these arguments.

  • x(t) and y(t) have been replaced with var"x(t)" and var"y(t)" symbols throughout

the generated Expr. This makes sure that we are not actually calling the expressions x(t) or y(t) but instead passing the right values in place of the whole expression.

  • f is also a function-like symbol, same as x and y, but since the args array contains f as itself rather than as say, f(t), it does not become a var"f(t)". The generated function expects a function of one argument to be passed in the position of f.

An example invocation of this function is:

julia> executable = eval(toexpr(func))
#10 (generic function with 1 method)

julia> exec(1, 2.0, [2,3.0], x->string(x); var"z(t)" = sqrt(42))
"11.98074069840786"

SpawnFetch

SpawnFetch{ParallelType}(funcs [, args], reduce)

Run every expression in funcs in its own task, the expression should be a Func object and is passed to Threads.Task(f). If Func takes arguments, then the arguments must be passed in as args–a vector of vector of arguments to each function in funcs. We don't use @spawn in order to support RuntimeGeneratedFunctions which disallow closures, instead we interpolate these functions or closures as smaller RuntimeGeneratedFunctions.

reduce function is used to combine the results of executing exprs. A SpawnFetch expression returns the reduced result.

Use Symbolics.MultithreadedForm ParallelType from the Symbolics.jl package to get the RuntimeGeneratedFunction version SpawnFetch.

ParallelType can be used to define more parallelism types SymbolicUtils supports Multithreaded type. Which spawns threaded tasks.

SetArray

SetArray(inbounds, arr, elems)

An expression representing setting of elements of arr.

By default, every element of elems is copied over to arr,

but if elems contains AtIndex(i, val) objects, then arr[i] = val is performed in its place.

inbounds is a boolean flag, true surrounds the resulting expression in an @inbounds.

MakeArray

MakeArray(elems, similarto, [output_eltype=nothing])

An expression which constructs an array.

  • elems is the output array

  • similarto can either be a type, or some symbol that is an array whose type needs to be emulated. If similarto is a StaticArrays.SArray, then the output array is also created as an SArray, similarly, an Array will result in an Array, and a LabelledArrays.SLArray will result in a labelled static array.

  • output_eltype: if set, then forces the element type of the output array to be this. by default, the output type is inferred automatically.

You can define:

@inline function create_array(A::Type{<:MyArray},a
                              ::Nothing, d::Val{dims}, elems...) where dims

# and

@inlline function create_array(::Type{<:MyArray}, T, ::Val{dims}, elems...) where dims

which creates an array of size dims using the elements elems and eltype T, to allow MakeArray to create arrays similarto MyArrays.

MakeSparseArray

MakeSpaseArray(array)

An expression which creates a SparseMatrixCSC or a SparseVector.

The generated expression contains the sparsity information of array,

it only creates the nzval field at run time.

MakeTuple

MakeTuple(tup)

Make a Tuple from a tuple of expressions.

LiteralExpr

LiteralExpr(ex)

Literally ex, an Expr. toexpr on LiteralExpr recursively calls toexpr on any interpolated symbolic expressions.