Initializing help system before first use

Arrays

In the first part of this manual we have already encountered many examples that make use of arrays. The most important points are summarized in this section and here is an overview of the topics explained with other examples:

Array declaration

Here are some examples of array definition:

 declarations
  A1: array(1..3) of integer         ! Fixed size array
  F = {"a","b","c"}
  A2: array(F) of real               ! Fixed size array
  A3: array(R:range) of integer      ! Dense array with unknown index set
  A4: dynamic array(F) of real       ! Dynamic array
 end-declarations

 writeln("A1:", A1, " A2:", A2, " A3:", A3, " A4:", A4, " A5:", A5)

! Using the array initialization operator
 A1::[10,20,30]                      ! Range indices are known
 A2::(["a","b","c"])[1.1, 2.5, 3.9]  ! String indices must be stated
 A3::(1..3)[10,20,30]                ! Indices are not known upfront

 A2("a"):=5.1                        ! Redefine an entry

 setrandseed(3)
 forall(f in F) A4(f):= 10*random    ! Value assignment
 delcell(A4("a"))                    ! Deleting an array entry

 writeln("A1:", A1, " A2:", A2, " A3:", A3, " A4:", A4) 

The output produced by this model (file arraydef.mos) is the following.

A1:[0,0,0] A2:[0,0,0] A3:[] A4:[]
A1:[10,20,30] A2:[5.1,2.5,3.9] A3:[(1,10),(2,20),(3,30)] A4:[(`b',7.6693),(`c',5.89101)]

Arrays A1 and A2 are fixed size arrays: their size (i.e. the total number of objects/cells they contain) is known at their declaration because all their indexing sets are of fixed size (i.e. either constant or finalized). All the cells of fixed size arrays are created and initialized immediately, using default initialization values that depend on the array type. For Mosel's basic types these are the following values.
real, integer: 0
boolean: false
string: '' (i.e. the empty string)

Array A4 is explicitly marked as dynamic array using the qualifier dynamic. Dynamic arrays along with hashmap arrays are the two forms of sparse arrays in Mosel, a hashmap array is obtained by applying the qualifier hashmap in place of dynamic—both types are used in the same way, but their performance differs (dynamic arrays are generally faster for linear enumeration and require less memory whereas hashmap arrays are faster for random access). Sparse arrays are created empty. Their cells are created explicitly (see Paragraph create below) or when they are assigned a value, that is, the array size will grow `on demand'. It is also possible to delete some or all cells of a sparse array using the procedure delcell on an entry or the whole array (same as reset). The value of a cell that has not been created is the default initial value of the type of the array.

Array A3 is created empty since its indexing set is empty at the time of its declaration, but this array is not the same as a dynamic array. It is a dense array that will grow if elements are added to its index set. Please refer to Appendix Finalizing sets and dynamic arrays for further detail.

Multiple indices

Arrays with multiple indices are defined and accessed as follows:

 declarations
  C: array(range, set of string, set of real) of integer
  D: array(1..5) of array(1..10) of real
 end-declarations

 C(5,"a",1.5):= 10
 D(1,7):= 2.8 

As shown in the example, in order to access (or 'dereference') the cell of an array of arrays, the list of indices for the second array has to be appended to the list of indices of the first array.

create

Special care needs to be taken in the case of sparse arrays of decision variables (and indeed with any types that do not have an assignment operator). Writing x:=1 is a syntax error if x is of type mpvar. If an array of such a type is defined as dynamic or hashmap array, then the corresponding cells are not created. The entries of the array must be created explicitly by using the procedure create since they cannot be defined by assignment. Let us simply recall here the example from Section A transport example.

 declarations
  REGION: set of string                 ! Set of customer regions
  PLANT: set of string                  ! Set of plants
  TRANSCAP: dynamic array(PLANT,REGION) of real
                                        ! Capacity on each route plant->region
  flow: dynamic array(PLANT,REGION) of mpvar    ! Flow on each route
 end-declarations

 initializations from 'transprt.dat'
  TRANSCAP
 end-initializations

! Create the flow variables that exist
 forall(p in PLANT, r in REGION | exists(TRANSCAP(p,r)) ) create(flow(p,r))

For a more detailed discussion of decision variable creation please see Section Conditional variable creation and create.

Array initialization from file

When working with arrays, we distinguish between dense and sparse data formats. Dense data format means that only the data values are represented (see also Section A blending example); in sparse format each data entry is accompanied by its index tuple. Dense format data uses less storage space but this format can only be used if all indices are defined in the model and if no ambiguity results from the omission of the indices when transferring data. In all other cases sparse data format must be used and it is particularly recommended to use this representation if only few entries of a multidimensional array are actually defined.

 declarations
  A: array(1..2,1..3) of real          ! Can use dense format
  B: array(R:range,T:range) of real    ! Requires sparse format
  D: dynamic array(set of string, range) of real   ! Requires sparse format
  S: set of string
  M: dynamic array(S) of integer       ! Requires sparse format
  N: dynamic array(S) of string        ! Requires sparse format
 end-declarations

 initializations from "arrayinit.dat"
  A  B
  D as "SomeName"                      ! Data label different from model name
  D as "SomeName2"                     ! Add some more data to 'D'
  [M,N] as "MNData"                    ! 2 arrays read from the same table
 end-initializations

 writeln("A:", A, " B:", B, "\nD:", D, "\nM:", M, "\nN:", N)

With this contents of the data file arrayinit.dat

A: [2 4 6 8 10 12]
B: [(1 1) 2 (1 2) 4 (1 3) 6 (2 1) 8 (2 2) 10 (2 3) 12]
SomeName: [("a" 1) 2 ("a" 2) 4 ("b" 3) 6 ("c" 4) 8 ("b" 5) 10]
SomeName2: [("a" 3) 12 ("b" 2) 14 ("b" 5) 16]
MNData: [ ("A") [2 "a"] ("B") [* "b"]
          ("C") [6 *]   ("D") [8 "c"]
          ("E") [10 "b"] ] 

we see the following output display when executing the model arrayinit.mos shown above:

A:[2,4,6,8,10,12] B:[2,4,6,8,10,12]
D:[(`a',1,2),(`a',2,4),(`a',3,12),(`b',2,14),(`b',3,6),(`b',5,16),(`c',4,8)]
M:[(`A',2),(`C',6),(`D',8),(`E',10)]
N:[(`A',a),(`B',b),(`D',c),(`E',b)] 

By default, Mosel expects that data labels are the same as the model names. For array D we show how to read data using different labels. The contents of the second set of data labeled SomeName2 is added to what is read from SomeName. Note that the entry (b,5) is contained in both sets, and the corresponding array entry takes its value from the last label that is read.

Arrays such as M and N, that share the same index sets (but not necessarily the same entries) can be read from a single label/data table. The `*' in certain entries of MNData indicates that the entry does not exist in one of the arrays.

The syntax of initializations blocks remains the same when switching to other data sources. Sections Reading data from spreadsheets and databases and Excel spreadsheets discuss examples of using databases or spreadsheets instead of text files for array initialization. For further detail on data I/O using different data sources the reader is refered to the Xpress whitepaper Using ODBC and other database interfaces with Mosel.

Automatic arrays: the array operator

The keyword array can be used as an aggregate operator in order to create an array that will exist only for the duration of the expression. This automatic array may be used wherever a reference to an array is expected, for instance, in function calls or in initializations blocks.

In the following example we use the array operator to extract the (1-dimensional) rows and column arrays from a 2-dimensional array, we further generate a subarray with a selection of entries and the transposed (inversed indices) array.

model "Automatic arrays"

 declarations
  B: array(S:set of string, I:set of real) of integer
 end-declarations

 B::(["a","b"], [3,1.5,7])[1,2,3,4,5,6]
 writeln("B: ", B)

 forall(s in S) writeln("Row ", s, ": ", array(i in I) B(s,i))
 forall(i in I) writeln("Column ", i, ": ", array(s in S) B(s,i))

 writeln("B filtered: ", array(s in S,i in I | s<>"a" and i<5) B(s,i))

 writeln("Transpose: ", array(i in I, s in S) B(s,i))
end-model 

And this is the output generated by the model autoarray.mos.

B: [1,2,3,4,5,6]
Row a: [1,2,3]
Row b: [4,5,6]
Column 3: [1,4]
Column 1.5: [2,5]
Column 7: [3,6]
B filtered: [(`b',3,4),(`b',1.5,5)]
Transpose: [1,4,2,5,3,6] 

Note that while it is possible to use index sets of type real for Mosel arrays this is not a generally encouraged practice: due to the underlying floating point representation it is not always guaranteed that two index values that look the same are indeed identical.

On the topic of output to file using initializations to, see Chapter Output, and particularly the note on solution output using arrays generated 'on the fly' in combination with evaluation of in Section Solution output with initializations to.

© 2001-2019 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.