## Working with Array Equations in Version 10

STELLA/*iThink* version 10 introduces several new array features, including simplified and more powerful Apply-To-All equations that are designed to reduce the need to specify equations for every individual element.

**Dimension names are optional**

When an equation is written using other array names, the dimension names are not normally needed. For example, given arrays *A*, *B*, and *C*, each with the dimensions *Dim1* and *Dim2*, *A* can be set to the sum of *B* and *C* with this equation:

B + C

Dimension names are still needed when the dimensions do not match. For example, to also add in the first 2-dimensional slice of the 3-dimensional array *D*[*Dim1*, *Dim2*, *Dim3*], the equation becomes:

B + C + D[Dim1, Dim2, 1]

**The wildcard * is optional**

When an array builtin is used, the * is normally not needed. For example, to find the sum of the elements of a 2-dimensional array *A*[*Dim1,* *Dim2*] requires this equation:

SUM(A)

If, however, the sum of only the first column of *A* is desired, the * is still needed:

SUM(A[*, 1])

**Simplified array builtins**

There are five array builtins: SIZE, SUM, MEAN, STDDEV, and RANK. In addition, the MIN and MAX functions have been extended to take either one or two array arguments. All but RANK can also be applied to queues and conveyors.

SUM, MEAN, and STDDEV all work in a similar way (see examples of SUM above).

Using the MAX function, it is possible to find the maximum value in array *A,*

MAX(A)

the maximum value in array *A*, or zero if everything is negative,

MAX(A, 0)

or the maximum across two arrays *A* and *B*,

MAX(A, B)

MIN works the same way, but finds the minimum.

The SIZE function requires an array parameter, but within an array, the special name SELF can be used to refer to the array whose equation is being set. In addition, wildcards can be used to determine the size of any array slice. In the equation for array *A*[*Dim1*, *Dim2*],

SIZE(SELF)

gives the total number of elements in array *A* while

SIZE(SELF[*, 1])

gives the size of the first dimension of A, i.e., the number of elements – or rows – in the first column. Likewise,

SIZE(SELF[1, *])

gives the size of the second dimension of A, i.e., the number of elements – or columns – in the first row.

Since RANK returns the index of the element with the given rank, it can also be used to find the index of the minimum element (using rank 1) or the maximum element (using rank SIZE(array)). Given array *A*[*Dim1*, *Dim2*], the index of the minimum element in the first row can be found with the equation:

RANK(A[1, *], 1)

However, to find the minimum element in the entire array, use:

RANK(A, 1)

This returns a single index that can be mapped to an array element using the special parentheses subscripting:

A(RANK(A, 1))

will be the value of the minimum element in *A*, i.e, the same value as MIN(A). However, if array *B* has the same dimensions as *A* (i.e., for this example, *B*[*Dim1*, *Dim2*]), the value of the element in *B* that corresponds to the minimum element in *A* is found with:

B(RANK(A, 1))

**Accessing elements of queues and conveyors**

Use an array subscript to access an element of a queue or conveyor. The indices start on the outflow side (at 1) and increase toward the inflow side (up to SIZE(queue) or SIZE(conveyor)). This allows the entire contents of a queue or conveyor to be assigned to an array allowing additional calculations, for example, a weighted average. Given a conveyor named *Lag*, a new array *weighted_by_time*[*Slat*] can be created with the equation:

(Slat*DT)*Lag[Slat]

Note the subscript is required for the conveyor. Otherwise, the total value of the conveyor will be used. Note also that the size of the dimension *Slat* must be at least large enough to hold all of the conveyor elements (the remaining elements in *weighted* will be set to zero). The value of Slat*DT is the amount of time remaining before the material in that slat exits the conveyor.

A converter, *average_latency*, which is the average time remaining for the contents to exit (a weighted mean), can now be defined with the equation:

SUM(weighted_by_time)/Lag

**Transposition**

It is sometimes helpful to transpose an array. To facilitate this, the ' (apostrophe) operator was added. Given arrays *A*[*Dim1*, *Dim2*, *Dim3*] and *B*[*Dim3*, *Dim2*, *Dim1*], the array *A* can be set equal to *B* transposed with the following equation:

B '

Note that a space *is* required between the array name and the apostrophe. This is equivalent to the following equation that uses dimension names:

B[Dim3, Dim2, Dim1]

This is especially helpful for square matrices or other arrays that use the same dimension name many times. Given arrays *C*[*Dim*, *Dim*, *Dim*] and *D*[*Dim*, *Dim*, *Dim*], the array *C* can be set equal to *D* transposed with the following equation, which reverses all the dimensions:

D '

This is equivalent to the following equation that uses the new positional dimension names:

D[@3, @2, @1]

Within a subscript, the @ operator can be followed by an integer that represents the dimension position in the array whose equation is being set. In the example above, @3 represents the third dimension name of *A*. This is particularly useful if straight transposition is not needed and all the dimension names are the same. For example,

D[@2, @1, @3]

flips the first two dimensions of *D* (when assigning to *A*) while leaving the third alone.

**Subscript expressions**

Subscripts can contain any valid expression. Given an array *A* and a variable *x*, an element at a variable index that is one more than twice *x* can be accessed with:

A[2*x + 1]

Element labels can also appear within these expressions.

In Apply-To-All arrays, dimension names can be used. The following equation sets the values in array *A*[*Dim1*] to every even-indexed elements in array *B*[*Dim1*], filling the second half of *A* with zeroes:

B[2*Dim1]

Dimension names can also be used outside subscripts. The following equation slides the elements of *B* up one position in *A*, placing 10 in the first element of *A* (without the IF, the first element would contain 0).

IF Dim1 = 1 THEN 10 ELSE B[Dim1 - 1]

Even if *Dim1* is labeled, it must be compared to the numeric index 1 in the IF expression because element labels can only be used within a subscript. Note that numeric indices are always valid for any array dimension, even if it is labeled.

**Array Ranges**

A range of an array can be specified using the range operator : (colon), which takes a lower bound on the left and an upper bound on the right (e.g., 1:10 means “from 1 to 10”). Just as wildcards allow control over which dimensions to include, ranges control which range of elements to include in each dimension. For example, the follow equation sums the top-left 3×4 rectangle of array *A*[*Dim1*, *Dim2*]:

SUM(A[1:3, 1:4))

We hope you find these new array capabilities useful in your modeling work and welcome any comments and suggestions.

*If you enjoyed this post, make sure you subscribe to my RSS feed!*