罗兰谈MATLAB的艺术

将想法转化为MATLAB

请注意

罗兰谈MATLAB的艺术已存档,不会更新。

MATLAB算法在R2016b中扩展

我很荣幸地介绍今天的客座博主,我的同事,史蒂夫·埃丁.他一直积极参与我们工具中的图像处理功能,最近还对MATLAB语言的设计添加和改进做出了重大贡献。

今年夏天早些时候,我正在写一些颜色空间转换代码。在代码中,我有一个Px3矩阵叫做RGB,包含P种颜色,每行一种。我还有一个1x3的向量,v.我需要把每一列相乘RGB的对应元素v,像这样:

RGB_c = [RGB (: 1) * v (1) RGB (:, 2) * v (2) RGB (:, 3) * v (3)];

但由于我使用的是MATLAB R2016(9月14日发布)的内部开发版本,所以我没有输入上面的代码。相反,我输入了以下内容:

RGB_c = RGB .* v;

在R2016a和更老的MATLAB版本中,这行代码产生了一个错误:

>> RGB_c = RGB .* v错误使用.*矩阵维数必须一致。

在新的版本中,MATLAB隐式地扩大向量v与矩阵的大小相同RGB然后进行元素乘法运算。我说“隐式”是因为MATLAB实际上并没有在内存中生成展开向量的副本。

今天我想解释一下MATLAB算术算子(以及一些函数)的这种新的隐式展开行为。我会讲一下它是如何运作的以及我们为什么要这么做。在讨论的下一部分,我将使用MathWorks中几乎每个人在讨论这个主题时都会使用的一个示例:从矩阵中减去列。

内容

减法列的意思是:每十年

假设你有一个矩阵一个

A = rand(3,3)
A = 0.48976 0.70936 0.6797 0.44559 0.75469 0.6551 0.64631 0.27603 0.16261

假设你想修改每一列一个减去这一列的均值。的的意思是函数方便地给出了每一列的方法:

ma = mean(A)
Ma = 0.52722 0.58003 0.49914

但自是不是尺寸一样一个它不是标量,不能直接相减一个直接。相反,你必须扩张大小与…相同一个然后做减法。

在MATLAB的第一个十年中,专家用户通常使用一种称为托尼的技巧进行展开。

Ma_expanded = ma(ones(3,1),:)
Ma_expanded = 0.52722 0.58003 0.49914 0.52722 0.58003 0.49914 0.52722 0.58003 0.49914
A - ma_expanded
Ans = -0.037457 0.12934 0.18057 -0.081635 0.17466 0.15596 0.11909 -0.304 -0.33653

在MATLAB的第二个十年(粗略地说),大多数人开始使用一个名为repmat(“复制矩阵”的简称)来进行展开。

Ma_expansion = repmat(ma,3,1)
Ma_expansion = 0.52722 0.58003 0.49914 0.52722 0.58003 0.49914 0.52722 0.58003 0.49914

使用函数repmat比使用托尼的方法更具可读性,但它仍然在内存中创建了扩展的矩阵。对于真正大的问题,额外的内存分配和内存复制会显著降低计算速度,甚至导致内存不足错误。

在MATLAB的第三个十年里,我们引入了一个新的函数叫做bsxfun这样就可以直接做减法运算而不用在内存中做一个展开的向量。你这样称呼它:

bsxfun (@minus、马)
Ans = -0.037457 0.12934 0.18057 -0.081635 0.17466 0.15596 0.11909 -0.304 -0.33653

函数名中的“bsx”指的是“二进制单例展开”,这里的术语“二进制”指的是接受两个输入的操作符。(不,这不是所有人最喜欢的函数名。)

这个函数可以工作,并且已经被使用了很多次。截至一年前,大约有1800次使用bsxfun740个文件。

但也有人抱怨bsxfun

bsxfun痛苦

除了这个尴尬的名字,还有其他的可用性和性能问题bsxfun

  • 没有多少人知道这个函数。在寻求减法方面的帮助时,人们应该去找它,这一点都不明显。
  • 使用bsxfun需要一定程度的编程抽象(调用一个函数将另一个函数应用到一组输入),这似乎与应用程序不匹配(基本算术)。
  • 使用bsxfun需要相对高级的MATLAB编程知识。你必须理解函数句柄,你必须知道MATLAB算术运算符的函数等价(例如+-,rdivide).
  • 这是比较困难的MATLAB执行引擎生成器生成与基本算术代码一样高效的代码。
  • 使用bsxfun看起来不像数学。(有些人甚至说它很丑。)

就这样,14年后克里夫硅藻土最初提出做的时候,我们用MATLAB改变了双输入算术算子、逻辑算子、关系算子,以及几个双输入函数来做bsxfun样式的隐式展开,只要输入有兼容的大小

兼容的大小

表达式A - b只要一个而且B兼容的大小.如果对于每个维度,输入的维度大小相同或其中一个为1,则两个数组具有兼容的大小。在最简单的情况下,如果两个数组大小完全相同,或者其中一个是标量,则它们是兼容的。

