MiniZinc - Array of (multidimensional) array - arrays

I would like to know if it’s possible to have array of (multidimensional) array in MiniZinc language.
Indeed, I would like to resolve a timetabling problem with workers. My goal is to check if they are available at least 1 day per week. Each worker is indexed by an integer and I have their schedule per week.
For me, an array like : [[number_of_week, weekday]] could be a solution.
For example, worker 1 who is available monday/friday in week 1 and tuesday/thursday in week 2 can be modeled by the following array : (« 1 » means that the worker is available)
[[| 1,0,0,0,1,
| 0,1,0,1,0 |],
[...], ...]
If it's possible, how to declare this kind of array ? And I’m also open to any advice on modeling this constraint.
Sorry for my imperfect English and thank you in advance,
Nicolas.

I'm not sure I understand your question fully, but you can have multi dimensional arrays as follows:
array[1..3,1..3] of int: a = array2d(1..3,1..3,
[1,0,0,
0,1,0,
1,1,0]);
or using another syntax:
array[1..3,1..3] of int: a =
[|1,0,0
|0,1,0
|1,1,0|];
However, you cannot have arrays in arrays, i.e. this is not allowed:
% This is NOT valid MiniZinc code!
array[1..3,1..3] of int: a =
[[[1,0,0],[1,0,1]],
[[0,1,0],[0,1,1]],
[[1,1,0],[0,0,0]]];
Note also that one cannot have "ragged" arrays: the number of element in each row and each column must be the same.
Regarding you specific constraint you might - if I understand your description - define a 3D array like this:
int:_num_workers = 2;
int: num_weeks = 3;
int: num_days = 4;
array[1..num_workers,1..num_weeks,1..num_days] of int: a =
array3d(1..num_workers,1..num_weeks,1..num_days,
[
% worker 1
% week1 week2 week3
1,0,0,1, 1,1,0,1, 0,0,1,1,
% worker 2
% week1 week2 week3
1,0,0,0, 0,0,0,1, 1,1,1,0
]);
Note especially the array3d construct which creates a 3D array. So you can access day d of week e for worker w with a[w,e,d].

Related

MiniZinc basic problem. n Workers and n Tasks

I am learning Minizinc for the first time and am kind of stuck on a very basic problem. It involves n workers and n tasks. I am given a 2D-profit matrix where profit[w,t] will give the profit for worker w and task t. The assignment problem's aim is to maximize the profit.
I have tried the following:
include "all_different.mzn";
include "globals.mzn";
int: n = 4;
array [1..n,1..n] of int: profit =
[| 7,1,3,4 |
8,2,5,1 |
4,3,7,2 |
3,1,6,3 |];
set of int: WORKERS = 1..n;
set of int: TASKS = 1..n;
array[WORKERS] of var TASKS: task;
constraint alldifferent(task);
constraint maximize sum(w in WORKERS) (profit[w,task[w]]);
But this does not work, because the maximize function is not set up that way, however I do not know what else to do.
Any tips/solutions?
Thanks :)
You are almost there. The problem is in the last line which should be solve, not constraint:
solve maximize sum(w in WORKERS) (profit[w,task[w]]);
The output is:
task: [4, 1, 2, 3]
----------
==========

Is there any mechanism to auto squeeze in Matlab / Octave

For an nD array, it would be nice to be able to auto squeeze to remove singleton dimensions. Is there a way to do this that I don't know about? This would be especially useful for aggregate functions (e.g. sum, mean, etc) where you always expect a result with fewer dimensions.
Here's a simple example:
>> A = ones(3,3,3);
>> B = mean(A);
>> size(B)
ans =
1 3 3
>> squeeze(B)
ans =
1 1 1
1 1 1
1 1 1
It would be nice if Matlab/Octave would automatically do the squeezing for me. Or if there was a way to turn that option on (something similar to hold on for plots).
As far as I know, Matlab does not have that. And I don't think it would be a good idea. Consider a modified version of your example:
>> A = ones(3,1,1,3);
>> B = mean(A);
>> size(B)
ans =
1 1 1 3
What should "auto-squeeze" do here? Reduce B to size [1 1 3] or to [1 3]?
You could argue that it should remove the same dimension that mean has turned into a singleton. But then it would have to be done within the mean function, perhaps with an optional input argument. Once you get the function output, there is no information how it was obtained.
Or you could argue that it should remove all singleton dimensions, like squeeze (more or less) does. But then it would remove dimensions that were already singleton in the function input, which is probably unwanted.
If you ask me, having a second input in squeeze specifiyng which (singleton) dimensions to remove would be a nice addition (in the same vein as you can use mean(A, 1) to force the operation to be applied along the first dimension even if A happens to be a row vector).
I agree with Luis and Cris, but I would add the following.
Both Matlab and Octave do automatically squeeze extra dimensions, in a very particular scenario: any dimensions at the end that have been reduced to singletons, are automatically squeezed out.
E.g.
A = ones([1,2,3,4]);
B = mean(A, 4);
size(B)
% ans = 1 2 3
Note, how the answer is [1,2,3], and not [1,2,3,1]. This is in contrast to languages like python, for instance, where a size of (1,1) is very different to a size of (1,).
Therefore, with regard to your questions, one way to use this to your advantage could be to ensure that the dimension that is to be reduced is always found at the end, and thus automatically simplified.
This becomes even more useful when you realise that:
size(A(:)) % ans = 24 1 (i.e. 24)
size(A(:,:)) % ans = 1 24
size(A(:,:,:)) % ans = 1 2 12
size(A(:,:,:,:)) % ans = 1 2 3 4
Meaning, if you order your dimensions hierarchically you can ensure that any operations that need to take place over the higher dimensions, can a) be vectorised easily, and b) give a natural result, without the need to waste time squeezing or permuting the resulting dimensions.

