Tech Blog

Tips and Tricks for AIMMS Users

Aborting execution of AIMMS

With AIMMS it is possible to interrupt long running executions by pressing the keyboard shortcut CTRL-Shift-S. However, this requires multiple statements to be executed and/or generation of multiple constraints because this shortcut key only works between two statements executed that are executed or constraints that are generated. The only exception to this is the solve statement in AIMMS, although this is a single statement, you can still interrupt it with this keyboard shortcut as most solvers allow user interrupt between solver iterations.

With AIMMS 3.10 and later, an additional program that is installed with the AIMMS installation is AimmsInterrupt. You can find this after you have installed AIMMS in the Start Menu. This new tool is more powerful than the original CTRL-Shift-S shortcut, as it is even able to interrupt long running statements.

For example, the assignment

someParameter1(i,j,k,l) := if someParameter2(i,j,k,l) <= 2 then 1 endif ;

potentially can take a lot of time in case there are billions of combinations possible with the 4 indices. If AIMMS is executing the above assignment statement, you are not able to interrupt it with the CTRL-Shift-S shortcut (as it is a single statement). However, with the new AimmsInterrupt tool, you are able to interrupt this long-running assignment statement.

Screenshot AimmsInterrupt

Screenshot AimmsInterrupt

After you start AimmsInterrupt from the Start Menu, it will add a trayicon to the windows traybar. When you click on this AIMMS icon, you will get a list of all the AIMMS processes on your computer that can be interrupted as can be seen in the picture on the right.

Please keep in mind that after you interrupted execution, you cannot rely on the data of parameter someParameter, because the assignment statement was not finished.

The AimmsInterrupt tool is particular useful if somehow you made a mistake and the execution of an assignment statement or evaluation of the definition of a parameter takes forever and you did not yet save your project. Another useful case is when you are working on a large project and it is not clear which statement and/or constraint requires an unexpectedly large amount of time.

This entry was posted in Beginner, Technical on by .

AIMMS on Facebook and Twitter

To stay updated with news about AIMMS (including notifications for new articles on this blog) you now have two additional possibilities:


Like the AIMMS page on Facebook:
[rawr]


[/rawr]


Follow @aimms on Twitter.

 

This entry was posted in Uncategorized on by .

End-user feature for 2D-Chart object

One of the features in the 2D chart page object that is not very well-known is that the end-user can actually change the view of the chart (e.g. scaling or moving)

  • Scale the chart by pressing CTRL, holding down both mouse buttons. Then move the mouse up to increase the size of the chart or move the mouse down to decrease the size of the chart.
  • Move the chart by pressing SHIFT and, while holding both mouse buttons down, moving the mouse.
  • Zoom to an area in the chart by using CTRL and the left mouse button. Drag the mouse to pick the area to zoom on.
  • Rotate the chart by holding both mouse buttons and moving the mouse up or down. Note: this will no yield an effect in every chart type.

Perhaps the most important of these features is that you can reset the view to the original by pressing the ‘r‘ key on your keyboard.

This entry was posted in Beginner, Technical on by .

Tricks to Improve AIMMS 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. In practice, lots of the performance improvements are done on by trials and errors.  And AIMMS diagnostic tools, such as Debugger, Profiler, and Identifier Cardinalities Viewer can help with identifying the performance bottleneck. You can refer to AIMMS User’s Guide for how to use them. And more insights of AIMMS execution engine can be found in chapter Sparse Execution Engine in AIMMS Language References.
This entry was posted in Beginner, Technical on by .

Creating StopWatch in AIMMS to time execution

There are situations where you would like to know how long the execution of something in AIMMS took.

When you are working as an AIMMS developer, one of the tools you have for this is the AIMMS profiler. This profiler will provide you with information about how long each statement in an execution took, as well as how long the evaluation of the definition of a parameter took. More information about the profiler can be found in the chapter “Debugging and Profiling an AIMMS Model” in the AIMMS User’s Guide.

