Python Mlab - cannot import name find_available_releases
Asked Answered
K

2

8

I am new to Python. I am trying to run MATLAB from inside Python using the mlab package. I was following the guide on the website, and I entered this in the Python command line:

from mlab.releases import latest_release

The error I got was:

cannot import name find_available_releases

It seems that under matlabcom.py there was no find_available_releases function.

May I know if anyone knows how to resolve this? Thank you!

PS: I am using Windows 7, MATLAB 2012a and Python 2.7

Kelsy answered 18/12, 2013 at 13:33 Comment(3)
I am using windows 7, MATLAB 2012a and Python 2.7Kelsy
Are you trying to execute Python script from Matlab?Marieann
@Marieann He is trying to run MATLAB from Python.Owain
S
6

I skimmed through the code, and I don't think all of the README file and its documentation match what's actually implemented. It appears to be mostly copied from the original mlabwrap project.

This is confusing because mlabwrap is implemented using a C extension module to interact with the MATLAB Engine API. However the mlab code seems to have replaced that part with a pure Python implementation as the MATLAB-bridge backend. It comes from "Dana Pe'er Lab" and it uses two different methods to interact with MATLAB depending on the platform (COM/ActiveX on Windows and pipes on Linux/Mac).


Now that we understand how the backend is implemented, you can start looking at the import error.

Note: the Linux/Mac part of the code tries to find the MATLAB executable in some hardcoded fixed locations, and allows to choose between different versions.

However you are working on Windows, and the code doesn't really implement any way of picking between MATLAB releases for this platform (so all of the methods like discover_location and find_available_releases are useless on Windows). In the end, the COM object is created as:

self.client = win32com.client.Dispatch('matlab.application')

As explained here, the ProgID matlab.application is not version-specific, and will simply use whatever was registered as the default MATLAB COM server. We can explicitly specify what MATLAB version we want (assuming you have multiple installations), for instance matlab.application.8.3 will pick MATLAB R2014a.

So to fix the code, IMO the easiest way would be to get rid of all that logic about multiple MATLAB versions (in the Windows part of the code), and just let it create the MATLAB COM object as is. I haven't attempted it, but I don't think it's too involved... Good luck!


EDIT:

I download the module and I managed to get it to work on Windows (I'm using Python 2.7.6 and MATLAB R2014a). Here are the changes:

$ git diff
diff --git a/src/mlab/matlabcom.py b/src/mlab/matlabcom.py
index 93f075c..da1c6fa 100644
--- a/src/mlab/matlabcom.py
+++ b/src/mlab/matlabcom.py
@@ -21,6 +21,11 @@ except:
   print 'win32com in missing, please install it'
   raise

+def find_available_releases():
+    # report we have all versions
+    return [('R%d%s' % (y,v), '')
+        for y in range(2006,2015) for v in ('a','b')]
+
 def discover_location(matlab_release):
     pass

@@ -62,7 +67,7 @@ class MatlabCom(object):
     """
     self._check_open()
     try:
-      self.eval('quit();')
+      pass    #self.eval('quit();')
     except:
       pass
     del self.client
diff --git a/src/mlab/mlabraw.py b/src/mlab/mlabraw.py
index 3471362..16e0e2b 100644
--- a/src/mlab/mlabraw.py
+++ b/src/mlab/mlabraw.py
@@ -42,6 +42,7 @@ def open():
     if is_win:
         ret = MatlabConnection()
         ret.open()
+        return ret
     else:
         if settings.MATLAB_PATH != 'guess':
             matlab_path = settings.MATLAB_PATH + '/bin/matlab'
diff --git a/src/mlab/releases.py b/src/mlab/releases.py
index d792b12..9d6cf5d 100644
--- a/src/mlab/releases.py
+++ b/src/mlab/releases.py
@@ -88,7 +88,7 @@ class MatlabVersions(dict):
         # Make it a module
         sys.modules['mlab.releases.' + matlab_release] = instance
         sys.modules['matlab'] = instance
-        return MlabWrap()
+        return instance

     def pick_latest_release(self):
         return get_latest_release(self._available_releases)

First I added the missing find_available_releases function. I made it so that it reports that all MATLAB versions are available (like I explained above, it doesn't really matter because of the way the COM object is created). An even better fix would be to detect the installed/registered MATLAB versions using the Windows registry (check the keys HKCR\Matlab.Application.X.Y and follow their CLSID in HKCR\CLSID). That way you can truly choose and pick which version to run.

I also fixed two unrelated bugs (one where the author forgot the function return value, and the other unnecessarily creating the wrapper object twice).

Note: During testing, it might be faster NOT to start/shutdown a MATLAB instance each time the script is called. This is why I commented self.eval('quit();') in the close function. That way you can start MATLAB using matlab.exe -automation (do this only once), and then repeatedly re-use the session without shutting it down. Just kill the process when you're done :)

Here is a Python example to test the module (I also show a comparison against NumPy/SciPy/Matplotlib):

test_mlab.py

# could be anything from: latest_release, R2014b, ..., R2006a
# makes no difference :)
from mlab.releases import R2014a as matlab

# show MATLAB version
print "MATLAB version: ", matlab.version()
print matlab.matlabroot()

# compute SVD of a NumPy array
import numpy as np
A = np.random.rand(5, 5)
U, S, V = matlab.svd(A, nout=3)
print "S = \n", matlab.diag(S)

# compare MATLAB's SVD against Scipy's SVD
U, S, V = np.linalg.svd(A)
print S

# 3d plot in MATLAB
X, Y, Z = matlab.peaks(nout=3)
matlab.figure(1)
matlab.surf(X, Y, Z)
matlab.title('Peaks')
matlab.xlabel('X')
matlab.ylabel('Y')
matlab.zlabel('Z')

# compare against matplotlib surface plot
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='jet')
ax.view_init(30.0, 232.5)
plt.title('Peaks')
plt.xlabel('X')
plt.ylabel('Y')
ax.set_zlabel('Z')
plt.show()

Here is the output I get:

C:\>python test_mlab.py
MATLAB version: 8.3.0.532 (R2014a)
C:\Program Files\MATLAB\R2014a
S =
[[ 2.41632007]
 [ 0.78527851]
 [ 0.44582117]
 [ 0.29086795]
 [ 0.00552422]]
[ 2.41632007  0.78527851  0.44582117  0.29086795  0.00552422]

matlab_peaks_surf matplotlib_peaks_surf


EDIT2:

The above changes have been accepted and merged into mlab.

Serge answered 15/7, 2014 at 16:8 Comment(3)
Yes, windows and path discovery via com/registry is just not implemented. Was on my todo list for a while. sorry. Since this is so important for people I'll apply a patch asap. BTW indeed this is mostly the copy from original mlabwrap which is discontinued as well.Qualitative
Applied the patch to 1.1.3 after testing it on Windows and Linux. Updated PyPI so that 'pip install mlab' should contain it now. +1 for good work. Thanks.Qualitative
@YauhenYakimovich: Thanks for joining the conversation! I'll be happy to test it on Windows when you apply the registry discovery fix.. As for the description, perhaps it would help if you add a note near the top with something like: "Below is the original README from the mlabwrap project. Most of it still applies, but the underlying implementation is different (COM/Pipes replaced the use of the MATLAB Engine API)."Serge
E
1

You are right in saying that the find_available_releases() is not written. 2 ways to work this out

  • Check out the code in linux and work on it (You are working on windows !)
  • Change the Code as below

Add the following function in matlabcom.py as in matlabpipe.py

def find_available_releases():
    global _RELEASES
    if not _RELEASES:
        _RELEASES = list(_list_releases())
    return _RELEASES

If you see mlabraw.py file, the following code will give you a clear idea why I am saying this !

import sys
is_win = 'win' in sys.platform
if is_win:
    from matlabcom import MatlabCom as MatlabConnection
    from matlabcom import MatlabError as error
    from matlabcom import discover_location, find_available_releases
    from matlabcom import WindowsMatlabReleaseNotFound as MatlabReleaseNotFound
else:
    from matlabpipe import MatlabPipe as MatlabConnection
    from matlabpipe import MatlabError as error
    from matlabpipe import discover_location, find_available_releases
    from matlabpipe import UnixMatlabReleaseNotFound as MatlabReleaseNotFound
Eton answered 15/7, 2014 at 10:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.