Main Content

本页翻译不是最新的。点击此处可查看最新英文版本。

在代码生成的可变大小支持方面与 MATLAB 的不兼容性

在标量扩展方面与 MATLAB 的不兼容性

标量扩展是对标量数据进行转换以便与向量或矩阵数据的维度相匹配的一种方法。如果一个操作数是标量,而另一个不是,则标量扩展会将标量应用于另一个操作数中的每个元素。

在代码生成过程中,标量扩展规则同样适用,除非处理的是两个可变大小表达式。在这种情况下,两个操作数必须具有相同的大小。否则,即使其中一个可变大小表达式在运行时变为标量,生成的代码也不会执行标量扩展。因此,如果启用了运行时错误检查,可能会发生运行时错误。

以如下函数为例:

function y = scalar_exp_test_err1(u) %#codegen
y = ones(3);
switch u
    case 0
        z = 0;
    case 1
        z = 1;
    otherwise
        z = zeros(3);
end
y(:) = z;

当您为此函数生成代码时,代码生成器会将 z 确定为一个上界为 3 的可变大小。

This image shows the report for the function scalar_exp_test_err1. z is variable-size with an upper bound of 3, as indicated in the size column. The semicolon next to each dimension indicates that the dimension is variable-size.

如果您运行 u 等于 0 或 1 的 MEX 函数,即使 z 在运行时为标量,生成的代码也不会执行标量扩展。因此,如果启用了运行时错误检查,可能会发生运行时错误。

scalar_exp_test_err1_mex(0)
Subscripted assignment dimension mismatch: [9] ~= [1].

Error in scalar_exp_test_err1 (line 11)
y(:) = z;

为了避免此问题,请使用索引强制 z 为标量值:

function y = scalar_exp_test_err1(u) %#codegen
y = ones(3);
switch u
    case 0
        z = 0;
    case 1
        z = 1;
    otherwise
        z = zeros(3);
end
y(:) = z(1);

在确定可变大小 N 维数组的大小方面与 MATLAB 的不兼容性

对于可变大小 N 维数组,size 函数可能在生成的代码中返回与 MATLAB® 不同的结果。在生成的代码中,size(A) 返回固定长度输出,因为它不会舍弃可变大小 N 维数组的尾部单一维度。与之相比,MATLAB 中的 size(A) 则返回可变长度输出,因为它舍弃了尾部单一维度。

例如,如果数组 A 的形状为 :?x:?x:?size(A,3)==1,则 size(A)

  • 在生成的代码中返回三元素向量

  • 在 MATLAB 代码中返回二元素向量

解决办法

如果您的应用程序要求生成的代码返回与 MATLAB 代码大小相同的可变大小 N 维数组,请考虑以下解决办法之一:

  • 使用具有两个参数的 size

    例如,size(A,n) 在生成的代码和 MATLAB 代码中返回相同的答案。

  • 重写 size(A)

    B = size(A);
    X = B(1:ndims(A));

    此版本返回具有可变长度输出的 X。但是,不能将可变大小 X 传递给需要固定大小参数的矩阵构造函数,例如 zeros

在确定空数组的大小方面与 MATLAB 的不兼容性

空数组的大小在生成的代码中可能与 MATLAB 源代码中不同。此大小在生成的代码中可能为 1x00x1,但在 MATLAB 中可能为 0x0。因此,不应编写依赖特定大小的空矩阵的代码。

以如下代码为例:

function y = foo(n) %#codegen
x = [];
i = 0;
while (i < 10)
    x = [5 x];
    i = i + 1;
end
if n > 0
    x = [];
end
y = size(x);
end

串联要求其操作数与未串联的维度的大小相匹配。在前面的串联中,标量值的大小为 1x1x 的大小为 0x0。为了支持此用例,代码生成器将 x 的大小确定为 [1 x :?]。因为串联之后还有一次赋值 x = [],所以在生成的代码中,x 的大小为 1x0 而不是 0x0

