Interfacing LuaTeX with Julia
Posted on December 27, 2017
One cool feature of LuaTeX is ability to interface with external libraries using Lua’s Foreign Function Interface (FFI). An extreme example of this is Luigi Scarso’s LuaTeX lunatic, which provides a two way bridge between Python and LuaTeX. Not being a Python fan, I never looked into the implementation details, in spite of Luigi’s impressive examples.
Recently, Henri Menke posted an interesting example that shows how to use a function from GNU Scientific Library (GSL) to inteface with pgfplots. This got me interested in looking into Lua’s FFI in detail. After a bit of trial and error, I finally figured out how to interface LuaTeX with my current favorite programming language: Julia.
Calling Julia from Lua
The basic idea of calling julia (or any other C library for that matter) from Lua is the following:
local ffi = require("ffi")
local JULIA = ffi.load("julia", true)
ffi.cdef [[
void jl_init(void);
typedef struct _jl_value_t jl_value_t;
jl_value_t *jl_eval_string(const char*);
]]
JULIA.jl_init()
code = [[
x = [1 2 3]'
A = [1 0 1; 0 1 1; 1 1 0]
y = x'*A*x
print(y[1])
]]
JULIA.jl_eval_string(code)
Save this as, say test-1.lua
and run:
luatex test-1.lua
You should see 23
as output.
Phew! That wasn’t difficult. A couple of points to note:
-
It took me quite some time to figure out that I need
ffi.load("julia", true)
rather thanffi.load("julia")
. Lua’s FFI API documentation says that when the second argument istrue
, the library symbols are loaded into the global namespace, too.. I don’t completely understand that statement. The only hint that this is needed is a note on Embedding Julia page that says Currently, dynamically linking with the libjulia shared library requires passing theRTLD_GLOBAL
option. Since I was not familiar with working with FFI, I could not put two and two together. -
I don’t quite understand why one needs to include the function signatures in
ffi.cdef
. It would be much simpler if I could simply askffi
to include a C header file. Otherwise, I need to hunt for all the needed function signatures fromjulia.h
file. Some of these functions are defined using#define
and I don’t know how to include them inffi.cdef
.[Edit: After browsing through some of the old threads in the Lua mailing list, the main concern is that parsing
.h
files requires the C pre-processor and writing that in pure Lua is a lot of effort. ] -
If julia has been compiled with threading support, then
jl_init
goes under the name ofjl_init__threading
(note the double underscore). So you have to changejl_init
tojl_init__threading
in the above example.
Creating a TeX inteface
Now that we know how to call Julia from Lua, the rest is just adding some scafolding to create a TeX inteface. Lua’s FFI interface allows one to call any function defined in a C library and convert return values to Lua objects (strings, numbers, or tables). As a proof of concept, I decided to only pass and receive strings. This is limited (and you need to ensure that the Julia code returns a string), but easier to implement.
To do so, I defined a Lua function julia.eval(str, flag)
, which takes a
string str
and evals it using julia. If flag
is set to true
, then it
converts the return value to a lua string and typesets it using ConTeXt.
Note that in the example below, I use ffi.load("/usr/lib/libjulia.so", true)
rather than ffi.load("julia", true)
. For some reason, the latter does not
work when compiling a file through context
(although, running context test-1.lua
on the above example runs fine).
\startluacode
local ffi = require("ffi")
local JULIA = ffi.load("/usr/lib/libjulia.so", true)
local format = string.format
ffi.cdef [[
void jl_init(void);
typedef struct _jl_value_t jl_value_t;
jl_value_t *jl_eval_string(const char*);
char *jl_string_ptr(jl_value_t *);
]]
JULIA.jl_init()
julia = julia or {}
function julia.eval(str, flag)
local jval = JULIA.jl_eval_string(str)
if flag then
local lval = ffi.string(JULIA.jl_string_ptr(jval))
context(lval)
end
end
\stopluacode
Then, at the TeX end, I define two macros: \julia
which simply evaluates its
argument using julia and \ctxjulia
which evaluates its argument using julia
and typesets the result.
\define[1]\ctxjulia{\ctxlua{julia.eval([===[#1]===], true) }}
\define[1]\julia {\ctxlua{julia.eval([===[#1]===], false)}}
Here is a simple usage example.
\starttext
\julia{a = 10}
\julia{b = 5}
\ctxjulia{string(a*b)}
\stoptext
which, as expected, prints 50. Sure, for something as simple as this, I could have simply used Lua as well. In a future post, I’ll show how to create a nicer user-interface and do show some more interesting examples.
This entry was posted in FFI and tagged julia.