Modelica® - A Unified Object-Oriented Language for Systems Modeling Language Specification Version 3.4

Chapter 10 Arrays

An array can be regarded as a collection of values, all of the same type. Modelica arrays can be multidimensional and are “rectangular,” which in the case of matrices has the consequence that all rows in a matrix have equal length, and all columns have equal length.

Each array has a certain dimensionality, i.e., number of dimensions. The degenerate case of a scalar variable is not really an array, but can be regarded as an array with zero dimensions. Vectors have one dimension, matrices have two dimensions, etc. [So-called row vectors and column vectors do not exist in Modelica and cannot be distinguished since vectors have only one dimension. If distinguishing these is desired, row matrices and column matrices are available, being the corresponding two-dimensional entities. However, in practice this is seldom needed since the usual matrix arithmetic and linear algebra operations have been defined to give the expected behavior when operating on Modelica vectors and matrices.]

Modelica is a strongly typed language, which also applies to array types. The number of dimensions of an array is fixed and cannot be changed at run-time [in order to permit strong type checking and efficient implementation.] However, the sizes of array dimensions can be computed at run-time, [allowing fairly generic array manipulation code to be written as well as interfacing to standard numeric libraries implemented in other programming languages.]

An array is allocated by declaring an array variable or calling an array constructor. Elements of an array can be indexed by Integer, Boolean, or enumeration values.

10.1 Array Declarations

The Modelica type system includes scalar number, vector, matrix (number of dimensions, ndim=2), and arrays of more than two dimensions. [There is no distinguishing between a row and column vector.]

The following table shows the two possible forms of declarations and defines the terminology. C is a placeholder for any class, including the built-in type classes Real, Integer, Boolean, String, and enumeration types. The type of a dimension upper bound expression, e.g. n, m, p,… in the table below, need to be a subtype of Integer or EB for a class EB that is an enumeration type or subtype of the Boolean type. Colon (:) indicates that the dimension upper bound is unknown and is a subtype of Integer.

Upper and lower array dimension index bounds are described in section 10.1.1.

An array indexed by Boolean or enumeration type can only be used in the following ways:

  • Subscripted using expressions of the appropriate type (i.e. Boolean or the enumerated type)

  • Binding equations of the form x1 = x2 as well as declaration assignments of the form x1 := x2 are allowed for arrays independent of whether the index types of dimensions are subtypes of Integer, Boolean, or enumeration types.

Table 10.1: General forms of declaration of arrays.
Modelica form 1 Modelica form 2 # dimensions 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 index by enumeration or Boolean type EB
C[n, m] x; C x[n, m]; 2 Matrix n x m Matrix
C[n1, n2,…,nk] x; C x[n1, n2,…,nk]; k Array Array with k dimensions (k>=0).

[The number of dimensions and the dimensions sizes are part of the type, and shall be checked for example at redeclarations. Declaration form 1 displays clearly the type of an array, whereas declaration form 2 is the traditional way of array declarations in languages such as Fortran, C, C++.

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

It is possible to mix the two declaration forms although it might be confusing.

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

The reason for this order is given by examples such as:

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

Using a type for “a” and “b” in this way is normal, and substituting a type by its definition allow “c”.

A vector y indexed by enumeration values

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

]

Zero-valued dimensions are allowed, so: C x[0]; declares an empty vector and: C x[0,3]; an empty matrix. [Special cases:

Table 10.2: Declaration of arrays as 1-vectors, row-vectors, or column-vectors of arrays.
Modelica form 1 Modelica form 2 # dimensions 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 x 1 – Matrix, representing a scalar
C[n,1] x; C x[n, 1]; 2 Matrix n x 1 – Matrix, representing a column
C[1,n] x; C x[1, n]; 2 Matrix 1 x n – Matrix, representing a row

]

The type of an array of array is the multidimensional array which is constructed by taking the first dimensions from the component declaration and subsequent dimensions from the maximally expanded component type. A type is maximally expanded, if it is either one of the built-in types (Real, Integer, Boolean, String, enumeration type) or it is not a type class. Before operator overloading is applied, a type class of a variable is maximally expanded.

[Example:

  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];

The components p1 and p2 have identical types.

  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,:]

]