这里有一些不同情况下的兼容尺寸的说明。

  • 两个大小完全相同的输入。

  • 一个输入是标量。

  • 一个输入是一个矩阵,另一个输入是具有相同行数的列向量。

  • 一个输入是列向量,另一个输入是行向量。请注意,在这种情况下,两个输入都隐式展开,每个都朝不同的方向展开。

  • 一个输入是一个矩阵,另一个输入是一个具有相同行数和列数的三维数组。注意矩阵的大小一个在第三维中被隐式地认为是1,因此一个可以在三维空间中展开到和B

  • 一个输入是一个矩阵,另一个输入是一个三维数组。维数都是相同的,或者其中一个是1。注意,这是两个输入都隐式展开的另一种情况。

金宝app支持的操作符和函数

这是MATLAB算子和函数的初始集合它们现在具有隐式展开行为。

+ - .* ./ .\ .^ < <= > >= == ~= | & xor bitor bitand bitxor min Max mod rem hypot atan2

我预计,随着时间的推移,其他功能将被添加到这个集合中。

反对

这种对MATLAB算法的改变在MathWorks中并非没有争议。有些人担心用户编写的代码在某种程度上依赖于这些操作符在某些情况下产生错误。但是,在检查了我们自己的代码库,并预览了R2016a和R2016b预发布版中的更改之后,我们没有看到在实践中出现重大的兼容性问题。

其他人认为新的运算符行为没有充分地基于线性代数符号。然而,与其将MATLAB视为纯粹的线性代数符号,不如将MATLAB视为矩阵和数组计算符号更为准确。从这个意义上说,MATLAB在发明符号方面有很长的历史,这些符号被广泛接受,包括反斜杠、冒号和各种形式的下标。

最后,有些人担心当用户试图将两个向量相加而没有意识到其中一个是列,另一个是行时会发生什么。在MATLAB的早期版本中,这会产生一个错误。在R2016b中,它产生一个矩阵。(我喜欢称这个矩阵为外和这两个向量。)但我们相信,这个问题会立即被注意到,并很容易得到纠正。事实上,我认为注意到这个问题比错误地使用运算符而不是.*操作符。此外,MATLAB中相对较新的对数组大小的保护限制(Preferences -> MATLAB -> Workspace -> MATLAB数组大小限制)可以防止MATLAB试图形成一个可能导致内存不足的超大矩阵。

实践中的隐式展开

在决定做出这一改变之前,作为研究的一部分,我们回顾了人们是如何使用的bsxfun.在这篇文章的最后,我将向你展示一些最常见的用法bsxfun就像你在R2016b中使用隐式展开重写它们一样。

将蒙版应用到真彩色图像。

%蒙版:480x640 % rgb: 480x640x3
% OLD rgb2 = bsxfun(@times,rgb,mask);
% NEW rgb = rgb .*掩码;

归一化矩阵列(减去平均值,除以偏差)。

% X: 1000x4 mu = mean(X);sigma = std(X);
% OLD Y = bsxfun(@rdivide,bsxfun(@minus,X,mu),sigma);
% NEW Y = (X -) ./;

计算成对距离矩阵。

对于两组向量,计算每个向量对之间的欧几里得距离。

% X: 4x2(4个向量)% Y: 3x2(3个向量)X =重塑(X,[4 1 2]);Y =重塑(Y,[1 3 2]);
% OLD m = bsxfun(@负,X,Y);D = hypot(m(:,:,1),m(:,:,2));
% NEW m = X - Y;D = hypot(m(:,:,1),m(:,:,2));

计算外层和。

这个例子是来自实现的托普利兹函数。另见my2008年2月25日邻居索引的帖子另一个应用。

Cidx = (0:m-1)';Ridx = p:-1:1;
% OLD ij = bsxfun(@plus,cidx,ridx);
% NEW ij = cidx + ridx;

找出互为倍数的整数。

这个例子是来自于Redheffer矩阵的计算画廊函数。它说明了函数中的隐式展开行为,而不是运算符。

I = 1:n;
% OLD A = bsxfun(@rem,i,i') == 0;
% NEW A = rem(i,i') == 0;

你自找的

最后,我想对所有的MATLAB用户说一声这些年来他们一直要求这种行为。文件交换贡献者当他把这句话包含在他的实现中时,他代表了你们所有人提交

我们到处都需要完全的单例扩展。。为什么不是% % [1 2]+ [0 1]' == [1 2;2 3]?% % bsxfun()是一个彻头彻尾的黑客,并且污染了%所有人的代码。

尤瓦尔,这是给你的。

读者们,你们用过吗bsxfun过吗?你会使用新的算术行为吗?让我们知道在这里




发布与MATLAB®R2016b


评论

如欲留言,请点击在这里登录您的MathWorks帐户或创建一个新帐户。