# 数组


非记录数组可以看作是类型兼容的值的集合,见类型相容的表达式。记录数组可以包含标量记录值,其元素的维度大小不同,但除此之外,它们必须具有相同的类型。这种异构数组只能按指定切片或索引使用。一个数组的数组对于所有数组必须具有相同的维度大小(同样的,记录数组对于此要求例外)。数组可看作是相同类型的值的集合。Modelica 数组可以是多维的、“矩形的”,当为矩阵时,矩阵中所有行的长度相等,所有列的长度相等。

每一个数组有确定的维,也就是维的数目。数组退化--标量,不是真正的数组,但是可以看作是维数为零的数组。向量有一维,矩阵有二维,等等。在 Modelica 里不能区分所谓的行向量和列向量, 因为向量只有一维。如果真想做出区分,可以区分行矩阵和列矩阵,因为它是二维的。但是,实际上不需要这样,因为已定义的通常的矩阵算法和线性代数的操作,作用在 Modelica 的向量和矩阵上时, 能得到预期的结果。

Modelica 是一种强类型语言,也包括数组类型。数组的维数是固定的,在运行时间不能更改。固定的数组维度保证了执行强类型检查和高效的实现。但是,数组维的长度可在运行期间计算,这使得可以写出通用的数组操作代码,以及与其它语言编写的标准库的接口。

数组的分配(allocate)是通过声明数组变量或者通过调用数组构造器(constructor)。数组元素可以用 Integer, Boolean, or enumeration(整数、布尔数或者枚举)值来索引。

# 数组声明

Modelica 的类型系统包括标量数、向量、矩阵(维数 ndim=2)和维数大于 2 的数组。

下表表示声明两的种可能形式,并定义了术语。C 是任何类型的占位符,包括内置类型 Real、 Integer、Boolean、String 和枚举类型。表中某一维的上界表达式的类型,如 n、m、nk 等, 应为 Integer 的子类型、或为枚举类型 E 的名字 E、或为 Boolean 类型。

冒号(:)表示维的上界为知, 且为 Integer 的子类型。这样一个变量的大小可以从它的绑定方程中确定,或者从它的任何数组属性的大小中确定,参见函数中的大小可变的数组以及数组大小的改变。大小不能从其他方程或算法中确定。

数组维的索引的上下边界见数组维的上下界描述。

以布尔或枚举类型索引的数组只能按以下方式使用:

  • 使用正确类型(即布尔或枚举类型)的表达式作下标。
  • 数组允许使用形如 x1 = x2 的绑定方程以及形如 x1 := x2 的赋值声明,不管数组下标类型是整型的子类型、布尔型或枚举类型。

EB 表示枚举类型或布尔类型。一般数组可以有一个或多个维度(k≥1)。下表是数组声明的一般形式:

Modelica form 1 Modelica form 2 # dims Designation Explanation
C x; C x; 0 Scalar Scalar
C[n] x; C x[n]; 1 Vector n - vector
C[EB] x; C x[EB] 1 Vector Vector indexed by EB
C[n, m] x; C x[n, m]; 2 Matrix n × m matrix
C[n₁, n₂, ..., nₖ] x; C x[n₁, n₂, ..., nₖ]; k Array General array

用数组维度声明的组件,或者元素类型为数组类型的组件,称为数组变量。维数和维大小是类型的一部份,例如在重新声明的时候,应该进行检查。声明形式 1 清楚地显示了数组的类型,而形式 2 是诸如 FORTRAN、C、C++ 等语言的数组定义的传统形式。

Real[:] v1, v2 // Vectors v1 and v2 have unknown sizes.
                  // The actual sizes may be different. 

可以混合使用这两种声明形式,尽管这可能会造成混淆。

Real [3 , 2] x [4 , 5]; // x has type Real[ 4 , 5 , 3 , 2 ] ;

作出这一排序的原因可通过以下示例说:

type R3 = Real[3];
R3 a;
R3 b[1] = {a};
Real[3] c[1] = b; 

正常情况下对“a”和“b”使用 type 关键字,用类型的定义取代该类型名,就得到“c”的定义。

用枚举值索引的的向量:

ype TwoEnums = enumeration(one,two); 
Real[TwoEnums] y;

允许维大小为 0,这样 C x[0];声明了一个空的向量,而 C x[0,3];声明了一个空矩阵。下表给出了一些大小为 1 的数组维数的例子:

Modelica form 1 Modelica form 2 # dims Designation Explanation
C[1] x; C x[1]; 1 Vector 1-vector, representing a scalar
C[1, 1] x; C x[1, 1]; 2 Matrix (1×1)-matrix, representing a scalar
C[n, 1] x; C x[n, 1]; 2 Matrix (n×1)-matrix, representing a column
C[1, n] x; C x[1, n]; 2 Matrix (1×n)-matrix, representing a row

数组的数组是多维数组类型,其前面部分的维定义来自组件声明,其后面部分的维定义来自组件类型。如果一个类型是内置内型之一(Real, Integer, Boolean, String, 枚举),或者是用 type 定义的类,该类型将被最大程度地扩展。在算子重载之前, 一个变量的 type class 被最大程度地扩展。

示例:

type Voltage = Real(unit = "V");
type Current = Real(unit = "A");
connector Pin
  Voltage v; // type class of v = Voltage, type of v = Real
  flow Current i; // type class of i = Current, type of i = Real
end Pin;
type MultiPin = Pin[5];
MultiPin[4] p; // type class of p is MultiPin, type of p is Pin[4, 5];
type Point = Real[3];
Point p1[10];
Real p2[10, 3]; 

组件 p1 和 p2 具有相同的类型。

p2[5] = p1[2]+ p2[4]; // equivalent to p2[5,:] = p1[2,:] + p2[4,:] 
Real r[3] = p1[2]; // equivalent to r[3] = p1[2,:]

仿真时自动断言(assertion):

令 A 为声明的数组,i 为声明的第 di 维的长度。如果在编译时不能检查 i >= 0,则生成断言语句 assert( i >= 0, …)。断言失败时能否生成好的错误信息,反映了语言实现的水平。

令 A 为已声明的数组,i 为访问 di 维指标的指标。对于每次指标的访问,如果在编译时候不能检查断言 assert(i >= 1 and i <= size(A,di), …),则生成该断言语句。

因为效率的原因,可选择抑制这些隐含的断言语句。

# 数组维的上下界

以整数、布尔数或者枚举值索引的数组维,其上下界规定如下:

  • 以整数索引的数组维,其下界为 1,上界是维的长度。
  • 以布尔值索引的数组维,其下界为 false,上界为 true。
  • 以枚举类型 E=enumeration(e1, e2, ..., en) 的值索引的数组维,其下届是 E.e1,上界是 E.en。

# 可变的数组大小

关于可变大小的数组和在函数中改变数组的大小,参见初始化和方程绑定

# 内置的数组操作函数

Modelica 提供多个用于数组操作的内置函数。

下面的 promote 函数不能在 Modelica 中使用,但在下面用于定义其他的数组操作符和数组操作函数:

Expression Description Details
promote(A, n) Append dimensions of size 1 Operator 10.1

Operator 10.1 promote

promote (A, n)

将数组 A 从右侧填充尺寸为 1 的维度直至达到 n 维,其中要求 n ≥ ndims(A)。

,且 ,那么 ,对于 ;对于

参数 n 必须是一个在平移过程中可被评估的常量,因为它决定了返回数组的维度数量。

(一个非常量 n 在平移过程中为促进计算而进行的求值会使矩阵处理复杂化,因为它可能以微妙的方式改变矩阵方程(例如将内积运算转变为矩阵乘法)。)

以下示例展示了如何使用下面五小节中定义的函数:

Real x[4,1,6];
size(x,1) = 4;
size(x); // vector with elements 4, 1, 6 
size(2*x+x ) = size(x);
Real[3] v1 = fill(1.0, 3); 
Real[3,1] m = matrix(v1); 
Real[3] v2 = vector(m);
Boolean check[3,4] = fill(true, 3, 4);

# 数组维和维长度的操作函数

下面列出的函数对表达式类型的数组维数进行操作:

Expression Description Details
ndims(A) Number of dimensions Operator 10.2
size(A, i) Size of single array dimension Operator 10.3
size(A) Sizes of all array dimensions Operator 10.4

Operator 10.2 ndims

ndims (A)

返回表达式 A 的维度数 k,其中 k ≥ 0。

Operator 10.3 size

size (A, i)

返回数组表达式 A 的第 i 维大小,其中 0 ≤ i ≤ ndims(A)。

若 A 指向可扩展连接器的某个组件,则该组件必须是该可扩展连接器的已声明组件,且不得使用冒号(:)来指定第 i 维的数组大小。

Operator 10.4 size

size (A)

返回一个包含 A 维度大小的向量,其长度为 ndims(A)。

若 A 指代可扩展连接器的组件,则该组件必须是已声明的可扩展连接器组成部分,且不得使用冒号(:)来指定任何数组维度的尺寸。

# 维转换函数

下列转换函数通过增减单元素维度,将标量、向量和数组转换为标量、向量或矩阵。

Expression Description Details
scalar(A) Extract only element Operator 10.5
vector(A) Vector of all elements Operator 10.6
matrix(A) Two - dimensional array Operator 10.7

Operator 10.5 scalar

scalar (A)

返回数组 A 中的单一元素。要求对于 1 ≤ i ≤ ndims(A) 范围内的维度,size(A, i) = 1 必须成立。

Operator 10.6 vector

vector (A)

若 A 为标量则返回 1 维向量,否则当数组最多只有一个维度大小大于 1 时,返回包含该数组所有元素的向量。

Operator 10.7 matrix

matrix (A)

若 A 为标量或向量则返回 promote(A, 2),否则将前两个维度的元素作为矩阵返回。要求对于 2 < i ≤ ndims(A) 满足 size(A, i) = 1。

# 特殊的数组构造函数

数组构造函数构造并返回一个据其参数计算出的数组。下面表格中的多数构造函数是按一定模式向数组填值来构造数组,另外几个是将所有数组元素置为相同值。一般的构造数组的语法为:array(…) 或者 {…}。

