Coverting C style code into Matlab - matlab

I've the following code in C:
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
a[b[i]][c[j]]+=1;
}
}
Is there a way to write this in Matlab without using for loops? I mean the Matlab way using (:) which is faster.
Something like a(b(:),c(:))=a(b(:),c(:))+1 gives me out of memory error.

Interesting. While I don't (yet) have a solution for you (solution at bottom), I have a few notes and pointers:
1. The out of memory error is because you're creating a 512*256 by 512*256 element temporary matrix on the right hand side (a(b(:),c(:))+1). That is 2^34 bytes — 17GB! So that's why you're getting an out of memory error. Note, too, that this array isn't even what you want! Look at this example:
>> a = magic(5);
>> b = [1 5 4]; % The rows that contain the numbers 1,2,3 respectively
>> c = [3 4 5]; % The columns that contain ^ ...
Now, a(1,3) == 1, a(5,4) == 2, etc. But when you say a(b,c), you're selecting rows (1,5,4) and columns (3,4,5) for every one of those rows!
>> a(b,c)
ans =
1 8 15
25 2 9
19 21 3
All you care about is the diagonal. The solution is to use sub2ind to convert your subscript pairs to a linear index.
>> a(sub2ind(size(a),b,c))
ans =
1 2 3
2. Your proposed solution doesn't do what you want it to, either. Since Matlab lacks an increment operator, you are simply incrementing all indices that exist in (b,c) by one. And no more. It'll take some creative thinking to vectorize this. Use a smaller array to see what's going on:
>> a = zeros(4,4);
>> b = ones(8,4);
>> c = ones(8,4);
>> a(b,c) = a(b,c) + 1;
>> a
a =
1 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
Edit Here we go! Vectorized incrementation:
>> idxs = sub2ind(size(a),b(:),c(:)); % convert subscripts to linear indices
>> [unique_idxs,~,ic] = unique(idxs); % Get the unique indices and their locations
>> increment_counts = histc(ic,1:max(ic)); % Get the number of occurrences of each idx
>> a(unique_idxs) = a(unique_idxs) + increment_counts;

Assuming you have the following matrices:
a = zeros(256); % or initialized with other values
b = randi(256, [512 256]);
c = randi(256, [512 256]);
Here is an even faster vectorized solution:
a = a + sparse(b,c,1,size(a,1),size(a,2));
Here is another one:
a = a + accumarray([b(:) c(:)], 1, size(a));

Answer: Yes.
a(b, c) = a(b, c) + 1;
Example:
>> a = zeros(5);
>> b = [1,3];
>> c = [2,4,5];
>> a(b,c) = a(b,c) + 1;
>> a
a =
0 1 0 1 1
0 0 0 0 0
0 1 0 1 1
0 0 0 0 0
0 0 0 0 0

Related

Using 'find' on rows of a matrix without looping

