4

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!

kirbo
  • 1,707
  • 5
  • 26
  • 32

1 Answers1

4

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];
Dekker1
  • 5,565
  • 25
  • 33
  • OK, I see, could you please clarify your statement about compilation time and memory usage. Does array3d(0..60, 1..3, 1..501) takes more computational/memory resources than 60 array2d(1..3, 1..501) ? I understood your solution, basically I just merge my array2ds into one 3d and then map indexes. The only downside is that the data will be more difficult to comprehend for humans. But ofcourse I dont have to change the model. Thanks! – kirbo Sep 18 '17 at 10:13
  • 2
    I think you got the point! Regarding the memory consumption: 60 2d-arrays don't take more memory during compilation than the combined 3d arrays. However, if you don't want to do the translation step and choose to add extra (empty) arrays in the 3d-array that were not among the 2d arrays then there could be a big memory overhead. – Dekker1 Sep 18 '17 at 22:47
  • 1
    This answer is now outdated, since MiniZinc now supports [record types](https://www.minizinc.org/doc-2.7.2/en/tuple_and_record_types.html). – Anderson Green May 28 '23 at 22:18