Efficient way to compute a tensor - matlab

Suppose c is a d-dimensional vector. I want to compute the following third-order tensor
where e_i stands for the ith standard basis of the Euclidean space. Is there a efficient way to compute this? I am using the following for-loop and the Kruskal-tensor ktensor to compute it using the tensor toolbox managed by Sandia National Labs:
x=ktensor({c,c,c});
I=eye(d);
for i=1:d
x=x+2*c(i)*ktensor({I(:,i),I(:,i),I(:,i)}
end
for i=1:d
for j=1:d
x=x- c(i)*c(j)*(ktensor({I(:,i),I(:,i),I(:,j)})+ktensor({I(:,i),I(:,j),I(:,i)})+ktensor({I(:,i),I(:,j),I(:,j)}))
end
end

Here is a possibility.
I used an optimization for the second term, as it places values of c along the "diagonal" of the tensor.
For the first term, there isn't much room for optimization, as it is a dense multiplication, so bsxfun seems appropriate.
For the third term, I stick to bsxfun, but as the result is somewhat sparse, you may benefit from filling it "by hand" if the size of your matrix is large.
Here is the code:
dim = 10;
c = [1:dim]';
e = eye(dim);
x = zeros([dim, dim, dim]);
% initialize with second term
x(1:dim*(dim+1)+1:end) = 2 * c;
% add first term
x = x + bsxfun(#times, bsxfun(#times, c, shiftdim(c, -1)), shiftdim(c, -2));
% add third term
x = x - sum(sum(bsxfun(#times, shiftdim(c*c',-3), ...
bsxfun(#times, bsxfun(#times, permute(e, [1, 3, 4, 2, 5]), permute(e, [3, 1, 4, 2, 5])), permute(e, [3, 4, 1, 5, 2])) +...
bsxfun(#times, bsxfun(#times, permute(e, [1, 3, 4, 2, 5]), permute(e, [3, 1, 4, 5, 2])), permute(e, [3, 4, 1, 2, 5])) +...
bsxfun(#times, bsxfun(#times, permute(e, [1, 3, 4, 5, 2]), permute(e, [3, 1, 4, 2, 5])), permute(e, [3, 4, 1, 2, 5]))), 5), 4);
EDIT
A much more efficient (esp. memory-wise) computation of the third term:
ec = bsxfun(#times, e, c);
x = x - ...
bsxfun(#times, ec, shiftdim(c, -2)) -...
bsxfun(#times, c', reshape(ec, [dim, 1, dim])) -....
bsxfun(#times, c, reshape(ec, [1, dim, dim]));

You could try the Parallel Computing Toolbox that is namely parfor loops.
x=ktensor({c,c,c});
I=eye(d);
y = zeros(d,d,d, d);
parfor i=1:d
y(:,:,:, i) = 2*c(i)*ktensor({I(:,i),I(:,i),I(:,i)};
end
x = x + sum(y, 4);
z = zeros(d,d,d, d,d);
parfor i=1:d
for j=1:d % only one layer of parallelization is allowed
z(:,:,:, i,j) = c(i)*c(j)*(ktensor({I(:,i),I(:,i),I(:,j)})+ktensor({I(:,i),I(:,j),I(:,i)})+ktensor({I(:,i),I(:,j),I(:,j)}));
end
end
x = x - sum(sum(z, 5), 4);
x % is your result
It just runs the untouched ktensor commands but in separate threads, so the Toolbox takes care of running the code in parallel.
Because of the independence property of each iteration, which means, for example, c_{i+1, j+1} does not rely on c_{i, j}, this is possible.
Depending on the number of cores (and hyperthreading) of your system, there could be up to #-of-cores-times of speed-up.

Related

Matlab: slower execution although less operation

I'm new to Matlab. This is my playground script:
function speedtest()
a = reshape(1:1:30000, 10000, 3);
tic;
for i = 1:100
a(:, [1, 2]) = bsxfun(#minus, a(:, [1, 2]), [1, 1]);
end
toc
tic;
for i = 1:100
a = bsxfun(#minus, a, [1, 1, 0]);
end
toc
end
And the execution time:
Elapsed time is 0.007709 seconds.
Elapsed time is 0.001803 seconds.
The first method has less operation, but it runs much slower. Is this a vectorization issue? If so, why can't Matlab "vectorize" my a(:, [1, 2]) selection?
Update:
As per #thewaywewalk, I put the code to individual function, remove the loop and use timeit. Here's the result:
# a(:, [1, 2]) = bsxfun(#minus, a(:, [1, 2]), [1, 1]);
1.0064e-04
# a = bsxfun(#minus, a, [1, 1, 0]);
6.4187e-05
the overhead of the first approach came from sub-matrix slicing. changing it to
tic;
b=a(:,[1,2]);
for i = 1:100
b = bsxfun(#minus, b, [1, 1]);
end
a(:,[1,2])=b;
toc
makes it significantly faster

Find values in 3d matrix

I would like to do the equivalent of
x = [1, 0, 3; 2, 3, 0; 0, 0, 3];
[yy, xx, vals] = find(x);
where I really need the vals variable. I need all three, but vals is important. Now consider the 3d case, and flip one, so it's more interesting.
x = repmat(x, [1, 1, 3]);
x(:, :, 2) = fliplr(x(:, :, 1));
I'd like to do the same as before. I found this in several places
[yy, xx, zz] = ind2sub(size(x), find(x));
but then I don't know how to extract vals properly... I also don't really care about zz, but I'm sure they somehow need to be used for indexing.
Any help would be appreciated.
find with one output argument, as you used in your last statement:
[yy, xx, zz] = ind2sub(size(x), find(x));
returns linear indices into the matrix. You can use these to index:
index = find(x);
vals = x(index);
[xx,yy,zz] = ind2sub(size(x), index);
I'm not sure I've understood what you want to achieve, nevertheless, considering your last matrix x
x = [1, 0, 3; 2, 3, 0; 0, 0, 3]
z = repmat(x, [1, 1, 3]);
x(:, :, 2) = fliplr(x(:, :, 1))
with
[yy, xx, vals] = find(x)
you have:
yy the indices of the rows of the found elements
xx the indices of the columns of the found elements
then you can use
lin_idx=sub2ind(size(x),yy,xx)
to get the linear indices of the values inside the matrix x
now you can use
[a,b,c]=ind2sub(size(x),lin_idx)
to get the 3D indices of the elements in the matrix
You can access the values using that indices:
for i=1:length(a)
k(i)=x(a(i),b(i),c(i))
end
Now the array k contains the values (as per the array vals returned by find).

Is it possible to use mldivide "\" on a 3D matrix in Matlab

Is it possible to use mldivide (\) on a 3D matrix in MATLAB? I would like to avoid using a for loop?
Sample:
A = rand(4, 100, 5);
B = rand(4,4);
I need to perform:
C = B\A;
What I'm doing now:
Apply the mldivide on a for loop for each "slice" i:
for i = 1:size(A, 3)
C(:,:,i) = B \ A(:,:,i);
end
You can reshape A into a 2D matrix to perform the division and then back to the expected size afterwards. The reshape operations should be relatively quick due to the fact that MATLAB doesn't alter the underlying data.
C = reshape(B \ reshape(A, size(A, 1), []), size(B, 1), size(A, 2), []);
And if we break that down:
%// Reshape A to be 4 x 500
Anew = reshape(A, size(A, 1), []);
%// Perform left division
C = B \ Anew;
%// Reshape C to be the expected size (4 x 100 x 5)
C = reshape(C, size(B, 1), size(A, 2), []);
This should work for any valid (size(A, 1) == size(B, 2)) matrices A and B of any size.

Create a 3-dim matrix from two 2-dim matrices

I already have a N_1 x N_2 matrix A, and a N_2 x N_3 matrix B.
I want to create a N_1 x N_2 x N_3 matrix C, such that C(i,j,k) = A(i,j)*B(j,k).
I was wondering if it is possible to create C using some Matlab operation, instead of doing it element by element?
You can do the same thing as the OP's answer using bsxfun (which actually works internally using a similar method, but is a little bit cleaner):
C = bsxfun(#times, A, permute(B, [3 1 2]));
This is also quite a bit faster (bsxfun must do some magic internally - probably takes advantage of MATLAB's internal ability to do certain operations using multiple threads, or it might just be that permuting the smaller matrix is a lot faster, or some combination of similar factors):
>> N1 = 100; N2 = 20; N3 = 4; A = rand(N1, N2); B = rand(N2, N3);
>> tic; for n = 1:10000; C = repmat(A, [1, 1, size(B, 2)]) .* permute(repmat(B, [1, 1, size(A, 1)]), [3, 1, 2]); end; toc
Elapsed time is 2.827492 seconds.
>> tic; for n = 1:10000; C2 = bsxfun(#times, A, permute(B, [3 1 2])); end; toc
Elapsed time is 0.287665 seconds.
Edit: moving the permute inside the repmat shaves a little bit of time off, but it's still nowhere near as fast as bsxfun:
>> tic; for n = 1:10000; C = (repmat(A, [1 1 size(B, 2)]) .* repmat(permute(B, [3 1 2]), [size(A, 1) 1 1])); end; toc
Elapsed time is 2.563069 seconds.
Rather clumsy, but it seems to work:
C = repmat(A, [1, 1, size(B, 2)]) .* permute(repmat(B, [1, 1, size(A, 1)]), [3, 1, 2]);

What's the difference between [A,B] and [A;B] in MatLab?

% CAT(2,A,B) is the same as [A,B].
% CAT(1,A,B) is the same as [A;B].
Seems I need to know this to understand what cat does.
[A,B]
is a matrix formed by placing B to the right of A, while
[A;B]
is a matrix formed by placing B below A.
Learn also about horzcat and vertcat.
[A, B] does col cat
[A; B] does row cat
eg:
x = [1, 2, 3];
y = [7, 8, 9];
[x, y] == > [1, 2, 3, 7, 8, 9]
becomes a 1x6 array
[x; y] == > [1, 2, 3]
[7, 8, 9]
becomes a 2x3 array
Just try it in Matlab and open ans to see the difference