Minizinc, how to create a map or a dictionary datastructure

693 views Asked by At

I have a simple question regarding the Minizinc's syntax. My input .dzn file contain a set of 2 dimentional arrays (approximately up to 30 arrays), declared as follows:

rates_index_0 = array2d(1..3, 1..501, [ 15, 20, 23, ....
rates_index_12 = array2d(1..3, 1..501, [ 21, 24, 27, ....
...

note: index numbers have gaps in them (e.g., 12 -> 20)

In my model, I need to use one of these arrays depending on the value of the variable. In common programming language I would solve it using a map or a dictionary datastructure. But in Minizinc I am hardcoding this in the following way:

function var int: get_rate(int: index, var int: load, int: dc_size) =

        if index == 0 then
          rates_index_0[dc_size, load]
        else if index == 12 then
          rates_index_12[dc_size, load]
        else if index == 16 then
          rates_index_16[dc_size, load]
        else if index == 20 then
          rates_index_20[dc_size, load]
        else
          assert(false, "unknown index", 0)
        endif endif endif endif;

The one obvious problem with this code is that I need to change model each time I change input. Is there a better way how I can code this in a generic way?

Thanks!

1

There are 1 answers

3
Dekker1 On BEST ANSWER

In an more abstract way a map-structure is nothing more than a function mapping inputs of a certain type to an array. A map can thus be replaced by a array and a function. (The difference being you will have to define the function yourself)

Before I get started with the rest I would like to point out that if your model compiles generally fast, you might want to try a triple array without the function, rates_index = array3d(0..60, 1..3, 1..501, [ 15, 20, 23, ..... This will cost more memory, but will make the model more flexible.

The general way of using a map-structure would be to define a function map_index, that maps your input, in this case integers, to the index of the array, also integers. This means that we can then define a extra level array to point to the right one: rates_index = array3d(0..nr_arrays, 1..3, 1..501, ..... This means that the content of get_rates can then be: rates_index[map_index(index), dc_size, load].

The function map_index itself in its simplest form would contain another version of your if-then-else statements:

function int: map_index(int: index) =  
    if index == 0 then
        0
    else if index == 12 then
        1
    else if index == 16 then
        2
    else if index == 20 then
        3
    else
        assert(false, "unknown index", 0)
    endif endif endif endif;

However you can make it dynamic by generating an extra array containing the array number for each index, putting -1 for all arrays that are not available. For your example the mapping would look like this: array[0..20] of int: mapping = [0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 2, -1, -1, -1, 3];. The map_index function could then dynamically be defined as:

function int: map_index(int: index) =  
    mapping[index];