Internal Procedures

AIMMS and internal procedures

Internal procedures are pieces of execution code to perform a dedicated task. For most tasks, and particularly large ones, it is strongly recommended that you use procedures to break your task into smaller, purpose-specific tasks. This provides code structure which is easier to maintain and run. Often it is appropriate to write procedures to obtain input data from users, databases and files, to execute data consistency checks, to perform side computations, to solve a mathematical program, and to create selected reports. Procedures can be called both inside the model text and inside the graphical user interface.

Declaration and attributes

Procedures are added by inserting a special type of node in the model tree. The attributes of a Procedure specify its arguments and execution code. All possible attributes of a Procedure node are given in this table.

Attribute

Value-type

See also page

Arguments

argument-list

Property

UndoSafe

Body

statements

Execution Statements

Comment

comment string

Formal arguments

The arguments of a procedure are given as a parenthesized, comma-separated list of formal argument names. These argument names are only the formal identifier names without reference to their index domains. AIMMS allows formal arguments of the following types:

  • simple sets and relations, and

  • scalar and indexed parameters (either element-valued, string-valued or numerical).

The type and dimension of every formal argument is not part of the argument list, and must be specified as part of the argument’s (mandatory) local declaration in a declaration subnode of the procedure.

Interactive support

When you add new formal arguments to a procedure in the AIMMS Model Explorer, AIMMS provides support to automatically add these arguments as local identifiers to the procedure. For all formal arguments which have not yet been declared as local identifiers, AIMMS will pop up a dialog box to let you choose from all supported identifier types. After finishing the dialog box, all new arguments will be added as (scalar) local identifiers of the indicated type. When an argument is indexed, you still need to add the proper IndexDomain manually in the attribute form of the argument declaration.

Range checking

If the declaration of a formal argument of a procedure contains a numerical range, AIMMS will automatically perform a range check on the actual arguments based on the specified range of the formal argument.

Input or output

In the declaration of each argument you can specify its type by setting one of the properties

  • Input,

  • Output,

  • InOut (default), or

  • Optional.

AIMMS passes the values of any Input and InOut arguments when entering the procedure, and passes back the values of Output and InOut arguments. For this reason an actual Input argument can be any expression, but actual Output and InOut arguments must be parameter references or set references.

Optional arguments

An argument can be made optional by setting the property Optional in its declaration. Optional arguments are always input, and must be scalar. When an optional argument is not provided in a procedure call, AIMMS will pass its default value as specified in its declaration.

The Body attribute

In the Body attribute you can specify the sequence of AIMMS execution statements that you want to be executed when the procedure is run. All statements in the body of a procedure are executed in their order of appearance.

Example

The following example illustrates the declaration of a simple procedure in AIMMS. The body of the procedure has only been outlined.

Procedure ComputeShortestDistance {
    Arguments  : (City, DistanceMatrix, Distance);
    Comment    : {
        "This procedure computes the distance along the shortest path
        from City to any other city j, given DistanceMatrix."
         Body: {
        Distance(j) := DistanceMatrix(City,j);

        for ( j | not Distance(j) ) do
            /*
             *  Compute the shortest path and the corresponding distance
             *  for cities j without a direct connection to City.
             */
        endfor
    }
}

The procedure ComputeShortestDistance has three formal arguments, which must be declared in a declaration subnode of the procedure. Their declarations within this subnode could be as follows.

ElementParameter City {
    Range        : Cities;
    Property     : Input;
}
Parameter DistanceMatrix {
    IndexDomain  : (i,j);
    Property     : Input;
}
Parameter Distance {
    IndexDomain  : j;
    Property     : Output;
}

From these declarations (and not from the argument list itself) AIMMS can deduce that

  • the first actual (input) argument in a call to ComputeShortestDistance must be an element of the (global) set Cities,

  • the second (input) argument must be a two-dimensional parameter over Cities\({}\times{}\)Cities, and

  • the third (output) arguments must be a one-dimensional parameter over Cities.

Arguments declared over global sets

As in the example above, arguments of procedures can be indexed identifiers declared over global sets. An advantage is that no local sets need to be defined. A disadvantage is that the corresponding procedure is not generic. Procedures with arguments declared over global sets are preferred when the procedure is uniquely designed for the application at hand, and direct references to global sets add to the overall understandability and maintainability.

Arguments declared over local sets

The index domain or range of a procedure argument need not always be defined in terms of global sets. Also sets that are declared locally within the procedure can be used as index domain or range of that procedure. When a procedure with such arguments is called, AIMMS will examine the actual arguments, and pass the global domain set to the local set identifier by reference. This allows you to implement procedures performing generic functionality for which a priori knowledge of the index domain or range of the arguments is not relevant.

Local sets are read-only

When you pass arguments defined over local sets, AIMMS does not allow you to modify the contents of these local sets during the execution of the procedure. Because such local sets are passed by reference, this will prevent you from inadvertently modifying the contents of the global domain sets. When you do want to modify the contents of the global domain sets, you should pass these sets as explicit arguments as well.

Unit analysis of arguments

Whenever your model contains one or more Quantity declarations (see The Quantity Declaration), AIMMS allows you to associate units of measurements with every argument. Similarly as the index domains of multidimensional arguments can be expressed either in terms of global sets, or in terms of local sets that are determined at runtime, the units of measurements of function and procedure arguments can also be expressed either in terms of globally defined units, or in terms of local unit parameters that are determined runtime by AIMMS. The unit analysis of procedure arguments is discussed in full detail in Unit Analysis of Procedures and Functions.

Local identifiers

Besides the arguments, you can also declare other local scalar or indexed identifiers in a declaration subnode of a procedure or function in AIMMS. Local identifiers cannot have a definition, and their scope is limited to the procedure or function itself.

The property RetainsValue

For each local identifier of a procedure or function that is not a formal argument, you can specify the option RetainsValue. With it you can indicate that such a local identifier must retain its last assigned value between successive calls to that procedure or function. You can use this feature, for instance, to retain local data that must be initialized once and can be used during every subsequent call to the procedure, or to keep track of the number of calls to a procedure.

Execution subnodes

In addition to AIMMS execution statements, you can include references to (named) execution subnodes to the body of a procedure. AIMMS supports several types of execution subnodes. They can either contain just execution statements or provide a graphical input form for complicated statements like the READ, WRITE and SOLVE statement. The contents of the execution subnodes will be expanded by AIMMS into the body of the procedure at the position of their references.

Top-down implementation

By partitioning the body of a long procedure into several execution subnodes, you can effectively implement the procedure in a self-documenting top-down approach. While the body can just contain the outermost structure of the procedure’s execution, the implementation details can be hidden behind subnode references with meaningful names.

The RETURN statement

In some situations, you may want to return from a procedure or function before the end of its execution has been reached. You use the RETURN statement for this purpose. It can be subject to a conditional WHEN clause similar to the SKIP and BREAK statements in loops. The syntax follows.

Syntax

return-statement:

image/svg+xmlRETURN return-value WHEN logical-expression ;

Return value

Procedures in AIMMS can have an (integer) return value, which you can pass by means of the RETURN statement. You can use the return value only in a limited sense: you can assign it to a scalar parameter, or use it in a logical condition in, for instance, an IF statement. You cannot use the return value in a compound numerical expression. For more details, refer to Calls to Procedures and Functions.

The Property attribute

In the Property attribute of internal procedures you can specify a single property, UndoSafe. With the UndoSafe property you can indicate that the procedure, when called from a page within the graphical end-user interface of a model, should leave the stack of end-user undo actions intact. Normally, procedure calls made from within the end-user interface will clear the undo stack, because such calls usually make additional modifications to (global) data based on end-user edits.

Procedures summarized

The following list summarizes the main characteristics of AIMMS procedures.

  • The arguments of a procedure can be sets, set elements and parameters.

  • The arguments, together with their attributes, must be declared in a local declaration subnode.

  • The domain and range of indexed arguments can be in terms of either global or local sets.

  • Each argument is of type Input, Output, Optional or InOut (default).

  • Optional arguments must be scalar, and you must specify a default value. Optional arguments are always of type Input.

  • AIMMS performs range checking on the actual arguments at runtime, based on the specified range of the formal arguments.