Get index of current element of a 2-D matrix within arrayfun - matlab

Let M be some matrix:
M = rand(1000, 2000);
Consider the following code example:
A = zeros(size(M));
for row = 1:1000
for col = 1:2000
A(row, col) = M(row,col)*(row + col);
end
end
How to compute the matrix A without for loops?
There is arrayfun function, but I don't know how get the index of the current element:
A = arrayfun(#(x)(x*(index(1) + index(2))), M); %// but how to get index???
Perhaps there are other solutions (and without extra loops)?

You can do something simple like as follows to get a matrix that will represent row+col and then multiply that by M
M = rand(1000, 2000);
rowPlusCol = bsxfun(#plus,(1:size(M,1)).',1:size(M,2));
A = M.*rowPlusCol;
From my experience bsxfun is an extremely powerful function and can definitely save some run time, and this is a perfect example of that.

Here's an alternative solution, boasting another fancy one-liner, for the sake of diversity:
A = M .* hankel(2:size(M, 1) + 1, size(M, 1) + 1:sum(size(M)));

I don't think it's possible with arrayfun.
You can get row and column numbers using meshgrid and then do some simple matrix math.
M = rand(1000, 2000);
[cols,rows] = meshgrid(1:size(M,2), 1:size(M,1));
A = M .* (cols + rows);

Related

Matlab : Vectorize technics in 3 dimensions matrix

I actually vectorizing one of my code and I have some issues.
This is my initial code:
CoordVorBd = random(N+1,3)
CoordCP = random(N,3)
v = random(1,3)
for i = 1 : N
for j = 1 : N
ri1j = (-CoordVorBd (i,:) + CoordCP(j,:));
vij(i,j,:) = cross(v,ri1j))/(norm(ri1j)
end
end
I have start to vectorize that creating some matrix that contains 3*1 Vectors. My size of matrix is N*N*3.
CoordVorBd1(1:N,:) = CoordVorBd(2:N+1,:);
CoordCP_x= CoordCP(:,1);
CoordCP_y= CoordCP(:,2);
CoordCP_z= CoordCP(:,3);
CoordVorBd_x = CoordVorBd([1:N],1);
CoordVorBd_y = CoordVorBd([1:N],2);
CoordVorBd_z = CoordVorBd([1:N],3);
CoordVorBd1_x = CoordVorBd1(:,1);
CoordVorBd1_y = CoordVorBd1(:,2);
CoordVorBd1_z = CoordVorBd1(:,3);
[X,Y] = meshgrid (1:N);
ri1j_x = (-CoordVorBd_x(X) + CoordCP_x(Y));
ri1j_y = (-CoordVorBd_y(X) + CoordCP_y(Y));
ri1j_z = (-CoordVorBd_z(X) + CoordCP_z(Y));
ri1jmat(:,:,1) = ri1j_x(:,:);
ri1jmat(:,:,2) = ri1j_y(:,:);
ri1jmat(:,:,3) = ri1j_z(:,:);
vmat(:,:,1) = ones(N)*v(1);
vmat(:,:,2) = ones(N)*v(2);
vmat(:,:,3) = ones(N)*v(3);
This code works but is heavy in terms of variable creation. I did'nt achieve to apply the vectorization to all the matrix in one time.
The formule like
ri1jmat(X,Y,1:3) = (-CoordVorBd (X,:) + CoordCP(Y,:));
doesn't work...
If someone have some ideas to have something cleaner.
At this point I have a N*N*3 matrix ri1jmat with all my vectors.
I want to compute the N*N rij1norm matrix that is the norm of the vectors
rij1norm(i,j) = norm(ri1jmat(i,j,1:3))
to be able to vectorize the vij matrix.
vij(:,:,1:3) = (cross(vmat(:,:,1:3),ri1jmat(:,:,1:3))/(ri1jmatnorm(:,:));
The cross product works.
I tried numbers of method without achieve to have this rij1norm matrix without doing a double loop.
If someone have some tricks, thanks by advance.
Here's a vectorized version. Note your original loop didn't include the last column of CoordVorBd, so if that was intentional you need to remove it from the below code as well. I assumed it was a mistake.
CoordVorBd = rand(N+1,3);
CoordCP = rand(N,3);
v = rand(1,3);
repCoordVor=kron(CoordVorBd', ones(1,size(CoordCP,1)))'; %based on http://stackoverflow.com/questions/16266804/matlab-repeat-every-column-sequentially-n-times
repCoordCP=repmat(CoordCP, size(CoordVorBd,1),1); %repeat matrix
V2=-repCoordVor + repCoordCP; %your ri1j
nrm123=sqrt(sum(V2.^2,2)); %vectorized norm for each row
vij_unformatted=cat(3,(v(:,2).*V2(:,3) - V2(:,2).*v(:,3))./nrm123,(v(:,3).*V2(:,1) - V2(:,3).*v(:,1))./nrm123,(v(:,1).*V2(:,2) - V2(:,1).*v(:,2))./nrm123); % cross product, expanded, and each term divided by norm, could use bsxfun(#rdivide,cr123,nrm123) instead, if cr123 is same without divisions
vij=permute(reshape( vij_unformatted,N,N+1,3),[2,1,3]); %reformat to match your vij
Here is another way to do it using arrayfun
% Define a meshgrid of indices to run over
[I, J] = meshgrid(1:N, 1:(N+1));
% Calculate ril for each index
rilj = arrayfun(#(x, y) -CoordVorBd (y,:) + CoordCP(x,:), I, J, 'UniformOutput', false);
%Calculate vij for each point
temp_vij1 = arrayfun(#(x, y) cross(v, rilj{x, y}) / norm(rilj{x, y}), J, I, 'UniformOutput', false);
%Reshape the matrix into desired format
temp_vij2 = cell2mat(temp_vij1);
vij = cat(3, temp_vij2(:, 1:3:end), temp_vij2(:, 2:3:end), temp_vij2(:, 3:3:end));

Create a variable number of terms in an anonymous function that outputs a vector

I'd like to create an anonymous function that does something like this:
n = 5;
x = linspace(-4,4,1000);
f = #(x,a,b,n) a(1)*exp(b(1)^2*x.^2) + a(2)*exp(b(2)^2*x.^2) + ... a(n)*exp(b(n)^2*x.^2);
I can do this as such, without passing explicit parameter n:
f1 = #(x,a,b) a(1)*exp(-b(1)^2*x.^2);
for j = 2:n
f1 = #(x,a,b) f1(x,a,b) + a(j)*exp(b(j)^2*x.^2);
end
but it seems, well, kind of hacky. Does someone have a better solution for this? I'd like to know how someone else would treat this.
Your hacky solution is definitely not the best, as recursive function calls in MATLAB are not very efficient, and you can quickly run into the maximum recursion depth (500 by default).
You can introduce a new dimension along which you can sum up your arrays a and b. Assuming that x, a and b are row vectors:
f = #(x,a,b,n) a(1:n)*exp((b(1:n).^2).'*x.^2)
This will use the first dimension as summing dimension: (b(1:n).^2).' is a column vector, which produces a matrix when multiplied by x (this is a dyadic product, to be precise). The resulting n * length(x) matrix can be multiplied by a(1:n), since the latter is a matrix of size [1,n]. This vector-matrix product will also perform the summation for us.
Mini-proof:
n = 5;
x = linspace(-4,4,1000);
a = rand(1,10);
b = rand(1,10);
y = 0;
for k=1:n
y = y + a(k)*exp(b(k)^2*x.^2);
end
y2 = a(1:n)*exp((b(1:n).^2).'*x.^2); %'
all(abs(y-y2))<1e-10
The last command returns 1, so the two are essentially identical.

Efficient way to apply arrayfun to a matrix (i.e. R^N to R^M)

I have a function that transforms R^N to R^M. For simplicity, lets just let it be the identity function #(z) z where z may be a vector. I want to apply a function to a list of parameters of size K x N and have it map to K x M output.
Here is my attempt:
function out_matrix = array_fun_matrix(f, vals)
for i=1:size(vals,1)
f_val = f(vals(i,:));
if(size(f_val,1) > 1) %Need to stack up rows, so convert as required.
f_val = f_val';
end
out_matrix(i,:) = f_val;
end
end
You can try it with
array_fun_matrix(#(z) z(1)^2 + z(2)^2 + z(3), [0 1 0; 1 1 1; 1 2 1; 2 2 2])
The question: Is there a better and more efficient way to do this with vectorization, etc.? Did I miss a built-in function?
Examples of non-vectorizable functions: There are many, usually involving elaborate sub-steps and numerical solutions. A trivial example is something like looking for the numerical solution to an equation, which in term is using numerical quadrature. i.e. let params = [b c] and solve for the a such that int_0^a ((z + b)^2) dz = c
(I know here you could do some calculus, but the integral here is stripped down). Implementing this example,
find_equilibrium = #(param) fzero(#(a) integral(#(x) (x + param(1)).^2 - param(2), 0, a), 1)
array_fun_matrix(find_equilibrium, [0 1; 0 .8])
You can use the cellfun function, but you'll need to manipulate your data a bit:
function out_matrix = array_fun_matrix(f, vals)
% Convert your data to a cell array:
cellVals = mat2cell(vals, ones(1,size(vals,1)));
% apply the function:
out_cellArray = cellfun(f, cellVals, 'UniformOutput', false);
% Convert back to matrix:
out_matrix = cell2mat(out_cellArray);
end
If you don't like this implementation, you can improve the performance of yours by preallocating the out_matrix:
function out_matrix = array_fun_matrix(f, vals)
firstOutput = f(vals(1,:));
out_matrix = zeros(size(vals,1), length(firstOutput)); % preallocate for speed.
for i=1:size(vals,1)
f_val = f(vals(i,:));
if(size(f_val,1) > 1) %Need to stack up rows, so convert as required.
f_val = f_val';
end
out_matrix(i,:) = f_val;
end
end

Shorten this in Matlab

Let x = [1,...,t] be a vector with t components and A and P arrays. I asked myself whether there is any chance to shorten this, as it looks very cumbersome:
for n = 1:t
for m = 1:n
H(n,m) = A(n,m) + x(n) * P(n,m)
end
end
My suggestion: bsxfun(#times,x,P) + A;
e.g.
A = rand(3);
P = rand(3);
x = rand(3,1);
for n = 1:3
for m = 1:3
H(n,m) = A(n,m) + x(n) * P(n,m);
end
end
H2 = bsxfun(#times,x,P) + A;
%//Check that they're the same
all(H(:) == H2(:))
returns
ans = 1
EDIT:
Amro is right! To make the second loop is dependent on the first use tril:
H2 = tril(bsxfun(#times,x,P) + A);
Are the matrices square btw because that also creates other problems
tril(A + P.*repmat(x',1,t))
EDIT. This is for when x is row vector.
If x is a column vector, then use tril(A + P.*repmat(x,t,1))
If your example code is correct, then H(i,j) = 0 for any j > i, e.g. X(1,2).
For t = 3 for example, you would have.
H =
'A(1,1) + x(1) * P(1,1)' [] []
'A(2,1) + x(2) * P(2,1)' 'A(2,2) + x(2) * P(2,2)' []
'A(3,1) + x(3) * P(3,1)' 'A(3,2) + x(3) * P(3,2)' 'A(3,3) + x(3) * P(3,3)'
Like I pointed out in the comments, unless it was a typo mistake, the second for-loop counter depends on that of the first for-loop...
In case it was intentional, I came up with the following solution:
% some random data
t = 10;
x = (1:t)';
A = rand(t,t);
P = rand(t,t);
% double for-loop
H = zeros(t,t);
for n = 1:t
for m = 1:n
H(n,m) = A(n,m) + x(n) * P(n,m);
end
end
% vectorized using linear-indexing
[a,b] = ndgrid(1:t,1:t);
idx = sub2ind([t t], nonzeros(tril(a)), nonzeros(tril(b)));
xidx = nonzeros(tril(a));
HH = zeros(t);
HH(tril(true(t))) = A(idx) + x(xidx).*P(idx);
% check the results are the same
assert(isequal(H,HH))
I like #Dan's solution better. The only advantage here is that I do not compute unnecessary values (since the upper half of the matrix is zeros), while the other solution computes the full matrix and then cut back the extra stuff.
A good start would be
H = A + x*P
This may not be a working solution, you'll have to check conformability of arrays and vectors, and make sure that you're using the correct multiplication, but this should be enough to point you in the right direction. If you're new to Matlab be aware that vectors can be either 1xn or nx1, ie row and column vectors are different species unlike in so many programming languages. If x isn't what you want on the rhs, you may want its transpose, x' in Matlab.
Matlab is, from one point of view, an array language, explicit loops are often unnecessary and frequently not even a good way to go.
Since the range for second loop is 1:n, you can take the lower triangle parts of matrices A and P for calculation
H = bsxfun(#times,x(:),tril(P)) + tril(A);

Generalized Matrix Product

I'm fairly new to MATLAB. Normal matrix multiplication of a M x K matrix by an K x N matrix -- C = A * B -- has c_ij = sum(a_ik * b_kj, k = 1:K). What if I want this to be instead c_ij = sum(op(a_ik, b_kj), k = 1:K) for some simple binary operation op? Is there any nice way to vectorize this in MATLAB (or maybe even a built-in function)?
EDIT: This is currently the best I can do.
% A is M x K, B is K x N
% op is min
C = zeros(M, N);
for i = 1:M:
C(i, :) = sum(bsxfun(#min, A(i, :)', B));
end
Listed in this post is a vectorized approach that persists with bsxfun by using permute to create singleton dimensions as needed by bsxfun to let the singleton-expansion do its work and thus essentially replacing the loop in the original post. Please be reminded that bsxfun is a memory hungry implementation, so expect speedup with it only until it is stretched too far. Here's the final solution code -
op = #min; %// Edit this with your own function/ operation
C = sum(bsxfun(op, permute(A,[1 3 2]),permute(B,[3 2 1])),3)
NB - The above solution was inspired by Removing four nested loops in Matlab.
if the operator can operate element-by-element (like .*):
if(size(A,2)~=size(B,1))
error(blah, blah, blah...);
end
C = zeros(size(A,1),size(B,2));
for i = 1:size(A,1)
for j = 1:size(B,2)
C(i,j) = sum(binaryOp(A(i,:)',B(:,j)));
end
end
You can always write the loops yourself:
A = rand(2,3);
B = rand(3,4);
op = #times; %# use your own function here
C = zeros(size(A,1),size(B,2));
for i=1:size(A,1)
for j=1:size(B,2)
for k=1:size(A,2)
C(i,j) = C(i,j) + op(A(i,k),B(k,j));
end
end
end
isequal(C,A*B)
Depending on your specific needs, you may be able to use bsxfun in 3D to trick the binary operator. See this answer for more infos: https://stackoverflow.com/a/23808285/1121352
Another alternative would be to use cellfun with a custom function:
http://matlabgeeks.com/tips-tutorials/computation-using-cellfun/