[Automatic assertions at simulation time:

Let A be a declared array and i be the declared maximum dimension size of the di-dimension, then an assert statement assert(i>=0, ...) is generated provided this assertion cannot be checked at compile time. It is a quality of implementation issue to generate a good error message if the assertion fails.

Let A be a declared array and i be an index accessing an index of the di-dimension. Then for every such index-access an assert statement assert(i>=1 and i<=size(A,di), ... ) is generated, provided this assertion cannot be checked at compile time.

For efficiency reasons, these implicit assert statement may be optionally suppressed.]

10.1.1 Array Dimension Lower and Upper Index Bounds

The lower and upper index bounds for a dimension of an array indexed by Integer, Boolean, or enumeration values are as follows:

  • An array dimension indexed by integers has a lower bound of 1 and an upper bound being the size of the dimension.

  • An array dimension indexed by Boolean values has the lower bound false and the upper bound true.

  • An array dimension indexed by enumeration values of the type E=enumeration(e1, e2, …, en) has the lower bound E.e1 and the upper bound E.en.

10.2 Flexible Array Sizes

Regarding flexible array sizes and resizing of arrays in functions, see section 12.4.5.

10.3 Built-in Array Functions

Modelica provides a number of built-in functions that are applicable to arrays.

The following promote function cannot be used in Modelica, but is utilized below to define other array operators and functions:

Table 10.3: Promote function (cannot be used in Modelica).
promote(A,n) Fills dimensions of size 1 from the right to array A upto dimension n, where ”n >= ndims(A)” is required. Let C = promote(A,n), with nA=ndims(A), then ndims(C) = n, size(C,j) = size(A,j) for 1 <= j <= nA, size(C,j) = 1 for nA+1 ¡= j ¡= n, C[i_1, …, i_nA, 1, …, 1] =A[i_1, …, i_nA]

[The function promote cannot be used in Modelica, because the number of dimensions of the returned array cannot be determined at compile time if n is a variable. Below, promote is only used for constant n.

Some examples of using the functions defined in the following section 10.3.1 to section 10.3.5:

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

]

10.3.1 Array Dimension and Size Functions

The following built-in functions for array dimensions and dimension sizes are provided:

Table 10.4: Built-in array dimension and size functions.
Modelica Explanation
ndims(A) Returns the number of dimensions k of expression A, with k >= 0.
size(A,i) Returns the size of dimension i of array expression A where i shall be > 0 and <= ndims(A).
size(A) Returns a vector of length ndims(A) containing the dimension sizes of A.

10.3.2 Dimensionality Conversion Functions

The following built-in conversion functions convert scalars, vectors, and arrays to scalars, vectors, or matrices by adding or removing 1-sized dimensions.

Table 10.5: Built-in dimensionality conversion functions.
Modelica Explanation
scalar(A) Returns the single element of array A. size(A,i) = 1 is required for 1 <= i <= ndims(A).
vector(A) Returns a 1-vector, if A is a scalar and otherwise returns a vector containing all the elements of the array, provided there is at most one dimension size > 1.
matrix(A) Returns promote(A,2), if A is a scalar or vector and otherwise returns the elements of the first two dimensions as a matrix. size(A,i) = 1 is required for 2 < i <= ndims(A).

10.3.3 Specialized Array Constructor Functions

An array constructor function constructs and returns an array computed from its arguments. Most of the constructor functions in the table below construct an array by filling in values according to a certain pattern, in several cases just giving all array elements the same value. The general array constructor with syntax array (…) or {…} is described in section 10.4.

Table 10.6: Specialized array constructor functions.
Modelica Explanation
identity(n) Returns the n x n Integer identity matrix, with ones on the diagonal and zeros at the other places.
diagonal(v) Returns a square matrix with the elements of vector v on the diagonal and all other elements zero.
zeros(n1,n2,n3,...) Returns the n1 x n2 x n3 x … Integer array with all elements equal to zero (ni >= 0). The function need one or more arguments, that is zeros() is not legal.
ones(n1,n2,n3,...) Return the n1 x n2 x n3 x … Integer array with all elements equal to one (ni >=0 ). The function need one or more arguments, that is ones() is not legal.
fill(s,n1,n2,n3,...) Returns the n1 x n2 x n3 x … array with all elements equal to scalar or array expression s (ni >= 0). The returned array has the same type as s. Recursive definition: fill(s,n1,n2,n3,…) = fill(fill(s,n2,n3, …), n1,); fill(s,n)={s,s,…, s} The function needs two or more arguments; that is fill(s) is not legal.
linspace(x1,x2,n) Returns a Real vector with n equally spaced elements, such that v=linspace(x1,x2,n), v[i] = x1 + (x2-x1)*(i-1)/(n-1) for 1 <= i <= n. It is required that n >= 2. The arguments x1 and x2 shall be numeric scalar expressions.