Count the number of different elements in an array

I am new to constraint programming and to Minizinc.
I have look for a solution to this not relly hard task but I found nothing.
I want to count the number of different elements that appear in an array:
This is the declaration of my array:
array[1..n,1..n] of var 1..n: countLeft;
I have try to do like this:
constraint
forall(j in 1..n) (
length(array2set([countLeft[i,j]|i in 1..stopCountLeft[j]]) )==left_vision[j]
);
But apparently my array is of type: array[int]of var opt int and is not accept by the function array2set.
Any ideas?
There might be different approaches you could take, but an approach that is similar to what you try would be to split the counting of different elements in an array into two steps:
Counting the occurrence of the values in the domain.
Counting the amount of times the occurrence is higher than zero.
We can use the global global_cardinality constraint to count the occurrences and then use a simply count constraint over its result.
include "global_cardinality_fn";
array[1..n] of var int: occurrences = global_cardinality(YOURARRAY, [i | i in 1..n]);
var int: num_diff = count(o in occurrences) (o > 0);
Note, however, that this might not be the best code for your model. For some solvers global_cardinality might not be perform well enough. Similarly if your stopCountLeft contains variables, then that means that you are creating a array of optional variables, and global_cardinality might not be defined for optional variables.
Instead we can write an implication graph instead. The idea is still the same, but instead of counting the number of a value occurring, we just use a boolean value to signal wether the value is in use.
array[1..n] of var bool: occurs;
constraint forall(i,j in 1..n) (YOURARRAY[i] = j -> occurs[j]);
var int: num_diff = count(occurs);
Note that the problem with this approach is the exponential number of implications posted in the forall loop. However, I suspect that, with implications being small, it would perform reasonably well.
In MiniZinc 2.5.0, you can do something like this:
array[int, int] of int: a =
[| 1, 1,
| 2, 3,
| 3, 4 |];
set of int: Rows = index_set_1of2(a);
set of int: Cols = index_set_2of2(a);
int: values = card({ a[r, c] | r in Rows, c in Cols });
output ["\(values) values in "] ++
[if c == 1 then "\n" else "" endif ++
"\(a[r, c]) " | r in Rows, c in Cols];

Compute the product of the next n elements in array

