-
Notifications
You must be signed in to change notification settings - Fork 21
/
cp_wopt.m
386 lines (341 loc) · 12.4 KB
/
cp_wopt.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
function [P, P0, output] = cp_wopt(Z,W,R,varargin)
%CP_WOPT Fits a weighted CP model to a tensor via optimization.
%
% K = CP_WOPT(X,W,R) fits an R-component weighted CANDECOMP/PARAFAC
% (CP) model to the tensor X, where W is an indicator for missing
% data (0 = missing, 1 = present). The result K is a ktensor. It is
% assumed that missing entries of X have been sent to zero (but not
% that all zeros correspond to missing entries.) The function being
% optimized is F(K) = 1/2 || W .* (X - K) ||^2.
%
% K = CP_WOPT(X,W,R,'param', value,...) specifies additional
% parameters for the method. Specifically...
%
% 'alg' - Specfies optimization algorithm (default: 'ncg')
% 'ncg' Nonlinear Conjugate Gradient Method
% 'lbfgs' Limited-Memory BFGS Method
% 'tn' Truncated Newton
%
% 'init' - Initialization for factor matrices. (default:
% 'random'). This can be a cell array with the initial matrices or
% one of the following strings:
% 'random' Randomly generated via randn function
% 'nvecs' Selected as leading left singular vectors of X(n)
%
% 'alg_options' - Parameter settings for selected optimization
% algorithm. For example, type OPTIONS = NCG('defaults') to get
% the NCG algorithm options which can then me modified as passed
% through this function to NCG.
%
% 'fun' - Specifies the type of implementation (default: 'auto')
% 'auto' Dense implementation
% 'sparse' Sparse implementation
% 'sparse_lowmem' Memory efficient sparse implementation
%
% [K, U0] = CP_WOPT(...) also returns the initial guess.
%
% [K, U0, OUT] = CP_WOPT(...) also returns a structure with the
% optimization exit flag, the final relative fit, and the full
% output from the optimization method.
%
% REFERENCE: E. Acar, D. M. Dunlavy, T. G. Kolda and M. Mørup, Scalable
% Tensor Factorizations for Incomplete Data, Chemometrics and Intelligent
% Laboratory Systems 106(1):41-56, March 2011
% (doi:10.1016/j.chemolab.2010.08.004)
%
% See also CP_OPT.
%
%MATLAB Tensor Toolbox.
%Copyright 2015, Sandia Corporation.
% This is the MATLAB Tensor Toolbox by T. Kolda, B. Bader, and others.
% http://www.sandia.gov/~tgkolda/TensorToolbox.
% Copyright (2015) Sandia Corporation. Under the terms of Contract
% DE-AC04-94AL85000, there is a non-exclusive license for use of this
% work by or on behalf of the U.S. Government. Export of this data may
% require a license from the United States Government.
% The full license terms can be found in the file LICENSE.txt
%% Check for POBLANO
if ~exist('poblano_params','file')
error(['CP_WOPT requires Poblano Toolbox for Matlab. This can be ' ...
'downloaded at http://software.sandia.gov/trac/poblano.']);
end
%% Set parameters
params = inputParser;
params.addParameter('alg','ncg', @(x) ismember(x,{'ncg','tn','lbfgs'}));
params.addParameter('init','random', @(x) (iscell(x) || ismember(x,{'random','nvecs'}))); %#ok<*NVREPL>
params.addParameter('fun','auto', @(x) ismember(x,{'auto','default','sparse','sparse_lowmem'}));
params.addParameter('alg_options', '', @isstruct);
params.parse(varargin{:});
%% Set up optimization algorithm
switch (params.Results.alg)
case 'ncg'
opthandle = @ncg;
case 'tn'
opthandle = @tn;
case 'lbfgs'
opthandle = @lbfgs;
end
%% Set up optimization algorithm options
if isempty(params.Results.alg_options)
options = feval(opthandle, 'defaults');
else
options = params.Results.alg_options;
end
%% Set up function handle
normZsqr = norm(Z)^2;
funtype = params.Results.fun;
if (isequal(funtype,'auto') && isa(Z,'tensor')) || isequal(funtype,'default')
funhandle = @(x) tt_cp_wfun(Z,W,x,normZsqr);
else
if ~isa(Z,'sptensor') || ~isa(W,'sptensor')
warning('Converting dense tensor to sparse');
Z = sptensor(Z);
W = sptensor(W);
end
Zvals = tt_cp_wfg_sparse_setup(Z,W);
fflag = ~isequal(funtype,'sparse_lowmem');
funhandle = @(x) tt_cp_wfun(Zvals,W,x,normZsqr,fflag);
end
%% Initial guess
sz = size(Z);
N = length(sz);
if iscell(params.Results.init)
P0 = params.Results.init;
elseif strcmpi(params.Results.init,'random')
P0 = cell(N,1);
for n=1:N
P0{n} = randn(sz(n),R);
for j=1:R
P0{n}(:,j) = P0{n}(:,j) / norm(P0{n}(:,j));
end
end
elseif strcmpi(params.Results.init,'nvecs')
P0 = cell(N,1);
for n=1:N
P0{n} = nvecs(Z,n,R);
end
else
error('Initialization type not supported')
end
%% Fit CP using CP_WOPT by ignoring missing entries
out = feval(opthandle, funhandle, tt_fac_to_vec(P0), options);
P = ktensor(tt_cp_vec_to_fac(out.X,Z));
if nargout > 1
output.ExitFlag = out.ExitFlag;
output.FuncEvals = out.FuncEvals;
output.f = out.F;
output.G = tt_cp_vec_to_fac(out.G,W);
output.OptOut = out;
end
%% Clean up final result
% Arrange the final tensor so that the columns are normalized.
P = arrange(P);
% Fix the signs
P = fixsigns(P);
function [f,G] = tt_cp_wfg(Z,W,A,normZsqr)
%TT_CP_WFG Function and gradient of CP with missing data.
%
% [F,G] = TT_CP_WFG(Z,W,A) computes the function and gradient values of
% the function 0.5 * || W .* (Z - ktensor(A)) ||^2. The input A is a
% cell array containing the factor matrices. The input W is a (dense
% or sparse) tensor containing zeros wherever data is missing. The
% input Z is a (dense or sparse) tensor that is assumed to have
% zeros wherever there is missing data. The output is the function F
% and a cell array G containing the partial derivatives with respect
% to the factor matrices.
%
% [F,G] = TT_CP_WFG(Z,W,A,NORMZSQR) also passes in the pre-computed
% norm of Z, which makes the computations faster.
%
% See also TT_CP_WFUN, TT_CP_WFG_SPARSE, TT_CP_WFG_SPARSE_SETUP.
%
%MATLAB Tensor Toolbox.
%Copyright 2015, Sandia Corporation.
% This is the MATLAB Tensor Toolbox by T. Kolda, B. Bader, and others.
% http://www.sandia.gov/~tgkolda/TensorToolbox.
% Copyright (2015) Sandia Corporation. Under the terms of Contract
% DE-AC04-94AL85000, there is a non-exclusive license for use of this
% work by or on behalf of the U.S. Government. Export of this data may
% require a license from the United States Government.
% The full license terms can be found in the file LICENSE.txt
%% Compute B = W.*ktensor(A)
if isa(W,'sptensor')
B = W.*ktensor(A);
else
B = W.*full(ktensor(A));
end
%% Compute normZ
if ~exist('normZsqr','var')
normZsqr = norm(Z)^2;
end
% function value
f = 0.5 * normZsqr - innerprod(Z,B) + 0.5 * norm(B)^2;
% gradient computation
N = ndims(Z);
G = cell(N,1);
T = Z - B;
for n = 1:N
G{n} = zeros(size(A{n}));
G{n} = -mttkrp(T,A,n);
end
function [f,g] = tt_cp_wfun(Zdata,W,x,normZsqr,memflag)
%TT_CP_WFUN Computes function and gradient for weighted CP.
%
% [F,G] = TT_CP_WFUN(Z,W,x,normZsqr) calculates the function and gradient
% for the function 0.5 * || W .* (Z - ktensor(A)) ||^2 where W is an
% indicator for missing data (0 = missing, 1 = present), Z is the data
% tensor that is being fit (assumed that missing entries have already
% been set to zero), A is a cell array of factor matrices that is created
% from the vector x, and normZsqr in the norm of Z squared.
%
% [F,G] = TT_CP_WFUN(Zvals,W,x,normZsqr) is a special version that takes
% just the nonzeros in Z as calculated by the helper function
% CP_WFG_SPARSE_SETUP.
%
% [F,G] = TT_CP_WFUN(....,false) uses a more memory efficient version for
% the sparse code.
%
% See also TT_CP_WFG, TT_CP_WFG_SPARSE, TT_CP_WFG_SPARSE_SETUP
%
%MATLAB Tensor Toolbox.
%Copyright 2015, Sandia Corporation.
% This is the MATLAB Tensor Toolbox by T. Kolda, B. Bader, and others.
% http://www.sandia.gov/~tgkolda/TensorToolbox.
% Copyright (2015) Sandia Corporation. Under the terms of Contract
% DE-AC04-94AL85000, there is a non-exclusive license for use of this
% work by or on behalf of the U.S. Government. Export of this data may
% require a license from the United States Government.
% The full license terms can be found in the file LICENSE.txt
%% Convert x to factor matrices (i.e., a cell array).
% Normally we would pass in the data tensor, but we may have a data
% tensor or a data array if we are doing the sparse
% calculation. Therefore, we exploit the fact that W is the same
% size as Z and pass it into the function.
A = tt_cp_vec_to_fac(x,W);
%% Compute the function and gradient
if isa(Zdata,'tensor') || isa(Zdata,'sptensor')
if ~exist('normZsqr','var')
normZsqr = norm(Zdata)^2;
end
[f,G] = tt_cp_wfg(Zdata,W,A,normZsqr);
else
if ~exist('normZsqr','var')
normZsqr = sum(Zdata.^2);
end
if ~exist('memflag','var')
memflag = true;
end
[f,G] = tt_cp_wfg_sparse(Zdata,W,A,normZsqr,memflag);
end
%% Convert gradient to a vector
g = tt_fac_to_vec(G);
function Zvals = tt_cp_wfg_sparse_setup(Z,W)
%CP_WFG_SPARSE_SETUP Creates a special array.
%
% ZVALS = CP_WFG_SPARSE_SETUP(Z,W) creates an array ZVALS that
% contains the values of Z corresponding to the indices specified
% by W.subs.
%
% See also CP_WFG_SPARSE.
%
%MATLAB Tensor Toolbox.
%Copyright 2015, Sandia Corporation.
% This is the MATLAB Tensor Toolbox by T. Kolda, B. Bader, and others.
% http://www.sandia.gov/~tgkolda/TensorToolbox.
% Copyright (2015) Sandia Corporation. Under the terms of Contract
% DE-AC04-94AL85000, there is a non-exclusive license for use of this
% work by or on behalf of the U.S. Government. Export of this data may
% require a license from the United States Government.
% The full license terms can be found in the file LICENSE.txt
Zsubs = Z.subs;
Wsubs = W.subs;
Zvals = zeros(size(W.vals));
[junk,loc] = ismember(Zsubs,Wsubs,'rows');
Zvals(loc) = Z.vals;
function [f,G] = tt_cp_wfg_sparse(Zvals,W,A,normZsqr,memflag)
%TT_CP_WFG_SPARSE Computes weighted CP function and gradient.
%
% [F,G] = TT_CP_WFG_SPARSE(ZVALS,W,A) computes the function and
% gradient with respect to A of || W .* (Z - ktensor(A)) ||^2 where
% Z = W .* X. The variable ZVALS contains the values of the tensor Z
% at the locations specified by W.subs. (ZVALS can be computed using
% a provided preprocessing function.) The variable A is a cell array
% of component matrices. The tensor W is a sparse tensor that has
% ones in entries where we know the values.
%
% [F,G] = TT_CP_WFG_SPARSE(ZVALS,W,A,NORMZSQR) also passes in the
% pre-computed norm of Z, which makes the computations faster.
%
% [F,G] = TT_CP_WFG_SPARSE(ZVALS,A,W,NORMZSQR,false) uses less memory
% but more time and is appropriate for very large sparse tensors.
%
% See also TT_CP_WFG_SPARSE_SETUP, CP_WFG, CP_WFUN.
%
%MATLAB Tensor Toolbox.
%Copyright 2015, Sandia Corporation.
% This is the MATLAB Tensor Toolbox by T. Kolda, B. Bader, and others.
% http://www.sandia.gov/~tgkolda/TensorToolbox.
% Copyright (2015) Sandia Corporation. Under the terms of Contract
% DE-AC04-94AL85000, there is a non-exclusive license for use of this
% work by or on behalf of the U.S. Government. Export of this data may
% require a license from the United States Government.
% The full license terms can be found in the file LICENSE.txt
%% Set-up
N = ndims(W);
R = size(A{1},2);
sz = cellfun(@(x)size(x,1),A);
Wsubs = W.subs;
Wvals = W.vals;
Nvals = length(Wvals);
if ~exist('memflag','var')
memflag = true;
end
%% Compute B = W.*ktensor(A)
Bvals = zeros(Nvals,1);
for r = 1:R
newvals = Wvals;
for n = 1:N
bigArn = A{n}(Wsubs(:,n),r);
newvals = newvals .* bigArn;
end
Bvals = Bvals + newvals;
end
%% Compute normZ
if ~exist('normZsqr','var')
normZsqr = sum(Zvals.^2);
end
%% function value: f = 0.5 * normZsqr - innerprod(Z,B) + 0.5 * norm(B)^2
f = 0.5 * normZsqr - Zvals'*Bvals + 0.5 * sum(Bvals.^2);
%% gradient computation
Tvals = Zvals - Bvals;
G = cell(N,1);
for n = 1:N
G{n} = zeros(size(A{n}));
end
for r = 1:R
if (memflag)
bigAr = cell(N,1);
for n = 1:N
bigAr{n} = A{n}(Wsubs(:,n),r);
end
for SkipN = 1:N
newvals = Tvals;
for n = [1:SkipN-1,SkipN+1:N]
newvals = newvals .* bigAr{n};
end
G{SkipN}(:,r) = accumarray(Wsubs(:,SkipN),newvals,[sz(SkipN) 1]);
end
else
for SkipN = 1:N
newvals = Tvals;
for n = [1:SkipN-1,SkipN+1:N]
bigArn = A{n}(Wsubs(:,n),r);
newvals = newvals .* bigArn;
end
G{SkipN}(:,r) = accumarray(Wsubs(:,SkipN),newvals,[sz(SkipN) 1]);
end
end
end
for n = 1:N
G{n} = -G{n};
end