10.3.4 Reduction Functions and Operators

A reduction function “reduces” an array (or several scalars) to one value (normally a scalar - but the sum reduction function may give an array as result and also be applied to an operator record). Note that none of these operators (particularly min and max) generate events themselves (but arguments could generate events). The restriction on the type of the input in section 10.3.4.1 for reduction expressions also apply to the array elements/scalar inputs for the reduction operator with the same name.

The sum reduction function (both variants) may be applied to an operator record, provided that the operator record defines ’0’ and ’+’. It is then assumed to form an additive group.

The following reduction functions are available:

Table 10.7: Array reduction functions and operators.
Modelica Explanation
min(A) Returns the least element of array expression A; as defined by <.
min(x,y) Returns the least element of the scalars x and y; as defined by <.
min(e(i, ..., j)
 for i in  u,
   ...,  j in  v)
Also described in section 10.3.4.1
Returns the least value (as defined by <) of the scalar expression e(i, …, j) evaluated for all combinations of i in u, …, j in v:
max(A) Returns the greatest element of array expression A; as defined by >.
max(x,y) Returns the greatest element of the scalars x and y; as defined by >.
max(e(i, ..., j)
 for i in  u,
   ...,  j in  v)
Also described in section 10.3.4.1 Returns the greatest value (as defined by >) of the scalar expression e(i, …, j) evaluated for all combinations of i in u, …, j in v:
sum(A)
Returns the scalar sum of all the elements of array expression:
A[1,...,1]+A[2,...,1]+....+A[end,...,1]+A[end,...,end]
sum(e(i, ..., j)
 for i in  u,
   ...,  j in  v)
Also described in section 10.3.4.1
Returns the sum of the expression e(i, …, j) evaluated for all combinations of i in u, …, j in v: e(u[1],… ,v[1])+e(u[2],… ,v[1])+… +e(u[end],… ,v[1])+…+e(u[end],… ,v[end]) The type of sum(e(i, …, j) for i in u, …, j in v) is the same as the type of e(i,…j).
product(A)
Returns the scalar product of all the elements of array expression A.
A[1,…,1]*A[2,…,1]*….*A[end,…,1]*A[end,…,end]
product(e(i, ..., j)
 for i in  u,
   ...,  j in  v)
Also described in section 10.3.4.1.
Returns the product of the scalar expression e(i, …, j) evaluated for all combinations of i in u, …, j in v:
  e(u[1],...,v[1])*e(u[2],...,v[1])*...
  *(u[end],...,v[1])*...*e(u[end],...,v[end])
The type of product(e(i, …, j) for i in u, …, j in v) is the same as the type of e(i,…j).

10.3.4.1 Reduction Expressions

An expression:

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

is a reduction-expression. The expressions in the iterators of a reduction-expression shall be vector expressions. They are evaluated once for each reduction-expression, and are evaluated in the scope immediately enclosing the reduction-expression.

For an iterator:

IDENT in expression2

the loop-variable, IDENT, is in scope inside expression1. The loop-variable may hide other variables, as in for-clauses. The result depends on the function-name, and currently the only legal function-names are the built-in operators array, sum, product, min, and max. For array, see section 10.4. If function-name is sum, product, min, or max the result is of the same type as expression1 and is constructed by evaluating expression1 for each value of the loop-variable and computing the sum, product, min, or max of the computed elements. For deduction of ranges, see section 11.2.2.1; and for using types as ranges see section 11.2.2.2.

Table 10.8: Reduction expressions with iterators.
Function-name Restriction on expression1 Result if expression2 is empty
sum Integer or Real zeros(...)
product Scalar Integer or Real 1
min Scalar enumeration, Boolean, Integer or Real
Greatest value of type
( Modelica.Constants.inf for Real)
max Scalar enumeration, Boolean, Integer or Real
Least value of type
( -Modelica.Constants.inf for Real)

[Example:

  sum(i for  i in 1:10) // Gives i=110i=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 i{1376}i2=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

]

10.3.5 Matrix and Vector Algebra Functions

The following set of built-in matrix and vector algebra functions are available. The function transpose can be applied to any matrix. The functions outerProduct, symmetric, cross and skew require Real/Integer vector(s) or matrix as input(s) and returns a Real vector or matrix:

