Solution
In this category of scripting features we have two types of statements. First are the SUBMODEL and ENDSUBMODEL statements used to identify submodels, which are separate models contained within larger models. We also have statements pertaining to solving (in various ways) models and submodels, e.g., the @SOLVE statement is used for solving models and the @DEBUG statement is used to help debug models that are judged as being infeasible or unbounded.
SUBMODEL and ENDSUBMODEL:
These two statements are used to bracket a submodel within a larger, composite model. The SUBMODEL statement must be followed immediately by a unique name of up to 32 characters that follows the normal LINGO naming conventions. The name must then be followed by a colon. You must also place a ENDSUBMODEL statement immediately after the last statement of your submodel. Submodels may only exist in the model section and are not allowed in data, init and calc sections. As an example, the following illustrates a submodel for solving a knapsack type model:
SUBMODEL Pattern_Gen:
[R_OBJ] MAX = @SUM( FG(i): PRICE(i)* Y(i));
[R_WIDTH] @SUM( FG(i): WIDTH(i)*Y(i)) <= RMWIDTH;
@FOR( FG(i): @GIN(Y(i)));
ENDSUBMODEL
In this example, the submodel is titled Pattern_Gen and contains one objective, R_OBJ, one constraint, R_WIDTH, and one @FOR loop to force the Y variables to be general integers via the @GIN function.
@DEBUG( [SUBMODEL_NAME[, …, SUBMODEL_NAME_N]])
Infeasible or unbounded submodels may be debugged in a calc section with the use of the @DEBUG statement. Refer to the Solver|Debug command for more information of model debugging.
If your model contains submodels, you can choose to debug a particular submodel by specifying its name as an argument to @DEBUG. If desired, you may also specify more than one submodel name, in which case, LINGO will simultaneously debug all the specified models as one combined model. If a submodel name is omitted, LINGO will solve all model statements occurring before the @DEBUG statement and not lying within a submodel section. It is the user’s responsibility to make sure the submodels together make sense, e.g., at most one submodel in an @DEBUG invocation can have an objective function.
In the following example, we solve a small submodel and then invoke the debugger if the solution is found to be non-optimal:
MODEL:
SUBMODEL M:
MIN = X + Y;
X>4;
Y<3;
Y>X;
ENDSUBMODEL
CALC:
@SOLVE( M);
@IFC( @STATUS() #NE# 0: @DEBUG( M));
ENDCALC
END
Note: | Submodels must be defined in the model prior to any references to them via the @DEBUG statement. |
@NEXTALTOPT()
@NEXTALTOPT is used in a calc section to find alternate optimal solutions to linear programs, assuming they exist. An alternate optimal solution is a solution with the same objective value as the original optimal solution but with a different set of values for the decision variables.
First, you must call @SOLVE (see below) to perform an initial solve of the model, which yields the first optimal solution. At this point, you may iteratively call @NEXTKBEST to generate one or more additional, unique alternate optimal solutions. For example, consider the following, small linear program (see http://www.math.clemson.edu/~shierd/Shier/MthSc440/Alternative%20Optimal%20Solutions.pdf):
MAX = 6*X1 + 4*X2 ;
X1 + 4*X2 <= 40 ;
3*X1 + 2*X2 <= 30 ;
3*X1 + X2 <= 24 ;
This model has two alternate optimal solutions, both with an objective value of 60, but with (X1,X2)=(6,6) in one case and (X1,X2)=(4,9) in the other case. In the model below, first we call @SOLVE to get the original optimal solution, then we have an @WHILE loop to call the @NEXTALTOPT function until no feasible solutions remain:
MODEL:
! Find all the alternate optimal solutions (2 in this case)
to a linear program;
! The linear program;
SUBMODEL MYLP:
[R_OBJ] MAX = 6*X1 + 4*X2 ;
X1 + 4*X2 <= 40 ;
3*X1 + 2*X2 <= 30 ;
3*X1 + X2 <= 24 ;
ENDSUBMODEL
PROCEDURE REPORT:
! Give a brief report on current solution;
@WRITE( @NEWLINE( 1));
@WRITE( ' Soln ', I, ', Objective = ', R_OBJ,
', X1, X2 = ', X1, ' ', X2,
);
ENDPROCEDURE
CALC:
! Suppress all output except errors;
@SET( 'TERSEO', 2);
! Solve the original model;
@SOLVE( MYLP);
! Suppress final infeasible error;
@SET( 'TERSEO', 3);
! While we continue to have an optimal solution,
solve for next alternate optimal solution;
I = 0;
@WHILE( @STATUS() #EQ# 0:
! Write current soln summary;
I = I + 1;
REPORT;
! Get next alternate optimal soln;
@NEXTALTOPT();
);
! Fall through here when no further alternate
optimal solutions exist;
@IFC( @STATUS() #EQ# 1:
@WRITE( @NEWLINE( 2),
' All Done! No further alt solns found.');
@ELSE
@WRITE( @NEWLINE( 2),
' *** Error solving model ***');
);
ENDCALC
END
Model: ALTOPTCALC
Running this model then gives us the following report, summarizing the two alternate optimal points:
Soln 1, Objective = 60, X1, X2 = 6 6
Soln 2, Objective = 60, X1, X2 = 4 9
All Done! No further alt solns found.
Note: | The @NEXTALTOPT function is available only for linear programs. If your model is a mixed integer linear program (MILP), then you may wish to refer to the @NEXTKBEST function below for determining a selection of best solutions for integer models. |
Note: | The @NEXTALTOPT function is not exhaustive in that for numerically difficult models it's not guaranteed to find all alternate solutions, potentially finding a subset of them instead. |
@NEXTKBEST()
@NEXTKBEST can be used in a calc section to find the "next best" (in terms of objective value) solutions to binary integer programs.
First, you must call @SOLVE (see below) to perform an initial solve of the model, which yields a solution with the best objective value. At this point, you may iteratively call @NEXTKBEST to generate one or more additional solutions ranked by objective value. This gives you the ability to determine if there are solutions close or equal in value to the optimal solution that might be preferred due to some secondary criteria. An example would be in a staff scheduling model, where there might be two solutions of equal cost, however, one of the solutions requires fewer workers on Sundays.
The LINGO sample model set includes the model LOOPKBEST. This model generates the 5 best solutions to a knapsack problem. The code of interest that generates these solutions is:
CALC:
! Reduce the amount of output;
@SET( 'TERSEO', 2);
NBEST = 0;
MXBEST = 5;
! Solve original model;
@SOLVE( KNAPSACK);
! Loop to display additional K-Best solutions;
! Stop when we either have a infeasible solution
or we reach MXBEST solutions.;
@WHILE( @STATUS() #EQ# 0 #AND# NBEST #LT# MXBEST:
NBEST = NBEST + 1;
! Print summary of current solution;
REPORT;
!Solve for next best solution;
@NEXTKBEST();
);
ENDCALC
Model: LOOPKBEST
As mentioned, @SOLVE is called first to get an optimal solution to the model. We then have a @WHILE loop that call @NEXTKBEST until we either have an infeasible solutions, or we've generated 5 total solutions. Note that the initial @SOLVE call counts as one solution. We also make use of a procedure, REPORT, to print a summary report of each solution. You may open the model file in LINGO if you care to view this reporting procedure.
Note: | Calling @NEXTKBEST indefinitely can yield all the feasible solutions to a 0/1 program. However, for some models this may entail a very large number of solutions. |
Note: | If the model is not convex and you are not using the global solver, then the K-Best solutions may not be generated according to objective rank due to finding locally optimal solutions along the way. |
@SOLVE( [SUBMODEL_NAME[, …, SUBMODEL_NAME_N]])
Submodels may be solved in a calc section with the use of the @SOLVE statement. If your model contains submodels, you can choose to solve a particular submodel by specifying its name as an argument to @SOLVE. If desired, you may also specify more than one submodel name, in which case, LINGO will simultaneously solve all the specified models as one combined model. If a submodel name is omitted, LINGO will solve all model statements occurring before the @SOLVE statement and not lying within a submodel section. It is the user’s responsibility to make sure the submodels together make sense, e.g., at most one submodel in an @SOLVE invocation can have an objective function.
As an example, to solve the submodel Pattern_Gen listed immediately above, you would add the following statement to a calc section:
@SOLVE( Pattern_Gen);
Note: | Submodels must be defined in the model prior to any references to them via the @SOLVE statement. |