Initializing sets

You can initialize sets in three different ways.

Externally

As stated in Initializing sets, in the Sets topic, the simplest way to initialize a set is by listing its values explicitly in the .dat file.

For example, the declaration:


/* .mod file */
tuple Precedence {
  int before;
  int after;
}
{Precedence} precedences = ...;
/* .dat file */
precedences = {<1,2>, <1,3>, <3,4>};

initializes a set of tuples.

Internally

You can also initialize sets internally (in the .mod file), more precisely by using set expressions using previously defined sets and operations such as union, intersection, difference, and symmetric difference. The symmetric difference of two sets A and B is

(A union symbol B) \ (A intersection symbol B)

described in Expressions.

For example, the declarations:


{int} s1 = {1,2,3}; 
{int} s2 = {1,4,5}; 
{int} i = s1 inter s2; 
{int} j = {1,4,8,10} inter s2; 
{int} u = s1 union {5,7,9}; 
{int} d = s1 diff s2; 
{int} sd = s1 symdiff {1,4,5}; 

initialize i to {1}, u to {1,2,3,5,7,9}, d to {2,3}, and sd to {2,3,4,5}.

It is also possible to initialize a set from a range expression. For example, the declaration:


{int} s = asSet(1..10) 

initializes s to {1,2,..,10}

It is important to point out at this point that sets initialized by ranges are represented explicitly (unlike ranges). As a consequence, a declaration of the form


{int} s = asSet(1..100000); 

creates a set where all the values 1, 2, ..., 100000 are explicitly represented, while the range


range s = 1..100000; 

represents only the bounds explicitly.

Note that when writing the assignment s2=s1, you add one element to s1, that element is also added to s2. If you do not want this, write

s1={i|i in s2}

For example, compare the statements in the following table.

Table 1. Initializing sets in the model file
If you write the result is

{int} s1={1,2};

{int} s2=s1;

execute

{

s2.add(3);

writeln(s1);

}

{1 2 3}

{int} s1={1,2};

{int} s2={ i | i in s1};

//{int} s2=s1;

execute

{

s2.add(3);

writeln(s1);

}

{1 2}

As generic sets

OPL supports generic sets which have an expressive power similar to relational database queries. For example, the declaration:


{int} s = {i | i in 1..10: i mod 3 == 1}; 

initializes s with the set {1,4,7,10}. A generic set is a conjunction of expressions of the form


p in S : condition 

where p is a parameter (or a tuple of parameters), S is a range or a finite set, and condition is a Boolean expression. These expressions are also used in forall statements and aggregate operators and are discussed in detail in Formal parameters.

The declaration:


{string} Resources ...; 
{string} Tasks ...; 
Tasks res[Resources] = ...; 
tuple Disjunction { 
     {string} first; 
     {string} second; 
} 
{Disjunction} disj = {<i,j> | 
       r in Resources, ordered i,j in res[r] 
}; 

is a more interesting example, showing a conjunction of expressions, and is explained in detail in Formal parameters. Generic sets are often useful when you transform a data structure (e.g. the data stored in a file) into a data structure more appropriate for stating the model effectively. Consider, for example, the declarations:


{string} Nodes ...; 
int edges[Nodes][Nodes] = ...; 

which describe the edges of a graph in terms of a Boolean adjacency matrix. It may be important for the model to use a sparse representation of the edges (because, for instance, edges are used to index an array). The declaration:


tuple Edge { 
    Nodes o; 
    Nodes d; 
}
{Edge} setEdges = {<o,d> | o,d in Nodes : edges[o][d]==1}; 

computes this sparse representation using a simple generic set. It is of course possible to define generic arrays of sets. For example, the declaration:


{int} a[i in 3..4] = {e | e in 1..10: e mod i == 0};

initializes a[3] to {3,6,9} and a[4] to {4,8}.