Table 10.9: Matrix and vector algebra functions.
Modelica Explanation
transpose(A) Permutes the first two dimensions of array A. It is an error, if array A does not have at least 2 dimensions.
outerProduct(v1,v2) Returns the outer product of vectors v1 and v2 ( = matrix(v1)*transpose( matrix(v2) ) ).
symmetric(A) Returns a matrix where the diagonal elements and the elements above the diagonal are identical to the corresponding elements of matrix A and where the elements below the diagonal are set equal to the elements above the diagonal of A, i.e., B := symmetric(A) -> B[i,j] := A[i,j], if i <= j,  B[i,j] := A[j,i], if i > j.
cross(x,y) Returns the cross product of the 3-vectors x and y, i.e. cross(x,y) = vector( [ x[2]*y[3]-x[3]*y[2];  x[3]*y[1]-x[1]*y[3]; x[1]*y[2]-x[2]*y[1]  ] );
skew(x) Returns the 3 x 3 skew symmetric matrix associated with a 3-vector, i.e., cross(x,y) = skew(x)*y; skew(x) = [0, -x[3], x[2]; x[3], 0, -x[1]; -x[2], x[1], 0];

10.4 Vector, Matrix and Array Constructors

The constructor function array(A,B,C,...) constructs an array from its arguments according to the following rules:

  • Size matching: All arguments must have the same sizes, i.e., size(A)=size(B)=size(C)=

  • All arguments must be type compatible expressions (section 6.6) giving the type of the elements. The data type of the result array is the maximally expanded type of the arguments. Real and Integer subtypes can be mixed resulting in a Real result array where the Integer numbers have been transformed to Real numbers.

  • Each application of this constructor function adds a one-sized dimension to the left in the result compared to the dimensions of the argument arrays, i.e., ndims(array(A,B,C)) = ndims(A) + 1 = ndims(B) + 1, ...

  • {A, B, C, ...} is a shorthand notation for array(A, B, C, ...).

  • There must be at least one argument [i.e., array() or {} is not defined].

[Examples:

  {1,2,3} is a 3-vector of type Integer.
  {{11,12,13}, {21,22,23}} is a 2x3 matrix of type Integer
  {{{1.0, 2.0, 3.0}}} is a 1x1x3 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].

]

10.4.1 Array Constructor with Iterators

An expression:

"{" expression for iterators "}"

or

array "(" expression for iterators ")"

is an array constructor with iterators. The expressions inside the iterators of an array constructor shall be vector expressions. They are evaluated once for each array constructor, and are evaluated in the scope immediately enclosing the array constructor.

For an iterator:

IDENT in array_expression

the loop-variable, IDENT, is in scope inside expression in the array construction. The loop-variable may hide other variables, as in for-clauses. The loop-variable has the same type as the type of the elements of array_expression; and can be simple type as well as a record type. The loop-variable will have the same type for the entire loop - i.e. for an array_expression {1,3.2} the iterator will have the type of the type-compatible expression (Real) for all iterations. For deduction of ranges, see section 11.2.2.1; and for using types as range see section 11.2.2.2.

10.4.1.1 Array Constructor with One Iterator

If only one iterator is used, the result is a vector constructed by evaluating expression for each value of the loop-variable and forming an array of the result.

[Example:

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}

10.4.1.2 Array Constructor with Several Iterators

