GNU Octave 首页有一句承诺,很容易在匆匆浏览时被读得过满:它“能够与许多 Matlab 脚本直接兼容”。真正需要留意的是许多二字。Octave 11.3.0 发布于 2026 年 6 月 1 日,已经是一套成熟的科学编程环境,矩阵语法、绘图、控制台、GUI,以及便于从 shell 调用的执行方式一应俱全。这项承诺有明确范围,并未覆盖每一种依赖工具箱的应用、编译扩展或数值边缘情形;仅官方 MEX 文档就要求从源码重新编译,还说明了这一边界上的数据表示成本。[1][4]
这一区分改变了迁移工作的重心。薄弱的计划只问:“这个文件能在 Octave 中通过解析吗?”真正有用的计划会问:“对于我们关心的输入,一套受控的 Octave 环境能否产出可接受的结果、制品、运行时间和故障信号?”语法只是第一道关口。迁移的基本单位,是代码周围的计算契约。
封面照片来自 Commons,由贡献者上传,画面中 MATLAB 的创造者 Cleve Moler 正在研讨会讲台前发言。[7] 这张照片放在这里,是因为 Octave 迁移仍在 MATLAB 的技术谱系中延续。迁移要沿着这条谱系移交选定的工作,同时保住已经写进函数、工具箱、数据格式、编译代码与长期实验室实践中的决策。阿贡国家实验室旧 Carbon 集群的一则 2014 年说明,曾建议用户先尝试 Octave,再去使用该系统唯一的 MATLAB 许可证,同时承认高要求或厂商专有的任务仍会需要 MATLAB。这属于特定集群的历史说明,不能当作 2026 年的支持声明;其中对适用范围的区分依然值得沿用。[6]
因此,对小型研究组或工程团队而言,风险较低的路线是并行移植,整体切换要留到证据充分之后。
以工作负载为迁移单位
先选一项范围清楚的任务:参数扫描、信号处理批任务、报告生成器,或大型应用背后的数值核心。它应有代表性的输入数据、已知的运行命令、理解结果的负责人,以及可供比较的输出。一个装着二十年来互不相干的 .m 文件的目录,范围远远超出了单个迁移单位。
打开 Octave 之前,先记录让这项任务在 MATLAB 一侧正常运行的环境:
- MATLAB 版本与操作系统;
- 所有必需的工具箱和支持包;
- 启动文件及其对路径的修改;
- MEX 二进制文件,尤其要确认源码和编译说明是否存在;
- Java、Python、编译器、硬件或外部命令依赖;
- 输入文件格式、区域设置和工作目录假设;
- 操作人员用于判断成功与否的输出文件、图表、警告、日志和运行时间。
在未经改动的原环境中运行这项任务,并保存证据。现有系统若连自身认可的结果都无法复现,迁往 Octave 后也无从得到可信基线。描述当前行为本身就是移植工作的一部分,即使这一过程暴露出 MATLAB 一侧的缺陷。
首批候选任务适合以核心语言功能上的数值函数和脚本为主,只需少量软件包,也没有厂商专有图形或硬件工作流的刚性依赖。在团队证明已有 Octave 方案之前,若任务真正交付的是 App Designer 界面、Simulink 模型、代码生成目标或硬件支持栈,就应把它列为单独的探索项目。这些工作流仍有替换空间,只是每一项都相当于一次独立的应用迁移,不能藏进名为“MATLAB 兼容性”的普通事项里。
改代码之前,先建立答案集
一份基准输出文件很有用,但单个文件很少足以描述数值契约。应从多个代表性案例中建立一套小型答案集:
- 覆盖常用执行分支的普通案例;
- 在相关任务中包含空数组、奇异矩阵、稀疏数组、复数值、
NaN或Inf的边界案例; - 能暴露内存和运行时间特征的生产规模案例;
- 错误或警告具有运维意义的已知失败案例。
每个案例都要保存最终标量之外的信息:数组形状与数据类、选定的中间值、收敛状态、会影响解释的迭代次数,以及交给下游系统的文件格式约定。对于随机代码,还要记录生成器、种子和所检验的性质;只有经过专门验证,才能把两套引擎的伪随机序列视为一致。
容差应取自计算本身的含义,不能取决于某一台笔记本上恰好相同的小数位数。Octave 的测试工具清楚区分了两类容差:assert(observed, expected, tol) 把正容差视为绝对容差,把负容差视为相对容差;%!test 块则可以和函数实现放在一起。[3] 压力场、特征值排序、优化结果与货币总额所需的比较规则各不相同。每项 fixture 旁边都应写明规则。
图表也需要同样的约束。逐像素比较检验的往往是字体、抗锯齿和图形后端。应比较定义图表的数据、坐标轴变换、标签和导出尺寸,再目视抽查少量图表,寻找布局回退。如果下游软件会解析 PDF、MAT、CSV、HDF5 或图像文件,也要验证这个消费端。工作区里的数值即使正确,产出的制品仍会出现兼容问题。
答案集就绪后,就为两套引擎各自建立自动化运行器。每个运行器都应从洁净状态启动,打印引擎版本,报告已加载的软件包或工具箱,执行同一份 fixture 清单,并把结果写入彼此独立的目录。第二次运行的结果必须保存在另一目录,以完整保留第一次结果。比较报告应区分四种状态:通过、预期差异、不受支持的依赖、原因未明的差异。“脚本已完成”不在这四种状态之中。
把工具箱清单画成依赖图
Octave 的语言兼容性常常让首次运行收获颇多,也会遮住一个更大的问题:熟悉的函数名无法证明周围完整的工具箱契约已经存在。
Octave Packages 目录是一套独立且版本清晰可见的软件包体系,涵盖控制、图像处理、I/O、优化、信号处理和统计等领域。[2] 因此,目录里一个熟悉的名称只能作为清点线索,不能证明 MathWorks 工具箱已经覆盖完整接口及其边缘行为。应从选定工作负载出发,逐函数建立清单,不要把采购单上的名称直接换成相似的软件包名。
对每一个非核心调用,都要判断它在 Octave 中属于哪一类:
- 调用形式一致,行为可接受: 函数存在,答案集中的案例全部通过;
- 调用形式一致,边缘行为有差异: 常见案例通过,但形状、类型、选项、警告或异常行为不同;
- 可替换的基础操作: 小型适配器或替代软件包能够满足所需契约;
- 架构级依赖: 该功能对应一套模型、GUI、设备、部署目标或完整工作流,无法局部替换。
完成分类时,要固定 Octave 版本和软件包版本。每次测试都保存 ver 与 pkg list 的输出,并显式加载必需软件包,避免依赖开发者的启动文件。软件包目录本身已经给出理由:各软件包由不同团队独立维护,更新日期与速度各不相同。[2] “可在 Octave 中运行”这句话若没有附上使其成立的环境,信息就是残缺的。
避开整库范围的搜索替换。只要可读性仍然良好,就优先采用两套引擎都接受的语法。遇到确实存在的行为分歧,把差异收进一个窄小的适配器,并显式检测引擎。少数几处明确命名的适配点,比散落在各处的数十个 if 分支更便于审查。在积累信心的阶段,它们也能保留两套实现并行运行的选择。
MEX 是一条源码边界
看似简单的移植,往往在编译扩展处停下。MATLAB MEX 二进制文件不能直接复制到 Octave 环境中充当成品。Octave 提供与 MEX 兼容的源码 API,但扩展必须针对 Octave 及其运行平台重新编译。Octave 手册还提醒,由于 MATLAB 暴露的内部表示与 Octave 的原生表示不同,兼容层会产生额外的内存复制;对于新编写的性能敏感型扩展,手册建议采用 Octave 原生 oct-file API。[4]
MEX 因此仍有迁移通道,代价是把源码可用性、编译器配置和性能测试纳入迁移契约。可移植的数值核心要与轻量的 MATLAB/Octave 封装分开。应按引擎与平台组成构建矩阵,分别生成专用二进制文件;单一二进制文件不能以通用产物的身份提交。编译路径也要使用纯 .m 代码所用的同一套答案集来检验。
FOCUS 超声模拟软件包的一次实际移植展示了其中的取舍。维护者报告称,为支持 Octave,语法、函数调用和构建工具只需少量调整。使用 Octave 的 MEX API 重新编译简化了转换过程,额外的内存移动则带来了相对于原生 oct-files 的性能代价。[5] 这个案例不能推导出所有科学软件包都容易迁移。所谓“MEX 兼容”,只在源码和调用约定上架起一座桥;它没有证明二进制可以移植、速度相等或结果一致。
如果 MEX 源码缺失,就把该依赖标记为受阻。对二进制文件做逆向工程,或悄悄换成另一套算法,已经超出常规移植范围。受影响的工作负载可以继续留在 MATLAB 上;也可以把该组件作为单独评审的项目予以替换,或者缩小 Octave 的适用范围,绕开这一组件。
每次只切换一条执行通道
第一次在 Octave 中成功运行,只适合作为金丝雀测试,距离迁移完成的宣告仍有距离。把它放进 CI 或可重复的批处理环境,并在固定答案集上与 MATLAB 并行运行。随后分阶段扩大证据范围:
第一步,证明测试装置具有确定性。 同一引擎内的重复运行应在所选容差下相符。如果结果有差异,先隔离非确定性,再比较两套引擎。
第二步,解释每一项跨引擎差异。 已接受的差异应进入一份简短台账,记录 fixture、观测偏差、容差或适配器、负责人和原因。原因未明的差异即使看上去无害,也必须保持可见。
第三步,影子运行真实工作负载。 向两套引擎输入形态与生产环境一致的数据,期间仍由既有 MATLAB 通道发布结果。除了数值,还要比较运行时间、峰值内存、日志和下游制品。
第四步,迁移一个低风险消费端。 让 Octave 发布一份非关键报告或内部制品,同时保留可运行的 MATLAB 后备方案。还要有意演练回滚;从移植开始后再未运行过的后备方案,只存在于文档里。
最后,判断双引擎支持的成本是否仍有价值。 保留两套引擎会增加测试、打包和调试工作。对于广泛共享的库,这种可移植性本身就可以是产品价值。对于私有批任务,则可在预先设定的验证期后退出 MATLAB,以简化维护。团队应主动做出选择,避免无意间积累一套永久的影子系统。
达到以下状态,才适合切换:代表性 fixture 和边界 fixture 全部通过;所有剩余偏差均有解释;编译依赖都能从源码生成;生产规模案例满足真实的运行时间与内存预算;下游消费端接受产出的制品;即使原移植作者不在场,操作人员也能完成部署和回滚。语法兼容本身推导不出其中任何一项。
最终结果可以是部分迁移
Octave 尤其适合学生、可复现研究、由 shell 驱动的数值任务、教学环境,以及要让协作者在未取得专有软件许可证时也能运行代码的团队。它的控制台和脚本执行方式便于把数值工作负载放进常规自动化;它与许多 MATLAB 脚本兼容,也是很有价值的起点。[1]
适用范围既由技术条件划定,也由组织条件划定。独立研究者若只维护少量经过测试的 .m 函数,通常可以独自负责移植及其环境。实验室若有仪器、图形模型和分析链依赖多个厂商工具箱,就需要软件包管理、CI 容量、领域评审人员,并为每一处不受支持的适配点指定负责人。条件不足时,转移许可证比转移责任容易得多。
部分成功仍然有价值。在 MATLAB 和 Octave 中同时运行核心算法,可以暴露隐藏假设,让测试可以移植,为协作者保留开放的运行渠道,并把持久的数学部分从单一厂商的工作流中分离出来。旧 Carbon 说明提出的有限建议——先用 Octave 试跑现有代码,同时把高要求或厂商专有任务留给 MATLAB——既是谨慎的起点,也描述了一个有用的终点。[6]
决定性产物是答案集及与之配套的自动化。仓库代码的转换只覆盖迁移的一部分;答案集与自动化提供证据,证明一项具名的工作负载在一套具名的环境中继续保持同样的含义。证据一旦成立,语法就成了容易处理的部分。
来源
- GNU Octave project,“GNU Octave”——当前版本、支持的平台、执行方式,以及项目有意限定范围的 MATLAB 兼容性声明。
- GNU Octave project,“Octave Packages”——软件包目录、版本、更新日期,以及由不同团队独立维护的功能覆盖。
- GNU Octave manual,“Test Functions”——内嵌测试块,以及
assert的绝对容差与相对容差行为。 - GNU Octave manual,“Mex-Files”——源码层面的 MEX 兼容性、重新编译要求、内存复制开销与原生 oct-file 边界。
- Jacob S. Honer 与 Robert J. McGough,“Enabling support for GNU Octave within the FOCUS software package”,Proceedings of Meetings on Acoustics 54,022003,2024——一项独立的科学代码移植及其 MEX 取舍。
- Argonne National Laboratory,Center for Nanoscale Materials,“HPC/Applications/matlab”——旧 Carbon 集群说明,最后修改于 2014 年 11 月 24 日,内容是在使用该系统唯一的 MATLAB 许可证之前先尝试 Octave。
- Wikimedia Commons,“Cleve B Moler in 2017 Argonne Seminars GPU Computing”——研讨会照片的来源页、上传者署名、尺寸与出处信息。