Minizinc - assign job to specific machine - minizinc

I have say n jobs and m machines and have a jobtype array with type of job. If the job is of specific type in job type array , i have to assign even numbered machine among the available else to the odd numbered machine. Is it possible with minizinc?
Snippet I tried given below:
forall(w in 1..num_workers) (
if jobtype[job] == "NC" then assignment[job,(w mod 2 ==0)]=1
else assignment[job,(w mod 2 !=0)]=1 endif
)
which is giving the following warning
WARNING: undefined result becomes false in Boolean context
(array access out of bounds)
TIA

Here is one model that might be what you want, i.e. to assign even numbered machines to the jobs marked as "NC". The important constraint is the following, which might be the one you want. Here we use a temporary decision variable w in the range of 1..num_workers, and then ensure that for the NC jobs the machine number must be even:
forall(job in 1..num_jobs) (
let { var 1..num_workers: w; } in
% Jobs with NC must be assigned to even numbered workers (machines)
if jobtype[job] == "NC" then w mod 2 == 0 else w mod 2 == 1 endif
/\ assignment[job,w]=1
)
Here is the full model - as I imagined it - with 7 jobs and 7 workers. I assume that a worker/machine can only be assigned to at most one job. Well, it's a lot of guesswork...
int: num_workers = 7;
int: num_jobs = 7;
array[1..num_jobs] of string: jobtype = ["NC","X","NC","X","X","NC","X"];
% decision variables
array[1..num_jobs, 1..num_workers] of var 0..1: assignment;
solve satisfy;
constraint
forall(job in 1..num_jobs) (
let { var 1..num_workers: w; } in
% Jobs with NC must be assigned to even numbered workers (machines)
if jobtype[job] == "NC" then w mod 2 == 0 else w mod 2 == 1 endif
/\
assignment[job,w]=1
)
/\ % at most one worker for each job (or perhaps not)
forall(job in 1..num_jobs) (
sum([assignment[job,worker] | worker in 1..num_workers]) <= 1
)
/\ % and at most one job per worker (or perhaps not)
forall(worker in 1..num_workers) (
sum([assignment[job,worker] | job in 1..num_jobs]) <= 1
)
;
output [
if w == 1 then "\n" else " " endif ++
show(assignment[j,w])
++ if w == num_workers then " (\(jobtype[j]))" else "" endif
| j in 1..num_jobs, w in 1..num_workers
];
The model yields 144 different solutions. Here's the first:
0 0 0 0 0 1 0 ("NC")
0 0 0 0 0 0 1 ("X")
0 0 0 1 0 0 0 ("NC")
0 0 0 0 1 0 0 ("X")
0 0 1 0 0 0 0 ("X")
0 1 0 0 0 0 0 ("NC")
1 0 0 0 0 0 0 ("X")

Related

Performing an averaging operation over every n elements in a vector

I have a logical vector in which I would like to iterate over every n-elements. If in any given window at least 50% are 1's, then I change every element to 1, else I keep as is and move to the next window. For example.
n = 4;
input = [0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 1 0 0 0 1];
output = func(input,4);
output = [0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 1];
This function is trivial to implement but is it possible to apply a vectorized implementation using logical indexing?. I am trying to build up the intuition of applying this technique.
here's a one liner (that works for your input):
func = #(input,n) input | kron(sum(reshape(input ,n,[]))>=n/2,ones(1,n));
of course, there are cases to solve that this doesnt answer, what if the size of the input is not commensurate in n? etc...
i'm not sure if that's what you meant by vectorization, and I didnt benchmark it vs a for loop...
Here is one way of doing it. Once understood you can compact it in less lines but I'll details the intermediate steps for the sake of clarity.
%% The inputs
n = 4;
input = [0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 1 0 0 0 1];
1) Split your input into blocks of size n (note that your final function will have to check that the number of elements in input is a integer multiple of n)
c = reshape(input,n,[]) ;
Gives you a matrix with your blocks organized in columns:
c =
0 0 0 0 0
0 1 0 1 0
0 1 0 0 0
1 0 1 1 1
2) Perform your test condition on each of the block. For this we'll take advantage that Matlab is working column wise for the sum function:
>> cr = sum(c) >= (n/2)
cr =
0 1 0 1 0
Now you have a logical vector cr containing as many elements as initial blocks. Each value is the result of the test condition over the block. The 0 blocks will be left unchanged, the 1 blocks will be forced to value 1.
3) Force 1 columns/block to value 1:
>> c(:,cr) = 1
c =
0 1 0 1 0
0 1 0 1 0
0 1 0 1 0
1 1 1 1 1
4) Now all is left is to unfold your matrix. You can do it several ways:
res = c(:) ; %% will give you a column vector
OR
>> res = reshape(c,1,[]) %% will give you a line vector
res =
0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 1

