AMoR - Amazing Modelling with Ruby

The mathematical modelling language you will fall in love with!

Show me how it worksAMoR is a DSL (domain specific language) for Ruby. It shall help you with formulating models for linear and integer programming. There are other modelling languages with the same goal (AIMMS, AMPL and GAMS to name a few) and which do a great job in a lot of standard cases. But they usually share a common problem: as soon as you need to express more complex conditions or need to bind in data from some uncommon source or have any other special requirement, it can easily happen that working with them becomes very painful. AMoR was created to resolve this issue. Instead of building a new language from scratch, it builds on top of Ruby. That means you get the best from both worlds. A simple syntax for formulating your problems while being able to use the power of Ruby to resolve issues you encounter on the way. And as if that is not enough, you can use AMoR completely free of charge - even commercially.

Currently AMoR supports the great open source solver SCIP as well as exporting models into LP format. In the near future there shall be support for additional solvers. Also there is no support for nonlinear programming yet. Maybe this will also be added in a later stage.

AMoR is in a very early stage and should not (yet) be used for production code. But feel free to try it out, report bugs and send in suggestions for improvements. Also you are very welcome to contribute and join the Team.

You have a bunch of things at home that you want to pack. Unfortunately not all of them fit into your knapsack. The table shows the items you have, how much they are worth to you and their volume. You can pack items up to a total volume of 15. What item should you pack?

Below you see an example AMoR script. After installing AMoR you can just copy it into a file (e.g. named 'knapsack.amor') and run

`amor knapsack.amor`

in order to see the optimal solution.

Item | Value | Size |
---|---|---|

Pepper | 6 | 3 |

Pumpkin | 12 | 6 |

Melon | 11 | 5 |

Garlic | 5 | 2 |

Apple | 5 | 3 |

Peach | 7 | 3 |

# The data fruits = ['Pepper', 'Pumpkin', 'Melon', 'Garlic', 'Apple', 'Peach'] value = { 'Pepper' => 6, 'Pumpkin' => 12, 'Melon' => 11, 'Garlic' => 5, 'Apple' => 5, 'Peach' => 7 } size = { 'Pepper' => 3, 'Pumpkin' => 6, 'Melon' => 5, 'Garlic' => 2, 'Apple' => 3, 'Peach' => 3 } # Maximize total value max sum(fruits) {|f| value[f] * x(f)} # Keeping in mind our knapsack capacity st sum(fruits) {|f| size[f] * x(f) } <= 15 # Either pack fruit or leave it at home forall(fruits) {|f| binary x(f)} # Run SCIP scip # Output puts "You should pack: #{fruits.select{|f| x(f) > 0}.join(', ')}." puts "That is a total value of #{objective}."

In order to use AMoR you need a working installation of Ruby. See here how you can install Ruby on your system.

Installing AMoR is as simple as running

`gem install amor`

on the command line.

In order to solve a model you need to install a solver. Currently only SCIP can be called directly (more comming soon). Make sure that you can execute `scip`

in the command line before using it in an AMoR script. You can also save your models in the ".lp" format and run them with your favorite solver.

You can find the sources on Github

AMoR is a Ruby DSL. Therefore anything available in Ruby can be used in AMoR. The new commands available in AMoR are

x(index)

If the model was not yet solved, it returns the variable corresponding to the given index. It creates a new variable if there is none for that index yet. The index can be any Ruby object.

If the model was solved, it returns the value of the variable in the best solution.

min(expression)

Tells the model that it shall minimize the given expression. There can only be one objective for a model, therefore calling min or max repeatedly will overwrite the last objective. An expression can be any linear combination of variables and constants like `3*x(1) + 4 - 2*x(1) + x(2)`

.

max(expression)

Tells the model that it shall maximize the given expression. There can only be one objective for a model, therefore calling min or max repeatedly will overwrite the last objective.

st(constraint)

Adds a constraint to the model. A constraint consists of two expressions separated by either `<=`

, `==`

or `>=`

, like `3*x(1) + 4 - 2*x(1) + x(2) >= 4*x(1) - 2`

.

positive(variable)

Specifies a lower bound of 0 for the given variable.

integer(variable)

Specifies that the given variable shall be integral. Can be stacked with `postive`

like `positive integer x(1)`

.

integer(variable)

Specifies that the given variable shall be integral. Can be stacked with `postive`

like `positive integer x(1)`

.

sum(container) &block

Adds up the given block for every element of the container to one expression. For example `sum([1,2,3,4]) {|i| x(i)}`

is equivalent to `x(1) + x(2) + x(3) + x(4)`

.

forall(container) &block

Calls the given block for each element in the container. If the block returns a constraint, it is directly added to the model (no need to call `st`

).

save_lp(filename)

Saves the model in LP format to the specified file.

scip

Calls SCIP (if installed on the computer) with the current model and retrieves the solution.

Furthermore you can include AMoR into existing (or new) Ruby code via `require 'amor'`

. Afterwards you can define a new model `m = Amor::Model.new`

. Now you can call AMoR commands on the model, eg., `m.max m.x(1) + 3 * m.x(2) - 2 * m.x(3)`

or `m.scip`

.

Developer

contact@ignorefdahms.com