Expression Description Details
identity(n) Identity matrix Operator 10.8
diagonal(v) Diagonal matrix Operator 10.9
zeros(n₁, n₂, n₃, ...) Array with all elements being 0 Operator 10.10
ones(n₁, n₂, n₃, ...) Array with all elements being 1 Operator 10.11
fill(s, n₁, n₂, n₃, ...) Array with all elements equal Operator 10.12
linspace(x₁, x₂, n) Vector with equally spaced elements Operator 10.13

Operator 10.8 identity

identity (n)

返回 n×n 整数单位矩阵,对角线元素为 1,其余位置为 0。

Operator 10.9 diagonal

diagonal (v)

返回一个方阵,其对角线元素为向量 v 的元素,其余所有元素均为零。

Operator 10.10 zeros

zeros (n1 , n2 , n3 , . . .)

返回一个所有元素均为零的 整数数组()。该函数需要一个或多个参数,即 zeros() 是不合法的。

Operator 10.11 ones

ones (n1 , n2 , n3 , . . .)

返回一个 的整数数组,其中所有元素均为 1()。该函数需要一个或多个参数,因此 ones() 是不合法的。

Operator 10.12 fill

fill (s, n1 , n2 , n3 , . . .)

返回一个 的数组,其所有元素均等于标量或数组表达式 s()。\n返回的数组与 s 具有相同类型。

递归定义:

该函数需要两个或更多参数;即 fill(s) 的写法不合法。

Operator 10.13 linspace

linspace (x1 , x2 , n)

返回一个包含 n 个等间距元素的实数向量,使得 v = linspace(x1, x2, n) 的结果为:

要求 n ≥ 2。参数 x1 和 x2 应为数值标量表达式。

# 归约函数和操作符

下列归约函数将数组(或多个标量)“归约”为单一值(通常为标量,但求和归约函数可能返回数组结果,也可应用于运算符记录)。需注意这些运算符(尤其是 min 和 max)本身不会生成事件(但参数可能生成事件)。

Expression Description Details
min(A) Least element or array Operator 10.14
min(x, y) Least of two scalars Operator 10.15
min(… for …) Reduction to least value Operator 10.16
max(A) Greatest element or array Operator 10.17
max(x, y) Greatest of two scalars Operator 10.18
max(… for …) Reduction to greatest value Operator 10.19
sum(A) Sum of scalar array elements Operator 10.20
sum(… for …) Sum reduction Operator 10.21
product(A) Product of scalar array elements Operator 10.22
product(… for …) Product reduction Operator 10.23

Operator 10.14 min

min (A)

返回数组表达式 A 的最小元素;按 < 运算符定义。

Operator 10.15 min

min (x, y)

返回标量 x 和 y 中的最小元素;根据 < 定义。

Operator 10.16 min

min (e(i, . . ., j) for i in u, . . ., j in v)

返回标量表达式 e(i, ..., j) 在所有 i 属于 u,...,j 属于 v 的组合下求值所得的最小值(由 < 运算符定义)。

Operator 10.17 max

max (A)

返回数组表达式 A 中的最大元素;按 > 运算符定义。

Operator 10.18 max

max (x, y)

返回标量 x 和 y 中的最大元素;按 > 运算符定义。

Operator 10.19 max

max (e(i, . . ., j) for i in u, . . ., j in v)

该函数返回标量表达式 e(i,...,j) 在 u 集合中的 i 与 v 集合中的 j 等所有组合求值时的最大值(按 > 运算符定义)。

Operator 10.20 sum

sum (A)

返回数组表达式 A 中所有元素的标量和。等价于对所有数组索引进行求和归约(见下文,包括对运算符记录的应用):sum(A[j, k, ...] 其中 j, k, ...)。

Operator 10.21 sum

sum (e(i, . . ., j) for i in u, . . ., j in v)

返回表达式 e(i, ..., j) 针对 u 中的 i 至 v 中的 j 所有组合求值的总和。

求和归约函数(两种变体)可应用于运算符记录,前提是该运算符记录定义了 '0' 和 '+'。此时假定其构成一个加法群。

对于整数索引而言:

e(u[1], ..., v[1]) + e(u[2], ..., v[1]) + ...
+ e(u[end], ..., v[1]) + ...
+ e(u[end], ..., v[end]) 

对于非整数索引,该方法会使用所有有效索引而非仅限于 1..end 范围。

表达式 sum(e(i, ..., j) for i in u, ..., j in v) 的类型与 e(i, ..., j) 的类型相同。

Operator 10.22 product

product (A)

返回数组表达式 A 中所有元素的标量积。等价于对所有数组索引进行乘积归约:product(A[j, k, . . .] for j, k, . . .)。

Operator 10.23 product

product (e(i, . . ., j) for i in u, . . ., j in v)

返回表达式 e(i,...,j) 在 u 中的 i 与 v 中的 j 等所有组合求值后的乘积。

对于整数索引而言:

e(u[1], ..., v[1]) * e(u[2], ..., v[1]) * ...
* e(u[end], ..., v[1]) * ...
* e(u[end], ..., v[end]) 

对于非整数索引,该方法会使用所有有效索引而非仅限于 1..end 范围。

乘积类型(对于 i 在 u 中、...、j 在 v 中的 e(i,...,j))与 e(i,...,j) 的类型相同。

# 归约表达式

表达式:

function-name "(" expression1 for iterators ")"

