跳转到主要内容

从Python调用Matlab(需要Matlab)

项目描述

version python status license

contributors downloads

Transplant是从Python调用Matlab的一种简单方式。

import transplant
matlab = transplant.Matlab()
# call Matlab functions:
length = matlab.numel([1, 2, 3])
magic = matlab.magic(2)
spectrum = matlab.fft(numpy.random.randn(100))
# inject variables into Matlab:
matlab.signal = numpy.zeros(100)

Python列表被转换为Matlab中的单元数组,字典被转换为映射,NumPy数组被转换为本地Matlab矩阵。

可以从Python访问所有Matlab函数和对象。

Transplant在BSD 3条款许可的条款下授权
(c) 2014 Bastian Bechtold

open-issues closed-issues open-prs closed-prs

近期更改

  • 针对在Windows上查找libzmq的修复(感谢,hardmstar)

  • 现在正确地将布尔ndarrays编码为逻辑数组(感谢,Júlio)

  • 针对与Matlab包一起工作的修复(感谢,dani-l)

  • 针对Matlab关闭时的递归修复(感谢,dani-l)

  • 现在应该可靠地引发Matlab意外死亡时的错误。

  • 关键字参数现在自动转换为Matlab中的字符串值对。

  • close 已更名为 exit。尽管Python通常使用 close 来关闭文件和连接,但这与Matlab自己的 close 函数冲突。

  • Matlab现在将在当前工作目录中启动。

  • 现在可以通过 pip install transplant 安装Transplant。

  • 现在可以使用 jvm=Falsedesktop=False 自动提供Matlab的常见命令行参数。

启动Matlab

matlab = transplant.Matlab()

将启动一个Matlab会话并连接到它。这需要几秒钟,因为Matlab正在启动。所有Matlab的输出都将发送到标准输出,并会与Python输出交织显示。为了使REPL正常工作,标准输入被抑制,因此Matlab的input函数将无法工作。

默认情况下,这将尝试在命令行上调用matlab。如果您想使用不同的Matlab版本,或者matlab不在PATH中,请使用Matlab(executable='/path/to/matlab')

默认情况下,Matlab使用-nodesktop-nosplash(在Windows上为-minimize),因此不会显示IDE或启动画面。您可以通过设置desktop=True来更改此设置。

您可以通过设置jvm=False来启动Matlab而不加载基于Java的GUI系统('-nojvm'),这将大大加快启动速度,但您将无法再打开图形。

如果您想以额外的命令行参数启动Matlab,您可以像这样提供它们:Matlab(arguments=['-c licensefile'])

默认情况下,Matlab将在本地计算机上启动。要在不同的计算机上启动Matlab,提供该计算机的IP地址:Matlab(address='172.168.1.5')。这仅在计算机可通过ssh访问、Matlab在另一台计算机的命令行上可用且transplant在另一Matlab的路径中时才有效。

请注意,由于Windows上Matlab的限制,Windows上运行的Matlab的命令行输出对Transplant是不可见的。

调用MATLAB

matlab.disp("Hello, World")

将以'Hello, World'作为参数调用Matlab的disp函数。这相当于Matlab中的disp('Hello, World')。返回值将返回到Python,错误将转换为Python错误(Matlab堆栈跟踪也将提供)!

输入参数被转换为Matlab数据结构

Python参数

Matlab类型

str

char vector

float

double scalar

int

一个int{8,16,32,64}标量

True/False

logical scalar

None

[]

list

cell

dict

containers.Map

transplant.MatlabStruct(dict)

struct

numpy.ndarray

double matrix

scipy.sparse

sparse matrix

proxy object

original object

proxy function

original function

返回值也以类似的方式处理

Matlab返回值

Python类型

char vector

str

numeric scalar

number

logical scalar

True/False

[]

None

cell

list

structcontainers.Map

dict

numeric matrix

numpy.ndarray

sparse matrix

scipy.sparse

function

proxy function

object

proxy object

如果函数返回一个函数句柄或对象,将创建一个匹配的Python函数/对象,该对象将转发所有对Matlab的访问。对象也可以返回给Matlab,并按预期工作。

f = matlab.figure() # create a Figure object
f.Visible = 'off' # modify a property of the Figure object
matlab.set(f, 'Visible', 'on') # pass the Figure object to a Matlab function

在Matlab中,一些函数的执行行为取决于输出参数的数量。默认情况下,Transplant使用Matlab函数nargout来确定函数的返回值数量。如果nargout也无法确定输出参数数量,Matlab函数将在函数调用后返回ans的值。

在某些情况下,nargout会报告错误的输出参数数量。例如,nargout profile会报告为1,但x = profile('on')会引发错误,提示使用了过多的输出参数。为了解决这个问题,每个函数都有一个关键字参数nargout,在这些情况下可以使用:matlab.profile('on', nargout=0)调用没有输出参数的profile ons, f, t, p = matlab.spectrogram(numpy.random.randn(1000), nargout=4)返回spectrogram的所有四个输出参数。

