Is there a way to modify the isdag matlab function in order for it to ignore cycles of length zero? - matlab

recently I've been tasked with programming an algorithm that optimizes job-shop scheduling problems and I'm following an approach which uses directed graphs for this. In these directed graphs nodes represent events and edges represent time precedence constraints between events, i.e. time inequalities. So, for example, 2 consecutive nodes A & B separated by a directed edge of length 2 which goes from A to B would represent the inequality tB-tA>=2. It follows that equality would be represented by 2 directed edges of opposite directions, one with positive length and the other one with negative length. Thus, we end up with a graph which has some cycles of length zero.
Matlab has a function called isdag which returns true if a directed graph has no cycles and false otherwise; is there a way to modify this function in order for it to ignore the cycles of length zero? If not, has anyone got any idea on how to program this? Thanks in advance!!
I also tried this but it doesn't work. I've tried it with the adjacency matrix adjMatrix = [0, 10, 0; -9, 0, 5; 0, 0, 0] it should return true as it has a cycle between nodes 1 and 2 of length 10+(-9)=1, but it returns false,
function result = hasCycleWithPositiveWeight(adjMatrix)
n = size(adjMatrix,1);
visited = false(1,n);
path = zeros(1,n);
result = false;
for i = 1:n
pathStart = 1;
pathEnd = 1;
path(pathEnd) = i;
totalWeight = 0;
while pathStart <= pathEnd
node = path(pathStart);
visited(node) = true;
for j = 1:n
if adjMatrix(node,j) > 0
totalWeight = totalWeight + adjMatrix(node,j);
if visited(j)
if j == i && totalWeight > 0
result = true;
return;
end
else
pathEnd = pathEnd + 1;
path(pathEnd) = j;
end
end
end
pathStart = pathStart + 1;
totalWeight = totalWeight - adjMatrix(node, path(max(pathStart-1,1)));
visited(node) = false;
end
end
end

If you want to find the cycle between just two consecutive nodes use this:
a = [0, 10, 0; -9, 0, 5; 0, 0, 0];
b = a.';
And = (a & b);
Add = (a + b);
result = any( And .* Add, 'all');
It returns true if there is a cycle that its length isn't 0.
Explanation:
In the graph if the length between node 2 and 5 is 12 we set the element A(2 , 5) of the adjacency matrix to 12 and if the length between node 5 and 2 is -8 we set the element A(5, 2) of the adjacency matrix to 8. So there is a symmetry between nodes relationship. The matrix transpose changes the position of (2, 5) to (5, 2) and (5, 2) to (2, 5).
If we And a matrix with its transpose the result matrix shows that there is a cycle between two nodes if it is 1.
If we Add a matrix to its transpose the result matrix shows the sum of the pairwise lengths between nodes.
If we multiplyelement-wise the two matrices Add and And the result matrix shows the sum of the pairwise lengths between nodes but the sum of lengths of two nodes that don't form a cycle is set to 0.
Now the function any can be used to test if the matrix has a cycle that its length isn't 0.

Related

Optimize algorithm that generates the number of units in each binary state