在确定表示为 '' 的空字符向量的大小时,此行为会持续存在。以如下代码为例:

function out = string_size
out = size('');
end

此处,out 的值在生成代码中可能是 1x00x1,但在 MATLAB 中是 0x0

要了解在确定因删除数组元素而产生的空数组的大小时与 MATLAB 的不兼容性,请参阅删除数组元素所生成的空数组的大小

解决方法

如果您的应用程序会检查矩阵是否为空,请使用以下解决办法之一:

  • 重写代码以使用 isempty 函数,而不是 size 函数。

  • 不要使用 x=[] 创建空数组,而应使用 zeros 创建具有特定大小的空数组。例如:

    function y = test_empty(n) %#codegen
    x = zeros(1,0);
    i=0;
    while (i < 10)
        x = [5 x];
        i = i + 1;
    end
    if n > 0
        x = zeros(1,0);
    end
    y=size(x);
    end

在确定空数组的类方面与 MATLAB 的不兼容性

空数组的类在生成的代码中可能与 MATLAB 源代码中不同。因此,不应编写依赖空矩阵的类的代码。

以如下代码为例:

function y = fun(n)
x = [];
if n > 1
    x = ['a' x];
end
y=class(x);
end 
fun(0) 在 MATLAB 中返回 double,但在生成的代码中返回 char。当语句 n > 1 为 false 时,MATLAB 不会执行 x = ['a' x]x 的类为 double,即空数组的类。但是,代码生成器将考虑所有执行路径。它基于语句 x = ['a' x] 确定 x 的类为 char

解决方法

不要使用 x=[] 创建空数组,而应创建特定类的空数组。例如,使用 blanks(0) 创建空字符数组。

function y = fun(n)
x = blanks(0);
if n > 1
    x = ['a' x];
end
y=class(x);
end

在矩阵-矩阵索引方面与 MATLAB 的不兼容性

在矩阵-矩阵索引中,您可以使用一个矩阵来索引另一个矩阵。在 MATLAB 中,矩阵-矩阵索引的一般规则是,结果的大小和方向与索引矩阵的大小和方向保持一致。例如,如果 AB 是矩阵,则 size(A(B)) 等于 size(B)。当 AB 是向量时,MATLAB 将应用特殊规则。特殊的向量-向量索引规则是,结果的方向等于数据矩阵的方向。例如,如果 A 是 1×5,B 是 3×1,则 A(B) 是 1×3。

代码生成器应用与 MATLAB 相同的矩阵-矩阵索引规则。如果 AB 是可变大小矩阵,为了应用矩阵-矩阵索引规则,代码生成器会假设 size(A(B)) 等于 size(B)。如果 AB 在运行时变成向量且方向不同,则该假设不正确。因此,如果启用了运行时错误检查,可能会发生错误。

为了避免此问题,请通过使用冒号操作符进行索引,强制您的数据为向量。例如,假设您的代码在运行时有意要在向量和常规矩阵之间进行切换。您可以对向量-向量索引执行显式检查:

...
if isvector(A) && isvector(B)
    C = A(:);
    D = C(B(:));
else
    D = A(B);
end
...

第一个分支中的索引指定 CB(:) 是编译时向量。因此,代码生成器将应用使用一个向量索引另一个向量的索引规则。结果的方向等于数据向量 C 的方向。

在建立向量-向量索引方面与 MATLAB 的不兼容性

在 MATLAB 中,特殊的向量-向量索引规则是,结果的方向等于数据向量的方向。例如,如果 A 是 1×5,B 是 3×1,则 A(B) 是 1×3。但是,如果数据向量 A 是标量,则 A(B) 的方向等于索引向量 B 的方向。

代码生成器应用与 MATLAB 相同的向量-向量索引规则。如果 AB 是可变大小向量,为了应用索引规则,代码生成器会假设 B 的方向与 A 的方向一致。如果 A 在运行时变成标量,并且 AB 的方向不一致,则该假设不正确。因此,如果启用了运行时错误检查,可能会发生运行时错误。