How to create an error function comparing two matrices?

I have two matrices in MATLAB. Each one is filled with 1 and 0 at different positions. I want to compare each element:
If there is a 1 match, I want it to record as True Positive.
If there is a 0 match, I want it to record as True Negative.
If one says 1 and the other says 0, I want to record as False Positive.
If one says 0 and the other says 1, I want to record as False Negative.
I tried just comparing the two matrices:
idx = A == B
But, that gives me a simple match, not telling me when there is a True Positive or Negative, etc.
Is there any specific function I could use, or any alternative?
You could just add the matrices in a prescribed way....
a = [1 0 1 0
1 1 0 0
0 0 1 1];
b = [1 0 0 0
0 0 0 1
0 0 1 0];
C = a + 2*b;
% For pairs [a,b] we expect
% [0,0]: C = 0, true negative
% [1,0]: C = 1, false positive
% [0,1]: C = 2, false negative
% [1,1]: C = 3, true positive
% C =
% [ 3 0 1 0
% 1 1 0 2
% 0 0 3 1 ]
If you have the Statistics and Machine Learning toolbox and you only want a summary, you might just need the function confusionmat.
From the docs:
C = confusionmat(group,grouphat) returns the confusion matrix C determined by the known and predicted groups in group and grouphat. [...]. C is a square matrix with size equal to the total number of distinct elements in group and grouphat. C(i,j) is a count of observations known to be in group i but predicted to be in group j.
For example:
a = [1 0 1 0
1 1 0 0
0 0 1 1];
b = [1 0 0 0
0 0 0 1
0 0 1 0];
C = confusionmat( a(:), b(:) );
% C =
% [ 5 1
% 4 2]
% So for each pair [a,b], we have 5*[0,0], 2*[1,1], 4*[1,0], 1*[0,1]
A similar function for those with the Neural Network Toolbox instead would be confusion.
You could just use bitwise operators to produce the four different values:
bitor(bitshift(uint8(b),1),uint8(a))
Produces an array with
0 : True Negative
1 : False Negative (a is true but b is false)
2 : False Positive (a is false but b is true)
3 : True Positive
One naive approach would be four comparisons, case by case:
% Set up some artificial data
ground_truth = randi(2, 5) - 1
compare = randi(2, 5) - 1
% Determine true positives, false positives, etc.
tp = ground_truth & compare
fp = ~ground_truth & compare
tn = ~ground_truth & ~compare
fn = ground_truth & ~compare
Output:
ground_truth =
1 0 1 0 0
0 1 1 0 1
1 1 0 1 0
0 1 0 1 1
0 0 0 1 0
compare =
0 1 1 0 1
0 1 1 1 0
1 1 0 0 1
1 1 1 0 0
1 1 1 1 1
tp =
0 0 1 0 0
0 1 1 0 0
1 1 0 0 0
0 1 0 0 0
0 0 0 1 0
fp =
0 1 0 0 1
0 0 0 1 0
0 0 0 0 1
1 0 1 0 0
1 1 1 0 1
tn =
0 0 0 1 0
1 0 0 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
fn =
1 0 0 0 0
0 0 0 0 1
0 0 0 1 0
0 0 0 1 1
0 0 0 0 0
That works, because 0 and 1 (or any positive value) are alternative representations for true and false.
To keep your main code clean, set up a separate function, say my_stats.m
function [tp, fp, tn, fn] = my_stats(ground_truth, compare)
% Determine true positives, false positives, etc.
tp = ground_truth & compare;
fp = ~ground_truth & compare;
tn = ~ground_truth & ~compare;
fn = ground_truth & ~compare;
end
and call it in your main code:
% Set up some artificial data
ground_truth = randi(2, 5) - 1
compare = randi(2, 5) - 1
[tp, fp, tn, fn] = my_stats(ground_truth, compare)
Hope that helps!
I found that I can use the find method and set two conditions, then just find the numbers of the element in each variable
TruePositive = length(find(A==B & A==1))
TrueNegative = length(find(A==B & A==0))
FalsePositive = length(find(A~=B & A==1))
FalseNegative = length(find(A~=B & A==0))
The confusionmatrix() method suggested by #Wolfie is also really neat, especially if you use the confusionchart() which provides a nice visualisation.

Number of blocks of linked elements in a matrix

How can one find the number of separated linked blocks using a symmetric matrix of 0s and 1s in Matlab?
For example in matrix A, if A(n,m)=1, member n and m are connected. Connected elements make blocks. In below matrix, members 2,3,4,5,6,8,9 are connected and make a block. Also, there are two clusters of size equal to 2 and one block of size 7.
A = [1 0 0 0 0 0 0 0 0 0
0 1 1 1 1 1 0 1 1 0
0 1 1 1 1 1 0 1 1 0
0 1 1 1 1 1 0 1 1 0
0 1 1 1 1 1 0 1 1 0
0 1 1 1 1 1 0 1 1 0
0 0 0 0 0 0 1 0 0 0
0 1 1 1 1 1 0 1 1 0
0 1 1 1 1 1 0 1 1 0
0 0 0 0 0 0 0 0 0 1]
following your previous question (link) you can use labeling instead of binary indications and then sqrt the number of members in each block:
A = false(10);
% direct connections
A(2,3) = 1;
A(3,4) = 1;
A(5,6) = 1;
A(4,9) = 1;
A = A | A';
B = double(A | diag(ones(1,10))); % each node is connected to it self
B(B == 1) = 1:nnz(B); % set initial unique labels
while true
B_old = B;
% connect indirect connected nodes
for node = 1:size(B,1)
row = B(node,:);
col = row';
row = row > 0;
col(col > 0) = max(col);
cols = repmat(col,[1 nnz(row)]);
% set the same label for each group of connected nodes
B(:,row) = max(B(:,row) , cols);
end
if isequal(B,B_old)
break
end
end
% get block size
u = unique(B);
counts = hist(B(:),u);
% remove non connections
counts(u == 0) = [];
u(u == 0) = [];
% remove self connections
u(counts == 1) = [];
counts(counts == 1) = [];
% block size
blocksize = sqrt(counts);

Enumerate all possible decisions rules