When running in End-user mode, the profiler is not available. To still be able to give the end-user feedback on how much time certain steps took, you can create a ‘stopwatch’ in AIMMS code. This can be achieved by introducing the following identifiers into your model:

QUANTITY:
   identifier  :  SI_Time_Duration
   base unit   :  s
   conversions :  tick -&gt; s : # -&gt; # / 100
   comment     :  "Expresses the value for the duration of periods." ;
 
STRING PARAMETER:
   identifier  :  StartTime
   comment     :  "Time the stopwatch was started" ;
 
PARAMETER:
   identifier  :  ElapsedTime
   unit        :  s
   comment     :  "Time that has elapsed since the stopwatch was started. The 
                   value for this is updated by the StopStopwatch procedure";
 
PROCEDURE
  identifier :  StartStopwatch
  comment    :  "Set the starttime of the stopwatch"
  body       :  
    !Use the CurrentToString AIMMS function to store the current time 
    !in YYYY-MM-DD HH:MM:SS:TT format
    StartTime := CurrentToString( "%c%y-%m-%d %H:%M:%S:%t");
 
ENDPROCEDURE  ;
 
PROCEDURE
  identifier :  StopStopwatch
  comment    :  "Deterine how many ticks have elapsed since the start 
                 of the stopwatch"
  body       :  
    !Using the CurrentToMoment AIMMS function, we can ask for the number
    !of ticks that have elapsed at the moment since the given StartTime 
    !(which was stored by calling the StartStopwatch procedure).
    !Please note that we do not actually 'stop' the stopwatch, but only 
    !store the time elapsed.
    ElapsedTime := CurrentToMoment( [tick], StartTime );
 
ENDPROCEDURE  ;

If your model already contains the SI_Time quantity, just make sure that the units second and tick (1/100th of one second) are defined (either as conversion, or as base unit).

You can download the above code as an aim file from the link below. Please see the instructions in the post Exporting a section and importing it in another AIMMS project to import this aim file into your own project. If your project already contains the SI_Time quantity, please remove the quantity from the aim file after downloading it.

StopwatchSupport
Title: StopwatchSupport (0 click)
Caption:
Filename: StopwatchSupport.aim
Size: 1 kB

After you have imported the section, the stopwatch code can be used as follows:

StartStopwatch ; 
SomeLongLastingProcedure ; 
StopStopwatch ; 
DialogMessage(formatstring("Execution of procedure took %n seconds"
                           , ElapsedTime)
             ) ;

When running this code, you will get a dialog window telling you how many seconds the execution of SomeLongLastingProcedure took.

This entry was posted in Beginner, Technical on by .

Improve performance of for loop containing if-then statement

I often get the question from customers to help them with improving the performance of their project. There are different components of your AIMMS project that you can optimize. For example, you can try to reduce the time required by the solver by reformulating your problem, or in the case of CPLEX tune the options (see the article Tuning CPLEX options from within AIMMS for more details).

Sometimes the efficiency of the AIMMS code in procedures can be improved. One simple example is when your code looks like the following:

for someIndex do
   if conditionParameter(someIndex) then
      !Do something based on the value of someIndex	
   endif ; 
endfor ;

This code loops over all elements of the set of index someIndex. For each of these elements, it checks if conditionParameter(someIndex) has a non-default value and if this is the case it will perform some other tasks.

This way of using the if condition in a for loop is standard in most programming languages. The problem however is that the if-condition is evaluated for each element in the set of someIndex. With AIMMS, you can improve the performance of such loops.

By making use of domain conditions, you can restrict the for-loop to loop over only those elements for which the conditionParameter holds, therefore eliminating the need for checking the if condition for each element. If you modify the above code to the following, performance will improve:

for someIndex | conditionParameter(someIndex) do
   !Do something based on the value of someIndex	
endfor ;

To find out which parts of your model are taking the most amount of time, you can use the AIMMS profiler. More details about the usage of the profiler can be found in the section “Debugging and Profiling an AIMMS Model” in the AIMMS User’s Guide.

For more advanced information about improving the execution performance, please see the chapters “Sparse Execution Engine” and “Execution Efficiency Cookbook” in the AIMMS Language Reference.