TL;DR: I need to find all possible combinations of N row vectors (of size 1xB), whose row-wise sum produces the desired result vector (also of size 1xB).
I have a binary matrix (1 or 0 entries only) of size N x B where N denotes the number of units and B denotes the number of bins. Each unit, i.e., each row, of the matrix can be in one of 2^B states. That is, if B=2, the states possible are {0,0}, {0,1}, {1,0} or {1,1}. If B=3, then the possible states are {0,0,0}, {0,0,1}, {0,1,0}, {0,1,1}, {1,0,0}, {1,0,1}, {1,1,0} or {1,1,1}. Basically the binary representation of the numbers from 0 to 2^B-1.
For the matrix, I know the sum over the rows of the matrix, for example, {1,2}. This sum can be achieved through different binary matrices like [0,0;0,1;1,1] or [0,1;0,1;1,0]. The number of units in each state are {1,1,0,1} and {0,2,1,0}, respectively for each of the matrices, where the first number corresponds to the first state {0,0}, second to the second state {0,1} and so on in increasing order. My problem is to find all possible vectors of these numbers of states that satisfy a particular matrix sum.
Now to implement this in MATLAB, I used recursion and a global variable. This to me was the easiest approach, however, it takes a lot of time. The code I used is given below:
function output = getallstate()
global nState % stores all the possible vectors
global nStateRow % stores the current row of the vector
global statebin %stores the binary representation of all the possible states
nState = [];
nStateRow = 1;
nBin = 2; % number of columns or B
v = [1 2]; % should always be of the size 1 x nBin
N = 3; % number of units
statebin = de2bi(0:(2 ^ nBin - 1), nBin) == 1; % stored as logical because I use it to index later
getnstate(v, 2 ^ nBin - 1, nBin) % the main function
checkresult(v, nState, nBin) % will result in false if even one of the results is incorrect
% adjust for max number of units, because the total of each row cannot exceed this number.
output = nState(1:end-1, :); % last row is always repeated (needs to be fixed somehow)
output(:, 1) = N - sum(output(:, 2:end), 2); % the first column, that is the number of units in the all 0 state is always determined by the number of units in the other states
if any(output(:, 1) < 0)
output(output(:, 1) < 0, :) = [];
end
end
function getnstate(r, state, nBin)
global nState
global nStateRow
global statebin
if state == 0
if all(r == 0)
nStateRow = nStateRow + 1;
nState(nStateRow, :) = nState(nStateRow - 1, :);
end
else
for a = 0:min(r(statebin(state + 1, :)))
nState(nStateRow, state + 1) = a;
getnstate(r - a * statebin(state + 1, :), state - 1, nBin);
end
end
end
function allOk = checkresult(r, nState, nBin)
% just a function that checks whether the obtained vectors all result in the correct sum
allstate = de2bi(0:(2 ^ nBin - 1), nBin);
allOk = true;
for iRow = 1:size(nState, 1)
sumR = sum(bsxfun(#times, allstate, nState(iRow, :).'), 1);
allOk = allOk & isequal(sumR,r);
end
end
function b = de2bi(d, n)
d = d(:);
[~, e] = log2(max(d));
b = rem(floor(d * pow2(1-max(n, e):0)), 2);
end
The above code works fine and gives all possible states but, as is expected, it gets slower as you increase the number of columns (B) and the number of units (N). Also, it uses globals. The following are my questions:
Is there a way to generate these without using globals?
Is there a non-recursive way for this algorithm?
EDIT 1
In what way do the above and still have an optimised algorithm which is faster than the current version?
EDIT 2
Added the de2bi function to remove dependency on the Communications Toolbox.

Can operations on submatrices (and subvectors) be vectorized?

I'm currently working on an edge detector in octave. Coming from other programming languages like Java and Python, I'm used to iterating in for loops, rather than performing operations on entire matrices. Now in octave, this causes a serious performance hit, and I'm having a bit of difficulty figuring out how to vectorize my code. I have the following two pieces of code:
1)
function zc = ZeroCrossings(img, T=0.9257)
zc = zeros(size(img));
# Iterate over central positions of all 3x3 submatrices
for y = 2:rows(img) - 1
for x = 2:columns(img) - 1
ndiff = 0;
# Check all necessary pairs of elements of the submatrix (W/E, N/S, NW/SE, NE/SW)
for d = [1, 0; 0, 1; 1, 1; 1, -1]'
p1 = img(y-d(2), x-d(1));
p2 = img(y+d(2), x+d(1));
if sign(p1) != sign(p2) && abs(p1 - p2) >= T
ndiff++;
end
end
# If at least two pairs fit the requirements, these coordinates are a zero crossing
if ndiff >= 2
zc(y, x) = 1;
end
end
end
end
2)
function g = LinkGaps(img, k=5)
g = zeros(size(img));
for i = 1:rows(img)
g(i, :) = link(img(i, :), k);
end
end
function row = link(row, k)
# Find first 1
i = 1;
while i <= length(row) && row(i) == 0
i++;
end
# Iterate over gaps
while true
# Determine gap start
while i <= length(row) && row(i) == 1
i++;
end
start = i;
# Determine gap stop
while i <= length(row) && row(i) == 0
i++;
end
# If stop wasn't reached, exit loop
if i > length(row)
break
end
# If gap is short enough, fill it with 1s
if i - start <= k
row(start:i-1) = 1;
end
end
end
Both of these functions iterate over submatrices (or rows and subrows in the second case), and particularly the first one seems to be slowing down my program quite a bit.
This function takes a matrix of pixels (img) and returns a binary (0/1) matrix, with 1s where zero crossings (pixels whose corresponding 3x3 neighbourhoods fit certain requirements) were found.
The outer 2 for loops seem like they should be possible to vectorize somehow. I can put the body into its own function (taking as an argument the necessary submatrix) but I can't figure out how to then call this function on all submatrices, setting their corresponding (central) positions to the returned value.
Bonus points if the inner for loop can also be vectorized.
This function takes in the binary matrix from the previous one's output, and fills in gaps in its rows (i.e. sets them to 1). A gap is defined as a series of 0s of length <= k, bounded on both sides by 1s.
Now I'm sure at least the outer loop (the one in LinkGaps) is vectorizable. However, the while loop in link again operates on subvectors, rather than single elements so I'm not sure how I'd go about vectorizing it.
Not a full solution, but here is an idea how you could do the first without any loops:
% W/E
I1 = I(2:end-1,1:end-2);
I2 = I(2:end-1,3:end );
C = (I1 .* I2 < 0) .* (abs(I1 - I2)>=T);
% N/S
I1 = I(1:end-2,2:end-1);
I2 = I(3:end, 2:end-1);
C = C + (I1 .* I2 < 0) .* (abs(I1 - I2)>=T);
% proceed similarly with NW/SE and NE/SW
% ...
% zero-crossings where count is at least 2
ZC = C>=2;
Idea: form two subimages that are appropriately shifted, check for the difference in sign (product negative) and threshold the difference. Both tests return a logical (0/1) matrix, the element-wise product does the logical and, result is a 0/1 matrix with 1 where both tests have succeeded. These matrices can be added to keep track of the counts (ndiff).

