I have a situation where I am modelling an array S
that contains a set of values (a schedule) from a predefined domain 1..t
, plus 0
, which is a special value for "not there/not used".
I now want to post a constraint to sum a cost function, represented as a 2D array C
, for the list S'
, holding every non-zero element of S
in the same order, like so:
constraint x = sum([C[S'[d], S'[d + 1]] | d in 1..max - 1])
However, this cannot be easily done. Things I have tried:
roots
to get the set of indices to S
whose data is non-zero. The problem with that solution is:
[S[i] | i in 1..max where S[i] != 0]
) to select only the elements whose values are non-zero: this also doesn't work, as the where
clause on the list comprehension causes the list to be of type opt
, and also having the wrong number of elements (where I presume some of them will be <>
), essentially reducing the problem of filtering zeroes to the same problem again with <>
:s.What I would really like here is either filter
or zip
, which could both easily solve my problem, but I presume that there is some sort of standard solution that I'm missing. Otherwise, I would have to re-design the model.
It is possible to solve your problem by using a recursive function that calculates the costs by iterating over the indices of your array S
. I illustrate the function calculate_cost()
below in a small example:
int: t = 10; int: N = 5;
% cost array
array[1..t,1..t] of int: C = array2d(1..t,1..t,[ i | i in 1..t, j in 1..t]);
% variables
array[1..N] of var 0..t: S;
var 0..1000: x;
% constraints
constraint S[1] = 4; % setting some arbitrary values
constraint S[2] = 7;
constraint S[3] = 0;
constraint S[4] = 6;
constraint x = calculate_cost(1,2);
function var int: calculate_cost(int: index1, int:index2) =
if index1 > N then 0
elseif index2 > N then 0
else
let {
var bool: value_at_index1_is_zero = S[index1] == 0;
var bool: value_at_index2_is_zero = S[index2] == 0;
}
in
if value_at_index1_is_zero
then calculate_cost(index1+1, index1+2)
elseif value_at_index2_is_zero
then calculate_cost(index1, index2 + 1)
else
C[S[index1],S[index2]] + calculate_cost(index2, index2+1)
endif
endif;
solve satisfy;
This example has S = [4, 7, 0, 6, 0]
and calculates costs x = C[4,7] + C[7,6] = 4 + 7 = 11
.
In the function calculate_cost()
, I recursively calculate the sum by skipping indices that have a zero value in S
. In the first few lines, I check if the indices are out of bounds and return 0 in that case (base case of the recursion). Then I create two local variables that are true
if the value at S[index]
is zero for index
. Then, if case one of those cases are true, I ignore those indices, and recursively call the function again, and increase/adapt the respective index in the recursive call.
This works, but is probably not a very nice way of solving this issue because it introduces a lot of auxiliary variables in the FlatZinc model, so it might still be better to reformulate the problem.