从Python调用Matlab(需要Matlab)
项目描述
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函数和对象。
近期更改
针对在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=False 和 desktop=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 |
struct 或 containers.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 on。 s, 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 = 42,matlab.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 方法或上下文管理器来确保正确地停止进程。
安装
在您的计算机上安装 zeromq 库并将其添加到您的 PATH 中。如果您使用 conda,Transplant 会自动使用 conda 的 zeromq。
使用 pip install transplant 安装 Transplant。这将安装 pyzmq、numpy 和 msgpack 作为依赖项。
如果您想要通过网络运行 Transplant,远程 Matlab 必须能够访问 ZMQ.m 和 transplant_remote.m 以及 zeromq 库,并且必须可以通过 SSH 访达。
LINUX 的安装指南
通过您的包管理器安装最新版本的 zeromq。安装版本 4(通常称为 5)。
确保 Matlab 使用系统版本的 libstdc++。如果它使用不兼容的版本,启动 Transplant 可能会失败,并出现类似 GLIBCXX_3.4.21 not found 的错误。如果您遇到这种情况,可以通过删除/重命名 $MATLABROOT/sys/os/glnxa64/libstdc++ 或通过安装 matlab-support(如果您正在运行 Ubuntu)来禁用 Matlab 的自带 libstdc++。
Windows 的安装指南
从这里安装最新版本的 zeromq:[http://zeromq.org/distro:microsoft-windows](http://zeromq.org/distro:microsoft-windows) 或通过 conda。
安装一个编译器。请参阅以下支持的编译器列表:[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_global 和 get_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 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 39252d3f18698a0bde8b65b0f8f4bdcfbac17ff616a40aec6c43384b3cf640e0 |
|
MD5 | 303e27d4c6ca311bd83ec67a6de2d1fc |
|
BLAKE2b-256 | 4126e7a3ee526ed265d5c35d2de8d7b52870b964da75d75a67f8af327a74fec3 |
Transplant-0.8.11-py3-none-any.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | f540a066b578bc4af12896bd78bc56b91c314b48b0498fe7a4af41921549cfcd |
|
MD5 | e15121c8bb2614e37fefcd2309f3b882 |
|
BLAKE2b-256 | 16677faa22e4c771a9667157ea29212e2211289eb884bed9a80aa88f11e260cd |