I would like to compute the product of the next n adjacent elements of a matrix. The number n of elements to be multiplied should be given in function's input.
For example for this input I should compute the product of every 3 consecutive elements, starting from the first.
[p, ind] = max_product([1 2 2 1 3 1],3);
This gives [1*2*2, 2*2*1, 2*1*3, 1*3*1] = [4,4,6,3].
Is there any practical way to do it? Now I do this using:
for ii = 1:(length(v)-2)
p = prod(v(ii:ii+n-1));
end
where v is the input vector and n is the number of elements to be multiplied.
in this example n=3 but can take any positive integer value.
Depending whether n is odd or even or length(v) is odd or even, I get sometimes right answers but sometimes an error.
For example for arguments:
v = [1.35912281237829 -0.958120385352704 -0.553335935098461 1.44601450110386 1.43760259196739 0.0266423803393867 0.417039432979809 1.14033971399183 -0.418125096873537 -1.99362640306847 -0.589833539347417 -0.218969651537063 1.49863539349242 0.338844452879616 1.34169199365703 0.181185490389383 0.102817336496793 0.104835620599133 -2.70026800170358 1.46129128974515 0.64413523430416 0.921962619821458 0.568712984110933]
n = 7
I get the error:
Index exceeds matrix dimensions.
Error in max_product (line 6)
p = prod(v(ii:ii+n-1));
Is there any correct general way to do it?
Based on the solution in Fast numpy rolling_product, I'd like to suggest a MATLAB version of it, which leverages the movsum function introduced in R2016a.
The mathematical reasoning is that a product of numbers is equal to the exponent of the sum of their logarithms:
A possible MATLAB implementation of the above may look like this:
function P = movprod(vec,window_sz)
P = exp(movsum(log(vec),[0 window_sz-1],'Endpoints','discard'));
if isreal(vec) % Ensures correct outputs when the input contains negative and/or
P = real(P); % complex entries.
end
end
Several notes:
I haven't benchmarked this solution, and do not know how it compares in terms of performance to the other suggestions.
It should work correctly with vectors containing zero and/or negative and/or complex elements.
It can be easily expanded to accept a dimension to operate along (for array inputs), and any other customization afforded by movsum.
The 1st input is assumed to be either a double or a complex double row vector.
Outputs may require rounding.
Update
Inspired by the nicely thought answer of Dev-iL comes this handy solution, which does not require Matlab R2016a or above:
out = real( exp(conv(log(a),ones(1,n),'valid')) )
The basic idea is to transform the multiplication to a sum and a moving average can be used, which in turn can be realised by convolution.
Old answers
This is one way using gallery to get a circulant matrix and indexing the relevant part of the resulting matrix before multiplying the elements:
a = [1 2 2 1 3 1]
n = 3
%// circulant matrix
tmp = gallery('circul', a(:))
%// product of relevant parts of matrix
out = prod(tmp(end-n+1:-1:1, end-n+1:end), 2)
out =
4
4
6
3
More memory efficient alternative in case there are no zeros in the input:
a = [10 9 8 7 6 5 4 3 2 1]
n = 2
%// cumulative product
x = [1 cumprod(a)]
%// shifted by n and divided by itself
y = circshift( x,[0 -n] )./x
%// remove last elements
out = y(1:end-n)
out =
90 72 56 42 30 20 12 6 2
Your approach is correct. You should just change the for loop to for ii = 1:(length(v)-n+1) and then it will work fine.
If you are not going to deal with large inputs, another approach is using gallery as explained in #thewaywewalk's answer.
I think the problem may be based on your indexing. The line that states for ii = 1:(length(v)-2) does not provide the correct range of ii.
Try this:
function out = max_product(in,size)
size = size-1; % this is because we add size to i later
out = zeros(length(in),1) % assuming that this is a column vector
for i = 1:length(in)-size
out(i) = prod(in(i:i+size));
end
Your code works when restated like so:
for ii = 1:(length(v)-(n-1))
p = prod(v(ii:ii+(n-1)));
end
That should take care of the indexing problem.
using bsxfun you create a matrix each row of it contains consecutive 3 elements then take prod of 2nd dimension of the matrix. I think this is most efficient way:
max_product = #(v, n) prod(v(bsxfun(#plus, (1 : n), (0 : numel(v)-n)')), 2);
p = max_product([1 2 2 1 3 1],3)
Update:
some other solutions updated, and some such as #Dev-iL 's answer outperform others, I can suggest fftconv that in Octave outperforms conv
If you can upgrade to R2017a, you can use the new movprod function to compute a windowed product.

Is there a way to quickly extract the parts from a vector without looping?

Consider that I have a vector/array such that it looks as follows:
each part is a sub array of some size fixed and known size (that can only be accessed through indexing, i.e. its not a tensor nor a higher order array). So for example:
x1 = x(1:d);
if d is the size of each sub array. The size of each sub array is the same but it might vary depending on the current x we are considering. However, we do know n (the number of sub arrays) and d (the size of all of the sub arrays).
I know there is usually really strange but useful tricks in matlab to do things more optimized. Is there a way to extract those using maybe indexing and and make a matrix where the rows (or columns) are those parts? as in:
X = [x_1, ..., x_n]
the caveat is that n is a variable and we don't know aprior what it is. We can find what n is, but its not fixed.
I want to minimize the amount of for loops I actually write in matlab to hope its faster...just to add some more context.
First I would consider simple reshaping to keep the output as a simple double matrix
x = (1:15).' %'
d = 3;
out = reshape(x,d,[])
and further on just use indexing to access the columns out(:,idx);
There is no need to know n in advance, as reshape is calculating it based on d and the number of elements in x.
out =
1 4 7 10 13
2 5 8 11 14
3 6 9 12 15
If you'd insist on something like cell arrays, use accumarray with ceil to get the subs:
out = accumarray( ceil( (1:numel(x))/d ).', x(:), [], #(x) {x})

Resources