Calling Matlab from Python

This note is about calling Matlab from Python and moving data between the two. I've previously written about the reverse (i.e. calling Python from Matlab). There some different ways to approach this, the most obvious being to use the Matlab engine for Python.

The Matlab engine

We need a library to time the code and numpy, as well as the engine.

In [3]:
from time import perf_counter
import numpy as np
import matlab.engine

This starts the engine (so to speak):

In [4]:
eng=matlab.engine.start_matlab()

Firstly, we'll make a Matlab array (note: I'm using smaller arrays than in the previous example.)

In [5]:
tic=perf_counter()
mla=eng.rand(1e3,'double')
print("Elapsed time is",'%.6f' % (perf_counter()-tic),"seconds.")
Elapsed time is 0.141018 seconds.
In [6]:
type(mla)
Out[6]:
mlarray.double

Now to make a numpy array:

In [7]:
tic=perf_counter()
npa=np.random.rand(int(1e3),int(1e3))
print("Elapsed time is",'%.6f' % (perf_counter()-tic),"seconds.")
Elapsed time is 0.017033 seconds.

Use the Matlab engine to transpose the array a couple of times:

In [8]:
tic=perf_counter()
mla_t=eng.transpose(mla)
mla_t=eng.transpose(mla_t)
print("Elapsed time is",'%.6f' % (perf_counter()-tic)," seconds.")
Elapsed time is 5.606414  seconds.
In [9]:
mla==mla_t
Out[9]:
True

Convert the Matlab array to a numpy array:

In [10]:
tic=perf_counter()
mla_npa=np.array(mla)
print("Elapsed time is",'%.6f' % (perf_counter()-tic)," seconds.")
Elapsed time is 2.108567  seconds.
In [11]:
type(mla_npa)
Out[11]:
numpy.ndarray

Do the same tanspose operations in numpy (note: these may not be doing the same thing in both languages - I think Matlab actaully transposes the array, wheras numpy creates a 'view' that swaps rows and columns which is likely why it's faster).

In [12]:
tic=perf_counter()
mla_npa=np.transpose(mla_npa)
mla_npa=np.transpose(mla_npa)
print("Elapsed time is",'%.6f' % (perf_counter()-tic)," seconds.")
Elapsed time is 0.000093  seconds.

This won't run. While one can change a numpy array into a Matlab array from Matlab, it can't be done by the Matlab engine in Python. Only these data types are allowed.

In [13]:
#tic=perf_counter()
#mla_npa_mla=eng.double(mla_npa)
#print("Elapsed time is",'%.6f' % (perf_counter()-tic)," seconds.")

Octave

The inability to pass numpy arrays to Matlab could make working with the matlab engine very inconvenient. However, we can do this using octave (oct2py). Oct2py uses .mat files to pass data between the octave and python environments, which may become a slow process. However, it is probably less encumbered by licensing issues.

In [14]:
from oct2py import octave
In [15]:
tic=perf_counter()
octa=octave.rand(1e3,'double')
print("Elapsed time is",'%.6f' % (perf_counter()-tic),"seconds.")
Elapsed time is 0.061995 seconds.
In [16]:
type(octa) # we see this as a numpy array, not an octave native data type
Out[16]:
numpy.ndarray
In [17]:
tic=perf_counter()
octa_t=octave.transpose(octa)
octa_t=octave.transpose(octa_t)
print("Elapsed time is",'%.6f' % (perf_counter()-tic)," seconds.")
Elapsed time is 0.149952  seconds.
In [18]:
np.array_equal(octa,octa_t)
Out[18]:
True

Using the hard disk

The strategy, here, is to save a numpy array as a file that matlab can read, then read it using the matlab engine. hdf5 is the format we'll use. The benefit of this is we can use the actual matlab engine on the result, rather than octave.

In [19]:
import h5py #sometimes there are problems recognising this library

This converts a numpy array to a matlab array, but may need to be changed for higher dimensional arrays. There isn't any other obvious way of turning a numpy array into a matalb array - octave sees everything as a numpy array, it seems.

In [20]:
tic=perf_counter()
with h5py.File('temp.hdf5', 'w') as f:
    dset = f.create_dataset("default", data=np.transpose(npa)) #transpose required as python is row-major and matlab is column-major
mla_hdf = eng.h5read("temp.hdf5","/default")
print("Elapsed time is",'%.6f' % (perf_counter()-tic),"seconds.")
type(mla_hdf)
Elapsed time is 0.146657 seconds.
Out[20]:
mlarray.double

We can turn the matlab array back into a numpy array:

In [21]:
tic=perf_counter()
mla_hdf_npa=np.array(mla_hdf)
print("Elapsed time is",'%.6f' % (perf_counter()-tic)," seconds.")
Elapsed time is 2.069301  seconds.

And check it's the same as what we started with:

In [22]:
np.array_equal(npa,mla_hdf_npa)
Out[22]:
True