I have m input variables I_1, ..., I_m to a decision. Each variable may have n possible values. The decision outcome D is binary.
A decision rule R is a mapping from set D x I_1 x ... x I_m onto the set {0, 1} such that for any (i_1, ..., i_m) in I_1 x ... x I_m it holds that 1 = sum_(d in D) R(d, i_1, ..., i_m). That is: for any combination of input values only one decision is possible.
For example without any input variable you have two decision rules:
D R1 R2
a 0 1
b 1 0
That is the rule R1 selects decision b while R2 selects a.
With one binary input variable I you have four possible decision rules:
I D R1 R2 R3 R4
0 a 0 0 1 1
0 b 1 1 0 0
1 a 0 1 0 1
1 b 1 0 1 0
That is the decision rule R2 selects b if input is 0 and it selects a if input is 1.
With two binary input variables I and K you have 16 possible decision rules
I K D R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16
0 0 a 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
0 0 b 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
1 0 a 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
1 0 b 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0
0 1 a 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
0 1 b 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0
1 1 a 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 1 b 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
My question how can I enumerate all possible decision rules for an arbitrary set of input variables?
Disclaimer: This is part of a homework. However the homework is restricted to the case with one binary input variable such that one can simply enumerate all four cases. I passed this part of my homework - actually an enumeration is not necessary at all - but I am interested in a general solution in matlab.
How can I enumerate all possible decision rules for an
arbitrary set of input variables?
First by analyzing and understanding the repetitive patterns that are visible when we write down the binary permutations of the decision rules (R), based on the number (n) of input variables (V). And then building a set functions that generate these permutations automatically and display a table with the results like you would do it by hand.
In terms of code, there are many different valid approaches to solve this problem, but from my point of view, I think that working with a matrix of logicals is a good way to do it. I will call this matrix (M). This matrix has three parts (like the tables in your description):
Left: n columns for the input variables (V)
Center: 1 column for the decision (D)
Right: 2^(2^n) columns for the decision rules (R)
And since your problem has two decisions (A and B), we can treate them as logical values too:
A = 0
B = 1
Note: I chose this values for A and B, and not the opposite ones, because it allows us to generate the binary permutations (which I will call "states") of the input variables (V) and the decision (D) using natural binary count.
For n = 0, M looks like:
0 0 1
1 1 0
For n = 1, M looks like:
0 0 0 0 1 1
0 1 1 1 0 0
1 0 0 1 0 1
1 1 1 0 1 0
For n = 2, M looks like:
0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
0 0 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
0 1 1 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0
1 0 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
1 0 1 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0
1 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
And the size of M grows up pretty quickly as you mentioned:
Rows ("states") grow at a rate of 2^(n + 1)
Columns grow at a rate of (n + 1) + 2^(2^n): n columns for input variables + 1 column for decision (D) + 2^(2^n) columns for decision rules (R).
From the previous matrices we can hardly tell apart any repetitive patterns, but if we use colors we can clearly see some patterns in the decision rules (R) region:
For n = 0:
For n = 1:
For n = 2:
We can see that there are row-wise copies of the same "unit pattern" (boxed digits). Each "unit pattern" is 2 rows wide and 2^(2^n)/k columns wide (where k is the number of repetitions of a pattern for every two rows). The first pattern in M is always a single copy (k = 1), and k duplicates every 2 rows.
We will use all this information to create a set of functions that will allow us to enumerate all the possible decision rules through the use of a table that I will call (T).
I wrote a function called CalcParams which calculates all the necessary parameters of the problem (such as number of rows and columns of M, etc.) based on n:
function[a, b, c, d, e] = CalcParams(n)
% Calculate necessary parameters.
% Inputs:
% n - number of input variables.
% Number of states (rows).
a = 2^(n + 1);
% Number of decision rules (R) (decision rules columns).
b = 2^(2^n);
% Column index of first decision rule (R1).
c = n + 2;
% Number of columns of input variables (V) and decision (D).
d = n + 1;
% Total number of columns.
e = d + b;
end
Then I wrote a function called ValidDecRules which given n and M, checks if the input decision rules meet the requirement:
For any combination of input variables only one decision is possible.
If the decision rules meet the requirement the function returns 1 and displays the message VALID decision rules, otherwise the function returns 0 and displays the message INVALID decision rules.
function[val] = ValidDecRules(n, M)
% This function checks if the input decision rules meet the requirement:
% For any combination of input variables only one decision is possible.
% Inputs:
% n - number of input variables.
% M - binary matrix.
% Calculate necessary parameters.
[~, ~, c, ~, e] = CalcParams(n);
% Invalid decision rules by default.
val = 0;
% Extract odd rows from decision rules (R).
M_odd = M(1:2:end, c:e);
% Extract even rows from decision rules (R).
M_even = M(2:2:end, c:e);
% Check that all elements of the odd rows are different than the elements
% of the even rows.
if(all(all(M_odd ~= M_even, 1), 2))
% Valid decision rules.
val = 1;
disp('VALID decision rules');
else
% Invalid decision rules.
disp('INVALID decision rules');
end
end
Then I wrote a function called GenM which generates the binary matrix M based on n, and if you use the optional argument 'plot' it will plot the decision rules of M using imagesc.
function[M] = GenM(n, varargin)
% This function generates the binary matrix M.
% Inputs:
% n - number of input variables.
% Options:
% 'plot' - plot decision rules of M.
% Calculate necessary parameters.
[a, b, c, d, e] = CalcParams(n);
% Anonymous functions.
f1 = #(v, k) uint8(repmat(v, 1, k));
f2 = #(v, k) f1([v; ~v], k);
f3 = #(b, k) f2([false(1, b/(2*k)), ~false(1, b/(2*k))], k);
% Binary permutations of input variables (V) and decision (D).
Dec = 0:a-1; % Array: decimal representation of every state.
Bin = dec2bin(Dec); % Array: binary representation of every state.
% Preallocate matrix M.
M(length(Bin), d) = 0;
% Loop: input variables (V) and decision (D).
% Writes binary states in matrix M.
for i = 1:d
M(:, i) = uint8(str2num(Bin(:, i)));
end
% Loop: decision rules.
% Writes binary permutations of decision rules (R) in matrix (M).
% Start with k = 1.
k = 1;
for i = 1:2:a
M(i:(i + 1), c:e) = f3(b, k);
k = k*2;
end
% Continue only if decision rules (R) are valid.
if(ValidDecRules(n, M))
% Plot decision rules if 'plot' option is used.
if(~isempty(varargin))
if(any(strcmp(varargin, 'plot')))
% Visualize decision rules as image.
imagesc(M(:, c:e));
title('Decision Rules (R)');
colormap summer;
axis off;
end
end
else
% If decision rules are invalid, return empty output.
M = [];
end
end
Finally I wrote a function called EnumDecRules which takes n and generates a table T very similar to the ones on the description of your question. The function also returns the binary matrix M that was used to generate T. And if you use the 'plot' optional argument, it will plot the decision rules of M (like the GenM function).
The EnumDecRules function is the one that really answers your question because it has the behaviour that you are asking for.
function[T, M] = EnumDecRules(n, varargin)
% This function generates the table (T) with the results and also returns
% the binary matrix M that was used to generate T.
% Inputs:
% n - number of input variables.
% Options:
% 'plot' - plot decision rules of M.
% Calculate necessary parameters.
[a, ~, ~, d, e] = CalcParams(n);
% Generate the binary matrix M.
M = GenM(n, varargin{:});
if(~isempty(M))
% Loop: variable names to diplay in table header.
% Initialize indexes for numbering.
Vi = 1; % Input variable numbering index.
Ri = 1; % Decision rules numbering index.
for i = 1:e
if i <= n
% Input variables.
% Write V[Vi].
Names{i} = ['V', sprintf('%d', Vi)];
% Increase index.
Vi = Vi + 1;
elseif i == d
% Decision.
% Write D.
Names{i} = 'D';
elseif i > d
% Decision rules.
% Write R[Ri].
Names{i} = ['R', sprintf('%d', Ri)];
% Increase index.
Ri = Ri + 1;
end
end
% Generate table with results.
T = array2table(M, ...
'VariableNames', Names);
% Modify decision column (D) of table.
% Replace 0 with 'A'.
% Replace 1 with 'B'.
T.D = repmat({'A'; 'B'}, a/2, 1);
else
% If M is empty, return empty output.
T = [];
end
end
Usage example:
Make sure you save all the functions correctly in the same directory.
Example 1:
Call the EnumDecRules function to enumerate all possible decisions rules for n = 1:
[T, M] = EnumDecRules(1)
These are the outputs:
VALID decision rules
T =
V1 D R1 R2 R3 R4
__ ___ __ __ __ __
0 'A' 0 0 1 1
0 'B' 1 1 0 0
1 'A' 0 1 0 1
1 'B' 1 0 1 0
M =
0 0 0 0 1 1
0 1 1 1 0 0
1 0 0 1 0 1
1 1 1 0 1 0
Example 2:
Call the EnumDecRules function to enumerate all possible decisions rules for n = 2 and also plot the decision rules:
[T, M] = EnumDecRules(2, 'plot')
These are the outputs:
VALID decision rules
T =
V1 V2 D R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16
__ __ ___ __ __ __ __ __ __ __ __ __ ___ ___ ___ ___ ___ ___ ___
0 0 'A' 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
0 0 'B' 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
0 1 'A' 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
0 1 'B' 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0
1 0 'A' 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
1 0 'B' 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0
1 1 'A' 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 1 'B' 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
M =
Columns 1 through 9
0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 1
0 1 0 0 0 0 0 1 1
0 1 1 1 1 1 1 0 0
1 0 0 0 0 1 1 0 0
1 0 1 1 1 0 0 1 1
1 1 0 0 1 0 1 0 1
1 1 1 1 0 1 0 1 0
Columns 10 through 18
0 0 1 1 1 1 1 1 1
1 1 0 0 0 0 0 0 0
1 1 0 0 0 0 1 1 1
0 0 1 1 1 1 0 0 0
1 1 0 0 1 1 0 0 1
0 0 1 1 0 0 1 1 0
0 1 0 1 0 1 0 1 0
1 0 1 0 1 0 1 0 1
Column 19
1
0
1
0
1
0
1
0
And the plot:
Since this type of algorithm grows so fast, using EnumDecRules or GenM for n >= 5 can result in an out of memory error.
I really hope this will be helpful. If you have any questions about specific instructions of the code please leave a comment and I will be happy to answer them.

How to count number of 1 and 0 in the matrix?

I have an image of which I cut out only one column. After that I made it to be logical so there are be only 0 and 1 in this column.
Suppose my values in this column are
1111000110000000000000011111111
I want to count the length of each block of ones or each block of zeros.
The result would be
1 - 4 (first 1)
0 - 3 (first 0)
1 - 2
and so on...
I know only count for the entire column but I can't do it for each distinct block. Anyone please help me.
Let vec be a row vector (1-by-n) of zeros and ones, then you can use the following code
rl = ( find( vec ~= [vec(2:end), vec(end)+1] ) );
data = vec( rl );
rl(2:end) = rl(2:end) - rl(1:end-1);
rl will give you the number of consecutive zeros and ones, while data will tell you for each block if it is zero or one.
This question is closely related to run length coding.
Demo:
vec = [1 1 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1];
rl = ( find( vec ~= [vec(2:end), vec(end)+1] ) );
data = vec( rl ),
rl(2:end) = rl(2:end) - rl(1:end-1),
data =
1 0 1 0 1
rl =
4 3 2 14 8