Commit c943e18b authored by Tammy Kolda's avatar Tammy Kolda
Browse files

Merge branch 'scale_generalize' into 'master'

Generalize scale to handle also shift (e.g., subtracting out the mean) and use bsxfun.

See merge request !62
parents 7f6299ca 2ccf655a
Pipeline #340255832 passed with stage
in 16 seconds
function Y = scale(X,S,dims)
function Y = scale(X,S,dims,func)
%SCALE Scale along specified dimensions of tensor.
%
% Y = SCALE(X,S,DIMS) scales the tensor X along the dimension(s)
......@@ -6,6 +6,13 @@ function Y = scale(X,S,dims)
% only one dimension, then S can be a column vector. Otherwise, S
% should be a tensor.
%
% Y = SCALE(X,S,DIMS,FN) modifies the tensor X along the dimension(s)
% specified in DIMS using the data in S and the function specified by FN.
% The function FN should take two arguments, the first being the tensor
% data and the second being the scaling data, to be combined using
% bsxfun. If DIMS contains only one dimension, then S can be a column
% vector. Otherwise, S should be a tensor.
%
% Examples
% X = tenones([3,4,5]);
% S = 10 * [1:5]'; Y = scale(X,S,3)
......@@ -14,17 +21,29 @@ function Y = scale(X,S,dims)
% S = tensor(1:12,[3 4]); Y = scale(X,S,-3)
% S = tensor(1:60,[3 4 5]); Y = scale(X,S,1:3)
%
% X = tensor(1:24,[4 3 2]); %<-- Generate some data.
% mu = collapse(X,2,@mean); %<-- Calculate means of mode-2 fibers
% Y = scale(X,mu,[1 3],@(x,y) x-y); %<-- Center mode-2 fibers
% mu_new = collapse(Y,2,@mean) %<-- Mode-2 fibers have mean zero
%
% <a href="matlab:web(strcat('file://',fullfile(getfield(what('tensor_toolbox'),'path'),'doc','html','collapse_scale_doc.html')))">Documentation page for collapsing and scaling tensors</a>
%
% See also TENSOR, TENSOR/COLLAPSE.
%
%Tensor Toolbox for MATLAB: <a href="https://www.tensortoolbox.org">www.tensortoolbox.org</a>
dims = tt_dimscheck(dims,ndims(X));
remdims = setdiff(1:ndims(X),dims);
if ~exist('func','var')
ver = 2; % New version
func = @times; % Default function
elseif isa(func,'function_handle')
ver = 2; % New version with user-provided function
else
ver = 1; % Explicitly use old version, useful for testing
end
% Convert to a matrix so that each column of A can be scaled by a
% vectorized version of S.
A = double(tenmat(X,dims,remdims));
......@@ -48,18 +67,22 @@ end
% If the size of S is pretty small, we can convert it to a diagonal matrix
% and multiply by A. Otherwise, we scale A column-by-column.
if (m <= n)
B = diag(S) * A;
else
B = zeros(size(A));
for j = 1:n
B(:,j) = S .* A(:,j);
if ver == 1
if (m <= n)
B = diag(S) * A;
else
B = zeros(size(A));
for j = 1:n
B(:,j) = S .* A(:,j);
end
end
else % ver == 2
B = bsxfun(func,A,S);
end
% Convert the matrix B back into a tensor and return.
Y = tensor(tenmat(B,dims,remdims,X.size));
......@@ -16,6 +16,8 @@ Release notes follow below.
- Changed [`cp_wopt`](cp_wopt.m) to be able to properly zero out NaN's in data tensor. Prior version didn't work even when weight tensor had zeros for the missing data entries because 0 * NaN = NaN.
- Added ability to do shifting via the tensor [`scale`](@tensor/scale.m) function. Also changed this function to use `bsxfun` which should be generally more efficient.
## Changes from Version 3.2 (February 10, 2021)
- Changes to [`export_data`](export_data.m): support formatting of data and lambda values, improved write times for ['sptensor'](@sptensor/) when exporting (fixes #51)
......
......@@ -9,9 +9,11 @@
% </html>
%
% The |tensor| and |sptensor| classes support the notion of collapsing and
% scaling dimensions.
% scaling dimensions, and the |tensor| class also supports centering (see
% below).
%% Examples of collapsing a tensor
rng('default'); %<-- Make this reproducible.
X = tenrand([4 3 2]) %<-- Generate some data.
%%
Y = collapse(X,[2 3]) %<-- Sum of entries in each mode-1 slice.
......@@ -35,6 +37,22 @@ Y = collapse(X,-1) %<-- Same as above.
Z = collapse(X,2) %<-- Sum of entries in each row fiber.
%%
collapse(X,1:3) %<-- Sum of all entries.
%% Center tensor fibers for a dense tensor.
% Suppose that we want to center a tensor's fiber in mode-2 so
% that they each have mean zero. To do so, we can use |scale| function in
% an unusual way, passing it a function handle to do the differences.
% (Note that |sptensor| does not support centering because it would cause
% the tensor to become dense.)
X = tensor(1:24,[4 3 2]) %<-- Generate some data.
%%
% Calculate the means in mode 2
mu = collapse(X,2,@mean)
%%
% Show the means of the mode-2 fibers
Y = scale(X,mu,[1 3],@(x,y) x-y);
mu_new = collapse(Y,2,@mean)
%% Alternate accumulation functions for sptensor
Y = collapse(X,[1 2],@min) %<-- Min *nonzero* entry in each mode-3 slice.
%%
......
......@@ -6,7 +6,7 @@
<!--
This HTML was auto-generated from MATLAB code.
To make changes, update the MATLAB code and republish this document.
--><title>Collapsing and Scaling Tensors</title><meta name="generator" content="MATLAB 9.4"><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/"><meta name="DC.date" content="2021-01-12"><meta name="DC.source" content="collapse_scale_doc.m"><style type="text/css">
--><title>Collapsing and Scaling Tensors</title><meta name="generator" content="MATLAB 9.9"><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/"><meta name="DC.date" content="2021-07-20"><meta name="DC.source" content="collapse_scale_doc.m"><style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}:focus{outine:0}ins{text-decoration:none}del{text-decoration:line-through}table{border-collapse:collapse;border-spacing:0}
html { min-height:90%; margin-bottom:1px; }
......@@ -74,109 +74,133 @@ table td { padding:7px 5px; text-align:left; vertical-align:top; border:1px soli
&#62;&#62; <a href="working.html">Working with Tensors</a>
&#62;&#62; <a href="collapse_scale_doc.html">Collapsing and Scaling Tensors</a>
</p>
</p><p>The <tt>tensor</tt> and <tt>sptensor</tt> classes support the notion of collapsing and scaling dimensions.</p><!--/introduction--><h2>Contents</h2><div><ul><li><a href="#1">Examples of collapsing a tensor</a></li><li><a href="#6">Alternate accumulation functions for tensor</a></li><li><a href="#8">Examples of collapsing a sptensor</a></li><li><a href="#13">Alternate accumulation functions for sptensor</a></li><li><a href="#15">Scaling a tensor in different modes</a></li><li><a href="#21">Scaling a sptensor in different modes</a></li></ul></div><h2 id="1">Examples of collapsing a tensor</h2><pre class="codeinput">X = tenrand([4 3 2]) <span class="comment">%&lt;-- Generate some data.</span>
</p><p>The <tt>tensor</tt> and <tt>sptensor</tt> classes support the notion of collapsing and scaling dimensions, and the <tt>tensor</tt> class also supports centering (see below).</p><!--/introduction--><h2>Contents</h2><div><ul><li><a href="#1">Examples of collapsing a tensor</a></li><li><a href="#6">Alternate accumulation functions for tensor</a></li><li><a href="#8">Examples of collapsing a sptensor</a></li><li><a href="#13">Center tensor fibers for a dense tensor.</a></li><li><a href="#16">Alternate accumulation functions for sptensor</a></li><li><a href="#18">Scaling a tensor in different modes</a></li><li><a href="#24">Scaling a sptensor in different modes</a></li></ul></div><h2 id="1">Examples of collapsing a tensor</h2><pre class="codeinput">rng(<span class="string">'default'</span>); <span class="comment">%&lt;-- Make this reproducible.</span>
X = tenrand([4 3 2]) <span class="comment">%&lt;-- Generate some data.</span>
</pre><pre class="codeoutput">X is a tensor of size 4 x 3 x 2
X(:,:,1) =
0.5700 0.9278 0.8453
0.8867 0.6551 0.3683
0.9025 0.2050 0.1861
0.9622 0.2478 0.5658
0.8147 0.6324 0.9575
0.9058 0.0975 0.9649
0.1270 0.2785 0.1576
0.9134 0.5469 0.9706
X(:,:,2) =
0.6551 0.6102 0.2893
0.8380 0.8876 0.5269
0.0822 0.3187 0.7263
0.0506 0.5006 0.4659
0.9572 0.4218 0.6557
0.4854 0.9157 0.0357
0.8003 0.7922 0.8491
0.1419 0.9595 0.9340
</pre><pre class="codeinput">Y = collapse(X,[2 3]) <span class="comment">%&lt;-- Sum of entries in each mode-1 slice.</span>
</pre><pre class="codeoutput">Y is a tensor of size 4
Y(:) =
3.8977
4.1626
2.4208
2.7929
4.4393
3.4050
3.0047
4.4662
</pre><pre class="codeinput">Y = collapse(X,-1) <span class="comment">%&lt;-- Same as above.</span>
</pre><pre class="codeoutput">Y is a tensor of size 4
Y(:) =
3.8977
4.1626
2.4208
2.7929
4.4393
3.4050
3.0047
4.4662
</pre><pre class="codeinput">Z = collapse(X,2) <span class="comment">%&lt;-- Sum of entries in each row fiber.</span>
</pre><pre class="codeoutput">Z is a tensor of size 4 x 2
Z(:,:) =
2.3430 1.5546
1.9101 2.2525
1.2936 1.1272
1.7758 1.0171
2.4046 2.0347
1.9682 1.4368
0.5631 2.4416
2.4309 2.0354
</pre><pre class="codeinput">collapse(X,1:3) <span class="comment">%&lt;-- Sum of all entries.</span>
</pre><pre class="codeoutput">
ans =
13.2740
15.3152
</pre><h2 id="6">Alternate accumulation functions for tensor</h2><pre class="codeinput">Y = collapse(X,[1 2],@max) <span class="comment">%&lt;-- Max entry in each mode-3 slice.</span>
</pre><pre class="codeoutput">Y is a tensor of size 2
Y(:) =
0.9622
0.8876
0.9706
0.9595
</pre><pre class="codeinput">Z = collapse(X,-3,@mean) <span class="comment">%&lt;-- Average entry in each mode-3 slice.</span>
</pre><pre class="codeoutput">Z is a tensor of size 2
Z(:) =
0.6102
0.4960
0.6139
0.6624
</pre><h2 id="8">Examples of collapsing a sptensor</h2><pre class="codeinput">X = sptenrand([4 3 2],6) <span class="comment">%&lt;-- Generate some data.</span>
</pre><pre class="codeoutput">X is a sparse tensor of size 4 x 3 x 2 with 6 nonzeros
(1,1,2) 0.9194
(1,3,1) 0.9742
(3,1,1) 0.9788
(4,2,1) 0.4579
(4,3,1) 0.1540
(4,3,2) 0.9585
(1,3,1) 0.7655
(2,1,1) 0.7952
(3,1,1) 0.1869
(3,1,2) 0.4898
(3,3,2) 0.4456
(4,1,1) 0.6463
</pre><pre class="codeinput">Y = collapse(X,[2 3]) <span class="comment">%&lt;-- Sum of entries in each mode-1 slice.</span>
</pre><pre class="codeoutput">
Y =
1.8936
0
0.9788
1.5704
0.7655
0.7952
1.1222
0.6463
</pre><pre class="codeinput">Y = collapse(X,-1) <span class="comment">%&lt;-- Same as above.</span>
</pre><pre class="codeoutput">
Y =
1.8936
0
0.9788
1.5704
0.7655
0.7952
1.1222
0.6463
</pre><pre class="codeinput">Z = collapse(X,2) <span class="comment">%&lt;-- Sum of entries in each row fiber.</span>
</pre><pre class="codeoutput">Z is a sparse tensor of size 4 x 2 with 5 nonzeros
(1,1) 0.9742
(1,2) 0.9194
(3,1) 0.9788
(4,1) 0.6119
(4,2) 0.9585
(1,1) 0.7655
(2,1) 0.7952
(3,1) 0.1869
(3,2) 0.9354
(4,1) 0.6463
</pre><pre class="codeinput">collapse(X,1:3) <span class="comment">%&lt;-- Sum of all entries.</span>
</pre><pre class="codeoutput">
ans =
4.4428
</pre><h2 id="13">Alternate accumulation functions for sptensor</h2><pre class="codeinput">Y = collapse(X,[1 2],@min) <span class="comment">%&lt;-- Min *nonzero* entry in each mode-3 slice.</span>
</pre><pre class="codeoutput">
Y =
0.1540
0.9194
3.3293
</pre><h2 id="13">Center tensor fibers for a dense tensor.</h2><p>Suppose that we want to center a tensor's fiber in mode-2 so that they each have mean zero. To do so, we can use <tt>scale</tt> function in an unusual way, passing it a function handle to do the differences. (Note that <tt>sptensor</tt> does not support centering because it would cause the tensor to become dense.)</p><pre class="codeinput">X = tensor(1:24,[4 3 2]) <span class="comment">%&lt;-- Generate some data.</span>
</pre><pre class="codeoutput">X is a tensor of size 4 x 3 x 2
X(:,:,1) =
1 5 9
2 6 10
3 7 11
4 8 12
X(:,:,2) =
13 17 21
14 18 22
15 19 23
16 20 24
</pre><p>Calculate the means in mode 2</p><pre class="codeinput">mu = collapse(X,2,@mean)
</pre><pre class="codeoutput">mu is a tensor of size 4 x 2
mu(:,:) =
5 17
6 18
7 19
8 20
</pre><p>Show the means of the mode-2 fibers</p><pre class="codeinput">Y = scale(X,mu,[1 3],@(x,y) x-y);
mu_new = collapse(Y,2,@mean)
</pre><pre class="codeoutput">mu_new is a tensor of size 4 x 2
mu_new(:,:) =
0 0
0 0
0 0
0 0
</pre><h2 id="16">Alternate accumulation functions for sptensor</h2><pre class="codeinput">Y = collapse(X,[1 2],@min) <span class="comment">%&lt;-- Min *nonzero* entry in each mode-3 slice.</span>
</pre><pre class="codeoutput">Y is a tensor of size 2
Y(:) =
1
13
</pre><pre class="codeinput">Z = collapse(X,-3,@mean) <span class="comment">%&lt;-- Average *nonzero* entry in each mode-3 slice.</span>
</pre><pre class="codeoutput">
Z =
0.6412
0.9389
</pre><h2 id="15">Scaling a tensor in different modes</h2><pre class="codeinput">X = tenones([3,4,5]); <span class="comment">%&lt;-- Generate data</span>
</pre><pre class="codeoutput">Z is a tensor of size 2
Z(:) =
6.5000
18.5000
</pre><h2 id="18">Scaling a tensor in different modes</h2><pre class="codeinput">X = tenones([3,4,5]); <span class="comment">%&lt;-- Generate data</span>
S = 10 * [1:5]'; Y = scale(X,S,3) <span class="comment">%&lt;-- Scale in mode-3</span>
</pre><pre class="codeoutput">Y is a tensor of size 3 x 4 x 5
Y(:,:,1) =
......@@ -309,90 +333,90 @@ S = 10 * [1:5]'; Y = scale(X,S,3) <span class="comment">%&lt;-- Scale in mode-3<
49 52 55 58
50 53 56 59
51 54 57 60
</pre><h2 id="21">Scaling a sptensor in different modes</h2><pre class="codeinput">X = ones(sptenrand([3 4 5], 10)) <span class="comment">%&lt;-- Generate data.</span>
</pre><h2 id="24">Scaling a sptensor in different modes</h2><pre class="codeinput">X = ones(sptenrand([3 4 5], 10)) <span class="comment">%&lt;-- Generate data.</span>
</pre><pre class="codeoutput">X is a sparse tensor of size 3 x 4 x 5 with 10 nonzeros
(1,3,1) 1
(1,3,5) 1
(2,1,2) 1
(2,2,1) 1
(2,3,3) 1
(3,2,1) 1
(3,2,4) 1
(3,3,4) 1
(3,4,3) 1
(3,4,5) 1
(1,1,1) 1
(1,4,1) 1
(1,4,5) 1
(2,1,3) 1
(2,1,5) 1
(3,1,2) 1
(3,2,3) 1
(3,2,5) 1
(3,4,1) 1
(3,4,2) 1
</pre><pre class="codeinput">S = 10 * [1:5]'; Y = scale(X,S,3) <span class="comment">%&lt;-- Scale in one mode.</span>
</pre><pre class="codeoutput">Y is a sparse tensor of size 3 x 4 x 5 with 10 nonzeros
(1,3,1) 10
(1,3,5) 50
(2,1,2) 20
(2,2,1) 10
(2,3,3) 30
(3,2,1) 10
(3,2,4) 40
(3,3,4) 40
(3,4,3) 30
(3,4,5) 50
(1,1,1) 10
(1,4,1) 10
(1,4,5) 50
(2,1,3) 30
(2,1,5) 50
(3,1,2) 20
(3,2,3) 30
(3,2,5) 50
(3,4,1) 10
(3,4,2) 20
</pre><pre class="codeinput">S = tensor(10 * [1:5]',5); Y = scale(X,S,3) <span class="comment">%&lt;-- Same as above.</span>
</pre><pre class="codeoutput">Y is a sparse tensor of size 3 x 4 x 5 with 10 nonzeros
(1,3,1) 10
(1,3,5) 50
(2,1,2) 20
(2,2,1) 10
(2,3,3) 30
(3,2,1) 10
(3,2,4) 40
(3,3,4) 40
(3,4,3) 30
(3,4,5) 50
(1,1,1) 10
(1,4,1) 10
(1,4,5) 50
(2,1,3) 30
(2,1,5) 50
(3,1,2) 20
(3,2,3) 30
(3,2,5) 50
(3,4,1) 10
(3,4,2) 20
</pre><pre class="codeinput">S = tensor(1:12,[3 4]); Y = scale(X,S,[1 2]) <span class="comment">%&lt;-- Scale in two modes.</span>
</pre><pre class="codeoutput">Y is a sparse tensor of size 3 x 4 x 5 with 10 nonzeros
(1,3,1) 7
(1,3,5) 7
(2,1,2) 2
(2,2,1) 5
(2,3,3) 8
(3,2,1) 6
(3,2,4) 6
(3,3,4) 9
(3,4,3) 12
(3,4,5) 12
(1,1,1) 1
(1,4,1) 10
(1,4,5) 10
(2,1,3) 2
(2,1,5) 2
(3,1,2) 3
(3,2,3) 6
(3,2,5) 6
(3,4,1) 12
(3,4,2) 12
</pre><pre class="codeinput">S = tensor(1:12,[3 4]); Y = scale(X,S,-3) <span class="comment">%&lt;-- Same as above.</span>
</pre><pre class="codeoutput">Y is a sparse tensor of size 3 x 4 x 5 with 10 nonzeros
(1,3,1) 7
(1,3,5) 7
(2,1,2) 2
(2,2,1) 5
(2,3,3) 8
(3,2,1) 6
(3,2,4) 6
(3,3,4) 9
(3,4,3) 12
(3,4,5) 12
(1,1,1) 1
(1,4,1) 10
(1,4,5) 10
(2,1,3) 2
(2,1,5) 2
(3,1,2) 3
(3,2,3) 6
(3,2,5) 6
(3,4,1) 12
(3,4,2) 12
</pre><pre class="codeinput">Z = scale(X,Y,1:3) <span class="comment">%&lt;-- Scale by a sparse tensor.</span>
</pre><pre class="codeoutput">Z is a sparse tensor of size 3 x 4 x 5 with 10 nonzeros
(1,3,1) 7
(1,3,5) 7
(2,1,2) 2
(2,2,1) 5
(2,3,3) 8
(3,2,1) 6
(3,2,4) 6
(3,3,4) 9
(3,4,3) 12
(3,4,5) 12
(1,1,1) 1
(1,4,1) 10
(1,4,5) 10
(2,1,3) 2
(2,1,5) 2
(3,1,2) 3
(3,2,3) 6
(3,2,5) 6
(3,4,1) 12
(3,4,2) 12
</pre><pre class="codeinput">X .* Y <span class="comment">%&lt;-- Same as above.</span>
</pre><pre class="codeoutput">ans is a sparse tensor of size 3 x 4 x 5 with 10 nonzeros
(1,3,1) 7
(1,3,5) 7
(2,1,2) 2
(2,2,1) 5
(2,3,3) 8
(3,2,1) 6
(3,2,4) 6
(3,3,4) 9
(3,4,3) 12
(3,4,5) 12
(1,1,1) 1
(1,4,1) 10
(1,4,5) 10
(2,1,3) 2
(2,1,5) 2
(3,1,2) 3
(3,2,3) 6
(3,2,5) 6
(3,4,1) 12
(3,4,2) 12
</pre><p class="footer">Tensor Toolbox for MATLAB: <a href="index.html">www.tensortoolbox.org</a>.</p></div><!--
##### SOURCE BEGIN #####
%% Collapsing and Scaling Tensors
......@@ -406,9 +430,11 @@ S = 10 * [1:5]'; Y = scale(X,S,3) <span class="comment">%&lt;-- Scale in mode-3<
% </html>
%
% The |tensor| and |sptensor| classes support the notion of collapsing and
% scaling dimensions.
% scaling dimensions, and the |tensor| class also supports centering (see
% below).
%% Examples of collapsing a tensor
rng('default'); %<REPLACE_WITH_DASH_DASH Make this reproducible.
X = tenrand([4 3 2]) %<REPLACE_WITH_DASH_DASH Generate some data.
%%
Y = collapse(X,[2 3]) %<REPLACE_WITH_DASH_DASH Sum of entries in each mode-1 slice.
......@@ -432,6 +458,22 @@ Y = collapse(X,-1) %<REPLACE_WITH_DASH_DASH Same as above.
Z = collapse(X,2) %<REPLACE_WITH_DASH_DASH Sum of entries in each row fiber.
%%
collapse(X,1:3) %<REPLACE_WITH_DASH_DASH Sum of all entries.
%% Center tensor fibers for a dense tensor.
% Suppose that we want to center a tensor's fiber in mode-2 so
% that they each have mean zero. To do so, we can use |scale| function in
% an unusual way, passing it a function handle to do the differences.
% (Note that |sptensor| does not support centering because it would cause
% the tensor to become dense.)
X = tensor(1:24,[4 3 2]) %<REPLACE_WITH_DASH_DASH Generate some data.
%%
% Calculate the means in mode 2
mu = collapse(X,2,@mean)
%%
% Show the means of the mode-2 fibers
Y = scale(X,mu,[1 3],@(x,y) x-y);
mu_new = collapse(Y,2,@mean)
%% Alternate accumulation functions for sptensor
Y = collapse(X,[1 2],@min) %<REPLACE_WITH_DASH_DASH Min *nonzero* entry in each mode-3 slice.
%%
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment