The first set of interest in this problem is the set of analysts. This is a primitive set that can be declared simply as:

SETS:

  ANALYSTS;

ENDSETS

The final set we want to construct is a set consisting of all the potential pairings. This will be a derived set that we will build by taking the cross of the ANALYST set on itself. As a first pass, we could build the dense derived set:

PAIRS( ANALYSTS, ANALYST);

This set, however, would include both PAIRS( I, J) and PAIRS( J, I). Since only one of these pairs is required, the second is wasteful. Furthermore, this set will include "pairs" of the same analyst of the form PAIRS( I, I). As much as each of the analysts might like an office of their own, such a solution is not feasible. The solution is to put a membership filter on our derived set requiring each pair (I,J) in the final set to obey the condition J be greater than I. We do this with the set definition:

PAIRS( ANALYSTS, ANALYSTS) | &2 #GT# &1;

The start of the membership filter is denoted with the vertical bar character (|). The &1 and &2 symbols in the filter are known as set index placeholders. Set index placeholders are valid only in membership filters. When LINGO constructs the PAIRS set, it generates all combinations in the cross of the ANALYSTS set on itself. Each combination is then "plugged" into the membership filter to see if it passes the test. Specifically, for each pair (I,J) in the cross of the ANALYST set on itself, I is substituted into the placeholder &1 and J into &2 and the filter is evaluated. If the filter evaluates to true, (I,J) is added to the pairs set. Viewed in tabular form, this leaves us with just the diagonal elements of the (I,J) pairing table.

We will also be concerned with two attributes of the PAIRS set. First, we will need an attribute that corresponds to the incompatibility rating of the pairings. Second, we will need an attribute to indicate if analyst I is paired with analyst J. We will call these attributes RATING and MATCH. We append them to the PAIRS set definition as follows:

PAIRS( ANALYSTS, ANALYSTS) | &2 #GT# &1:

RATING, MATCH;

The completed sets section containing both set declarations is then:

SETS:

  ANALYSTS;

  PAIRS( ANALYSTS, ANALYSTS) | &2 #GT# &1:

   RATING, MATCH;

ENDSETS

Next, we initialize the ANALYSTS set and the RATING attribute to the incompatibility ratings in the data section:

DATA:

  ANALYSTS = 1..8;

  RATING =

     9  3  4  2  1  5  6

        1  7  3  5  2  1

           4  4  2  9  2

              1  5  5  2

                 8  7  6

                    2  3

                       4;

ENDDATA

We will use the convention of letting MATCH( I, J) be 1 if we pair analyst I with analyst J, otherwise 0. Given this, the MATCH attribute contains the decision variables for the model.

Our objective is to minimize the sum of the incompatibility ratings of all the final pairings. This is just the inner product on the RATING and MATCH attributes and is written as:

MIN = @SUM( PAIRS( I, J):

RATING( I, J) * MATCH( I, J));

There is just one class of constraints in the model. In words, it is:

For each analyst, ensure that the analyst is paired with exactly one other analyst.

Putting the constraint into LINGO syntax, we get:

@FOR( ANALYSTS( I):

@SUM( PAIRS( J, K) | J #EQ# I #OR# K #EQ# I:

 MATCH( J, K)) = 1

);

The feature of interest in this constraint is the conditional qualifier (J #EQ# I #OR# K #EQ# I) on the @SUM function. For each analyst I, we sum up all the MATCH variables that contain I and set them equal to 1. In so doing, we guarantee analyst I will be paired up with exactly one other analyst. The conditional qualifier guarantees we only sum up the MATCH variables that include I in its pairing.

One other feature is required in this model. We are letting MATCH( I, J) be 1 if we are pairing I with J. Otherwise, it will be 0. Unless specified otherwise, LINGO variables can assume any value from 0 to infinity. Because we want MATCH to be restricted to being only 0 or 1, we need to apply the @BIN variable domain function to the MATCH attribute. Variable domain functions are used to restrict the values a variable can assume. Unlike constraints, variable domain functions do not add equations to a model. The @BIN function restricts a variable to being binary (i.e., 0 or 1). When you have a model that contains binary variables, it is said to be an integer programming (IP) model. IP models are much more difficult to solve than models that contain only continuous variables. Carelessly formulated large IPs (with several hundred integer variables or more) can literally take forever to solve! Thus, you should limit the use of binary variables whenever possible. To apply @BIN to all the variables in the MATCH attribute, add the @FOR expression:

@FOR( PAIRS( I, J): @BIN( MATCH( I, J)));