为归约表达式。归约表达式的迭代器中的表达式必须为向量表达式,它们在每个归约表达式中只计算一次,且是在直接包含归约表达式的作用域中进行。

对于迭代器:

IDENT in expression2

循环变量 IDENT 在 expression1 内部的作用域中。同在 for 子句中一样,循环变量可以掩盖其他变量。结果依赖于 function-name,当前合法的函数仅只有内置操作符 array、sum、product、min 和 max。如果 function-name 是 sum、product、min 或 max,则结果与 expression1 具有相同类型,通过对循环变量的每个值计算 expression1,并计算出元素的和、积、最小值或最大值。

Reduction Restriction on expression1 Result for empty expression2
sum Integer or Real zeros(...)
product Scalar Integer or Real 1
min Scalar enumeration, Boolean, Integer or Real Greatest value of type
max Scalar enumeration, Boolean, Integer or Real Least value of type

示例:

sum(i for i in 1:10) // Gives $\sum_{i = 1}^{10} i = 1 + 2 +... + 10 = 55$
// Read it as: compute the sum of i for i in the range 1 to 10.
sum(i^2 for i in {1,3,7,6}) // Gives $\sum_{i \in \{1,3,7,6\}} i^2 = 1 + 9 + 49 + 36 = 95$
{product(j for j in 1:i) for i in 0:4} // Gives {1, 1, 2, 6, 24}
max(i^2 for i in {3,7,6}) // Gives 49 

# 矩阵和矢量的代数函数

下面列出了矩阵和向量代数的函数。函数 transpose 可以应用于任何矩阵。函数 outerProduct、symmetric、cross 和 skew 要求实向量 (s) 或矩阵作为输入 (s),并返回一个实向量或矩阵。

Expression Description Details
transpose(A) Matrix transpose Operator 10.24
outerProduct(x, y) Vector outer product Function 10.1
symmetric(A) Symmetric matrix, keeping upper part Function 10.2
cross(x, y) Cross product Function 10.3
skew(x) Skew symmetric matrix associated with vector Function 10.4

Operator 10.24 transpose

transpose (A)

对数组 A 的前两个维度进行置换。若数组 A 的维度不足两个,则报错。

Function 10.1 outerProduct

outerProduct (x, y)

返回向量 x 和 y 的外积,即:矩阵 (x) * 转置矩阵 (y)。

Function 10.2 symmetric

symmetric (A)

返回一个对称矩阵,该矩阵与方阵 A 在对角线及其上方完全相同。 即,若 B := symmetric(A),则 B 由以下公式给出:

Function 10.3 cross

cross (x, y)

返回三维向量 x 和 y 的叉积:

Function 10.4 skew

skew (x)

返回与三维向量相关联的 3×3 斜对称矩阵,即叉积 (x, y) = 斜对称矩阵 (x) * y。等价地,斜对称矩阵 (x) 由以下公式给出:

# 向量、矩阵和数组的构造

构造函数 array(A,B,C,…) 按照以下规则从其参数构造一个数组:

  • 大小匹配:所有参数必须具有相同大小,即,size(A) = size(B) = size(C) = ...。
  • 所有参数必须类型相容。结果数组的数据类型是参数类型的最大扩展类型。实型和整型子类型可以混用,产生实型结果数组,其中整型数被转为实型数。
  • 每应用一次该构造函数,就相对于参数数组的维数在结果数组左边增加了长度为 1 的一维, 即,ndims(array(A,B,C)) = ndimes(A) + 1 = ndims(B) + 1, ...。
  • {A, B, C, ...} 是 array(A, B, C, ...) 的简化记号。
  • 必须至少有一个参数。
    (array() 或 {} 没有定义的原因是至少需要一个参数来确定结果数组的类型。)

示例:

{1, 2, 3} is a 3-vector of type Integer.
{{11, 12, 13}, {21, 22, 23}} is a 2 x 3 matrix of type Integer
{{{1.0, 2.0, 3.0}}} is a 1 x 1 x 3 array of type Real.

Real[3] v = array(1, 2, 3.0);
type Angle = Real(unit="rad");
parameter Angle alpha = 2.0; // type of alpha is Real.
// array(alpha, 2, 3.0) or {alpha, 2, 3.0} is a 3−vector of type Real.
Angle[3] a = {1.0, alpha, 4}; // type of a is Real[3]. 

# 用迭代器构造数组

表达式:

"{" expression for iterators "}"

或者:

array "(" expression for iterators ")"

是带迭代器的数组构造。迭代器内的表达式应为向量表达式,构造数组时只对其计算一次,计算在直接包含数组构造器的作用域内进行。

对于一个迭代器:

IDENT in array_expression

循环变量 IDENT 在数组构造器的表达式内的作用域里。和在 for 子句中一样,循环变量可以掩盖其他变量。循环变量与 array_expression 元素具有相同的类型;并且可以是简单类型,也可以是记录类型。循环变量将在整个循环中具有相同的类型-即,对于 array_expression{1,3.2},迭代器将在所有迭代中具有类型兼容表达式(Real)的类型。

# 带一个迭代器的数组构造

如果只使用一个迭代器,则用循环变量每次的迭代值计算表达式,用形成的结果数组,来构造向量。