Loops on a matrix to look at all combinations of rows and columns [duplicate]

This question already has answers here:
Generate a matrix containing all combinations of elements taken from n vectors
(4 answers)
Closed 8 years ago.
I have an arbitrary n-by-n matrix. I want to look at sets of columns and rows of the matrix and do some analysis on them, for example by setting all elements of a specific set of rows and columns equal to zero. To do this I need to analyse all combinations of rows and columns.
For example, if n=3 the process selects the row and columns 1, 2, 3, 12, 13, 23, 123 in succession and creates a new variable for each row and column.
I am currently the technique below for a matrix of size 4:
H = [some 4-by-4 matrix]
for i1 = 1:n
for i2 = 1:n
for i3 = 1:n
for i4 = 1:n
% Set all rows and columns of all variables equal to 0
H(:,i1) = 0;
H(i1,:) = 0;
H(:,i2) = 0;
H(i2,:) = 0;
H(:,i3) = 0;
H(i3,:) = 0;
H(:,i4) = 0;
H(i4,:) = 0;
% Some more analysis on i1, i2, i3, i4...
end
end
end
end
This is an extremely crude method but it seems to work. Obviously, this technique looks at the set (1,1,1,1) which is equivalent to just (1) first, then (1,1,1,2) which is equivalent to (1,2), then (1,1,1,3) which is equivalent to (1,3)... and so on...
The problem here is that this is not a general process for any matrix of size n, this is only a crude process for a matrix of size 4.
Is there any way to generalise the process so that it works for any arbitrary n-by-n matrix?
Thanks!
You can reduce the arbitrary number of loops to one:
for k = 1:2^n-1
ind = dec2bin(k,n)=='1';
H(ind,:) = 0;
H(:,ind) = 0;
end
The trick is to use just one loop to create a logical index (ind) that tells which columns will be selected. So for n=4 the variable ind takes the values [0 0 0 1], [0 0 1 0], [0 0 1 1], ... [1 1 1 1].
Here is a neat way to do that with only two for loops and no magic function. It uses the binary representation of the integer numbers to decide whether to zero out a column and a row.
I just fix some values for the test
n = 3;
Mat = rand(n,n);
Then, we know that there are 2^n combinations, so let's number them from 0 to 2^n-1:
for tag=0:2^n-1
We make a copy to keep the original matrix untouched
myMat = Mat;
Now loop on the row and columns
for (i=1:n)
Here is the trick: if the i-th bit of tag (in binary) is 1, then we zero out the column and row, otherwise we keep it untouched.
if ( mod( floor(tag/2^(i-1)), 2) == 1 )
myMat(:,i) = 0;
myMat(i,:) = 0;
end
end
Finally display to check that we have what we need.
myMat
end

