%% Poblano matrix decomposition example
% One of the frequently recurring objectives in many machine learning
% problems are rank-k factorizations.  When unconstrainted, these can be
% solved optimally via the SVD.  Here, we "forget" this fact and use a
% nonlinear optimization routine to estimate the best rank-k approximation
% to a given matrix.  
%
% This example is built into Poblano, but we'll walk through it here as
% it's an interesting one exhibiting how Poblano is different from some of
% the other tools.

%% Problem setup
% In constrast to other tools, we need to provide Poblano with an explicit
% function and gradient. 

A = randn(5,4); % generate a random matrix to approximate
[x0,Data] = example2_init(5,4,2,A);


% We can use data to evaluate objectives and gradients
example2(x0,Data)

%% Let's look at the gradient from the SVD solution
[Usvd,Ssvd,Vsvd] = svd(A);
xsvd = [reshape(Usvd(:,1:2)*Ssvd(1:2,1:2),5*2,1); reshape(Vsvd(:,1:2),4*2,1)];
[fsvd,gsvd] = example2(xsvd,Data)

%% Run a poblano routine
tic, 
out = lbfgs(@(x) example2(x,Data), x0, 'RelFuncTol', 1e-16, 'StopTol', 1e-8, ...
    'MaxFuncEvals',1000,'Display','iter')
toc

%% Now we have to extract the solution
[Uopt,Vopt] = example2_extract(5, 4, 2, out.X)

%% See how good it is...
norm(Data.A - Uopt*Vopt')
norm(Data.A - Usvd(:,1:2)*Ssvd(1:2,1:2)*Vsvd(:,1:2)')
% So we get the same type of approximation!