This entry was posted in Beginner, Technical on by .

Tuning CPLEX options from within AIMMS

The CPLEX solver has a large amount of options that influence the way CPLEX solves your model. For certain types of constraints and/or models, you can make an educated guess which combination of options works best for your problem. Unfortunately, this is not always the case.

To help you out with this problem, CPLEX has the possibility to do some automatic tuning of the options. In AIMMS, you can access this CPLEX tuning tool via the following two functions:

  • GMP::Tuning::TuneMultipleMPS
  • GMP::Tuning::TuneSingleGMP

As the names already suggest, the first function will tune the CPLEX options for a set of LP/MPS files. As an argument, you will have to specify the directory containing the LP/MPS files. The second function will tune the options based on one single GMP, which you will have to provide as an argument. As the GMP::Tuning::TuneSingleGMP works as the other GMP function, the solver to use is known. In case of the function GMP::Tuning::TuneMultipleMPS, you must also provide which solver to use as an argument.

In order to create the MPS files for multiple instances of your problem, you can set the project setting General > MPS under the CPLEX specific solver options to “At every solve”. Each time that you solve an instance of your problem, the solver will generate a MPS file.

The rest of the options that you need to provide are the following three:

  • FixedOptions: A subset of the set AllOptions that are to be considered unchangeable and for which the current project options will be used
  • ApplyTunedSettings: A binary argument indicating whether the found tuned option settings should be set in the project options
  • OptionFileName: File to which the tuned option settings should be written. This file can be imported in the Project Options screen.

To use these functions, we first need the following declarations:

SET:
   identifier : FixedOptions
   subset of  : AllOptions
 
ELEMENT PARAMETER:
   identifier :  genMathProgram 
   range      :  AllGeneratedMathematicalPrograms

To actually tune the solver settings, you can use the following code:

 
!Determine which options we consider to be unchangable by CPLEX
!It will use the current value for this setting in the project options.
!As an example, we will forbid the tuning of the setting mip_search_strategy.
FixedOptions := { ’CPLEX 12.3::mip_search_strategy’ } ;
 
 
!First create the GMP out of the Math Program
genMathProgram := GMP::Instance::Generate( MP );
!Then call the TuneSingleGMP function, which will try to find a good
!combination of settings for the solver options for this instance.
!The settings that are in the set FixedOptions will not be considered
!for tuning.
!The found settings will be applied directly in the project settings
!and also will be written to the file "tuned_options_gmp.opt"
gmp::Tuning::TuneSingleGMP(
   GMP                : genMathProgram , 
   FixedOptions       : FixedOptions , 
   ApplyTunedSettings : 1 , !Save found settings directly in project
   OptionFileName     : "tuned_options_gmp.opt" ) ; !Store found settings in this file
 
 
 
!This call will try to find a combination of settings for
!the solver options that are good for the set of MPS/LP files that 
!are found in the subdirectory mps-files in the directory of 
!the project. 
!The settings that are in the set FixedOptions will not be considered
!for tuning.
!The found settings will be applied directly in the project settings
!and also will be written to the file "tuned_options_gmp_mps.opt"
gmp::Tuning::TuneMultipleMPS(
   DirectoryName      : "mps-files" , ! location of mps files, relative to project 
   Solver             : 'CPLEX 12.3' ,! Which solver to use, in this case CPLEX 12.3 
   FixedOptions       :  FixedOptions , !Consider these options unchangable. 
   ApplyTunedSettings :  1 , !Save found settings directly in project 
   OptionFileName     : "tuned_options_gmp_mps.opt" ) ;  !Store found settings in this file

Please note that you must always be careful when you are tuning: you must always ensure that you have one or more instances that are a good representation of all possible instances. If the instances you are tuning are not representative for all possible instances, you will over tune to one specific instance. This might lead to worse performances with the tuned settings for all instances combined.

This entry was posted in Advanced, Technical on by .

Determine where each identifier is used in your model