Get the neighbors of a matrix element

I have a matrix and for each element I want to get the index of its surrounding elements. All these results have to be stored into a matrix in the following way. Each row of the matrix corresponds to a matrix element and each of the columns of this matrix contain s the neighbor indexes. For example, for a 4x4 matrix we will get a 16x8 result array. Some of the matrix elements do not have 8 neighbors.
There is an example, I think it is working, I there any way to avoid for loop?:
ElementNeighbors = [];
for n = 1:numel(Matrix)
NeighborsMask = [ n-1 n+1 n+size(matrix,1) n-size(Matrix,1) n-size(Matrix,1)-1 n-size(Matrix,1)+1 ...
n+size(Matrix,1)-1 n+size(Matrix,1)+1 ];
ElementNeighbors = [ElementNeighbors ; NeighborsMask ];
end
ElementNeighbors (ElementNeighbors ==0|ElementNeighbors <0) = NaN;
Given the linear indices of a matrix M(n,m), you can convince yourself that the top left neighbor of element M(i,j) = M(i-1, j-1) = M(i-1 + n * (j-2))
In "linear index" space that means the offset of this element is
-n-1
Doing this for all other locations, we find
-n-1 | -1 | n-1
-n | x | n => [-n-1, -n, -n+1, -1, +1, +n-1, +n, +n+1]
-n+1 | +1 | n+1
Thus you can create a vector offset with the above values (replacing n with the first dimension). For example, if M is (5x4), then
offset = [-6 -5 -4 -1 1 4 5 6];
You then create all the indices:
indices = bsxfun(#plus, (1:m*n), offset(:));
bsxfun is a cool shorthand for "do this function on these elements; where one element has a singleton dimension and the other doesn't, expand accordingly". You could do the same with repmat, but that creates unnecessary intermediate matrices (which can sometimes be very large).
That command will create a (8 x m*n) matrix of indices of all 8 neighbors, including ones that may not really be the neighbors... something you need to fix.
Several possible approaches:
pad the matrix before you start
don't care about wrapping, and just get rid of the elements that fall off the edge
create a mask for all the ones that are "off the edge".
I prefer the latter. "Off the edge" means:
going up in the top row
going left in the left column
going down in the bottom row
going right in the right column
In each of these four cases there are 3 indices that are 'invalid'. Their position in the above matrix can be determined as follows:
mask = zeros(size(M));
mask(:,1) = 1;
left = find(mask == 1);
mask(:,end) = 2;
right = find(mask == 2);
mask(1,:) = 3;
top = find(mask == 3);
mask(end,:) = 4;
bottom = find(mask == 4);
edgeMask = ones(8,m*n);
edgeMask(1:3, top) = 0;
edgeMask([1 4 6], left) = 0;
edgeMask([3 5 8], right) = 0;
edgeMask(6:8, bottom) = 0;
Now you have everything you need - all the indices, and the "invalid" ones. Without loops.
If you were feeling ambitious you could turn this into a cell array but it will be slower than using the full array + mask. For example if you want to find the average of all the neighbors of a value, you can do
meanNeighbor = reshape(sum(M(indices).*edgeMask, 1)./sum(edgeMask, 1), size(M));
EDIT re-reading your question I see you wanted a M*N, 8 dimension. My code is transposed. I'm sure you can figure out how to adapt it...
ATTRIBUTION #Tin helpfully suggested many great edits to the above post, but they were rejected in the review process. I cannot totally undo that injustice - but would like to record my thanks here.
EXTENDING TO DIFFERENT REGIONS AND MULTIPLE DIMENSIONS
If you have an N-dimensional image matrix M, you could find the neighbors as follows:
temp = zeros(size(M));
temp(1:3,1:3,1:3) = 1;
temp(2,2,2) = 2;
offsets = find(temp==1) - find(temp==2);
If you want a region that is a certain radius in size, you could do
sz = size(M);
[xx yy zz] = meshgrid(1:sz(1), 1:sz(2), 1:sz(3));
center = round(sz/2);
rr = sqrt((xx - center(1)).^2 + (yy - center(2)).^2 + (zz - center(3)).^2);
offsets = find(rr < radius) - find(rr < 0.001);
You can probably figure out how to deal with the problem of edges along the lines shown earlier for the 2D case.
Untested - please see if you notice any problems with the above.

Generate random non-repeating integers from a small range

What I'm trying to accomplish is the following:
I wish to create a vector of integers, from a relatively small range, and ensure that none of the integers will be followed by the same integer.
i.e., This is a "legal" vector:
[ 1 3 4 2 5 3 2 3 5 4 ]
and this is an "illegal" vector (since 5 follows 5):
[ 1 3 4 2 5 5 2 3 5 4 ]
I've experimented with randi, and all sorts of variations with randperm, and I always get stuck when i try to generate a vector of around 100 elements, from a small range (i.e., integers between 1 and 5).
The function just runs for too long.
Here's one of the attempts that i've made:
function result = nonRepeatingRand(top, count)
result = randi(top, 1, count);
while any(diff(result) == 0)
result = randi(top, 1, count);
end
end
Any and all help will be much appreciated. Thanks !
The kind of sequence you are looking for can be defined by generating differences from 1 to top - 1 and then computing the cumulative sum modulus top, starting from a random initial value:
function result = nonRepeatingRand(top, count)
diff = randi(top - 1, 1, count);
result = rem(cumsum(diff) + randi(1, 1, count) - 1, top) + 1;
end
On my machine, this generates a non-repeating sequence of 10 million numbers out of 1:5 in 0.58 seconds.
you can use the following code for generate Non Repeating Random Numbers from 1 to M
randperm(M);
and for K Non Repeating Random Numbers from 1 to M
randperm(M, K);
enjoy
Do not regenerate the sequence every time, but fix the repetitions. E.g.:
function result = nonRepeatingRand(top, count)
result = randi(top, 1, count);
ind = (diff(result) == 0);
while any(ind)
result(ind) = [];
result(end + 1 : count) = randi(top, 1, count - numel(result));
ind = (diff(result) == 0);
end
end
On my machine, this generates a non-repeating sequence of 10 million numbers out of 1:5 in 1.6 seconds.
Taking the idea from A. Donda but fixing the implementation:
r=[randi(top,1,1),randi(top - 1, 1, count-1)];
d=rem(cumsum(r)-1,top)+1;
The first element of r is a randomly chosen element to start with. The following elements of r randomly choose the difference to the previous element, using modulo arithmetic.
How this?
top = 5;
count = 100;
n1 = nan;
out = [];
for t = 1: count
n2 = randi(top);
while n1 == n2
n2 = randi(top);
end
out = [out, n2];
n1 = n2;
end