If A is a nx by ny matrix of zeros and ones and I want to find the index of the the first and last zeros in each row. Currently I'm doing the following:
for ix = 1:nx
lhs_i = find(A(ix,:) < 1,1,'first');
rhs_i = find(A(ix,:) < 1,1,'last');
if ~isempty(lhs_i)
lhs(ix,k) = lhs_i;
rhs(ix,k) = rhs_i;
else
lhs(ix,k) = NaN;
rhs(ix,k) = NaN;
end
end
I'm sure there is a better way that doesn't involve a loop. Any suggestions?
You can use accumarray -
[R,C] = find(A==0);
out = zeros(size(A,1),2)
out(1:max(R),:) = [accumarray(R,C,[],#min) accumarray(R,C,[],#max)]
Finally, if need be, replace the zeros with NaNs, but zeros themselves look like good specifiers of invalid rows (rows without zeros).
Sample run -
>> A
A =
3 1 3 3 4
0 3 0 2 0
0 0 4 4 0
1 4 1 4 2
>> [R,C] = find(A==0);
>> out = zeros(size(A,1),2);
>> out(1:max(R),:) = [accumarray(R,C,[],#min) accumarray(R,C,[],#max)]
out =
0 0
1 5
1 5
0 0
Here's another using bsxfun and minmax -
P = bsxfun(#times,A==0,1:size(A,2));
P(P==0) = nan;
out = minmax(P)
With this solution, Inf/-Inf would act as the invalid specifier.
Sample run -
>> A
A =
0 4 0 2 2
3 4 3 1 1
0 4 3 1 2
1 0 3 4 0
>> P = bsxfun(#times,A==0,1:size(A,2));
>> P(P==0) = nan;
>> minmax(P)
ans =
1 3
-Inf Inf
1 1
2 5

Replace specific matrix position with array value without using for loop in MATLAB

can I know how can I replace values in specific matrix position without using for loop in MATLAB? I initialize matrix a that I would like to replace its value on specified row and column for each no. This has to be done a few time within num for loop. The num for loop is important here because I would want the update the value in the original code.
The real code is more complicated, I am simplifying the code for this question.
I have the code as follow:
a = zeros(2,10,15);
for num = 1:10
b = [2 2 1 1 2 2 2 1 2 2 2 2 1 2 2];
c = [8.0268 5.5218 2.9893 5.7105 7.5969 7.5825 7.0740 4.6471 ...
6.3481 14.7424 13.5594 10.6562 7.3160 -4.4648 30.6280];
d = [1 1 1 2 1 1 1 1 1 1 3 1 6 1 1];
for no = 1:15
a(b(no),d(no),no) = c(1,no,:)
end
end
A sample output for, say no 13 is as follows:
a(:,:,13) =
Columns 1 through 8
0 0 0 0 0 7.3160 0 0
0 0 0 0 0 0 0 0
Columns 9 through 10
0 0
0 0
Thank you so much for any help I could get.
It can be done using sub2ind, which casts the subs to a linear index.
Following your vague variable names, it would look like this (omitting the useless loop over num):
a = zeros(2,10,15);
b = [2 2 1 1 2 2 2 1 2 2 2 2 1 2 2];
d = [1 1 1 2 1 1 1 1 1 1 3 1 6 1 1];
c = [8.0268 5.5218 2.9893 5.7105 7.5969 7.5825 7.0740 4.6471 ...
6.3481 14.7424 13.5594 10.6562 7.3160 -4.4648 30.6280];
% // we vectorize the loop over no:
no = 1:15;
a(sub2ind(size(a), b, d, no)) = c;
Apart from the sub2ind based approach as suggested in Nras's solution, you can use a "raw version" of sub2ind to reduce a function call if performance is very critical. The related benchmarks comparing sub2ind and it's raw version is listed in another solution. Here's the implementation to solve your case -
no = 1:15
a = zeros(2,10,15);
[m,n,r] = size(a)
a((no-1)*m*n + (d-1)*m + b) = c
Also for pre-allocation, you can use a much faster approach as listed in Undocumented MATLAB blog post on Preallocation performance with -
a(2,10,15) = 0;
The function sub2ind is your friend here:
a = zeros(2,10,15);
x = [2 2 1 1 2 2 2 1 2 2 2 2 1 2 2];
y = [1 1 1 2 1 1 1 1 1 1 3 1 6 1 1];
z = 1:15;
dat = [8.0268 5.5218 2.9893 5.7105 7.5969 7.5825 7.0740 4.6471 ...
6.3481 14.7424 13.5594 10.6562 7.3160 -4.4648 30.6280];
inds = sub2ind(size(a), x, y, z);
a(inds) = dat;
Matlab provides a function 'sub2ind' may do what you expected.
with variable as the same you posted:
idx = sub2ind(size(a),b,d,[1:15]); % return the index of row a column b and page [1:15]
a(idx) = c;

Execute formulas ignoring zero elements

I want to run fast Matlab algorithms over Matrices by ignoring zero-elements.
In the past I just worked with a very slow double-for-loop e.g.
for i = 1 : size(x,1)
for j = 1 : size(x,2)
if x(i,j) ~= 0
... do something with x(i,j)
end
end
end
But how can I make the matrix operation on the whole matrix x?
E.g. how can I run
x(i,j) = log(x(i,j)) if x>0 else 0 <-- pseudo code
in Matlab on the whole matrix without for loops?
Finally I want to rewrite lines like
result = sum(sum((V.*log(V./(W*H))) - V + W*H));
with ignoring zeros.
I just need to understand the concept.
In case of need I could also use NaN instead of zero, but I didn't find e.g. the function
nanlog()
x~=0 returns you the indices of the locations not equal to zero. Then, you can use them to index corresponding locations of x such as follows:
>> x = [1 0 2 3; 0 4 0 5]
x =
1 0 2 3
0 4 0 5
>> mean(x(:)) %#mean of all elements
ans =
1.8750
>> mean(x(x~=0)) %#mean of nonzero elements
ans =
3
>> x(x~=0) = x(x~=0) + 1
x =
2 0 3 4
0 5 0 6
You can use NaN as a temporary and make use of the fact that log(NaN) = NaN, like so:
x(x==0) = NaN;
y = log(x);
y(isnan(y)) = 0;
alternatively, you can use logical indexing:
x(x~=0) = log(x(x~=0));
or, if you want to preserve x,
y = x;
y(y~=0) = log(y(y~=0));
For the example you provide, you can just do
result = nansum(nansum((V.*log(V./(W*H))) - V + W*H));
assuming that V == 0 is the problem.

Inserting variable number of zeros between non-zero elements in a vector in MATLAB

I have a vector like:
a = [1,2,3,4,5,6...,n]
and I would like to obtain a new vector like this:
a_new = [1,0,0,2,0,0,3,0,0,4,0,0,5,0,0,6,...,0,0,n]
where a fixed number of zeros (2 in the above example) are inserted between the non-zero elements. If I choose zero_p=3, the new vector would be:
a_new = [1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0,5,0,0,0,6,...,0,0,0,n]
etc.
How can I do this?
Try this:
zero_p=3;
a_new=zeros(1, (zero_p+1)*length(a)-zero_p);
a_new(1:(zero_p+1):end)=a;
(Untested, but should hopefully work.)
There's a few ways I can think of:
Kronecker product
The kronecker product is excellently suited for this.
In Matlab, kron is what you're looking for:
a = 1:4;
a = kron(a, [1 0 0])
ans =
1 0 0 2 0 0 3 0 0 4 0 0
or, generalized,
a = 1:4;
zero_p = 3;
b = [1 zeros(1,zero_p-1)];
a = kron(a, b)
ans =
1 0 0 2 0 0 3 0 0 4 0 0
If you want to have it end with a non-zero element, you have to do one additional step:
a = a(1:end-zero_p);
Or, if you like one-liners, the whole thing can be done like this:
a = 1:4;
zero_p = 3;
a = [kron(a(1:end-1), [1 zeros(1,zero_p-1)]), a(end)]
ans =
1 0 0 2 0 0 3 0 0 4
Zero padding
Probably the simplest method and best performance:
a = 1:4;
zero_p = 3;
a = [a; zeros(zero_p, size(a, 2))];
a = a(1:end-zero_p);
Matrix multiplication
Also simple, readable and great performance, although it might be overkill for many situations other than this particular scenario:
a = 1:4;
b = [1; zeros(zero_p, 1)];
a = b*a;
a = a(1:end-zero_p);
x = [1 2 3 4 5];
upsample(x,3)
o/p: 1 0 0 2 0 0 3 0 0 4 0 0 5 0 0
Cheers!!

How to vectorize row-wise diagonalization of a matrix

I have an n-by-m matrix that I want to convert to a mn-by-m matrix, with each m-by-m block of the result containing the diagonal of each row.
For example, if the input is:
[1 2; 3 4; 5 6]
the output should be:
[1 0; 0 2; 3 0; 0 4; 5 0; 0 6]
Of course, I don't want to assemble the matrix step by step myself with a for loop.
Is there a vectorized and simple way to achieve this?
For a vectorized way to do this, create the linear indices of the diagonal elements into the resulting matrix, and assign directly.
%# create some input data
inArray = [10 11;12 13;14 15];
%# make the index array
[nr,nc]=size(inArray);
idxArray = reshape(1:nr*nc,nc,nr)';
idxArray = bsxfun(#plus,idxArray,0:nr*nc:nr*nc^2-1);
%# create output
out = zeros(nr*nc,nc);
out(idxArray) = inArray(:);
out =
10 0
0 11
12 0
0 13
14 0
0 15
Here's a simple vectorized solution, assuming X is the input matrix:
Y = repmat(eye(size(X, 2)), size(X, 1), 1);
Y(find(Y)) = X;
Another alternative is to use sparse, and this can be written as a neat one-liner:
Y = full(sparse(1:numel(X), repmat(1:size(X, 2), 1, size(X, 1)), X'));
The easiest way I see to do this is actually quite simple, using simple index referencing and the reshape function:
I = [1 2; 3 4; 5 6];
J(:,[1,4]) = I;
K = reshape(J',2,6)';
If you examine J, it looks like this:
J =
1 0 0 2
3 0 0 4
5 0 0 6
Matrix K is just what wanted:
K =
1 0
0 2
3 0
0 4
5 0
0 6
As Eitan T has noted in the comments, the above is specific to the example, and doesn't cover the general solution. So below is the general solution, with m and n as described in the question.
J(:,1:(m+1):m^2) = I;
K=reshape(J',m,m*n)';
If you want to test it to see it working, just use
I=reshape(1:(m*n),m,n)';
Note: if J already exists, this can cause problems. In this case, you need to also use
J=zeros(n,m^2);
It may not be the most computationally efficient solution, but here's a 1-liner using kron:
A = [1 2; 3 4; 5 6];
B = diag(reshape(A', 6, 1) * kron(ones(3, 1), eye(2))
% B =
% 1 0
% 0 2
% 3 0
% 0 4
% 5 0
% 0 6
This can be generalized if A is n x m:
diag(reshape(A.', n*m, 1)) * kron(ones(n,1), eye(m))