Array comprehensions and 2d Arrays

1,185 views
Skip to first unread message

Alastair Andrew

unread,
Jun 8, 2021, 9:59:18 AM6/8/21
to MiniZinc
Hi,

I've been looking at the MiniZinc specification for Array Comprehensions, am I right that you can only use them to create flat 1d arrays and it isn't possible to initialise a 2d array with a computed value something like the following? 

set of int: A = 1..3;
set of int: B = 1..4;

array[A,B] of int: matrix = [ [ a * b | a in A ] | b in B ]; % may need nested the other way.

Assuming so, what's the most idiomatic way of initialising a 2d array? Declaring the variable and assigning the results of calling array2d?

set of int: A = 1..3;
set of int: B = 1..4;
array[A,B] of int: matrix = array2d(A, B, [ b | b in B ] ++ [ 2 * b | b in B ] ++ [ 3 * b | b in B ]);

solve satisfy;

output [ show2d(matrix) ];

Thanks,
Alastair

guido.tack

unread,
Jun 8, 2021, 8:56:53 PM6/8/21
to MiniZinc
Hi,

using array2d is indeed the only (and idiomatic) way of initialising a 2d array using a comprehension. However, you can use the fact that nested generators are executed in the same order as multi-dimensional arrays are laid out in memory, so you can simply write

array[A,B] of int: matrix = array2d(A, B, [ a*b | a in A, b in B ]);

We've been thinking about adding syntax for 2d and 3d comprehensions, so this may come in a future release.

Note that [ [a*b | a in A] | b in B ] would be a different type, array[B] of array[A] of int, since you would be able to write comprehensions that result in irregularly shaped arrays. E.g., [ [a*b] | a in 1..b | b in 1..10 ] can't be expressed as a single multi-dimensional array.

We could introduce syntax such as [ a*b | a in A | b in B ], where the two generator parts would be independent, i.e., [ a*b | a in 1..b | b in B ] would not be allowed since b isn't visible in the inner generator. This could of course be extended to three or more dimensions as well.

One problem that this syntax doesn't solve is that comprehensions generate 1-based integer index sets, so you wouldn't get any type safety when using enums. E.g. consider the following code:

enum X = {A,B,C};
enum Y = {D,E,F};
array[X,Y] of int: x = [ 1 | i in Y | j in X ];

This is incorrect, because we're iterating over Y and X in the wrong order. Currently, the compiler wouldn't catch this, and even worse, if you add an array2d call, it will happily coerce the index sets (because they have the same size). So it would be good to have a comprehension construct that automatically determines the resulting index sets as well, but I haven't been able to come up with a concise and clear syntax for that yet.

Cheers,
Guido

Alastair Andrew

unread,
Jun 9, 2021, 8:26:53 AM6/9/21
to MiniZinc
Thanks for the really comprehensive answer. 

Much appreciated. Look forward to seeing what emerges in future releases. 

Mikael Zayenz Lagerkvist

unread,
Jun 9, 2021, 8:49:59 AM6/9/21
to mini...@googlegroups.com
Hi,

See also this comment on an issue on another way to create an array2D
when one dimension has fairly low number of elements:
https://github.com/MiniZinc/libminizinc/issues/403#issuecomment-673162039

Cheers,
Mikael
> --
> You received this message because you are subscribed to the Google Groups "MiniZinc" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to minizinc+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/minizinc/e044e35c-4a9b-4ed3-a08b-6ada6594747fn%40googlegroups.com.



--
Mikael Zayenz Lagerkvist
Reply all
Reply to author
Forward
0 new messages