Python Mlab - cannot import name find_available_releases

前端 未结 2 2133
太阳男子
太阳男子 2021-02-15 16:11

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:

2条回答
  •  梦谈多话
    2021-02-15 16:34

    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.

提交回复
热议问题