为了避免出现此问题,应使向量的方向保持一致。或者通过指定行和列来索引单个元素。例如,A(row, column)

在代码生成的矩阵索引操作方面与 MATLAB 的不兼容性

以下限制适用于代码生成时的矩阵索引操作:

  • 初始化以下样式:

    for i = 1:10
        M(i) = 5;
    end
    

    在本例中,执行循环时 M 的大小会改变。代码生成不支持数组大小随着时间改变而增加。

    对于代码生成,需要预分配 M

    M = zeros(1,10);
    for i = 1:10
        M(i) = 5;
    end
    

以下限制适用于在禁用动态内存分配的情况下进行代码生成时的矩阵索引操作:

  • M(i:j),其中 ij 在循环中改变

    在代码生成过程中,不会为大小随着程序的执行而改变的表达式动态分配内存。要实现这种行为,请使用 for- 循环,如下所示:

    ...
    M = ones(10,10);
    for i=1:10
        for j = i:10
            M(i,j) = 2*M(i,j);
        end
    end
    ...

    注意

    矩阵 M 必须在进入循环之前定义。

在串联可变大小矩阵方面与 MATLAB 的不兼容性

要进行代码生成,当您串联可变大小数组时,未串联的维度必须完全匹配。

当串联内可变大小元胞数组的花括号索引不返回任何元素时的差异

假设:

  • c 是一个可变大小元胞数组。

  • 可以使用花括号访问 c 的内容。例如,c{2:4}

  • 将结果包含在串联中。例如,[a c{2:4} b]

  • c{I} 不返回任何元素。c 为空或者花括号内的索引生成空结果。

基于这些条件,MATLAB 将忽略串联中的 c{I}。例如,[a c{I} b] 变为 [a b]。代码生成器将 c{I} 视为空数组 [c{I}]。串联变为 [...[c{i}]...]。此串联将忽略 [c{I}] 数组。因此,[c{I}] 的属性与串联 [...[c{i}]...] 兼容,代码生成器将根据以下规则指定 [c{I}] 的类、大小和复/实性:

  • 类和复/实性与元胞数组的基类型相同。

  • 第二个维度的大小始终为 0。

  • 对于其余的维度,Ni 的大小取决于基类型中对应的维度是固定大小还是可变大小。

    • 如果基类型中对应的维度为可变大小,则结果中的维度大小为 0。

    • 如果基类型中对应的维度为固定大小,则结果中的维度也具有该大小。

假设 c 的基类型的类为 int8,大小为 :10x7x8x:?。在生成的代码中,[c{I}] 的类为 int8[c{I}] 的大小为 0x0x8x0。第二个维度为 0。第一个和最后一个维度为 0,因为这两个维度在基类型中为可变大小。第三个维度为 8,因为基类型的第三个维度的大小固定为 8。

在串联内部,如果可变大小元胞数组的花括号索引不返回任何元素,生成的代码与 MATLAB 可能存在以下差异:

  • 在生成的代码中,[...c{i}...] 的类可能不同于 MATLAB 中的类。

    c{I} 不返回任何元素时,MATLAB 将从串联中删除 c{I}。因此,c{I} 不影响结果的类。MATLAB 基于其余的数组并根据类的优先级确定结果的类。请参阅不同类的有效合并。在生成的代码中,由于代码生成器将 c{I} 视为 [c{I}],因此 [c{I}] 的类将影响整体串联 [...[c{I}]...] 的结果的类。根据前面所述的规则确定 [c{I}] 的类。

  • 在生成的代码中,[c{I}] 的大小可能不同于 MATLAB 中的大小。

    在 MATLAB 中,串联 [c{I}] 为 0×0 双精度值。在生成的代码中,根据前面所述的规则确定 [c{I}] 的大小。