Optimize Execution Time

The time spent by AIMMS applications can be divided into AIMMS execution time ( including evaluation parameters with definition, executing procedures, generate matrix for solvers, etc), the time spent by solvers, and the I/O time.  Here are some coding tricks that help you improve AIMMS execution time.

1. Avoid for Loop

Use bulk execution of assignment as much as possible. If a for loop is necessary, try to minimize calculation inside the loop. For example,

for (i,j) do
         A(i,j) := B(i,j) + C(i,j)
endfor;

can be written as the following bulk statement

A(i,j) := B(i,j) + C(i,j);

2. Pay attention to index order

When declaring a parameter with multiple indices, usually index with small cardinality goes first and running index goes last. For example, in the following statement, k is used as running index:

A(i,j) := Sum[(k), D(i,j,k)];

Another thing to keep in mind is to put the indices in same order. For example the following statement

isActive(p,t,s):= 1 $ (t >= Begin(p,s) and t < (Begin(p,s)+Duration(t,s)));

runs much faster than

isActive(p,s,t):= 1 $ (t >= Begin(p,s) and t < (Begin(p,s)+Duration(t,s)));

3. Use index domain condition

Domain condition puts restriction on the indices and thus reduces memory and time consumption. Use it whenever possible. The usage of index domain can be found on related posts. One thing to be careful when using domain condition is to avoid sub index expression.

A sub index expression is the expression depend on fewer indices than the entire expression. For example, in the following statement,

F(i,k) := G(i,k) * Sum[j | A(i,j) = B(i,j), H(j)]

the entire expression depends on indices (i,j,k), but expression Sum[j | A(i,j) = B(i,j), H(j)] only has (i,j). During calculating the value of F(i,k),  AIMMS will evaluate the result of sum term for each combination of (i,k), although the its result will be the same of all k. To avoid unnecessary evaluation for k, the one statement can be separated into two statements:

FP(i) := Sum[j | A(i,j) = B(i,j), H(j)] ;
F(i,k) := G(i,k) * FP(i) ;

Another example, although domain condition is added, the following statement is still inefficient:

sum[(t,s,i,j,k) | ElementPara(i,j) = k, ]

Since ElementPara(i,j) = k is a sub index expression, AIMMS will create a temporary identifier index over (t,s,i,j,k) to evaluate the condition over the full domain. And comparison operation is a dense operation, thus the calculation needs to go over every (t,s,i,j,k). The result will be time and memory consuming.

The problem can be solved by introducing a new parameter SumCondition(i,j,k) and having

SumCondition(i,j,k) := (ElementPara(i,j) = k);
sum[(t,sc,i,j,k) | SumCondition(i,j,k), ];

These are some general rules.

Improving skills

In practice, lots of the performance improvements are done by trial and error. To improve upon that time consuming process you can:

  1. Use the AIMMS diagnostic tools, such as Debugger, Profiler, and Identifier Cardinalities Viewer to identify performance bottlenecks.

  2. Read about the underlying technology to obtain insights in the AIMMS execution engine in the AIMMS Language Reference Chapters The AIMMS Sparse Execution Engine and Execution Efficiency Cookbook.

  3. Train yourself doing Execution Efficiency course at AIMMS E-Learning Center.