Functions built out of generic operations can be compiled down into fast, efficient functions that operate on doubles. The process is:
'x
in for all arguments. This will cause the function to return a "numerical expression", a syntax tree representing the function's body:g/simplify
the new function body. Sometimes this results in large simplifications:Apply "common subexpression elimination". Any subexpression inside the new, simplified body that appears more than once gets extracted out into a let-bound variable and calculated just once, then subbed in to the computation using the generated variable.
Wrap the new, efficient body up in (fn [x] ,,,)
with the proper number of arguments and pass it to eval
.
Amazing!
Function compilation is potentially expensive, so allocate a cache of function f
to compiled function for use below:
The next two forms allow us to walk an expression tree, and either:
If we were only dealing with Clojure here, we could get both of these pieces from a var. But forms like Math/pow
make this tricky, so we go with the map described below.
Armed with the above compiler optimization we can move on to the actual compilation step.
This library provides several compilation modes:
eval
in Clojure, transpiling in JavaScriptWe default to :native for performance.
Native compilation works on the JVM and in ClojureScript. Enable this mode by wrapping your call in
(binding [*mode* :native] ,,,)
compile.cljc
currently supports compilation of:
definite-integral
etc)A state function is a function that takes any number of arguments and returns a new function of a "structure", usually an up-or-down tuple or a Clojure sequence.
The compiled version of a state function like
Has a signature like
I.E., first the structure, then a vector of the original function's arguments.
The following functions compile state functions in either native or SCI mode. The primary difference is that native compilation requires us to explicitly replace all instances of symbols from compiled-fn-whitelist
above with actual functions.
SCI handles this behind its interface, and simply takes a reusable context that wraps the fn replacement mapping.
Now we come to the final interface for state function compilation. Two versions of the following function exist, one that uses the global cache defined above and one that doesn't.