The notation with several iterators is a shorthand notation for nested array constructors. The notation can be expanded into the usual form by replacing each ’,’ by ’} for’ and prepending the array constructor with a ’{’.

[Example:

  Real hilb[:,:]= { 1/(i+j-1) for  i in 1:n, j in 1:n};
  Real hilb2[:,:]={{ 1/(i+j-1) for  j in 1:n} for i in 1:n};

10.4.2 Array Concatenation

The function cat(k,A,B,C,...) concatenates arrays A,B,C,… along dimension k according to the following rules:

  • Arrays A, B, C, … must have the same number of dimensions, i.e., ndims(A) = ndims(B) = …

  • Arrays A, B, C, … must be type compatible expressions (section 6.6) giving the type of the elements of the result. The maximally expanded types should be equivalent. Real and Integer subtypes can be mixed resulting in a Real result array where the Integer numbers have been transformed to Real numbers.

  • k has to characterize an existing dimension, i.e., 1 <= k <= ndims(A) = ndims(B) = ndims(C); k shall be an integer number.

  • Size matching: Arrays A, B, C, … must have identical array sizes with the exception of the size of dimension k, i.e., size(A,j) = size(B,j), for 1 <= j <= ndims(A) and j <> k.

[Examples:

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

]

Concatenation is formally defined according to:

Let R = cat(k,A,B,C,...), and let n = ndims(A) = ndims(B) = ndims(C) =
...., then
  size(R,k) = size(A,k) + size(B,k) + size(C,k) + ...
  size(R,j) = size(A,j) = size(B,j) = size(C,j) = ...., for 1 <=j <= n and j <> k.
  R[i_1, ..., i_k, ..., i_n] = A[i_1, ..., i_k, ..., i_n], for i_k <= size(A,k),
  R[i_1, ..., i_k, ..., i_n] = B[i_1, ..., i_k - size(A,i), ..., i_n], for i_k <= size(A,k) + size(B,k),
    ....
  where 1 <= i_j <= size(R,j) for 1 <= j <= n.

10.4.2.1 Array Concatenation along First and Second Dimensions

For convenience, a special syntax is supported for the concatenation along the first and second dimensions.

  • Concatenation along first dimension:
    [A; B; C; ...] = cat(1, promote(A,n), promote(B,n), promote(C,n),  ...) where n = max(2, ndims(A), ndims(B), ndims(C), ....). If necessary, 1-sized dimensions are added to the right of A, B, C before the operation is carried out, in order that the operands have the same number of dimensions which will be at least two.

  • Concatenation along second dimension:
    [A, B, C, ...] = cat(2, promote(A,n), promote(B,n), promote(C,n), ...) where n = max(2, ndims(A), ndims(B), ndims(C), ....). If necessary, 1-sized dimensions are added to the right of A, B, C before the operation is carried out, especially that each operand has at least two dimensions.

  • The two forms can be mixed. [...,...] has higher precedence than [...;...], e.g., [a, b; c, d] is parsed as [[a,b];[c,d]].

  • [A] = promote(A,max(2,ndims(A))), i.e., [A] = A, if A has 2 or more dimensions, and it is a matrix with the elements of A, if A is a scalar or a vector.

  • There must be at least one argument (i.e. [] is not defined)

[Examples:

  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];

]

10.4.3 Vector Construction

Vectors can be constructed with the general array constructor, e.g.,

  Real[3] v = {1,2,3}.

The range vector operator or colon operator of simple-expression can be used instead of or in combination with this general constructor to construct Real, Integer, Boolean or enumeration type vectors. Semantics of the colon operator:

  • j : k is the Integer vector {j, j+1, …, k}, if j and k are of type Integer.

  • j : k is the Real vector {j, j+1.0, … n}, with n = floor(k-j), if j and/or k are of type Real.

  • j : k is a Real, Integer, Boolean, or enumeration type vector with zero elements, if j > k.

  • j : d : k is the Integer vector {j, j+d, …, j+n*d}, with n = div(k – j, d), if j, d, and k are of type Integer.

  • j : d : k is the Real vector {j, j+d, …, j+n*d}, with n = floor((k-j)/d), if j, d, or k are of type Real. In order to avoid rounding issues for the length it is recommended to use {j+d*i for i in 0:n} or linspace(j, k, n+1) – if the number of elements are known.

  • j : d : k is a Real or Integer vector with zero elements, if d > 0 and j > k or if d < 0 and j < k.

  • false : true is the Boolean vector {false, true}.

  • j:j is {j} if j is Real, Integer, Boolean, or enumeration type.

  • E.ei : E.ej is the enumeration type vector { E.ei, … E.ej} where E.ej> E.ei, and ei and ej belong to some enumeration type E=enumeration(…ei,…ej,…).

[Examples:

  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;

]

10.5 Array Indexing

The array indexing operator name[] is used to access array elements for retrieval of their values or for updating these values. An indexing operation is subject to upper and lower array dimension index bounds (section 10.1.1). [An indexing operation is assumed to take constant time, i.e., largely independent of the size of the array.] The indexing operator takes two or more operands, where the first operand is the array to be indexed and the rest of the operands are index expressions:

arrayname[indexexpr1, indexexpr2, …]

A colon is used to denote all indices of one dimension. A vector expression can be used to pick out selected rows, columns and elements of vectors, matrices, and arrays. The number of dimensions of the expression is reduced by the number of scalar index arguments. If the number of index arguments is smaller than the number of dimensions of the array, the trailing indices will use ”:”.

It is also possible to use the array access operator to assign to element/elements of an array in algorithm sections. If the index is an array the assignments take place in the order given by the index array. For assignments to arrays and elements of arrays, the entire right-hand side and the index on the left-hand side are evaluated before any element is assigned a new value.

[Examples:

  a[:, j] is a vector of the j-th column of a,
  a[j] is a vector of the j-th row of a: a[j, :]
  a[j : k] is {[a[j], a[j+1], ... , a[k]}
  a[:,j : k] is [a[:,j], a[:,j+1], ... , a[:,k]],
  v[2:2:8] = 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;

if ] x is a vector, x[1] is a scalar, but the slice x[1:5] is a vector (a vector-valued or colon index expression causes a vector to be returned).

]

[Examples given the declaration x[n,m], v[k], z[i,j,p]:

Table 10.10: Examples of scalars vs. array slices created with the colon index.
Expression # dimensions Type of value
x[1, 1] 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 x m – Matrix
x[1:1, :] 2 1 x m - ”row” matrix
x[{1, 3, 5\}, :] 2 3 x m – Matrix
x[: , v] 2 n x k – Matrix
z[: , 3, :] 2 i x p – Matrix
x[scalar([1]), :] 1 m – Vector
x[vector([1]), :] 2 1 x m - ”row” matrix

]

10.5.1 Indexing with Boolean or Enumeration Values

Arrays can be indexed using values of enumeration types or the Boolean type, not only by integers. The type of the index should correspond to the type used for declaring the dimension of the array.

[Example:

  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

]

10.5.2 Indexing with end

The expression end may only appear inside array subscripts, and if used in the i:th subscript of an array expression A it is equivalent to size(A,i) provided indices to A are a subtype of Integer. If used inside nested array subscripts it refers to the most closely nested array.

[Examples:

  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)] // since the first end is referring to end of v.

]

