问题
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