How MATLAB code generation infers output size with nested branches
Asked Answered
R

2

11

When generating C code using MATLAB Coder, the behaviour is different when an if happens in body of another if or in its elsesection. The following case easily creates C code with output having size 5x5:

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

Now this one works as well

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

But this one fails to generate code, complaining about size mismatch:

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

Here is the output in command-line:

>> 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)

I have seen this behaviour in MATLAB R2013b and R2015a.

Roebuck answered 26/11, 2015 at 13:27 Comment(0)
H
7

From the docs, Matlab codegen must know the size of a matrix at compile time unless codegen is told or infers that the matrix is of variable size. There are several ways to let Matlab know a matrix will be of variable size:

  1. Using coder.varsize function, a matrix can be explicitly declared to be of variable size.
  2. MATLAB can infer a matrix is of variable size from the structure of the code.

As your code suggests, option (2) apparently isn't robust. Matlab tries to infer in some cases when there's a simple if else statement, but that inference appears to be quite fragile, as shown by your example.

Rather than rely on MATLAB to correctly infer whether a matrix is variable size, a solution is to make an explicit declaration:

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

Why might Matlab want to know this information? If a matrix's size is known at compile time, all kinds of additional optimizations may be possible (loop unrolling etc...).

Hearken answered 30/11, 2015 at 21:6 Comment(6)
Just to clarify, since it was not explicitly mentioned: coder.varsize('y',[5,5]) specifies [5 5] as an upper bound for the variable size of the matrix. It's also possible to specify one or more dimensions as fixed and others as variable sized -- this and more in the documentation page linked in the answer.Brickbat
Worth noting is that none of the examples in the OP is valid C. Variables declared inside an if statement will have a separate scope in C. Since this code works in Matlab one can hope that the matlab coder would be able to solve this, but one cannot assume this. I would guess the problem in foo3 has to do with that y is used in the if statement (y = 2*y) and then redefined in the else statement, which is is written later in the text. I think this confuses the Matlab coder. I am not entirely sure though.Disagreeable
@patrik, there's no need for the examples to be valid C. The job of MATLAB Coder is to produce valid C from the subset of the MATLAB language which is supported for codegen. That said, declaring MATLAB variables up front, similar to the C style, can make the code easier for Coder to understand.Wondawonder
@RyanLivingston That is about what I meant, as I think is stated, the MATLAB Coder is not perfect. Going to far from C-code as defining variables with the same name and different size in different if-else branches and performing operations on them, might be a too complicated. This would require quite complicated predictions from the Coders side I guess. However, I guess we are onto the same track here, so thank you for clarifying.Disagreeable
@patrik, indeed, I didn't read carefully, apologies. If you want to post your explanation as an answer, I'll take mine down. I think it could be helpful to have the mental model for this processing spelled out in its own answer.Wondawonder
@RyanLivingston you can keep the answer. It was more about pointing out that sometimes it may be useful to help the Coder, and that this problem should rather be seen as a "flaw" of the Coder. It was stated in a comment because it was not so elaborated and pretty similar to this post.Disagreeable
W
2

I agree with Matthew Gunn's answer, this is to add some explanation for the behavior. A good mental model to have about how Coder analyzes your MATLAB code is that it looks at it from the top to the bottom.

Applying that mental model, in your first two examples, the assignments which determine sizes of y: y = zeros(2,2) and y = zeros(5,5), occur before the value of y is ever used. So Coder can merge both sizes to automatically make y a variable-sized array. In the third example, the assignment y = zeros(2,2) occurs and then y is used: y = 2 * y. At this point, Coder needs to determine the size and type of the multiplication 2 * y. Only the 2-by-2 assignment is seen so it is inferred that 2 * y must also return a 2-by-2 matrix.

Performing this inference then embeds the assumption that y is 2-by-2 into the code and essentially locks the size of y so that the subsequent assignment to y with a 5-by-5 matrix must fail.

Wondawonder answered 5/1, 2016 at 12:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.