10.6 Scalar, Vector, Matrix, and Array Operator Functions

The mathematical operations defined on scalars, vectors, and matrices are the subject of linear algebra.

In all contexts that require an expression which is a subtype of Real, an expression which is a subtype of Integer can also be used; the Integer expression is automatically converted to Real.

The term numeric or numeric class is used below for a subtype of the Real or Integer type classes.

10.6.1 Equality and Assignment

Equality a=b and assignment a:=b of scalars, vectors, matrices, and arrays is defined element-wise and require both objects to have the same number of dimensions and corresponding dimension sizes. The operands need to be type equivalent. This is legal for the simple types and all types satisfying the requirements for a record, and is in the latter case applied to each component-element of the records.

Table 10.11: Equality and assignment of arrays and scalars.
Type of a Type of b Result of a = b Operation (j=1:n, k=1:m)
Scalar Scalar Scalar a = b
Vector[n] Vector[n] Vector[n] a[j] = b[j]
Matrix[n, m] Matrix[n, m] Matrix[n, m] a[j, k] = b[j, k]
Array[n, m, …] Array[n, m, …] Array[n, m, …] a[j, k, …] = b[j, k, …]

10.6.2 Array Element-wise Addition, Subtraction, and String Concatenation

Addition a+b and subtraction a-b of numeric scalars, vectors, matrices, and arrays is defined element-wise and require size(a)=size(b) and a numeric type for a and b. Unary plus and minus are defined element-wise. Addition a+b of string scalars, vectors, matrices, and arrays is defined as element-wise string concatenation of corresponding elements from a and b, and require size(a)=size(b).

Table 10.12: Array addition, subtraction, and string concatenation.
Type of a Type of b Result of a +/- b Operation c := a +/- b (j=1:n, k=1:m)
Scalar Scalar Scalar c := a +/- b
Vector[n] Vector[n] Vector[n] c[j] := a[j] +/- b[j]
Matrix[n, m] Matrix[n, m] Matrix[n, m] c[j, k] := a[j, k] +/- b[j, k]
Array[n, m, …] Array[n, m, …] Array[n, m, …] c [j, k, …] := a[j, k, …] +/- b[j, k, …]

Element-wise addition a.+b and subtraction a.-b of numeric scalars, vectors, matrices or arrays a and b requires a numeric type class for a and b and either size(a) = size(b) or scalar a or scalar b. Element-wise addition a.+b of string scalars, vectors, matrices, and arrays is defined as element-wise string concatenation of corresponding elements from a and b, and require either size(a) = size(b) or scalar a or scalar b.