所有其他关键字参数都将透明地转换为Matlab中的键值对,即matlab.struct(a=1, b=2)是另一种编写matlab.struct('a', 1, 'b', 2)的方式。

当处理图形时,请注意,Matlab程序本身不会等待绘图。使用matlab.drawnow()可以使图形出现。

请注意,函数不是在基本工作区中调用的。访问当前非词法工作区的函数(这种情况非常少见)因此不会按预期工作。例如,matlab.truth = 42matlab.exist('truth')将找不到truth变量。在这种情况下,请使用matlab.evalin('base', "exist('truth')", nargout=1)

如果您按下Ctrl-C,KeyboardInterrupt将被应用于Python和Matlab,停止任何当前正在运行的功能。由于Matlab的限制,该函数的错误和堆栈跟踪将会丢失。

矩阵维度

Matlab和Python中多维数组的索引方式在本质上不同。幸运的是,二维情况按预期工作。

           Python         |        Matlab
--------------------------+------------------------
 array([[  1,   2,   3],  |     1   2   3
        [ 10,  20,  30]]) |    10  20  30

在这两种语言中,这个数组的形状为(2, 3)

对于高维数组,这变得更加困难。下一个数组又是相同的。

           Python         |        Matlab
--------------------------+------------------------
 array([[[  1,   2],      | (:,:,1) =
         [  3,   4]],     |              1    3
                          |             10   30
        [[ 10,  20],      |            100  300
         [ 30,  40]],     | (:,:,2) =
                          |              2    4
        [[100, 200],      |             20   40
         [300, 400]]])    |            200  400

尽管它们看起来不同,但它们的形状都是相同的(3, 2, 2),并且以相同的方式索引。Python中的位置a, b, c的元素与Matlab中的位置a+1, b+1, c+1的元素相同(由于零基/一基索引,+1)。

您可以将这种差异的表现形式想象成这样:Python将多维数组显示为[n,:,:],而Matlab将它们显示为(:,:,n)

停止Matlab

当Matlab实例超出作用域或显式使用exit方法关闭时,Matlab会处理结束。或者,可以使用Matlab类作为上下文管理器,它将正确清理。

如果您没有使用上下文管理器或exit方法,您会注意到一些Matlab进程在您期望它们死亡时并未死亡。如果您正在运行常规的python解释器,那么Matlab进程可能仍然被引用在sys.last_traceback中,它包含最后一次引发的异常的值。您的Matlab进程将在下一次引发异常时死亡。

如果您正在运行 ipython,那么所有赌注都不成立了。我注意到 ipython 保留了对所有各种事物的各种引用。有时,%reset 会清除它们,有时则不会。有时它们只在 ipython 退出时消失。有时,即使停止 ipython 也无法杀死它(这是怎么做到的?)。这可能会相当令人烦恼。请使用 exit 方法或上下文管理器来确保正确地停止进程。

安装

  1. 在您的计算机上安装 zeromq 库并将其添加到您的 PATH 中。如果您使用 conda,Transplant 会自动使用 conda 的 zeromq。

  2. 使用 pip install transplant 安装 Transplant。这将安装 pyzmqnumpymsgpack 作为依赖项。

如果您想要通过网络运行 Transplant,远程 Matlab 必须能够访问 ZMQ.mtransplant_remote.m 以及 zeromq 库,并且必须可以通过 SSH 访达。

LINUX 的安装指南

  1. 通过您的包管理器安装最新版本的 zeromq。安装版本 4(通常称为 5)。

  2. 确保 Matlab 使用系统版本的 libstdc++。如果它使用不兼容的版本,启动 Transplant 可能会失败,并出现类似 GLIBCXX_3.4.21 not found 的错误。如果您遇到这种情况,可以通过删除/重命名 $MATLABROOT/sys/os/glnxa64/libstdc++ 或通过安装 matlab-support(如果您正在运行 Ubuntu)来禁用 Matlab 的自带 libstdc++。

Windows 的安装指南

  1. 从这里安装最新版本的 zeromq:[http://zeromq.org/distro:microsoft-windows](http://zeromq.org/distro:microsoft-windows) 或通过 conda。

  2. 安装一个编译器。请参阅以下支持的编译器列表:[http://uk.mathworks.com/support/compilers/R2017a/](http://uk.mathworks.com/support/compilers/R2017a/) Matlab 需要一个编译器才能使用 loadlibrary 加载并使用 ZeroMQ 库。

它是如何工作的?

Transplant 以子进程的形式打开 Matlab(可选地通过 SSH),然后以请求-响应模式通过 0MQ 连接到它。Matlab 然后运行 transplant 远程并开始监听消息。现在,Python 可以向 Matlab 发送消息,Matlab 将做出响应。从 Python 到 Matlab 的发送/接收和编码/解码值的往返时间约为 2 毫秒。

所有消息都是 Msgpack 编码或 JSON 编码的对象。您可以使用 Matlab 构造函数的 msgformat 属性在 Msgpack(更快)和 JSON(较慢,可读)之间进行选择。

  • set_globalget_global 用于设置和检索全局变量。

  • del_proxy 用于删除缓存的对象。

  • call 用于调用 Matlab 函数,并带有一些函数参数,然后返回结果。

  • die 告诉 Matlab 关闭。

Matlab 可以通过以下三种消息类型之一进行响应

  • ack 表示成功执行。

  • value 表示返回值。

  • error 表示在执行过程中出现错误。

除了常规的 Msgpack/JSON 数据类型之外,_transplant_ 还使用特殊格式的 Msgpack/JSON 数组来传输数值矩阵作为二进制数据。一个包含 [[1, 2], [3, 4]] 的数值 2x2 32 位整数矩阵将被编码为 ["__matrix__", "int32", [2, 2], "AQAAAAIAAAADAAAABAAAA==\n"],其中 "int32" 是数据类型,[2, 2] 是矩阵形状,而长字符串是 base64 编码的矩阵内容。这允许高效的数据交换,并防止 JSON 序列化引起的舍入误差。在 Msgpack 中,数据不是 base64 编码的。

当MATLAB返回一个函数句柄时,它会被编码为 ["__function__", func2str(f)]。当MATLAB返回一个对象时,它会缓存其值并返回 ["__object__", cache_idx]。如果将这些数组传递给MATLAB,它们会被转换回原始的MATLAB值。

注意,此项目包含纯MATLAB编写的Msgpack序列化/反序列化器、JSON序列化/反序列化器和Base64编码/解码器。

常见问题解答

  • 我遇到了整数数的问题。许多MATLAB函数在用整数调用时会出现崩溃。将您的数字转换为 float 在Python中可以解决这个问题。

  • 如何将结构体传递给MATLAB?由于MATLAB结构体不能使用任意键,所有Python字典都转换为MATLAB containers.Map 而不是结构体。在Python中将您的字典包装在 transplant.MatlabStruct 中,以使它们转换为结构体。请注意,这会将所有无效键更改为MATLAB认为适当的键名,使用 matlab.lang.makeValidName

  • 我遇到了类似于 GLIBCXX_3.4.21 not found 的错误。MATLAB的libstdc++版本与您的操作系统版本不兼容。有关详细信息,请参阅Linux的《安装指南》。

  • Transplant在Python 2.7中工作吗?不,它不支持。

  • 如何将Transplant与Jupyter集成?使用提供的 transplant_magic.py,以获取 %%matlab 单元格魔法。

类似程序

我知道有两个程序尝试做类似于Transplant的事情

  • Mathwork自己的 MATLAB Engine API for Python 为Python提供了一组CPython扩展,用于从某些版本的Python调用MATLAB代码。根据我的经验,它比Transplant慢得多,功能不完整(不支持非标量结构、对象、方法、包、numpy),且使用起来更加繁琐(所有参数和返回值都需要包裹在 matlab.double 中,而不是Numpy数组)。以下是关于这两个比较的博客文章: Transplant 简介Transplant 速度

  • Oct2Py从Python调用Octave。它与Transplant非常相似,但使用Octave而不是MATLAB。这具有巨大的启动时间优势,但当然不支持所有MATLAB代码。

已知问题

Transplant是我的一项副项目,我用它来在一个小型计算集群上运行跨语言实验。因此,我对Transplant的使用非常有限,我没有看到在我典型使用中不会发生的错误。尽管如此,我已经使用了数百小时,处理了数百GB的数据,而没有出现错误。

如果您发现了一个错误,或者想讨论新功能,或者想贡献代码,请在Github上创建一个问题。

我没有Windows机器来测试Transplant。Windows支持可能包含错误,但至少有一个用户过去在Windows上使用过它。如果您在Windows上遇到问题,请在Github上创建一个问题。

通过网络运行Transplant可能包含错误。如果您遇到问题,请在Github上创建一个问题。

最后,我想提醒您,我是在业余时间免费开发这个项目的。尽管我会尽量配合,但我不能保证及时响应问题。在Github上发布开源软件并不意味着有义务 立即修复您的问题。请保持文明。

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分发

Transplant-0.8.11.tar.gz (37.2 kB 查看哈希值)

上传时间

构建分发

Transplant-0.8.11-py3-none-any.whl (37.8 kB 查看哈希值)

上传时间 Python 3

支持者