Over time, AIMMS projects tend to grow larger. During such an evolution of a project, some identifiers can become obsolete.

With larger models, often the question is where each identifier is used in the model, or more important, if it is still used in the model. In this article, we want to provide you with a way to determine which of the identifiers in your model might be obsolete.

The goal is to create the indexed subset sIdentfiersUsing, which contains a subset of AllIdentifiers for each identifier declared in your model.

SET:
   identifier   :  sIdentifiersUsing
   index domain :  iRegularIdentifier
   subset of    :  AllIdentifiers
 
SET:
   identifier :  sRegularIdentifiers
   subset of  :  AllIdentifiers
   index      :  iRegularIdentifier
   definition :  { indexIdentifiers |
                            IdentifierType(IndexIdentifiers) &lt;&gt; 'Section'
                        and IdentifierType(IndexIdentifiers) &lt;&gt; 'Module'
                        and IdentifierType(IndexIdentifiers) &lt;&gt; 'LibraryModule'
                        and IdentifierType(IndexIdentifiers) &lt;&gt; 'declaration'
                 }

As you can see, the set sIdentifiersUsing is indexed over a subset of AllIdentifers, namely all the identifiers except for the Sections, Modules, Libraries, and declaration sections.

To fill the sIdentifiersUsing set with the identifiers that are using the identifier iRegularIdentifier, we make use of the intrinsic procedure ReferencedIdentifiers (see Function Reference) in the following procedure:

PROCEDURE
  identifier :  prDetermineReferenced
 
  DECLARATION SECTION 
 
    SET:
       identifier :  sReferencedIdentifiers
       subset of  :  AllIdentifiers
       index      :  iReferencedIdentifier ;
 
    SET:
       identifier :  sIdentifierSingleton
       subset of  :  AllIdentifiers ;
 
  ENDSECTION  ;
 
  body       :
    !First empty the set
    empty sIdentifiersUsing ;
 
    for iRegularIdentifier do
        !For each non {section/module/library/declaration} identifier in your
        !model, create a singleton set, containing only this identifier
        !(needed because ReferencedIdentifiers must have a set as argument
        sIdentifierSingleton := iRegularIdentifier ;
 
        !Retrieve all the identifiers that are referenced in all attributes
        !of the current identifier
        sReferencedIdentifiers := ReferencedIdentifiers(
                                                sIdentifierSingleton,
                                                AllAttributeNames
                                                )
                                   * sRegularIdentifiers ;
 
        !We now know which identifiers are referenced by iRegularIdentifer.
        !For each of these identifiers, we must add a link that it is used by
        !iRegularIdentifier
        for iReferencedIdentifier do
            sIdentifiersUsing( iReferencedIdentifier ) += iRegularIdentifier ;
        endfor ;
     endfor ;
ENDPROCEDURE  ;

Please note that if the above procedure indicates that an identifier is not used anymore, this only means it does not have a reference within your model anymore.

We have created an .aim file of a section containing the above identifiers, which you can download via the following link:

Where_are_identifiers_used
Title: Where_are_identifiers_used (0 click)
Caption:
Filename: Where_are_identifiers_used.aim
Size: 2 kB


After downloading the above file, please follow the Importing instructions in the blog post Exporting a section and importing it in another AIMMS project to import this .aim file into your project.

Additional information: Even if an identifier is not used anymore in your model, it could still be present on pages in your project. With a combination of the two functions PageGetAll and PageGetUsedIdentifiers (See the Page functions in the Function Reference) you can first obtain a list of all the pages in your model, then query per page all the identifiers used on the page, and finally check if the selected identifier is one of them. This approach will give you per identifier the list of pages it is used on.

Alternatively, if you are only interested in the binary question “is an identifier used on any page yes or no?”, you can also make use of the new function IdentifierGetUsedInformation that has been introduced in AIMMS 3.12 FR2. This function also allows you to determine if there exists a reference to the identifier in any of the case types or menus.

This entry was posted in Advanced, Technical on by .

Exporting a section and importing it in another AIMMS project

Sometimes there are parts of a model that you would like to re-use in another AIMMS model. If it is a very generic component, you could choose to create an AIMMS library or an AIMMS module out of it. Please see the chapter “Organizing a Project Into Libraries” in the AIMMS User’s guide for more information about this. In the cases that you only want to quickly export/import a set of identifiers once, you can also use the export/import functionality in AIMMS.

Also, on this blog we will provide the AIMMS code where applicable as .aim files (AIMMS 3) or as .ams files (AIMMS 4). You  can import these into your existing projects with the instructions found below. In some cases where the whole project is needed (and not only some snippets), we will provide the whole project as an AimmsPack file (AIMMS 3) or as a .zip file (AIMMS 4).

Exporting a section

Before you start exporting, you should place all the identifiers you wish to export from your current model into one section in your AIMMS model. When you have done this, there are two possible ways of exporting this section:

  • Select the section in the model tree and select Export… in the edit menu
  • Use the source attribute of the section identifier to store the section in a separate file

The first option has the advantage that it requires less steps.
However, the second approach has the advantage that you can share code among projects.

In AIMMS 3, to export a section to an .aim file, please follow these steps:

  • Open attribute window of the section you wish to export
  • Select wizard button of the source file attribute
  • Select Write in the menu
  • Select the location you want to store the .aim file
  • Set the “Save as type” to “Text Model Files (*.aim)”
  • Set the filename to what you want and press Save
  • Select wizard button of the source file attributes again
  • Select “Remove (keep subtree)…” in the menu

If you do not perform the last two steps, all identifiers declared in the section will not be stored in the main project file anymore, but separately in the .aim or .ams file that you selected.

In AIMMS 4, to export a copy of a section to a .ams file, just highlight the section in the model editor and then, via the AIMMS Menu, select export.

In AIMMS 4, to make a section available for sharing among projects, please use the following steps:

  • Open attribute window of the section you wish to export
  • Select wizard button of the source file attribute
  • Select Write in the menu
  • Select the location you want to store the .ams file
  • Set the filename to what you want and press Save

Importing a section

To import an .ams, an .aim or an .amb file, please use the following steps:

  • Create a new section in your model that will hold the identifiers that will be imported
  • Select this section identifier in the model tree
  • In the Edit menu, select Import…
  • Select the .ams, .aim or .amb file that you want to import

After this, AIMMS will present you with a dialog that shows which identifiers will be imported and which ones are conflicting with already existing identifiers in your model.

Important note: When using the source file attribute of a section, you can store the contents of the section in a separate file (if you do not use the “Remove (keep subtree)…”). Please note that if you use an .ams or an .aim file for the source file attribute, changes that are made to this text file with any other program (e.g. a text editor or version management system) while the AIMMS project is open will not be picked up by AIMMS! Only after you close and re-open the project, will these changes be visible in your project.

Adapted for AIMMS 4 in March 2015

This entry was posted in Beginner, Technical on by .

Using google to search AIMMS documentation

Often I hear from customers that they are really happy with the amount of documentation that we have about AIMMS. However, sometimes they also tell me that due to the large amount of documentation available, it can be difficult to find exactly what they are looking for.

Since we have all of our documentation also online on our website, including all of our PDFs broken up into separate smaller files, luckily, Google can help with this last problem. One of the features they provide is that you can limit your search to certain domains by adding an additional term to your search query. In order to limit your search to only domains that are below aimms.com (e.g. www.aimms.com, blog.aimms.com), you must add the term

site:aimms.com

to your search query.

Below you can find an example that shows the results for searching for the function ExcelRetrieveTable. The results include not only the Function and Language Reference, but also an article posted on this blog.

Search through AIMMS.com with google

Search through AIMMS.com with google

If you only want to search through the contents of this blog, you can either use the search button on the top of this blog, or you can use the additional search term “site:blog.aimms.com” to your query.

This entry was posted in Technical on by .
  • Customer Reviews

    Read more AIMMS reviews
  • Review AIMMS on G2 Crowd
  • Recent Posts

  • Categories