Partial Reconstruction of a Tucker Tensor

Contents

Benefits of Partial Reconstruction

An advantage of Tucker decomposition is that the tensor can be partially reconstructed without ever forming the full tensor. The reconstruct function does this, resulting in significant time and memory savings, as we demonstrate below.

Load Miranda density tensor

The Miranda data is available at https://gitlab.com/tensors/tensor_data_miranda_sim. It loads a tensor X of size 384 x 384 x 256.

load('../../tensor_data_miranda_sim/density.mat');
size(X)
ans =

   384   384   256

Compute HOSVD

We compute the Tucker decomposition using ST-HOSVD with target relative error 0.001.

tic
T = hosvd(X,0.001);
hosvdtime = toc;
fprintf('Time to compute HOSVD: %0.2f sec\n', hosvdtime);
fprintf('\tCompression: %d X\n', round(whos('X').bytes/whos('T').bytes));
Computing HOSVD...
Size of core: 101 x 102 x 26
||X-T||/||X|| = 0.000932816 <=0.001000 (tol)

Time to compute HOSVD: 0.88 sec
	Compression: 107 X

Full reconstruction

We can create a full reconstruction of the data using the full command. Not only is this expensive in computational time but also in memory. Now, let's see how long it takes to reconstruct the approximation to X.

tic
Xf = full(T);
fulltime = toc;
fprintf('Time to reconstruct entire tensor: %0.2f sec\n', fulltime);
Time to reconstruct entire tensor: 0.16 sec

Partial reconstruction

If we really only want part of the tensor, we can reconstruct just that part. Suppose we only want the (:,150,:) slice. The reconstruct function can do this much more efficiently with no loss in accuracy.

tic
Xslice = reconstruct(T,2,150);
slicetime = toc;
fprintf('Time to reconstruct specific slice: %0.2f sec\n', slicetime);
fprintf('\tSpeedup compared to full recontruction: %d X\n', round(fulltime/slicetime));
fprintf('\tMemory savings versus full reconstruction: %d X\n',...
    round(whos('Xf').bytes/whos('Xslice').bytes));
fprintf('\tRel. error versus full reconstruction: %g\n',norm(squeeze(Xslice)-Xf(:,150,:))/norm(squeeze(Xslice)));
Time to reconstruct specific slice: 0.01 sec
	Speedup compared to full recontruction: 14 X
	Memory savings versus full reconstruction: 384 X
	Rel. error versus full reconstruction: 6.60045e-16

Down-sampling

Additionally, we may want to downsample high-dimensional data to something lower resolution. For example, here we downsample in modes 1 and 2 by a factor of 2 and see even further speed-up and memory savings. There is no loss of accuarcy as compared to downsampling after constructing the full tensor.

S1 = kron(eye(384/2),0.5*ones(1,2));
S3 = kron(eye(256/2),0.5*ones(1,2));
tic
Xds = reconstruct(T,1,S1,2,150,3,S3);
downslicetime = toc;
fprintf('Time to reconstruct downsampled slice: %0.2f sec\n', downslicetime);
fprintf('\tSpeedup compared to full recontruction: %d X\n', round(fulltime/downslicetime));
fprintf('\tMemory savings versus full reconstruction: %d X\n',...
    round(whos('Xf').bytes/whos('Xds').bytes));
Time to reconstruct downsampled slice: 0.01 sec
	Speedup compared to full recontruction: 29 X
	Memory savings versus full reconstruction: 1533 X

Compare visualizations

We can compare the results of reconstruction. There is no degredation in doing only a partial reconstruction. Downsampling is obviously lower resolution, but the same result as first doing the full reconstruction and then downsampling.

figure(1);

subplot(2,2,1)
imagesc(rot90(squeeze(double(X(:,150,:)))),[1 3])
axis equal
axis off
colormap("jet")
title('Original Slice')

subplot(2,2,2)
imagesc(rot90(squeeze(double(Xf(:,150,:)))),[1 3])
axis equal
axis off
title('Full Reconstruction')

subplot(2,2,3)
imagesc(rot90(squeeze(double(Xslice))),[1 3])
axis equal
axis off
title('Partial Reconstruction')
xl = xlim;
yl = ylim;

subplot(2,2,4)
imagesc(rot90(squeeze(double(Xds))),[1 3])
xlim(xl);
ylim(yl);
axis equal
axis off
title('Partial+Downsampled Reconstruction')