# \[ANN\] JuliaFromMATLAB.jl - Call Julia from MATLAB

**URL:** <https://discourse.julialang.org/t/ann-juliafrommatlab-jl-call-julia-from-matlab/66882>\
**Category:** Package Announcements\
**Tags:** interoperability, matlab\
**Created:** [August 23, 2021, 11:57pm UTC](https://discourse.julialang.org/t/ann-juliafrommatlab-jl-call-julia-from-matlab/66882 "2021-08-23T23:57:04Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![jondeuce](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jondeuce/32/16378_2.png) [@jondeuce](https://discourse.julialang.org/u/jondeuce)\
**Post date:** [August 23, 2021, 11:57pm UTC](https://discourse.julialang.org/t/ann-juliafrommatlab-jl-call-julia-from-matlab/66882/1 "2021-08-23T23:57:05Z")

</div>

Inspired by this [recent discussion](https://discourse.julialang.org/t/calling-julia-from-matlab-the-smoothest-path-to-sneak-julia-into-a-company/64845) regarding the state of calling Julia from MATLAB, I have put together a package to try to resolve some of the issues faced by previous approaches: [`JuliaFromMATLAB.jl`](https://github.com/jondeuce/JuliaFromMATLAB.jl). Please scroll down to see the Quickstart guide, pasted here from the package’s README for convenience.

In particular, I have chosen to avoid trying to keep up with the MATLAB Mex C/C++ interface. Instead, communication between MATLAB and Julia is performed by writing to temporary `.mat` files. This decision was made with the hope that it will allow `JuliaFromMATLAB.jl` to “just work”, if at the cost of some performance.

To that end, I would like to thank @dmolina for their work in developing [`DaemonMode.jl`](https://github.com/dmolina/DaemonMode.jl), used extensively in this package. With `DaemonMode.jl`, users of `JuliaFromMATLAB.jl` have the option to keep persistent Julia instances active in the background, eliminating the need to re-compile included Julia code on every call from MATLAB, drastically reducing overhead.

Lastly, this package is not yet registered, mostly because I am not fully committed to the name `JuliaFromMATLAB.jl` - suggestions welcome!

## Quickstart

Use the MATLAB function [`jlcall.m`](https://github.com/jondeuce/JuliaFromMATLAB.jl/blob/master/api/jlcall.m) to call Julia from MATLAB:

```matlab
>> jlcall('sort', {rand(2,5)}, struct('dims', int64(2)))

ans =

    0.1270 0.2785 0.6324 0.8147 0.9575
    0.0975 0.5469 0.9058 0.9134 0.9649

```

The positional arguments passed to [`jlcall.m`](https://github.com/jondeuce/JuliaFromMATLAB.jl/blob/master/api/jlcall.m) are:

1. The Julia function to call, given as a MATLAB `char` array. This can be any Julia expression which evaluates to a function. For example, `'a=2; b=3; x -> a*x+b'`. **Note:** this expression is wrapped in a `let` block and evaluated in the global scope
2. Positional arguments, given as a MATLAB `cell` array. For example, `args = {arg1, arg2, ...}`
3. Keyword arguments, given as a MATLAB `struct`. For example, `kwargs = struct('key1', value1, 'key2', value2, ...)`

The first time [`jlcall.m`](https://github.com/jondeuce/JuliaFromMATLAB.jl/blob/master/api/jlcall.m) is invoked:

1. `JuliaFromMATLAB.jl` will be installed into a local Julia project, if one does not already exist. By default, a folder `.jlcall` is created in the same folder as [`jlcall.m`](https://github.com/jondeuce/JuliaFromMATLAB.jl/blob/master/api/jlcall.m)
2. A Julia server will be started in the background using [`DaemonMode.jl`](https://github.com/dmolina/DaemonMode.jl)

All subsequent calls to Julia are run on the Julia server. The server will be automatically killed when MATLAB exits.

### Restarting the Julia server

In the event that the Julia server reaches an undesired state, the server can be restarted by passing the `'restart'` flag with value `true`:

```matlab
>> jlcall('x -> sum(abs2, x)', {1:5}, 'restart', true)

ans =

    55

```

### Julia multithreading

The Julia server can be started with multiple threads by passing the `'threads'` flag:

```matlab
>> jlcall('() -> Base.Threads.nthreads()', 'threads', 8, 'restart', true)

ans =

  int64

   8

```

The default value for `'threads'` is given by the output of the MATLAB function `maxNumCompThreads`.

**Note:** Julia cannot change the number of threads at runtime. In order for the `'threads'` flag to take effect, the server must be restarted.

### Loading modules

Julia modules can be loaded and used:

```matlab
>> jlcall('LinearAlgebra.norm', {[3.0; 4.0]}, 'modules', {'LinearAlgebra'})

ans =

     5

```

**Note:** modules are loaded using `import`, not `using`. Module symbols must therefore be fully qualified, e.g. `LinearAlgebra.norm` in the above example as opposed to `norm`.

### Persistent environments

By default, previously loaded Julia code is available on subsequent calls to [`jlcall.m`](https://github.com/jondeuce/JuliaFromMATLAB.jl/blob/master/api/jlcall.m). For example, following the [above call](https://github.com/jondeuce/JuliaFromMATLAB.jl#loading-modules) to `LinearAlgebra.norm`, the `LinearAlgebra.det` function can be called without loading `LinearAlgebra` again:

```matlab
>> jlcall('LinearAlgebra.det', {[1.0 2.0; 3.0 4.0]})

ans =

    -2

```

### Unique environments

Set the `'shared'` flag to `false` in order to evaluate each Julia call in a separate namespace on the Julia server:

```matlab
% Restart the server, setting 'shared' to false
>> jlcall('LinearAlgebra.norm', {[3.0; 4.0]}, 'modules', {'LinearAlgebra'}, 'restart', true, 'shared', false)

ans =

     5

% This call now errors, despite the above command loading the LinearAlgebra module, as LinearAlgebra.norm is evaluated in a new namespace
>> jlcall('LinearAlgebra.norm', {[3.0; 4.0]}, 'shared', false)
ERROR: LoadError: UndefVarError: LinearAlgebra not defined
Stacktrace:
 ...

```

### Unique Julia instances

Instead of running Julia code on a persistent Julia server, unique Julia instances can be launched for each call to [`jlcall.m`](https://github.com/jondeuce/JuliaFromMATLAB.jl/blob/master/api/jlcall.m) by passing the `'server'` flag with value `false`.

**Note:** this may cause significant overhead when repeatedly calling [`jlcall.m`](https://github.com/jondeuce/JuliaFromMATLAB.jl/blob/master/api/jlcall.m) due to Julia package precompilation and loading:

```matlab
>> tic; jlcall('x -> sum(abs2, x)', {1:5}, 'server', false); toc
Elapsed time is 4.181178 seconds. % call unique Julia instance

>> tic; jlcall('x -> sum(abs2, x)', {1:5}, 'restart', true); toc
Elapsed time is 5.046929 seconds. % re-initialize Julia server

>> tic; jlcall('x -> sum(abs2, x)', {1:5}); toc
Elapsed time is 0.267088 seconds. % call server; significantly faster

```

### Loading code from a local project

Code from a local Julia project can be loaded and called:

```matlab
>> jlcall('MyProject.my_function', args, kwargs, ...
    'project', '/path/to/MyProject', ...
    'modules', {'MyProject'})

```

**Note:** the value of the `'project'` flag is simply added to the Julia `LOAD_PATH`; it is the user’s responsibility to ensure that the project’s dependencies have been installed.

### Loading setup code

Julia functions may require or return types which cannot be directly passed from or loaded into MATLAB. For example, suppose one would like to query `Base.VERSION`. Naively calling `jlcall('() -> Base.VERSION')` would fail, as `typeof(Base.VERSION)` is not a `String` but a `VersionNumber`.

One possible remedy is to define a wrapper function in a Julia script:

```julia
# setup.jl
julia_version() = string(Base.VERSION)

```

Then, use the `'setup'` flag to pass the above script to [`jlcall.m`](https://github.com/jondeuce/JuliaFromMATLAB.jl/blob/master/api/jlcall.m):

```matlab
>> jlcall('julia_version', 'setup', '/path/to/setup.jl')

ans =

    '1.6.1'

```

In this case, `jlcall('() -> string(Base.VERSION)')` would work just as well. In general, however, interfacing with complex Julia libraries using MATLAB types may be nontrivial, and the `'setup'` flag allows for the execution of arbitrary setup code.

**Note:** the setup script is loaded into the global scope using `include`; when using [persistent environments](https://github.com/jondeuce/JuliaFromMATLAB.jl#persistent-environments), symbols defined in the setup script will be available on subsequent calls to [`jlcall.m`](https://github.com/jondeuce/JuliaFromMATLAB.jl/blob/master/api/jlcall.m).

### Handling Julia outputs

Output(s) from Julia are returned using the MATLAB `cell` array [`varargout`](https://www.mathworks.com/help/matlab/ref/varargout.html), MATLAB’s variable-length list of output arguments. A helper function `JuliaFromMATLAB.matlabify` is used to convert Julia values into MATLAB-compatible values. Specifically, the following rules are used to populate `varargout` with the Julia output `y`:

1. If `y::Nothing`, then `varargout = {}` and no outputs are returned to MATLAB
2. If `y::Tuple`, then `length(y)` outputs are returned, with `varargout{i}` given by `matlabify(y[i])`
3. Otherwise, one output is returned with `varargout{1}` given by `matlabify(y)`

The following `matlabify` methods are defined by default:

```julia
matlabify(x) = x # default fallback
matlabify(::Union{Nothing, Missing}) = zeros(0,0) # equivalent to MATLAB's []
matlabify(x::Symbol) = string(x)
matlabify(xs::Tuple) = Any[matlabify(x) for x in xs] # matlabify values
matlabify(xs::Union{<:AbstractDict, <:NamedTuple, <:Base.Iterators.Pairs}) = Dict{String, Any}(string(k) => matlabify(v) for (k, v) in pairs(xs)) # convert keys to strings and matlabify values

```

**Note:** MATLAB `cell` and `struct` types correspond to `Array{Any}` and `Dict{String, Any}` in Julia.

Conversion via `matlabify` can easily be extended to additional types. Returning to the example from the [above section](https://github.com/jondeuce/JuliaFromMATLAB.jl#loading-setup-code), we can define a `matlabify` method for `Base.VersionNumber`:

```julia
# setup.jl
JuliaFromMATLAB.matlabify(v::Base.VersionNumber) = string(v)

```

Now, the return type will be automatically converted:

```matlab
>> jlcall('() -> Base.VERSION', 'setup', '/path/to/setup.jl')

ans =

    '1.6.1'

```

### Communication between MATLAB and Julia

The `.mat` files which are used to store MATLAB input arguments and Julia output arguments can be configured with the `'infile'` and `'outfile'` flags, respectively. Pointing these files to a ram-backed file system is recommended when possible (for example, the `/tmp` folder on Linux is usually ram-backed), as read/write speed will likely improve. This is now the default; `'infile'` and `'outfile'` are created via the MATLAB `tempname` function (thanks to @mauro3 for this tip).

### Performance

MATLAB inputs and Julia ouputs are passed back and forth between MATLAB and the `DaemonMode.jl` server by writing to temporary `.mat` files. The location of these files can be configured with the `'infile'` and `'outfile'` flags, respectively. Pointing these files to a ram-backed file system is recommended when possible (for example, the `/tmp` folder on Linux is usually ram-backed), as read/write speed will likely improve. This is now the default; `'infile'` and `'outfile'` are created via the MATLAB `tempname` function (thanks to @mauro3 for this tip).

Nevertheless, this naturally leads to some overhead when calling Julia, particularly when the MATLAB inputs and/or Julia outputs have large memory footprints. It is therefore not recommended to use [`jlcall.m`](https://github.com/jondeuce/JuliaFromMATLAB.jl/blob/master/api/jlcall.m) in performance critical loops.

## MATLAB and Julia version compatibility

This package has been tested on a variety of MATLAB versions. However, for some versions of Julia and MATLAB, supported versions of external libraries may clash. For example, running [`jlcall.m`](https://github.com/jondeuce/JuliaFromMATLAB.jl/blob/master/api/jlcall.m) using Julia v1.6.1 and MATLAB R2015b gives the following error:

```matlab
>> jlcall

ERROR: Unable to load dependent library ~/.local/julia-1.6.1/bin/../lib/julia/libjulia-internal.so.1

Message: /usr/local/MATLAB/R2015b/sys/os/glnxa64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by ~/.local/julia-1.6.1/bin/../lib/julia/libjulia-internal.so.1)

```

This error results due to a clash of supported `libstdc++` versions, and does not occur when using e.g. Julia v1.5.4 with MATLAB R2015b, or Julia v1.6.1 with MATLAB R2020b.

If you encounter this issue, see the [`Julia`](https://github.com/JuliaLang/julia/blob/master/doc/build/build.md#required-build-tools-and-external-libraries) and [`MATLAB`](https://www.mathworks.com/support/requirements/supported-compilers.html) documentation for information on mutually supported external libraries.

---

<div class="post-metadata">

**Author:** ![c42f](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/c42f/32/52842_2.png) [@c42f](https://discourse.julialang.org/u/c42f)\
**Post date:** [August 24, 2021, 2:43am UTC](https://discourse.julialang.org/t/ann-juliafrommatlab-jl-call-julia-from-matlab/66882/2 "2021-08-24T02:43:35Z")

</div>

> [@jondeuce](#):
>
> However, for some versions of Julia and MATLAB, supported versions of external libraries may clash.

I’m fairly sure this can be avoided completely because you run your Julia code in a separate operating system process. I haven’t used matlab for a long time, but IIRC the problem here is because matlab likes to set environment variables `LD_LIBRARY_PATH` (and possibly `LD_PRELOAD`?) which breaks the invocation of user executables with matlab’s [`system`](https://www.mathworks.com/help/matlab/ref/system.html) function (sigh… matlab 🤦‍♂️)

This problem is described here: [Why does Matlab set a custom LD\_LIBRARY\_PATH when execting "system" on Linux? - MATLAB Answers - MATLAB Central](https://www.mathworks.com/matlabcentral/answers/181899-why-does-matlab-set-a-custom-ld_library_path-when-execting-system-on-linux)

The solution is to unset the necessary environment variables, or otherwise undo whatever crimes matlab has committed against your OS environment.

---

<div class="post-metadata">

**Author:** ![jondeuce](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jondeuce/32/16378_2.png) [@jondeuce](https://discourse.julialang.org/u/jondeuce)\
**Post date:** [August 24, 2021, 2:59am UTC](https://discourse.julialang.org/t/ann-juliafrommatlab-jl-call-julia-from-matlab/66882/3 "2021-08-24T02:59:52Z")

</div>

Thanks for the tip! I think you must be right. It didn’t seem right to me that Julia would fail to start up from within a MATLAB `system` call, yet work fine in a `bash` shell, but I was unsure how to debug the problem. I will look into this some more 👍🏻

---

<div class="post-metadata">

**Author:** ![mauro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mauro3/32/292_2.png) [@mauro3](https://discourse.julialang.org/u/mauro3)\
**Post date:** [August 24, 2021, 7:08am UTC](https://discourse.julialang.org/t/ann-juliafrommatlab-jl-call-julia-from-matlab/66882/4 "2021-08-24T07:08:39Z")

</div>

Nice! Maybe add to the Performance section that using a ram-backed file system, such as is usually the case in Linux for `/tmp` (it’s a `tmpfs`), would probably help improve read/write to the temporary .mat files.

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 24, 2021, 5:59pm UTC](https://discourse.julialang.org/t/ann-juliafrommatlab-jl-call-julia-from-matlab/66882/5 "2021-08-24T17:59:17Z")

</div>

Thanks @jondeuce !

Yes, I was looking for some time [into this](https://discourse.julialang.org/t/calling-julia-from-matlab-the-smoothest-path-to-sneak-julia-into-a-company/64845) and came to about the same conclusions: command-line (non-API) and MAT.jl. My code uses [Java-streams](https://stackoverflow.com/questions/68822890/a-java-matlab-julia-interface) and a simple julia -iq call, not requiring DaemonMode.jl.

This is one of my very first Julia codes, so no bells and whistles as your implementation.  
Also, my call pattern is a bit different,

```julia
 jl.call('[a, m] = addmult', 2, 3);
 a+m
 ans =
     11

```

I am curious to see how your matlabify type translation and other features chime in.

> **jlcall.jl**
>
> ```julia
> #=
> jlcall.jl
> Matlab-Julia interface, called from jlcall.m
> =#
> 
> using MAT
> 
> function jlcall(iofile)
> # read call arguments from iofile
> matvars = matread(iofile)
> outvars = matvars["outvars"]
> fun = matvars["fun"]
> sfun = Symbol(fun)
> inputs = matvars["varargin"]    
> # if necessary, load function, run Julia
> if true #!(@isdefined sfun)
> funfile = matvars["fun"] * ".jl"
> println("...loading $funfile")
> include(funfile)
> end
> results = @eval $sfun($inputs...) 
> # save output arguments to iofile
> d = Dict{String, Any}()
> for i = 1:length(outvars)
> d[outvars[i]] = results[i]
> end
> matwrite(iofile, d)
> println("OK")
> end
> 
> ```

> **jlcall.m**
>
> ```julia
> function jlcall(outvars_fun, varargin)
> %JLCALL Execute Julia function from Matlab
> % jlcall('[outvar1, outvar2, ...] = fun', var1, var2, ...)
> % fun is a Julia function, built-in or defined in <fun>.jl
> %
> % Example:
> %
> % >> jlcall('[a, m] = addmult', 2, 3)
> % >> a+m
> % ans =
> % 11
> 
> outvars_fun = split(outvars_fun, {'[',']','=',',',' '});
> outvars_fun(cellfun('isempty',outvars_fun)) = [];
> outvars = outvars_fun(1:end-1);
> fun = outvars_fun{end};
> 
> iofile = [tempname, '.mat'];
> save(iofile, 'outvars', 'fun', 'varargin');
> system(sprintf('julia jlcall.jl %s', strrep(iofile, '\', '/')));
> load(iofile, outvars{:});
> system(sprintf('rm %s', iofile));
> end
> 
> ```

---

<div class="post-metadata">

**Author:** ![jondeuce](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jondeuce/32/16378_2.png) [@jondeuce](https://discourse.julialang.org/u/jondeuce)\
**Post date:** [August 24, 2021, 6:14pm UTC](https://discourse.julialang.org/t/ann-juliafrommatlab-jl-call-julia-from-matlab/66882/6 "2021-08-24T18:14:12Z")

</div>

Thanks, good suggestion! Updating now.

---

<div class="post-metadata">

**Author:** ![jondeuce](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jondeuce/32/16378_2.png) [@jondeuce](https://discourse.julialang.org/u/jondeuce)\
**Post date:** [August 24, 2021, 6:36pm UTC](https://discourse.julialang.org/t/ann-juliafrommatlab-jl-call-julia-from-matlab/66882/7 "2021-08-24T18:36:42Z")

</div>

Interesting! Yes, it was actually your post I referenced at the start of this post that led to me creating this package.

I’m not familiar with Java-streams. The main reason I used DaemonMode was indeed for the bells and whistles. Particularly the ability to easily run expressions in anonymous modules.

The `matlabify` type translation is really quite simple, I’m sure you could easily incorporate a similar method. The main purpose of `matlabify` is just to have something that users can extend to custom types.

---

<div class="post-metadata">

**Author:** ![dmolina](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmolina/32/5246_2.png) [@dmolina](https://discourse.julialang.org/u/dmolina)\
**Post date:** [August 25, 2021, 11:50am UTC](https://discourse.julialang.org/t/ann-juliafrommatlab-jl-call-julia-from-matlab/66882/8 "2021-08-25T11:50:24Z")

</div>

Great work! I am considering translating a transition to Julia and the JuliaFromMATLAB could be useful for doing that.

Also, I am glad that you consider DaemonMode useful for your package. It is nice to see how packages complement each others in the Julia ecosystem.
