IBM ILOG Solver User's Manual > The Basics > Searching with Predefined Goals: Magic Square > Model

Once you have written a description of your problem, you can use Concert Technology classes to model it.

Step 2   -  

Open the example file

Open the example file YourSolverHome/examples/src/tutorial/magicsq_partial.cpp in your development environment. In this exercise, you use arrays of constrained integer variables and therefore you use the include file <ilsolver/ilosolverint.h>. This example accepts command line arguments, but is set to default to a magic square of size 5. Some of the code for adding constraints to the model is provided for you, as is the code for printing out the solution.

As you know by now, the first step in converting your natural language description of the problem into code using Concert Technology classes is to create an environment and a model. This code is provided for you:

int main(int argc, char* argv[]){
  IloEnv env;
  try {
    IloModel model(env);

Concert Technology gives you the means to represent the unknown information in this problem--the number in each position in the square--as an array of constrained integer variables. After you create an environment and a model, you declare the variables as an instance of IloIntVarArray with n2 elements, ranging from 1 to n2. These decision variables will contain the solution to the problem, once it is solved.

Step 3   -  

Declare the decision variables

Add the following code after the comment //Declare the decision variables

    IloIntVarArray square(env, n*n, 1, n*n);

This array will give you the number in each position in a magic square of size n. The element [0] in the array is the number in the first position in the magic square (row 0, column 0)--remember that Solver arrays start at [0]. The element [1] in the array is the number in the second position in the magic square (row 0, column 1), and so on.

You use two nested "for" loops to create a matrix to represent the magic square. In this matrix, each element of the array squares is situated in a particular row, column, or main diagonal. You will use loops like this to create the constraints on the sums of rows, columns, and main diagonals.

Examine the following matrix:

for (i = 0; i < n; i++){
  for (j = 0; j < n; j ++)
    square[n*i+j];
}    

In this matrix, the variable i represents the row number. The variable j represents the column number. For a magic square of size n=3, the following matrix is created:


 
j = 0  
column 0 
j = 1 
column 1 
j = 2 
column 2 
i = 0 
row 0 
3*0 + 0 = 0 
square[0] 
3*0 + 1 = 1 
square[1] 
3*0 + 2 = 2 
square[2] 
i = 1 
row 1 
3*1 + 0 = 3 
square[3] 
3*1 + 1 = 4 
square[4] 
3*1 + 2 = 5 
square[5] 
i = 2 
row 2 
3*2 + 0 = 6 
square[6] 
3*2 + 1 = 7 
square[7] 
3*2 + 2 = 8 
square[8] 

You can use these loops to identify the elements that make up each row. Once you have identified these elements, you can place constraints on their sums.

To model the sum of each row you use expressions, which are represented by the class IloExpr in Concert Technology. Values may be combined with variables to form expressions. The first parameter of the constructor is always the environment. Here is a constructor:

IloExpr(const IloEnv env);

To combine values with variables to form an expression, you can use the operators:

You use IloExpr and "for" loops to add the constraints on the sum of each row to the model. The constraints on columns and main diagonals, which are declared in a similar fashion, are provided for you in the code.

Step 4   -  

Add the constraints on rows

Add the following code after the comment //Add the constraints on rows

    for (i = 0; i < n; i++){
      IloExpr exp(env);
      for(j = 0; j < n; j++)
        exp += square[n*i+j];
      model.add(exp == sum);
      exp.end();
    }

In this code, the variable i represents the row and the variable j represents the column. You create an expression exp for the first row i = 0. Using the nested loop with variable j, you traverse that row, column by column, and use the self-assigned addition operator += to add the elements of the array square that are located in that row. You then add a constraint to the model that the expression exp for each row equals sum. In the code provided for you, sum is declared to equal n(n2 + 1)/2. Then you add the constraints for the other rows.

IloAllDiff

Using predefined constraints, such as IloAllDiff, makes modeling simpler.

The single global constraint IloAllDiff on n variables is logically equivalent to n(n+1)/2 instances of the "not equal" constraint, !=, between each pair of variables in the set.

The member function IloExpr::end lets Solver know that an IloExpr object will no longer be directly used. Solver can still use the object indirectly, for example through a constraint that is built on this object. Solver will not delete the object until it is no longer used either directly or indirectly.

Now you add the constraint that all the numbers in the square must be different. To do this, you use the Concert Technology predefined constraint IloAllDiff. It states that the value of each variable in an array must be different from that of every other variable in that array. The first parameter is the environment. The second parameter is the array of variables. The third parameter is an optional name used for debug and trace purposes. Here is a constructor:

IloAllDiff(const IloEnv env, 
           const IloNumVarArray vars = 0, 
           const char* name = 0); 

Step 5   -  

Add the constraint IloAllDiff

Add the following code after the comment //Add the constraint IloAllDiff

    model.add(IloAllDiff(env, square));