# 数组
非记录数组可以看作是类型兼容的值的集合,见类型相容的表达式。记录数组可以包含标量记录值,其元素的维度大小不同,但除此之外,它们必须具有相同的类型。这种异构数组只能按指定切片或索引使用。一个数组的数组对于所有数组必须具有相同的维度大小(同样的,记录数组对于此要求例外)。数组可看作是相同类型的值的集合。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 , . . .)
返回一个所有元素均为零的
Operator 10.11 ones
ones (n1 , n2 , n3 , . . .)
返回一个
Operator 10.12 fill
fill (s, n1 , n2 , n3 , . . .)
返回一个
递归定义:
该函数需要两个或更多参数;即 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。