Using Lentil with MATLAB

MATLAB Lentil Interface

Calling Python libraries from MATLAB is as simple as configuring MATLAB to use the appropriate Python implementation and prepending the Python command with py.. Before using Lentil in MATLAB, be sure that you you have configured MATLAB to use the correct version of Python installed on your system

Working with NumPy Arrays in MATLAB

Many Lentil methods return NumPy arrays. Unfortunately, MATLAB does not fully understand this datatype. The two MATLAB functions provided below can be used to easily convert back and forth between NumPy arrays and MATLAB matrices:

It’s also possible to read and write .npy files in MATLAB with npy-matlab

Interacting directly with Lentil from MATLAB

It is possible to interact directly with Lentil within MATLAB. For example, we can create a simple circle() mask with:

>> mask = py.lentil.util.circle([int16(256),int16(256)],int16(128));

Note that MATLAB automatically does type conversion when MATLAB data is passed to Python but because MATLAB’s default numeric type is double-precision floating point, we need to explicitly convert the shape and radius parameters to ints before passing them to Python.

This approach is fine for debugging and some light usage, but more robust and user-friendly solutions require developing a MATLAB interface.

Developing a MATLAB interface to a Lentil model

The easiest way to provide a MATLAB interface to an underlying Lentil model is to develop a separate MATLAB class that mimics the interface of a Lentil class and handles any data formatting issues that arise between MATLAB and Python. In this way, the model logic all resides within the Python code, and the MATLAB code is only responsible for managing the interface between languages. An example following this approach is available here.

When deploying a MATLAB interface to a Lentil model, it may be convenient to duplicate the mat2ndarray.m and ndarray2mat.m scripts in the project’s local matlab directory alongside the .m interfaces. This simplifies the end user experience because it does not require any additional path modification or management to use the model.

For an example, see: Simple MATLAB Interface Class.

Finally, a few links that may be helpful when developing a MATLAB interface:

Warning

It is not fully understood what happens when you wrap a call to a Python object in a parfor loop in MATLAB. Buyer beware.

Configuring MATLAB to use the correct version of Python

MATLAB automatically selects and loads a Python version when you type a Python command. It commonly defaults to using Python 2.7, which is not at all what we want. We need to tell MATLAB where to find the correct version of Python (>=3.7). For MATLAB r2020a or later:

pyenv('Version', '/path/to/python3/executable')

For MATLAB r2019b or earlier:

pyversion '/path/to/python3/executable'

The Python version can be set temporarily by executing the above command when MATLAB launches or automatically by adding the command to your startup.m file.

Note

If you’re using virtual environments to manage different Lentil models, the pyenv/pyversion configuration specified above won’t work. Instead, you’ll need to call pyenv/pyversion with the correct virtual environment version before working with a model. Don’t forget to call the MKL conflict fix as well. This is annoying. Sorry about that. It’s a MATLAB “feature”.

Warning

Once you’ve set pyenv/pyversion within a MATLAB session, the only way to change it is to restart MATLAB. This means that if you’re working with virtual environments to manage different models, you’ll have to restart MATLAB each time you want to switch models. This is annoying. Sorry about that. It’s a MATLAB “feature”.

For more help on setting MATLAB’s Python version, see System and Configuration Requirements.

Resolving MKL Conflicts

MATLAB doesn’t always load the correct libraries the underlying Python code relies on. In particular, there seems to be some confusion about when to load MKL. There is no telltale sign this has occurred. Sometimes MATLAB crashes while other times Python method calls will error out with messages that may or may not be useful. The following command will clear up MATLAB’s confusion by handing control of which libraries Python needs back to Python:

py.sys.setdlopenflags(int32(10));

This command sets the RTLD_NOW and RTLD_DEEPBIND flags when the active Python instance calls dlopen() [1] [2] [3]. Note that this command is Unix only and must be called before the Python interpreter is loaded within MATLAB but after pyenv/pyversion is set, making it a prime candidate for inclusion in startup.m.

Troubleshooting

Debugging MATLAB’s Undefined variable “py” or function “py.command” error

  1. Make sure Python is loaded and working:

>> py.print('test')

test
  1. Make sure Lentil is loaded and working:

>> mask = py.lentil.util.circle([int16(256),int16(256)],int16(128));

3. Verify there are no import errors in the Python code by importing Lentil and any custom models in a Python interpreter:

>>> import lentil
>>> import <<your-model>>

For more hints, see the MATLAB documentation on Undefined variable “py” or function “py.command”

Resolving “Python Error: ImportError: Importing the numpy c-extensions failed.” error

On Windows, if the system path is not correctly configured, Python will throw a lengthy error message when trying to import Numpy:

>> py.importlib.import_module('numpy')
Error using __init__><module> (line 54)
Python Error: ImportError:

IMPORTANT: PLEASE READ THIS FOR ADVICE ON HOW TO SOLVE THIS ISSUE!

Importing the numpy c-extensions failed. - Try uninstalling and reinstalling numpy.

...

There appear to be several causes, but the error is most likely triggered because Python is not able to locate the necessary Numpy DLL files. The most common culprit is not electing to add Anaconda to the Windows PATH (which for some reason is the recommended choice during installation). The issue is fixed by appending the system path. Note that it is safest to do this from within MATLAB, in case a different version of Python is on the system path and is being used by other applications.

setenv('path',['C:\Path\To\Anaconda3\Library\bin;', getenv('path')]);