AMoR - Amazing Modelling with Ruby
The mathematical modelling language you will fall in love with!
Show me how it works

About

What is AMoR?

AMoR 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.

Example

Packing your knapsack

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.

ItemValueSize
Pepper63
Pumpkin126
Melon115
Garlic52
Apple53
Peach73
# 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}."

Installation

Yes, it is that simple

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.

Sources

You can find the sources on Github

Documentation

A brief introduction into AMoR

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.

Team

You are welcome to join

Florian Dahms

Developer

Contact

contact@ignorefdahms.com