As you might guess, we will need two primitive sets in this model—one to represent the airplane models and one to represent the raw materials. We can construct these sets as follows:

PLANES:

 PROFIT, SETUP, QUANTITY, BUILD;

RESOURCES: AVAILABLE;

We added the following four attributes to the PLANES set:

PROFIT

stores profit contribution for the plane,

SETUP

stores setup cost to begin producing the plane,

QUANTITY

a variable for quantity of planes to produce, and

BUILD

a binary variable, 1 if we produce the plane, else 0.

The AVAILABLE attribute on the RESOURCES set will be used to store the availability of each resource.

We will also need to derive a dense set by taking the cross of the RESOURCES set with the PLANES set. We need this set in order to define a USAGE attribute to store the resource usage of each plane. We will call this derived set RXP, which, after inclusion into the sets section, gives us:

SETS:

  PLANES:

    PROFIT, SETUP, QUANTITY, BUILD;

  RESOURCES: AVAILABLE;

  RXP( RESOURCES, PLANES): USAGE;

ENDSETS

In our data section, we will initialize the set members: PLANES and RESOURCES, along with the data attributes: PROFIT, SETUP, AVAILABLE, and USAGE. Here is the data section we will use:

DATA:

  PLANES    PROFIT  SETUP =

    ROCKET     30     35

    METEOR     45     20

    STREAK     24     60

    COMET      26     70

    JET        24     75

    BIPLANE    30     30;    

  RESOURCES AVAILABLE =

    STEEL,800 COPPER,1160 PLASTIC,1780

    RUBBER,1050 GLASS,1360 PAINT,1240;

  USAGE =  1 4 0 4 2 0

           4 5 3 0 1 0

           0 3 8 0 1 0

           2 0 1 2 1 5

           2 4 2 2 2 4

           1 4 1 4 3 4;

ENDDATA

With the sets and data sections complete, we can now turn our attention to the objective function. For our objective, we want to maximize total net profit. Specifically, this is computed as the sum of profit times quantity produced of each plane, minus its setup cost multiplied by the BUILD binary variable. In LINGO syntax, we express the objective as:

MAX = @SUM( PLANES:

PROFIT * QUANTITY - SETUP * BUILD);

Since all attributes are defined on the index set, we can drop the set index variable and use implicit indexing.

For our first set of constraints, we want to be sure raw material supplies are not exceeded. In words, what we want is:

For each resource i, the sum over each plane j of the quantity of plane j built

 multiplied by the resource usage of resource i by plane j must be

   less-than-or-equal-to the availability of resource i.

Given the vagaries of the English language, it's highly likely one would find the equivalent LINGO notation more concise and easier to understand:

@FOR( RESOURCES( I):

@SUM( PLANES( J):

 USAGE( I, J) * QUANTITY( J)) <=

  AVAILABLE( I)

);

Our next set of constraints is not quite as intuitive. We are using the binary variable BUILD to represent if a plane is being built, so we can incorporate a fixed cost for the plane in the objective function. What we need is some constraint mechanism to force BUILD( I) to be 1 when we produce a nonzero quantity of plane I. The following constraint will do just that:

@FOR( PLANES:

QUANTITY <= 400 * BUILD;

 @BIN( BUILD)

);

Given that BUILD is 0/1, as soon as QUANTITY goes nonzero the only feasible solution is for BUILD to go to 1. Constraints of this form used to force a binary variable to an appropriate value are sometimes referred to as forcing constraints.

The coefficient of 400 in our forcing constraints was chosen because we know from scanning our data that no more than 400 of any plane can be built. Can you verify this? Coefficients used in this manner are sometimes called Big M coefficients. For solver efficiency reasons, it's best to try to keep Big M values as small as reasonably possible.

Because the Big M coefficient of 400 is dependent upon the model's data, it is actually bad modeling practice to embed the coefficient in the model's constraints as we have done here. As we have discussed, it is best to try to keep the constraints of your model independent of the data to facilitate model maintenance. A more data independent formulation would actually involve calculations to come up with a good Big M value. Can you think of how you might add such a feature to this model?

A reasonable question at this point would be: "We have the machinery to force BUILD to 1 when we build a plane. What forces BUILD to zero when we don't build a plane?" The fact that BUILD appears in the objective with a negative coefficient (we multiply it by SETUP and then subtract it from the objective) guarantees this. If a plane was not being built and the corresponding BUILD variable was 1, we could get a better solution by simply setting BUILD to 0. Since the goal is to maximize the objective, BUILD will always be driven to 0 when a plane is not built.

One final feature of our forcing constraints to note is that we have piggybacked the @BIN function call onto the @FOR statement for the forcing constraints. As you recall from the discussion of set looping functions in Using Sets, an @FOR function may contain multiple expressions as long as they are separated by a semicolon. We have capitalized on this feature by including the @BIN expression as well.

As a final feature, we can make the QUANTITY variables general integers with the expression:

@FOR( PLANES: @GIN( QUANTITY));