Table 10.13: Array element-wise addition, subtraction, and string concatenation.
Type of a Type of b Result of a .+/.- b Operation c := a .+/.- b (j=1:n, k=1:m)
Scalar Scalar Scalar c := a +/- b
Scalar Array[n, m, …] Array[n, m, …] c[j, k, …] := a +/- b[j, k, …]
Array[n, m, …] Scalar Array[n, m, …] c[j, k, …] := a[j, k, …] +/- b
Array[n, m, …] Array[n, m, …] Array[n, m, …] c [j, k, …] := a[j, k, …] +/- b[j, k, …]
Table 10.14: Unary operators. The element-wise (.+, .-) and normal (+, -) operators give the same results.
Type of a Result of +/- a Operation c := +/- a (j=1:n, k=1:m)
Scalar Scalar c := +/- a
Array[n, m, …] Array[n, m, …] c[j, k, …] := +/-a[j, k, …]

10.6.3 Array Element-wise Multiplication

Scalar multiplication s*a or a*s with numeric scalar s and numeric scalar, vector, matrix or array a is defined element-wise:

Table 10.15: Scalar and scalar to array multiplication of numeric elements
Type of s Type of a Type of s* a and a*s Operation c := s*a or c := a*s (j=1:n, k=1:m)
Scalar Scalar Scalar c := s * a
Scalar Vector [n] Vector [n] c[j] := s* a[j]
Scalar Matrix [n, m] Matrix [n, m] c[j, k] := s* a[j, k]
Scalar Array[n, m, …] Array [n, m, …] c[j, k, …] := s*a[j, k, …]

Element-wise multiplication a.*b of numeric scalars, vectors, matrices or arrays a and b requires a numeric type class for a and b and either size(a) = size(b) or scalar a or scalar b.

Table 10.16: Array element-wise multiplication
Type of a Type of b Type of a .* b Operation c:=a .* b (j=1:n, k=1:m)
Scalar Scalar Scalar c := a * b
Scalar Array[n, m, …] Array[n, m, …] c[j, k, …] := a* b[j, k, …]
Array[n, m, …] Scalar Array[n, m, …] c[j, k, …] := a[j, k, …]* b
Array[n, m, …] Array[n, m, …] Array [n, m, …] c[j, k, …] := a[j, k, …]* b[j, k, …]

10.6.4 Matrix and Vector Multiplication of Numeric Arrays

Multiplication a*b of numeric vectors and matrices is defined only for the following combinations:

Table 10.17: Matrix and vector multiplication of arrays with numeric elements.
Type of a Type of b Type of a* b Operation c := a*b
Vector [n] Vector [n] Scalar c := sumk(a[k]*b[k]), k=1:n
Vector [n] Matrix [n, m] Vector [m] c[j] := sumk(a[k]*b[k, j]), j=1:m, k=1:n
Matrix [n, m] Vector [m] Vector [n] c[j] := sumk(a[j, k]*b[k])
Matrix [n, m] Matrix [m, p] Matrix [n, p] c[i, j] = sumk(a[i, k]*b[k, j]), i=1:n, k=1:m, j=1:p

[Example:

  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
  tranpose([v])*A*v // vector with one element

]

10.6.5 Division of Scalars or Numeric Arrays by Numeric Scalars

Division a/s of numeric scalars, vectors, matrices, or arrays a and numeric scalars s is defined element-wise. The result is always of real type. In order to get integer division with truncation use the function div.

Table 10.18: Division of scalars and arrays by numeric elements.
Type of a Type of s Result of a / s Operation c := a / s (j=1:n, k=1:m)
Scalar Scalar Scalar c := a / s
Vector[n] Scalar Vector[n] c[k] := a[k] / s
Matrix[n, m] Scalar Matrix[n, m] c[j, k] := a[j, k] / s
Array[n, m, …] Scalar Array[n, m, …] c[j, k, …] := a[j, k, …] / s

10.6.6 Array Element-wise Division

Element-wise division a./b of numeric scalars, vectors, matrices or arrays a and b requires a numeric type class for a and b and either size(a) = size(b) or scalar a or scalar b. The result is always of real type. In order to get integer division with truncation use the function div.