示例:

array(i for i in 1:10)
// Gives the vector 1:10 = {1, 2, 3, ..., 10}
{r for r in 1.0 : 1.5 : 5.5}
// Gives the vector 1.0:1.5:5.5 = {1.0, 2.5, 4.0, 5.5}
{i^2 for i in {1,3,7,6}}
// Gives the vector {1, 9, 49, 36} 

# 带多个迭代器的数组构造

带多个迭代器的记法是嵌套数组构造的速记法。通过将每个‘,’替换为‘} for’,并为数组构造预先添加‘{’, 这种记法可以扩展为平常的形式。

示例:

Real toeplitz[:,:]= {i-j for i in 1:n, j in 1:n};
Real toeplitz2[:,:]={{i-j for i in 1:n} for j in 1:n}

# 数组串接

函数 cat(k, A, B, C, …) 按照以下规则沿着指定维 k 串接数组 A,B,C,…:

  • 数组 A, B, C, … 的维数必须相同,即,ndims(A) = ndims(B) = …。
  • 数组 A, B, C, … 必须是类型相容的表达式(见函数相容或函数子类型),其类型决定了结果数组的类型。最大扩展类型要求相等。实型和整型子类型可以混用,结果产生实型数组,这里整数被转为实数。
  • k 必须为已有的维,即,1<=k<=ndims(A)=ndims(B)=ndins(C);k 必须为整数。
  • 大小匹配:数组 A, B, C, … 必须具有相等的大小,除了第 k 维之外,即,size(A, j) = size(B, j), 对于 1<=j<=ndims(A) 且 j<>k。

示例:

Real[2,3] r1 = cat(1, {{1.0, 2.0, 3}}, {{4, 5, 6}});
Real[2,6] r2 = cat(2, r1, 2*r1);

形式上,连接运算 R = cat(k, A, B, C, ...) 定义如下:设 n = ndims(A) = ndims(B) = ndims(C) = ...,则 R 的维度大小由以下规则确定。

且 R 的数组元素由下式给出

其中 成立。

# 沿第一维和第二维方向的数组串接

为方便起见,为沿第一维和第二维方向的串接提供一种特殊的语法支持:

  • 沿着第一维方向的串接:
    其中 。如有必要,在执行操作之前,将大小为 1 的维加到 A、B、C 的右边,以使操作数具有相同的至少为 2 的维数。
  • 沿着第二维方向的串接:
    其中 。如果必要,在执行操作之前,将大小为 1 的维加到 A、B、C 的右边,特别地,每个操作数至少有 2 维。
  • 这两种形式能够混合。 的优先级高,例如, 解析为
  • ,即,如果 具有 2 维或更多维数,;如果 是标量或向量,为矩阵,且具有 的元素。
  • 至少要有一个参数(即 [ ] 没有定义)。

示例:

Real s1, s2, v1[n1], v2[n2], M1[m1,n],
M2[m2,n], M3[n,m1], M4[n,m2], K1[m1,n,k],
K2[m2,n,k];
[v1;v2] is a (n1+n2) x 1 matrix
[M1;M2] is a (m1+m2) x n matrix
[M3,M4] is a n x (m1+m2) matrix
[K1;K2] is a (m1+m2) x n x k array
[s1;s2] is a 2 x 1 matrix
[s1,s1] is a 1 x 2 matrix
[s1] is a 1 x 1 matrix
[v1] is a n1 x 1 matrix
Real[3] v1 = array(1, 2, 3);
Real[3] v2 = {4, 5, 6};
Real[3,2] m1 = [v1, v2];
Real[3,2] m2 = [v1, [4;5;6]]; // m1 = m2
Real[2,3] m3 = [1, 2, 3; 4, 5, 6];
Real[1,3] m4 = [1, 2, 3];
Real[3,1] m5 = [1; 2; 3]; 

# 向量构造

向量可以用构造一般数组的方法进行构造,如,Real[3] v = {1,2,3}。

simple-expression 中的范围向量操作符或冒号操作符,可用于代替一般数组构造器或与其组合, 来构造实型、整型、布尔型或枚举型向量。冒号操作符的语义为:

  • 为整型向量 ,如果 j 和 k 是整型。
  • 是实型向量 ,其中 ,如果 j 和 / 或 k 是实型。
  • 为实型、整型、布尔型或枚举类型的具有零个元素的向量,如果
  • 为整型向量 ,其中 ,如果 j、d 和 k 都为整型。
  • 为实型向量 ,其中 ,如果 j、d 或 k 为实型。
  • 为具有零个元素的实型或整型向量,如果 或者如果
  • 为布尔型向量
  • ,如果 j 为实型、整型、布尔型或枚举类型。
  • 为枚举类型向量 ,其中 ,且 ei 和 ej 属于枚举类型。

示例:

Real v1[5] = 2.7 : 6.8;
Real v2[5] = {2.7, 3.7, 4.7, 5.7, 6.7}; // = same as v1
Boolean b1[2] = false:true;
Colors = enumeration(red, blue, green);
Colors ec[3] = {Colors.red, Colors.green, Colors.green}; 

# 数组索引

数组索引操作符 name[…] 用于访问数组元素,读取或改变元素的值。索引操作不能超出维的上下界(假设索引操作需要常数时间,即与数组大小无关)。数组操作需要两个或更多操作数, 第一个操作数是被索引的数组,其它操作数是索引表达式:

arrayname[indexexpr1, indexexpr2, ...]

冒号(':')用于表示某一维度的所有索引。向量表达式可用于选取向量、矩阵和数组中的特定行、列及元素。当标量索引参数的数量增加时,表达式的维度数量会相应减少。若索引参数数量少于数组维度数量,则后续索引将默认使用冒号(':')。

在算法章节中,也可以使用数组访问运算符为数组的一个或多个元素赋值。这种操作被称为索引赋值语句。若索引本身是一个数组,则赋值操作将按照索引数组指定的顺序执行。对于数组及其元素的赋值操作,在给任何元素赋予新值之前,都会先完整计算右侧表达式和左侧索引的值。

(假设索引操作的时间复杂度为常数,即基本不受数组大小的影响。)

示例:数组索引表达式:

a[:, j] // Vector of the j'th column of a.
a[j] // Vector of the j'th row of a. Same as: a[j, :]
a[j : k] // Same as: {a[j], a[j+1], ..., a[k]}
a[:, j : k] // Same as: [a[:, j], a[:, j+1], ..., a[:, k]] 

范围向量运算符只是向量表达式的一种特例:

v [2 : 2 : 8] // Same as : v [ { 2 , 4 , 6 , 8 }]

赋值语句中的数组索引:

v [{ j , k }] := {2 , 3}; // Same as : v [ j ] := 2 ; v [ k ] := 3 ;
v [{1 , 1}] := {2 , 3}; // Same as : v [ 1 ] := 3 ;

若 x 为向量,则 x[1] 为标量,但切片 x[1:5] 仍为向量(使用向量索引或冒号索引表达式将返回向量值)。

标量与使用冒号索引创建的数组切片对比示例。下面示例利用了数组变量 x[n,m]、v[k] 和 z[i,j,p]。

Expression # dims Description
x[1, 1] 0 Scalar
x[:, 1] 1 n-vector
x[1, :] or x[1] 1 m-vector
v[1:p] 1 p-vector
x[1:p, :] 2 p×m matrix
x[1:1, :] 2 1×m "row" matrix
x[{1, 3, 5}, :] 2 3×m matrix
x[:, v] 2 n×k matrix
z[:, 3, :] 2 i×p matrix
x[scalar([1]), :] 1 m-vector
x[vector([1]), :] 2 1×m "row" matrix

# 用布尔值或枚举值索引

可以用枚举或布尔类型的值索引数组,而不仅仅能用整数。

示例:

type ShirtSizes = enumeration(small, medium, large, xlarge);
Real[ShirtSizes] w;
Real[Boolean] b2;
algorithm
w[ShirtSizes.large] := 2.28; // Assign a value to an element of w
b2[true] := 10.0;
b2[ShirtSizes.medium] := 4; // Error, b2 was declared with Boolean dimension
w[1] := 3; // Error, w was declared with ShirtSizes dimension 

# 用 end 索引

表达式 end 只能作为数组下标出现。假设A的索引是整数的子类型,假设 end 用在数组表达式 A 的第 i 个下标位置上,则 end 等于 size(A,i);如果用在嵌套的数组下标中,它指的是最内层的数组。

示例:

A[end - 1, end] is A[size(A,1) - 1, size(A,2)]
A[v[end], end] is A[v[size(v,1)], size(A,2)] // First end is referring to end of v.
Real B[Boolean];
B[end] is B[true] 

# 标量、向量、矩阵和数组操作符函数

定义在标量、向量和矩阵上的数学操作是线性代数的主题。

在所有需要实型子类型表达式的上下文环境中,可使用整型子类型表达式,整型表达式自动转换为实型。

术语数值或数值类在下面用于指实型的子类型或整型类的子类型。

# 等式与赋值

数值标量、向量、矩阵和数组的加法 a + b 与减法 a - b 按元素定义,要求 size(a) = size(b) 且 a 和 b 为数值类型。一元加法和减法同样按元素定义。字符串标量、向量、矩阵和数组的加法 a + b 定义为 a 和 b 对应元素的字符串连接操作,同样要求 size(a) = size(b)。

数组和标量的等式和赋值:

Size of a Size of b Size of a = b Operation
Scalar Scalar Scalar
n-vector n-vector n-vector
n×m matrix n×m matrix n×m matrix
n×m×... n×m×... n×m×...

# 加法、减法与字符串拼接

数值标量、向量、矩阵和数组的加法 a + b 与减法 a - b 按元素定义,要求 size(a) = size(b) 且 a 和 b 为数值类型。一元加法和减法则按元素定义。字符串标量、向量、矩阵和数组的加法 a + b 定义为对应元素的字符串连接操作,同样要求 size(a) = size(b)。

数组加法、减法及字符串连接:

Size of a Size of b Size of a ± b Operation c := a ± b
Scalar Scalar Scalar
n - vector n - vector n - vector
n×m matrix n×m matrix n×m matrix
n×m×... n×m×... n×m×...

数值标量、向量、矩阵或数组 a与 b 的逐元素加法 a .+ b 和减法 a .- b 要求 a 和 b 具有数值类型类别,并且满足 size(a) = size(b) 或其中一个是标量。字符串标量、向量、矩阵和数组的逐元素加法 a .+ b 被定义为 a 与 b 对应元素的逐元素字符串连接操作,要求满足 size(a) = size(b) 或其中一个是标量。

数组的 element-wise 加减和字符串串联:

Size of a Size of b Size of a.±b Operation c := a.±b
Scalar Scalar Scalar
Scalar n×m×... n×m×...
n×m×... Scalar n×m×...
n×m×... n×m×... n×m×...

一元运算符:

Size of a Size of ± a Operation c := ± a
Scalar Scalar
n×m×... n×m×...

# Element-wise 乘法

标量乘法 s * a 或 a * s 的定义是对数值标量 s 与数值标量、向量、矩阵或数组 a 进行逐元素运算:

数值数组的矩阵和向量乘法:

Size of s Size of a Size of s * a and a * s Operation c := s * a or c := a * s
Scalar Scalar Scalar
Scalar n - vector n - vector
Scalar n×m matrix n×m matrix
Scalar n×m×... n×m×...

数值标量、向量、矩阵或数组 a 与 b 的逐元素乘法 a .* b 要求 a 和 b 具有数值类型类别,并且满足 size(a) = size(b) 或其中一个是标量。

数组逐元素乘法:

Size of a Size of b Size of a .* b Operation c := a .* b
Scalar Scalar Scalar
Scalar n×m×... n×m×...
n×m×... Scalar n×m×...
n×m×... n×m×... n×m×...

# 矩阵与向量的乘法

数值向量与矩阵的乘法运算 a * b 仅针对以下组合情形定义:

矩阵与向量对具有数值元素的数组进行乘法运算:

Size of a Size of b Size of a * b Operation c := a * b
m - vector m - vector Scalar
m - vector m×n matrix n - vector
l×m matrix m - vector l - vector
l×m matrix m×n matrix l×n matrix

示例:

Real A[3, 3], x[3], b[3], v[3];
A * x = b;
x * A = b; // same as transpose([x])*A*b
[v] * transpose([v]) // outer product
v * A * v // scalar
transpose([v]) * A * v // vector with one element 

# 标量或者数值数组的标量除法

数值标量、向量、矩阵或数组 a 与数值标量 s 的除“a/s”按元素逐个定义。结果总为实型。为了得到带截断的整数除,使用函数 div:

标量和数组的数值元素除法:

Size of a Size of s Size of a / s Operation c := a / s
Scalar Scalar Scalar
n - vector Scalar n - vector
n×m matrix Scalar n×m matrix
n×m×... Scalar n×m×...

# 数组的 Element-wise 除法

数值标量、向量、矩阵或者数组 a 和 b 的 element 除法 a./b,要求要求 size(a) = size(b) 或者 a 为标量或者 b 为标量。

数组的 element-wise 除法:

Size of a Size of b Size of a./b Operation c := a./b
Scalar Scalar Scalar
Scalar n×m×... n×m×...
n×m×... Scalar n×m×...
n×m×... n×m×... n×m×...

示例:逐元素除以标量 (./) 与除以标量 (/) 是相同的:a ./ s = a / s:

2./[1, 2; 3, 4] // error; same as 2.0 / [1, 2; 3, 4]
2 ./[1, 2; 3, 4] // fine; element−wise division 

这是解析规则的结果,因为“2.”是一个词法单元。在字面量后使用空格即可解决该问题。

# 数值元素的标量指数

幂运算 a ^ b 总是返回一个实数标量值,并且要求 a 和 b 是标量实数或整数表达式。结果应对应以下特殊情况下的数学幂运算:

  • 对于任意值 a(包括0.0)和整数 b = 0,结果为 1.0。
  • 如果 a < 0 且 b 是整数,则结果定义为 ±,其符号取决于 b 是偶数(正)还是奇数(负)。
  • 不建议的语义是将 A< 0 和将一个实型的 b 当作是一个具有非零整数值的整数。
  • 对于 a = 0 且 b >0,结果为 0.0。
  • 其他特殊情况是非法的。例如:a =0.0 和 b = 0.0 对于 Realb, a = 0.0,而 b< 0,或者 a<0 且 b 不具有整数值。

(除了定义的特殊情况外,它对应于 ANSIC 库中的 pow(double a, double b)。其结果总是一个实数,因为当两个操作数都是整数时,负指数也可以给出非整数结果。对整数指数的特殊处理使得在幂级数 n 中使用 x 成为可能。)

数值标量、向量、矩阵或数组 a 和 b 的 element-wise 指数运算 a.^b,要求 a 和 b 为数值类型类(numeric type class),并且要求 size(a) = size(b)或者 a 为标量或者 b 为标量。

数组的 element-wise 指数运算:

Size of a Size of b Size of a.^b Operation c := a.^b
Scalar Scalar Scalar
Scalar n×m×... n×m×...
n×m×... Scalar n×m×...
n×m×... n×m×... n×m×...

示例:

2.^[1,2;3,4] // error, since 2.0^[1,2;3,4]
2 .^[1,2;3,4] // fine, element-wise exponentiation

提示

这是由于(语法)解析规则造成的,因为 2. 是一个词法单元。在它 2 后面加一个空格就消除了这个问题

# 数值方阵的标量指数

如果“a”是一个数值方阵,“s”是整型子类型的标量,s>=0,则乘方“a^s”有定义,乘方通过重复相乘进行。

示例:

a^3 = a * a * a;
a^0 = identity(size(a, 1));
assert(size(a, 1) == size(a, 2), "Matrix must be square");
a^1 = a; 

注意

禁止非整型乘方,因为这需要计算“a”的特征值和特征向量,这就不再是对元素的操作了。

# Slice 操作

以下内容适用于切片操作:

  • 若 a 是一个包含标量分量的数组,且 m 是这些分量中的一个分量,表达式 a.m 将被解释为切片操作。该操作返回由分量 {a[1].m,...} 构成的数组。
  • 若 m 同为数组元素,则切片操作仅在满足 size(a[1].m) = size(a[2].m) = ... 时有效。
  • 切片操作可以与索引结合使用,例如 a.m[1]。该操作返回由 {a[1].m[1], a[2].m[1], ...} 组成的数组,且不要求 size(a[1].m) = size(a[2].m)。m 的下标数量不得超过 m 的数组维数(该数量可以更小,此时缺失的尾部索引默认为‘:’),且仅当 size(a[1].m[...]) = size(a[2].m[...]) 时操作才有效。

示例:操作数的大小限制仅在第二个操作数的索引使用向量或冒号时适用:

constant Integer m=3;
Modelica.Blocks.Continuous.LowpassButterworth tf[m](n=2:(m+1));
Real y[m];
Real y2,y3;
equation
// Extract the x1 slice even though different x1's have different lengths
y = tf.x1[1]; // Legal, = {tf[1].x1[1], tf[2].x1[1], ... tf[m].x1[1]};
y2 = sum(tf.x1[:]); // Illegal to extract all elements since they have
// different lengths. Does not satisfy:
// size(tf[1].x1[:]) = size(tf[2].x1[:]) = ... = size(tf[m].x1[:])
y3 = sum(tf.x1[1:2]); // Legal.
// Since x1 has at least 2 elements in all tf, and
// size(tf[1].x1[1:2]) = ... = size(tf[m].x1[1:2]) = {2} 

在这个例子中,不同的 x1 向量具有不同的长度,但仍然可以对它们执行某些操作。

# 关系操作符

关系操作符 <、<=、>、>=、==、<> 只对简单类型的标量操作数有定义,对数组无定义,见等于、关系和逻辑操作符

# 布尔操作符

操作符“and”和“or”,以两上布尔类型的表达式为操作数,表达式可为标量或维数匹配的数组。操作符“not”以一个布尔类型的的表达式为操作数,表达式可为标量或数组。结果是对元素的逻辑操作。关于“and”和“or”的短路计算,见求值顺序

# 函数的向量化调用

函数中的大小可变的数组以及数组大小的改变

# 标准类型强制转换

在所有需要 Real 的子类型表达式的上下文中,也可以使用 Integer 的子类型表达式;Integer 表达式自动转换为实数。

这也适用于 Real 数组,以及记录表达式的字段。对于子类型没有类似的规则。

示例:

record RealR
  Real x,y;
end RealR;
record IntegerR
  Integer x,y;
end IntegerR;
parameter Integer a = 1;
Real y(start=a); // Ok, a is automatically coerced to Real
RealR r1 = IntegerR(a, a); // Ok, record is automatically coerced
RealR r2 = RealR(a, a); // Ok, a is automatically coerced to Real 

# 空数组

数组的维长度可以为 0,示例:

Real x[0]; // an empty vector
Real A[0, 3], B[5, 0], C[0, 0]; // empty matrices 

空矩阵可以用函数 fill 来构造,示例:

Real A[:, :] = fill(0.0, 0, 1); // a Real 0 x 1 matrix
Boolean B[:, :, :] = fill(false, 0, 1, 0); // a Boolean 0 x 1 x 0 matrix 

示例:虽然对标量索引到数组的空维度是一个错误,但并非所有对空数组应用索引的操作都是无效的:

Real[1, 0] a = fill(0.0, 1, 0); // a Real 1 x 0 matrix
Real[0] a1a = a[1]; // empty vector
Real[0] a1b = a[1, :]; // same as above
Real[0] a1c = a[1, 1 : end]; // same as above, as 1 : end is empty 

若某一维度为零,则运算(如加、减)的尺寸要求也必须满足。示例:

Real[3, 0] A, B;
Real[0, 0] C;
A + B // fine, result is an empty matrix
A + C // error, sizes do not agree 

两个空矩阵的乘法运算会产生对应数值类型的零矩阵,前提是结果矩阵不存在零维度尺寸的情况,即:

Real[0, m] * Real[m, n] = Real[0, n] // empty matrix
Real[m, n] * Real[n, 0] = Real[m, 0] // empty matrix
Real[m, 0] * Real[0, n] = fill(0.0, m, n) // non−empty matrix of zeros 

示例:

Real u[p], x[n], y[q], A[n, n], B[n, p], C[q, n], D[q, p];
der(x) = C * A * D + B * u; 

假设 n = 0,p > 0,q > 0:结果为 y = D * u。