我使用MATLAB已经 25 年了(之前,我还使用过MATRIXx,一个不太成熟的衍生品)。它不是我学习的第一个编程语言,但它伴随了我在计算领域的成长。熟悉 MATLAB 对我的职业生涯很有用。不过,我们不能忽视Python在科学计算领域中的崛起。MathWorks 也应该有同感。他们不仅增加了在 MATLAB 中直接调用 Python 的能力,而且还借用了 Python 的一些语言特性,比如对二进制操作符的操作数进行了更激进的广播。
我一直质疑自己在研究和教学中是否应该继续使用 MATLAB。然而,虽然使用起来得心应手,也投入了很多,但我很难调动起学习新东西的动力。
我和其他人合作写过一本介绍计算科学的 MATLAB 教材。这本书里有 40 多个函数和 160 多个计算样例,它涵盖了我认为在数值科学计算中 MATLAB 的所有基础使用方法。为了实现自我提升,并增强该书的实用性,今年我开始把这些代码用Julia和Python实现。这种实践让我对三种语言在科学计算中的应用有了更特别的体会,下面我将进行详细说明。
先不考虑成本和开放性问题。与 Python 和 Julia 不同,MATLAB 既不是免费软件,也不是开源软件。对很多人来说,这是一个极大的区别,甚至是致命性的,但我认为这是一个技术优势。多年来,MATLAB 在很多方面发挥了远超过其它任何免费产品的实用价值,使用免费产品并希望达到一定成效,成本反而会很高。这一点对语言和生态系统的理想化诉求,需要单独进行考虑。
如果不考虑成本,可以看到这些语言起源时的诸多不同之处。MATLAB 最早出现时,侧重于数学方面,尤其是在数值运算相关的数学领域。Python 在上世纪八十年代后期出现,关注计算科学。Julia 开始于 2009 年,力求在多个方面实现平衡。
MATLAB
最开始,MATLAB 里的每个值都是双精度浮点数组,设计之初就确定了使用数组和浮点数。
浮点数的 IEEE 754 标准直到 1985 年才被采用,那时的内存是用 K 而不是用 G 来测量的。浮点数的双精度表示并不是最有效的表示字符或整数的方法,但是它们是科学家、工程师以及越来越多的数学家大部分时间里更愿意使用的格式。此外,不需要声明变量,也不需要显式地分配内存。让计算机来处理这些任务,并快速处理数据类型,解放你的大脑去思考对数据进行操作的算法。
数组之所以重要,是因为线性代数中的数值算法正在以LINPACK和EISPACK的形式出现。但是,使用科学计算的标准载体 FORTRAN 77 来访问它们是一个多步骤的过程,包括声明变量、调用名称神秘的例程、编译代码,然后检查数据和输出文件。把矩阵乘法写成 A*B 的形式,然后马上把答案打印出来,这是一种对游戏规则的改变。
MATLAB 还使图形变得更简单、更容易访问。没有使用底层调用和硬件相关的库,只用一个 plot(x,y)就可以得到想要的图形。它还有很多创新之处,比如嵌入复数、稀疏矩阵、构建跨平台图形用户界面的工具,以及领先的 ODE 求解程序套件,这些都使 MATLAB 在科学计算上速度非常快。
然而,对于交互式计算(即使是长时间的计算)来说,理想的设计并不总是有助于编写高性能软件。在许多函数之间移动数据需要同时处理许多变量,并经常查阅关于输入和输出参数的文档。对于小型项目来说,在平面名称空间中为每个磁盘文件提供一个函数非常简单,但是对于大型项目来说就比较麻烦了。如果要避免速度瓶颈,必须应用某些编程模式(向量化、内存预分配)。科学计算现在被应用到更多领域,拥有大量不同类型的原生数据。
MathWorks 对 MATLAB 持续进行了创新:内联函数、嵌套函数、变量闭包、大量的数据类型、面向对象特性、单元测试框架等等。每一个创新都可能是一个重要问题的解决方案。但 40 年来这些变化的累积产生了副作用,削弱了概念的简单性和统一性。2009 年,我写了一本书,在不到 100 页的篇幅里,很好地涵盖了我认为 MATLAB 的基本内容。据我所知,它们现在仍可用。不过要想精通的话,还需要了解更多。
Python
从某种意义上说,Python 的历史似乎是 MATLAB 的一个镜像。两者都具有交互式命令行(现在通常叫做 REPL,即“read-eval-print loop”,交互式解释器)、可以自由定义变量和编译。不过 MATLAB 是为数值分析而开发的,而 Python 的诞生是为了满足人们内心的黑客梦。 经过改进和扩展,它们越来越趋于同化。
在我看来,Python 仍缺少数学上的吸引力。它有一些讨厌的小问题,如使用 ** 而不是^,用 @来表示矩阵的相乘(最近的改进!)、使用 shape 而不是 size of 来获取矩阵的大小、仍使用行数据存储等等。如果认为用 V.conj().T@D**3@V 可以很好地表达 V∗D3VV∗D3V,那真的很好笑。Python 里索引从 0 开始(与从 1 开始的索引相反)。我阅读过读取参数这篇文章,我认为这不是关键因素。很明显,这只是一个习惯问题——就像网上圣战一样的问题——因为你也可以给每种命名方式列举出不好的例子。我觉得最主要的是,我们在数学实践中从 1 开始标识向量和矩阵已经有数十年了,大部分的伪代码都做了这样的假设。
除了这些令人讨厌的地方之外,我发现 Python+NumPy+SciPy 的生态系统看起来凌乱且不一致。尽管编程语言致力于面向对象,仍存在矩阵类,但它的使用是不被鼓励而且正在逐渐消失的。也许 MATLAB 轻易俘获了我的心,但是我觉得矩阵是很重要的对象类型,应该给予保留和改进。用*可以实现对数组和矩阵不同的计算,这难道不是面向对象编程(OOP)的一大卖点吗?从这一点来看,有很多不合理的地方(为什么我要使用spsolve命令?稀疏据矩阵中,可以只调用solve吗……)。
这个生态系统有些地方看起来有点薄弱。比如,求积分和解常微分方程在 2019 年看来是一个最小的集合。据我所知,没有针对微分代数方程(DAE)、延时微分方程(DDE)、辛求解的方法或是允许内部 Krylov 子空间迭代的隐式算法。来看下这些函数的相关参考资料,它们都已经 30 年或更久了——仍然很好但远不够完整。Matplotlib 包是个了不起的成果,暂时看起来比 MATLAB 更好,但是它缺乏 3D 支持。
一些专家认为,Python 代码难以跟上编译语言的执行速度有深层次的原因。当看到“python很慢”的搜索结果时,我觉得很可笑。Python 的拥护者提出了很多与 MATLAB 相同的论点/道歉,并不是说他们错了,但这不仅仅是一个有没有远见的问题。
我想我知道为什么对于很多从事科学计算的人来说,Python 如此让人激动。它具有 MATLAB 风格的语法和功能,允许 REPL 运行环境;它有强大的工具,和其它语言在计算领域可以很好地配合使用;它是免费的,从长远来看,有更好的可重复性。很明显,它对很多人有效,没有必要进行语言的切换。
但对于我所知道的科学计算领域,Python 比我过去接触过的语言在学习和使用上更繁琐。我们还不知道它是会继续席卷整个社区,还是已经接近顶峰。我没有特别的预测能力,但我更倾向于认为它会呈下跌趋势。
Julia
作为后来者,Julia 有优势也有劣势。我为 Julia 的创建者鼓掌,因为我相信他们能做得更好:我们需要一门开源的语言,没有使用许可限制。我们希望它有 C 的速度,Ruby 的活力。我们需要像 homoiconic 一样的语言,它像 Lisp 一样有宏,但是也像 Matlab 一样有显而易见、熟悉的数学标记。我们希望它像 Python 一样普通适用,像 R 语言一样适用于统计,像 Perl 一样适用于字符串处理,像 Matlab 处理线性代数一样强大,像 DOS 命令一样擅长粘合程序。它简单易学,却让黑客因其挑战性而欢欣。我们希望它具有互动性且能够被编译。
在很大程度上,我相信他们已经做到了。在后续的 1.0 版本上,REPL 的重要性有所下降,有一些和 MATLAB 略有差别的改动(LinRange 确实比 linspace 更好吗?)。不过这些都是小事而已。
这是我使用的第一种超越 ASCII 的语言,我对其感到不可思议的满意。比如在使用 ϕ变量和≈操作符时,它不仅仅在表面表现优异,虽然需要复杂的教学和文档,但它可以让代码更接近我们所写的数学表达式,而这才是真正的优势。
使用 Julia,我才认识到我保留了很多 MATLAB 的编程习惯,而不是因为其先天优势。很多时候,矢量化并不容易。但在 Julia 中,可以给任意函数名上添加一个点将其矢量化,这让人大开眼界。Julia 用 comprehension 来创建一个矩阵,让 MATLAB 中的嵌套循环(或 meshgrid )看起来有点过时,这样就避免通过 generator 来做简单的汇总,免得大材小用(我记得 Python 也有这个功能)。
相较于面向对象,多重分派(multiple dispatch)让很多事情变得更简单清晰。例如,假设在传统的面向对象语言中,有 Wall 和 Ball 两个类,哪个类可以检测到 Ball 碰到了 Wall 呢?是否需要一个 Room 类来做裁判呢?这类问题让我很困扰。而通过多重分派,数据和对象类型绑定在一起,但是操作数据的方法不需要和类绑定。因此,知道类的类型,但它是独立定义的。我编程很久才意识到,多重分派的有趣和理解多重分派在语言扩展上的重要性。
数字生态系统演进速度非常快。第一个例子是DifferentialEquations.jl,由Chris Rackauckas编写。如果不尽快给这个软件颁发威尔金森奖,那真是太不应该了。
我还想看到 Julia 所承诺的基于 MATLAB 所做的速度提升。部分是因为我相对经验不足和我所承担任务需求,也因为 MathWorks 在自动化代码优化方面做了很出色的工作。在大部分时间里,我所关注的不是编程方面的问题。
用 Julia 编程让我花了一段时间才适应(或许因为我开始变老了,思想固化了)。它让我对数据类型的思考超出了我的想象,我还总觉得自己没有按正确方式去做事。但对于日常使用,我现在更倾向于使用 Julia,而不是 MATLAB。
总结
MATLAB 是一个企业级解决方案,尤其对于工程而言。对于基本数值任务来说,它是最容易学习的。详尽的文档和多年积累的学习工具也相当重要。
MATLAB 是科学计算领域中的宝马轿车。它很昂贵,还不包括附件(工具箱)。你需要为其坚固的车体、平稳的性能和服务而买单。但它也会带来与其价值不匹配的困扰。
Python 是福特皮卡。他在很多地方(美国)广受欢迎。它可以做你希望的任何事,也可以做到其他汽车所不能做到的。你可能时而想要借用一下,但它提供不了纯粹的驾驶体验。
Julia 是特斯拉。它的创造有一个大胆的目标,那就是改变未来。它也可能只是一个脚注,但与此同时,你会很舒服地到达你要去的地方,并且电量会有盈余。
作者介绍:
Toby Driscoll:计算科学教授,主要研究方向为科学计算、计算软件和生命科学中数学的应用。
原文链接:
评论