问题 MATLAB代码生成如何使用嵌套分支推断输出大小


当使用MATLAB Coder生成C代码时,行为是不同的 if 发生在另一个人的身体里 if 或者在其中 else部分。以下情况可以轻松创建输出大小为5x5的C代码:

function y = foo1(u)
if u > 0
    y = zeros(2,2);
else
    y = zeros(5,5);
end

现在这个也适用

function y = foo2(u,v)
if u > 0
    y = zeros(2,2);
else
    y = zeros(5,5);
    if v > 0
        y = 2 * y;
    end
end

但这个无法生成代码,抱怨大小不匹配:

function y = foo3(u,v)
if u > 0
    y = zeros(2,2);
    if v > 0
        y = 2 * y;
    end
else
    y = zeros(5,5);
end

这是命令行中的输出:

>> codegen foo1.m -args {0}
>> codegen foo2.m -args {0,0}
>> codegen foo3.m -args {0,0}
??? Size mismatch (size [2 x 2] ~= size [5 x 5]).
The size to the left is the size of the left-hand side of the assignment.

Error in ==> foo3 Line: 8 Column: 5
Code generation failed: Open error report.
Error using codegen (line 144)

我在MATLAB R2013b和R2015a中看到过这种行为。


12991
2017-11-26 13:27


起源



答案:


文档,Matlab codegen 必须在编译时知道矩阵的大小 除非  codegen 被告知或推断矩阵的大小可变。有几种方法可以让Matlab知道矩阵的大小可变:

  1. 运用 coder.varsize 函数,矩阵可以显式声明为可变大小。
  2. MATLAB可以根据代码的结构推断出矩阵的大小可变。

正如您的代码所暗示的,选项(2)显然并不健全。在某些情况下,Matlab会尝试推断出一个简单的东西 if  else 声明,但这种推断似乎非常脆弱,如您的示例所示。

而不是依靠MATLAB来正确地推断矩阵是否是可变大小,解决方案是做出明确的声明:

function y = foo3(u,v)
  coder.varsize('y', []);  % Let codegen know y is variable sized 
                           % and can be arbitrary dimensions
                           % an alternative is: coder.varsize('y',[5,5]);
  if u > 0
     y = zeros(2,2);
     if v > 0
       y = 2 * y;
     end
   else
     y = zeros(5,5);
   end

为什么Matlab想知道这些信息?如果在编译时已知矩阵的大小,则可能进行各种额外的优化(循环展开等...)。


7
2017-11-30 21:06



只是为了澄清,因为没有明确提到: coder.varsize('y',[5,5]) 指定 [5 5] 作为一个 上限 对于矩阵的可变大小。也可以将一个或多个维度指定为固定大小,将其他维度指定为可变大小 - 在答案中链接的文档页面中更多。 - mikkola
值得注意的是,OP中的所有示例都不是有效的C.在if语句中声明的变量将在C中具有单独的范围。由于此代码在Matlab中有效,因此可以希望matlab编码器能够解决此问题,但是人们不能假设这一点。我猜这个问题 foo3与此有关 y 在if语句中使用(y = 2*y)然后在else语句中重新定义,该语句稍后将在文本中写入。我认为这让Matlab编码器感到困惑。我不完全确定。 - patrik
@patrik,没有必要让这些例子有效C. MATLAB Coder的工作是从codegen支持的MATLAB语言子集中生成有效的C语言。也就是说,预先声明MATLAB变量,类似于C风格,可以让代码更容易理解代码。 - Ryan Livingston
@RyanLivingston这就是我的意思,正如我所说,MATLAB编码器并不完美。远离C代码作为在不同的if-else分支中定义具有相同名称和不同大小的变量并对它们执行操作可能过于复杂。这需要Coders方面的相当复杂的预测。但是,我想我们在这里是一样的,所以谢谢你的澄清。 - patrik
@patrik,的确,我没有仔细阅读,道歉。如果你想把你的解释作为答案发布,我会把我的解释。我认为让这个处理的心理模型在其自己的答案中详细说明可能会有所帮助。 - Ryan Livingston


答案:


文档,Matlab codegen 必须在编译时知道矩阵的大小 除非  codegen 被告知或推断矩阵的大小可变。有几种方法可以让Matlab知道矩阵的大小可变:

  1. 运用 coder.varsize 函数,矩阵可以显式声明为可变大小。
  2. MATLAB可以根据代码的结构推断出矩阵的大小可变。

正如您的代码所暗示的,选项(2)显然并不健全。在某些情况下,Matlab会尝试推断出一个简单的东西 if  else 声明,但这种推断似乎非常脆弱,如您的示例所示。

而不是依靠MATLAB来正确地推断矩阵是否是可变大小,解决方案是做出明确的声明:

function y = foo3(u,v)
  coder.varsize('y', []);  % Let codegen know y is variable sized 
                           % and can be arbitrary dimensions
                           % an alternative is: coder.varsize('y',[5,5]);
  if u > 0
     y = zeros(2,2);
     if v > 0
       y = 2 * y;
     end
   else
     y = zeros(5,5);
   end

为什么Matlab想知道这些信息?如果在编译时已知矩阵的大小,则可能进行各种额外的优化(循环展开等...)。


7
2017-11-30 21:06



只是为了澄清,因为没有明确提到: coder.varsize('y',[5,5]) 指定 [5 5] 作为一个 上限 对于矩阵的可变大小。也可以将一个或多个维度指定为固定大小,将其他维度指定为可变大小 - 在答案中链接的文档页面中更多。 - mikkola
值得注意的是,OP中的所有示例都不是有效的C.在if语句中声明的变量将在C中具有单独的范围。由于此代码在Matlab中有效,因此可以希望matlab编码器能够解决此问题,但是人们不能假设这一点。我猜这个问题 foo3与此有关 y 在if语句中使用(y = 2*y)然后在else语句中重新定义,该语句稍后将在文本中写入。我认为这让Matlab编码器感到困惑。我不完全确定。 - patrik
@patrik,没有必要让这些例子有效C. MATLAB Coder的工作是从codegen支持的MATLAB语言子集中生成有效的C语言。也就是说,预先声明MATLAB变量,类似于C风格,可以让代码更容易理解代码。 - Ryan Livingston
@RyanLivingston这就是我的意思,正如我所说,MATLAB编码器并不完美。远离C代码作为在不同的if-else分支中定义具有相同名称和不同大小的变量并对它们执行操作可能过于复杂。这需要Coders方面的相当复杂的预测。但是,我想我们在这里是一样的,所以谢谢你的澄清。 - patrik
@patrik,的确,我没有仔细阅读,道歉。如果你想把你的解释作为答案发布,我会把我的解释。我认为让这个处理的心理模型在其自己的答案中详细说明可能会有所帮助。 - Ryan Livingston


我同意Matthew Gunn的回答,这是为行为添加一些解释。关于Coder如何分析您的MATLAB代码的一个好的心理模型是它从上到下查看它。

在前两个示例中应用该心智模型,确定大小的分配 yy = zeros(2,2) 和 y = zeros(5,5),发生在价值之前 y 是用过的。因此Coder可以将两种尺寸合并为自动制作 y 一个可变大小的数组。在第三个例子中,赋值 y = zeros(2,2) 发生然后 y 用来: y = 2 * y。此时,编码器需要确定乘法的大小和类型 2 * y。只有 2-by-2 看到分配所以推断出 2 * y 还必须退货 2-by-2 矩阵。

执行此推断然后嵌入假设 y 是 2-by-2 进入代码并基本上锁定了大小 y 以便随后分配给 y 用一个 5-by-5 矩阵必须失败。


2
2018-01-05 12:38