How to implement tensor product for arbitrary order tensors in octave?

南楼画角 提交于 2019-12-11 07:09:10

问题


I dont have access to matlab, so I am trying some things with octave. How would you efficiently implement the tensor product described in the following formula?

My approach for arbitrary order tensors a and b is the following

% Tensor product
function out = tp(a,b)
  if isvector(a)
    da = prod(size(a));
  else
    da = size(a);
  endif
  if isvector(b)
    db = prod(size(b));
  else
    db = size(b);
  endif
  out = reshape(a(:)*(b(:)'),[da,db]);
endfunction

The if statements are only there in order to catch the case for a or b to be vectors. I dont know if this is an efficient approach since I dont usually program and I am new to octave. What would be your approach?

I will use the Frobenius norm, see picture below, to see if there is a difference with the explicit computation.

Below are some explicit computations for checking the implementation. It works fine, but I wanted to ask, if there is a better way to do this for arbitrary order tensors. Thanks!

% Frobenius norm
function out = nf(a)
  out = sqrt(a(:)'*a(:));
endfunction

% Tests for (m,n)
% (1,1)
disp("(1,1)")
a = rand(4,1);
b = rand(7,1);
c1 = tp(a,b);
c2 = zeros(4,7);
for i1=1:4 for i2=1:7
  c2(i1,i2) = a(i1)*b(i2);
endfor endfor
size(c1)
size(c2)
nf(c1-c2)

%(1,2)
disp("(1,2)")
a = rand(4,1);
b = rand(7,3);
c1 = tp(a,b);
c2 = zeros(4,7,3);
for i1=1:4 for i2=1:7 for i3=1:3
  c2(i1,i2,i3) = a(i1)*b(i2,i3);
endfor endfor endfor
size(c1)
size(c2)
nf(c1-c2)

%(2,1)
disp("(2,1)")
a = rand(4,2);
b = rand(1,3);
c1 = tp(a,b);
c2 = zeros(4,2,3);
for i1=1:4 for i2=1:2 for i3=1:3
  c2(i1,i2,i3) = a(i1,i2)*b(i3);
endfor endfor endfor
size(c1)
size(c2)
nf(c1-c2)

%(3,2)
disp("(3,2)")
a = rand(4,2,5);
b = rand(7,3);
c1 = tp(a,b);
c2 = zeros(4,2,5,7,3);
for i1=1:4 for i2=1:2 for i3=1:5 for i4=1:7 for i5=1:3
  c2(i1,i2,i3,i4,i5) = a(i1,i2,i3)*b(i4,i5);
endfor endfor endfor endfor endfor
size(c1)
size(c2)
nf(c1-c2)

EDIT

I took a look at the tensorlab package, see Metahominid's answer below, and it is fantastic. Just for curiosity, I wanted to check the time performance between my implementation, Andras Deak's implementation (see his answer below) and the tensorlab package.

% See Andras Deak answer
function c=tensorprod(a, b)
   b_inj = reshape(b, [ones(1,ndims(a)), size(b)]);
   c = a.*b_inj;
end

% Tests
a = rand(10,11,12);
b = rand(9,8,7);
tic; c1=outprod(a,b); t1=toc % tensorlab, see Metahominid's answer
tic; c2=tp(a,b); t2=toc % my approach
tic; c3=tensorprod(a,b); t3=toc % Andras Deak's approach
disp("Check size")
size(c1)
size(c2)
size(c3)
disp("Check Frobenius norm")
frob(c1) % from tensorlab
nf(c2)
disp("Check equality of elements")
nf(c1-c2)
nf(c1-c3)
disp("Compare time performance relative to tp(a,b)")
t1/t2
t3/t2

The ratio of the computation time t1 of the tensorlab implementation of outprod (corresponding to my tp) and t2 for tp is for the considered dimensions around 2-4 (at least on my computer). This is surely the case, since in my implementation I dont check for any errors in the input and dont catch any undefined cases. Almost the same is observed for t3/t2 comparing Andras Deak's approach to mine. Please, don't get me wrong, I am not trying to brag, but to give some final remarks for people who might be interested in this. Conclusion: if you need something for small sized tensors just to-go, my simple implementation might be useful for you, if you need more stuff, you should definitely take a look at tensorlab (see Metahominid's answer below). Thanks for the answers and references!


回答1:


There is a something called Tensorlab, which for all I can tell is usable with Octave.. You simply have to download it by getting a link.

Edit: It has both of these and will be vastly faster.

[H,Heff] = hankelize(linspace(0,1,1000),'order',3);
tic; disp(frob(H)); toc; % Using the dense tensor H
tic; disp(frob(Heff)); toc; % Using the efficient representation of H
3.2181e+03
Elapsed time is 0.026401 seconds.
3.2181e+03
Elapsed time is 0.000832 seconds.

outprod(T1,T2)

is the other command you want.




回答2:


What you have is a generalized Kronecker product. The definition lends itself perfectly to implement using array broadcasting in Octave. For this you only need to inject as many leading singleton dimensions into one of your arrays as the dimensions of the other array. This is sufficient since every array has an infinite number of implicit trailing dimensions.

function c=tensorprod(a, b)
   b_inj = reshape(b, [ones(1,ndims(a)), size(b)]);
   c = a.*b_inj;
end

If a has size (i,j,k) and b has (m,n), b_inj has size (1,1,1,m,n), and a is already implicitly compatible with size (i,j,k,1,1). So multiplying these two arrays elementwise gives you the desired result.

Proof that it should work the way you want it to:

octave:29> a = rand(2,3);
octave:30> b = rand(4,5);
octave:31> c = tensorprod(a,b);
octave:32> size(c)
ans =

   2   3   4   5

octave:33> c(1,3,2,3) == a(1,3)*b(2,3) % indices chosen by fair dice roll
ans =  1

If you want to handle vectors differently (i.e. you want the tensor product of two vectors to be a 2d matrix), you need to handle that special-case yourself, due to the way how Octave handles vectors as row/column matrices. This is only a trivial complication though.



来源:https://stackoverflow.com/questions/50679149/how-to-implement-tensor-product-for-arbitrary-order-tensors-in-octave

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!