Some problems require multi-dimensional data. Alike some other traditional system dynamics tools, AnyLogic supports arrays. An array is a storage of numbers that may have any number of dimensions. Each dimension has finite number of indices — subscripts.
Arrays are used when it is necessary to store a large set of coefficients and access them or when there are multiple model layers. The latter case is useful when you have defined a model for some subsystem and there are other subsystems, which have the same structure, as the first one, but other numerical parameters. One can implement such multi-dimensional models making copies of the default diagram and changing the parameters. Such approach has one great disadvantage: if you want to change the model, you need do it so much times, as many layers you have; the diagram grows and becomes incomprehensible. Array allows you to create a single diagram for all the layers. Therefore, model remains compact, and changes you make will affect the whole model, but not a single layer.
For example, you create a model of a nation's health, describing some social or health processes in respect to different groups of population. You may need to separate people by three characteristics: gender, age group, and social group. This example fits well in the array concept. Instead of dealing with multiple models describing different groups of people, you can just define an array with such enumerations: Gender(male, female), Age(child, teenager, adult, aged), and SocialGroup(wealthy, middleclass, deprived).
The following variables can be made arrays in AnyLogic: stock, flow, dynamic variable, parameter.
Demo model: Bass Diffusion Arrays Open the model page in AnyLogic Cloud. There you can run the model or download it (by clicking Model source files). Demo model: Bass Diffusion ArraysOpen the model in your AnyLogic desktop installation.To define an array variable
- Select the variable in a graphical editor or in the Projects view.
- To make the selected variable a variable of type array, check the Array check box in the Properties view.
- You can see that the variable’s name in the graphical editor is now followed with square brackets. In this way AnyLogic visualizes array variables.
Array variables in the graphical editor
- Click in the {...} to the right of the check box. The Array section of the Properties view will be shown.
- Define the dimensions of your array variable. Add the dimensions you need from the Available dimensions list to the Selected dimensions list. To add
some dimension, select it in the Available dimensions list and click the
button. To add all available dimensions, click the
button.
- If needed, you can remove some selected dimensions any time you like. To remove a dimension, select it in the Selected dimensions list and click the
button. To remove all selected dimensions, click the
button.
- The order of dimensions is also important. You can reorder the dimensions by clicking
and
buttons.
Some system dynamics variables of type array (stock variables, parameters, constant flow, and dynamic variables) need to be initialized.
AnyLogic provides easy and flexible tool for defining initial values of array elements. This is done because initialization of multidimensional arrays is non-trivial task, first of all as there is no way of intuitive visual representation of multidimensional data.
As arrays can have unlimited number of dimensions, each one possibly containing a great amount of elements, the sole convenient way of defining initial values of an array is layering this array (i.e. partitioning this array to layers) and successive initialization of each resulting layer.
You define a layer by fixing values in all but two dimensions of an array. When we say fix we mean that you select one particular element in the dimension. Thereby each fixation narrows the list of array dimensions by one dimension. Having finished narrowing dimensions to two ones, you get an ability to represent data of the resulting two-dimensional layer in tabular form. To define initial values for all array elements you need to go over all combinations of elements of non-fixed dimensions.
To open the editor of array initial values
- Select the array in a graphical editor or in the Projects view.
- Click the Edit... button in the Properties view.
- The editor will be opened. The editor consists of two panels. In the left panel you define a layer, whose elements you want to initialize. The right panel contains a table where you, in fact, define values of elements of the selected array’s layer.
To make clear how to initialize array elements, let us illustrate it by a pair of simple examples.
To initialize all array elements with one value
- Open the editor of array values.
- Make sure that in the left table the value [ALL] is selected in all cells of the Elements column.
- Remove marks from either
and
columns of this table.
- In the right table, enter the value you want to assign to all elements of this array.
- Click OK when finished.
Assume you are creating a model studying population dynamics in LA and NY. Let’s conditionally categorize people by three characteristics: gender, age and region. These characteristics are comfortably described using dimensions — enumerations: Gender(male, female), Age(child, teenager, adult, aged) and Region(LA, NY).
Let’s study how to initialize three-dimensional array Population[Region, Age, Gender] using our editor of array values:
To initialize all array elements with one value
- Open the editor of array values.
- You will see that two dimensions have marks in the left table of the editor: one in the
column and one in the
column. Marking this or that dimension you can choose dimensions, whose elements you will define in the right table. Elements of the dimension, selected in the
column, will be displayed there as rows, while elements of the dimension selected in the
column will be displayed as columns:
- If you will start entering values now, they will be assigned to the corresponding elements of all non-fixed dimensions of the array, i.e. if you enter, say, 1000000 in the cell adult - LA, thereby you will initialize both number of adult men and adult women in LA with this value.
- If you want to define different initial values for different elements of Gender dimension, you should fix this dimension as we mentioned above.
- Let’s first initialize the number of men. Select male in the cell Gender — Elements in the left table. Now you can define the number of men in your model in the right table — each cell of the table defines the value of the corresponding element of the population array, e.g. in the cell aged - NY the number of aged men living in NY can be defined. Define values of all elements of this array’s layer [Region, Age, male]:
- Now we should define the number of women in the similar way. This time you should select another element of the fixed dimension, whose elements are not fully initialized yet. Choose female in the cell Gender - Elements and define the number of women of each category in the right table:
- Now we have finished initialization of array’s layer [Region, Age, female] and therefore the initialization of the whole array (as in this simplest case our array has only two layers since it is three-dimensional and the only dimension we are fixing has only two elements).
- Click OK to finish the initialization.
You can check whether you have defined initial values correctly by inspecting variable value at the model runtime:
The resulting array’s initialization string is displayed in the text form in the Initial value field (or Default value, in the case of a parameter-array). If you need to initialize some another array with similar values, you can simply copy this string to the analogous property of that array and then modify the required values using the editor of array values.
There is a number of ways you can define the equation (or equations) for an array stock, flow or dynamic variable.
In the simplest case when the equation is the same for all elements of the array, you need only one equation field, where you type the expression, possibly referencing the dimensions of the array variable. For example, consider the stock Population which is an array with the following dimensions:
Region = {LA, NY} and Gender = { MALE, FEMALE }
In this case, the equation will look like:
d(Population[Gender, Region])/dt = Births[Gender, Region]-Deaths[Gender, Region]
To set the same equation for all array elements
- Select the array variable in the graphical editor and go to the Properties view.
- By default the stock’s formula is generated automatically according to flows flowing and out of this stock, it is so called classic equation mode, for more details please refer to the Classic and custom modes section. The value of inflows i.e. flows that increase stock value, are added and the value of outflows, i.e. flows that decrease the stock are subtracted from the current value of the stock. In the figure below you can see the auto-generated equation formed by AnyLogic for the stock Population from our example, see the disabled field d(Population [Region,Gender])/dt=.
- If you need to overcome the limitations of classic system dynamics formula of the stock anyway and want to edit it by yourself, select the Custom option from the Equation mode group of buttons and specify the formula in the d(stock_name)/dt field.
- If in our example Births are the same for MALE and FEMALE and differ only by region (so that variable Births is the array with one dimension Region), you may write:
The equation above is mapped to a very simple loop construct that looks like this:
for( r : Region )
for( g : Gender )
Population[ r, g ] = Births[ r ] - Deaths[ r, g ]If birth rate is the same even in different regions, you can use a scalar variable Births in the same equation:
There are cases however when equations are different for different elements of the array or different element sets. Suppose in the population example above people do intensively emigrate from NY region, but emigration from LA region is negligible. Then there will be two equations for the stock Population:
d(Population[Gender, LA])/dt = Births[Gender, LA] - Deaths[Gender, LA]
d(Population[Gender, NY])/dt = Births[Gender, NY] - Deaths[Gender, NY] - Emigration
To define several equations for different sub-arrays of an array variable
- Select the variable in a graphical editor or in the Projects view.
- Go to the Properties view.
- Specify the list of subscripts identifying element(s) of the array that should get the value calculated by the equation you specify. By default, all array dimensions are selected. You should modify this dimension list by specifying some particular elements for those enumerations that are presented in the sub-array by a subdimension or (in case of enumerations) an individual element. For such dimensions, click on the dimension name in the {...} to the right of the Array check box and choose the subdimension or individual element from the drop-down list.
- To define the first formula of the example described above, you should do the following:
- Enter the formula calculating the initial value for the defined sub-array in the d(<array_name>)/dt= edit box:
- If you need to define one more formula initializing another sub-array, click the Add formula button and define new formula in the same way.
- For the described example, you need to define the second equation describing the population of NY region habitants:
- If needed, you can remove formulas any time you like. To remove a formula, click the
button to the right of the formula.
In the equation sets like above you can also refer to element subsets by using subdimensions. For example, if there is dimension Income = { POOR, MIDDLECLASS, WEALTHY } and its subdimension AllButPoor = { MIDDLECLASS, WEALTHY }, you can write for a three-dimensional array Population:
d( Population[ Region, Gender, POOR ] )/dt =
Births[ Region, Gender ] - Deaths[ Region, Gender, POOR ] - Emigration
d( Population[ Region, Gender, AllButPoor ] )/dt =
Births[ Region, Gender ] - Deaths[ Region, Gender, AllButPoor ]
In some cases you need to refer to a different index of an array in the equation. Suppose you are modeling an ageing chain and your stock Population is an array with one dimension Age = { 0 .. 99 }. The inflow for an element of the array is the outflow of the element with the previous index for all element but 0, and for the element 0 the inflow is Births. To implement this you may define two subdimensions of Age: Age0 = { 0 } and AgesAllBut0 = { 1 .. 99 } and write:
d( Population[ Age0 ] )/dt =
Births - Deaths[ Age0 ] - Ageing[ Age0 ]
d( Population[ AgesAllBut0 ] )/dt =
Ageing[ AgesAllBut0 - 1 ] - Deaths[ AgesAllBut0 ] - Ageing[ AgesAllBut0 ]
To understand how it works it makes sense to map this into loop construct:
for( a : 0 }
Population[ a ] = Births - Deaths[ a ] - Ageing[ a ]
for( a : 1..99 }
Population[ a ] = Ageing[ a-1 ] - Deaths[ a ] - Ageing[ a ]
As you can see, the dimension names are simply substituted with loop indices in the equations. This gives you a high degree of flexibility when you define equations. You can define the dependency of element with a certain index on the element of same of another array having arbitrary other index.
AnyLogic supports a set of functions you can use to perform operations over arrays and manage the values of its elements. You can use indices to address the particular element(s).
Suppose you have a system dynamics element - stock Population, which is a hyper-array with two dimensions: [ Region, Gender ], where Region is { LA, NY } and Gender is { Male, Female }.
For example, to add 10 to the current value of the element (LA, Male) within this hyper-array you should call: Population.incrementBy( 10, LA, Male ).
You can also perform calculations on a subset of an array by using the INDEX_CAN_VARY constant. Being placed at an index position, it tells the methods that perform operation over subsets of hyper array elements that they can vary the corresponding index. Other indices are fixed. INDEX_CAN_VARY can be used to increment, to decrement, or to multiply across all the elements of a particular dimension.
In our example, the following function call will calculate the total number of people of both genders in LA region: Population.sum( LA, INDEX_CAN_VARY )
If you change the argument order and call Population.sum( INDEX_CAN_VARY, LA ), it will calculate the total number of males in both regions. The first argument value, INDEX_CAN_VARY, tells to consider all elements of the first dimension, Region, and the second argument takes LA value. Actually, LA constant stores the index of the LA element in the corresponding dimension Region: 1. Therefore, it is considered by the software as it defines the first index of the second dimension, Gender, so the function will return the total number of males in both regions.
- Array elements
-
Function Description int size() Returns the total number of elements in the hyper array. double get(int... indexes) Returns the value of the element of this array specified by the given dimension indexes. You must specify an index for each dimension.
indexes — dimension indexes specifying the elementint getPosOf(int... indexes) Returns the position of the element in the flat data array specified by the given dimension indexes. There must be an index for each existing dimension.
indexes — dimension indexes specifying the elementdouble[] getData() Returns the flat array of values of the hyper array elements. The actual array is returned, not a copy. The multi-dimensional data is stored in the flat array so that the first dimension index varies first. Thus, for a hyper array HA[ Region, Gender ] where Region is { N, S, E, W } and Gender is { M, F }, the flat data array would contain: [ (N,M), (S,M), (E,M), (W,M), (N,F), (S,F), (E,F), (W,F) ]. Position of an element with particular indexes can be obtained by calling getPosOf() function. Dimension[] getDimensions() Returns the dimensions of the hyper array. You should not modify the result!boolean hasNegativeValues() Checks if there are any negative elements in the hyper array. Returns true if at least one element is negative, otherwise returns false. void set(double value) Sets all elements of this array to a given value.
value — the valuevoid set(double value, int... indexes) Sets the element of this array specified by the dimension indexes to a given value.
value — the value
indexes — dimension indexes specifying the elementvoid set(double[] values) Sets the hyper array elements to the values from a given array, where the first dimension index varies first. The number of values provided may be less or greater than the one actually needed by the array (array has the number of elements equal to the product of its dimension sizes). The extra values are ignored, the missing values are not modified.
values — flat array of data valuesvoid setData(double[] values, int srcPos) Sets the hyper array elements to the values from a given array starting from the specified position, where the first dimension index varies first. The number of values provided may be smaller or bigger than the one actually needed by the array (array has the number of elements equal to the product of its dimension sizes). The extra values are ignored, the missing values are not modified.
values — flat array of data values
srcPos — the zero-based index of the first element of the given array from which copying starts - Changing values of elements
-
Function Description void increment(int... indexes) Increments element(s) with the given index(es) by 1.
indexes — the array of indices, INDEX_CAN_VARY for varying onesvoid incrementBy(double value, int... indexes) Increments element(s) with the given index(es) by the given value amount.
value — the value to add to and to subtract from array elements
indexes — the array of indexes, INDEX_CAN_VARY for varying onesvoid decrement(int... indexes) Decrements element(s) with the given index(es) by 1.
indexes — the array of indexes, INDEX_CAN_VARY for varying onesvoid decrementBy(double value, int... indexes) Decrements element(s) with the given index(es) by the given value amount.
value — the value to add to and to subtract from array elements
indexes — the array of indexes, INDEX_CAN_VARY for varying onesvoid multiply(double factor, int... indexes) Multiplies element(s) with the given index(es) by the given factor.
factor — the factor to apply to array elements
indexes — the array of indexes, INDEX_CAN_VARY for varying ones - Statistics (aggregation functions)
-
Function Description double average() Returns the mean value of the aggregated elements: double max() Returns the maximum value of all elements of the hyper array. double max(int... indexes) Returns the partial maximum of hyper array elements, namely maximum across all dimensions whose indexes equal INDEX_CAN_VARY in the given index array. Other indexes are considered fixed.
indexes — the array of indexes, INDEX_CAN_VARY for varying onesdouble min() Returns the minimum value of all elements of the hyper array. double min(int... indexes) Returns the partial minimum of hyper array elements, namely minimum across all dimensions whose indexes equal INDEX_CAN_VARY in the given index array. Other indexes are considered fixed.
indexes — the array of indexes, INDEX_CAN_VARY for varying onesdouble sum() Returns the sum of all elements of the hyper array: double sum(int... indexes) Returns the partial sum of hyper array elements, namely sum across all dimensions whose indexes equal INDEX_CAN_VARY in the given index array.
indexes — the array of indexes, INDEX_CAN_VARY for varying onesdouble prod() Returns the product of all hyper array elements: double prod(int... indexes) Returns the partial product of hyper array elements, namely product across all dimensions whose indexes equal INDEX_CAN_VARY in the given index array.
indexes — the array of indexes, INDEX_CAN_VARY for varying onesdouble stddev() Returns the standard deviation across all elements of the hyper array. Standard deviation formula: - Other
-
Function Description boolean equal(HyperArray a) Compares the hyper-array with another one. Returns true if the arrays are equal, i.e., they have same dimensions and same element values.
a — another hyper-arrayboolean equal(double val) Returns true if all elements of the hyper array equal the specified value.
val — the valuevoid copyFrom(HyperArray original) Copies all data to this hyper-array from another hyper-array. Dimensions of the two arrays must be identical.
original — the original hyper-arrayString toString() Returns the textual representation of the hyper array in the following form: - if the hyper array is dimensionless, just prints its only element value
- for a uni-dimensional hyper array prints every element value on a separate line
- for two-dimensional hyper array prints as a tab separated 2D table with element values (dimension 0 vs dimension 1)
- for three and more dimensions prints a number of such tables, a table for each combination of indexes in dimensions 2 and higher
-
How can we improve this article?
-