Table 10.19: Element-wise division of arrays
Type of a Type of b Type of a ./ b Operation c:=a ./ b (j=1:n, k=1:m)
Scalar Scalar Scalar c := a / b
Scalar Array[n, m, …] Array[n, m, …] c[j, k, …] := a / b[j, k, …]
Array[n, m, …] Scalar Array[n, m, …] c[j, k, …] := a[j, k, …] / b
Array[n, m, …] Array[n, m, …] Array [n, m, …] c[j, k, …] := a[j, k, …] / b[j, k, …]

[Element-wise division by scalar (./) and division by scalar (/) are identical: a./s = a/s.

Example:

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

This is a consequence of the parsing rules, since 2. is a lexical unit. Using a space after the literal solves the problem.]

10.6.7 Exponentiation of Scalars of Numeric Elements

Exponentiation ”a^b” is defined as pow(double a,double b) in the ANSI C library if both ”a” and ”b” are Real scalars. A Real scalar value is returned. If ”a” or ”b” are Integer scalars, they are automatically promoted to ”Real”. Consequences of exceptional situations, such as (a==0.0 and b<=0.0, a<0 and b is not an integer) or overflow are undefined

Element-wise exponentiation a.^b of numeric scalars, vectors, matrices, or arrays a and b requires a numeric type class for a and b and either size(a) = size(b) or scalar a or scalar b.

Table 10.20: Element-wise exponentiation of arrays
Type of a Type of b Type of a .^ b Operation c:=a .^ b (j=1:n, k=1:m)
Scalar Scalar Scalar c := a ^ b
Scalar Array[n, m, …] Array[n, m, …] c[j, k, …] := a ^ b[j, k, …]
Array[n, m, …] Scalar Array[n, m, …] c[j, k, …] := a[j, k, …] ^ b
Array[n, m, …] Array[n, m, …] Array [n, m, …] c[j, k, …] := a[j, k, …] ^ b[j, k, …]

[Example:

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

This is a consequence of the parsing rules, i.e. since 2. could be a lexical unit it seen as a lexical unit; using a space after literals solves the problem.]

10.6.8 Scalar Exponentiation of Square Matrices of Numeric Elements

Exponentiation a^s is defined if a is a square numeric matrix and s is a scalar as a subtype of Integer with s>=. The exponentiation is done by repeated multiplication

(e.g.:

  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;

[Non-Integer exponents are forbidden, because this would require computing the eigenvalues and eigenvectors of “a” and this is no longer an elementary operation].

10.6.9 Slice Operation

The following holds for slice operations:

  • If a is an array containing scalar components and m is a component of those components, the expression a.m is interpreted as a slice operation. It returns the array of components {a{[1].m,  ...}.

  • If m is also an array component, the slice operation is valid only if size(a[1].m)=size(a[2].m)=...

  • The slicing operation can be combined with indexing, e.g. a.m[1]. It returns the array of components {a[1].m[1],  a[2].m[1], ...}, and does not require that size(a[1].m)=size(a[2].m). The number of subscripts on m must not be greater than the number of array dimension for m (the number can be smaller, in which case the missing trailing indices are assumed to be ”:”), and is only valid if size(a[1].m[...])=size(a[2].m[...])..

[Example: The size-restriction on the operand is only applicable if the indexing on the second operand uses vectors or colon as in the example:

  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[2].x1[1:2])=…=size(tf[m].x1[1:2])={2}

In this example the different x1 vectors have different lengths, but it is still possible to perform some operations on them.]

10.6.10 Relational Operators

Relational operators <, <=, >, >=, ==, <>, are only defined for scalar operands of simple types, not for arrays, see section 3.5

10.6.11 Boolean Operators

The operators, and and or take expressions of Boolean type, which are either scalars or arrays of matching dimensions. The operator not takes an expression of Boolean type, which is either scalar or an array. The result is the element-wise logical operation. For short-circuit evaluation of and and or see section 3.3.

10.6.12 Vectorized Calls of Functions

10.7 Empty Arrays

Arrays may have dimension sizes of 0. E.g.

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

Empty matrices can be constructed with the fill function. E.g.

  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

It is not possible to access an element of an empty matrix, e.g. v[j,k] cannot be evaluated if v=[] because the assertion fails that the index must be bigger than one.

Size-requirements of operations, such as +, -, have also to be fulfilled if a dimension is zero. E.g.

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

Multiplication of two empty matrices results in a zero matrix of corresponding numeric type if the result matrix has no zero dimension sizes, i.e.,

  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, with zero elements).

[Example:

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

Assume n=0, p>0, q>0: Results in y = D*u

]