daniele@169
|
1 function [Dhat cost W] = rotatematrix(D,Phi,method,param)
|
daniele@184
|
2 %
|
daniele@169
|
3 %
|
daniele@169
|
4 %
|
daniele@169
|
5 % REFERENCE
|
daniele@169
|
6 % M.D. Plumbley, Geometrical Methods for Non-Negative ICA: Manifolds, Lie
|
daniele@169
|
7 % Groups and Toral Subalgebra, Neurocomputing
|
daniele@170
|
8
|
daniele@170
|
9 %% Parse inputs and set defaults
|
daniele@169
|
10 if ~nargin, testrotatematrix; return, end
|
daniele@169
|
11
|
daniele@170
|
12 if ~exist('param','var') || isempty(param), param = struct; end
|
daniele@170
|
13 if ~exist('method','var') || isempty(method), method = 'conjgradLie'; end
|
daniele@170
|
14 if ~isfield(param,'nIter'), param.nIter = 100; end %number of iterations
|
daniele@170
|
15 if ~isfield(param,'eps'), param.eps = 1e-9; end %tolerance level
|
daniele@170
|
16 if ~isfield(param,'step'), param.step = 0.01; end
|
daniele@169
|
17
|
daniele@170
|
18 J = @(W) 0.5*norm(D-W*Phi,'fro'); %cost function
|
daniele@169
|
19
|
daniele@170
|
20 % Initialise variables
|
daniele@170
|
21 cost = zeros(param.nIter,1); %cost at each iteration
|
daniele@170
|
22 W = eye(size(Phi,1)); %rotation matrix
|
daniele@170
|
23 grad = ones(size(W)); %gradient
|
daniele@170
|
24 t = param.step; %step size
|
daniele@170
|
25 Gprev = 0; %previous gradient
|
daniele@170
|
26 Hprev = 0; %previous Lie search direction
|
daniele@170
|
27 iIter = 1; %iteration counter
|
daniele@169
|
28
|
daniele@170
|
29 %% Main algorithm
|
daniele@170
|
30 while iIter<=param.nIter && norm(grad,'fro')>eps
|
daniele@170
|
31 cost(iIter) = J(W); %calculate cost
|
daniele@170
|
32 grad = (W*Phi-D)*Phi'; %calculate gradient
|
daniele@169
|
33 switch method
|
daniele@169
|
34 case 'unconstrained' % gradient descent
|
daniele@169
|
35 eta = param.step;
|
daniele@169
|
36 W = W - eta*grad; % update W by steepest descent
|
daniele@169
|
37 case 'tangent' % self correcting tangent
|
daniele@169
|
38 eta = param.step;
|
daniele@170
|
39 W = W - 0.5*eta*(grad - W*grad'*W);
|
daniele@170
|
40 [U , ~, V] = svd(W);
|
daniele@170
|
41 W = U*V';
|
daniele@170
|
42 case 'steepestlie' %steepest descent in Lie algebra
|
daniele@169
|
43 eta = param.step;
|
daniele@170
|
44 B = 2*skew(grad*W'); % calculate gradient in Lie algebra
|
daniele@169
|
45 W = expm(-eta*B)*W; % update W by steepest descent
|
daniele@170
|
46 case 'linesearchlie' % line search in Lie algebra
|
daniele@170
|
47 B = 2*skew(grad*W'); % calculate gradient in Lie algebra
|
daniele@169
|
48 H = -B; % calculate direction as negative gradient
|
daniele@170
|
49 t = searchline(J,H,W,t);% line search in one-parameter Lie subalgebra
|
daniele@169
|
50 W = expm(t*H)*W; % update W by line search
|
daniele@170
|
51 case 'conjgradlie' % conjugate gradient in Lie algebra
|
daniele@170
|
52 G = 2*skew(grad*W'); % calculate gradient in Lie algebra
|
daniele@169
|
53 H = -G + polakRibiere(G,Gprev)*Hprev; %calculate conjugate gradient direction
|
daniele@170
|
54 t = searchline(J,H,W,t);% line search in one-parameter Lie subalgebra
|
daniele@169
|
55 W = expm(t*H)*W; % update W by line search
|
daniele@170
|
56 Hprev = H; % save search direction
|
daniele@170
|
57 Gprev = G; % save gradient
|
daniele@169
|
58 end
|
daniele@170
|
59 iIter = iIter+1; % update iteration counter
|
daniele@169
|
60 end
|
daniele@170
|
61 Dhat = W*Phi; %rotate matrix
|
daniele@172
|
62 cost(iIter:end) = cost(iIter-1); %zero-pad cost vector
|
daniele@169
|
63 end
|
daniele@169
|
64
|
daniele@170
|
65 %% Support functions
|
daniele@169
|
66 function gamma = polakRibiere(G,Gprev)
|
daniele@170
|
67 %Polak-Ribiere rule for conjugate direction calculation
|
daniele@169
|
68 gamma = G(:)'*(G(:)-Gprev(:))/(norm(Gprev(:))^2);
|
daniele@169
|
69 if isnan(gamma) || isinf(gamma)
|
daniele@169
|
70 gamma = 0;
|
daniele@169
|
71 end
|
daniele@169
|
72 end
|
daniele@169
|
73
|
daniele@169
|
74 function t = searchline(J,H,W,t)
|
daniele@170
|
75 %Line search in one-parameter Lie subalgebra
|
daniele@169
|
76 t = fminsearch(@(x) J(expm(x*H)*W),t);
|
daniele@169
|
77 end
|
daniele@169
|
78
|
daniele@169
|
79 function B = skew(A)
|
daniele@170
|
80 %Skew-symmetric matrix
|
daniele@169
|
81 B = 0.5*(A - A');
|
daniele@169
|
82 end
|
daniele@169
|
83
|
daniele@169
|
84
|
daniele@170
|
85 %% Test function
|
daniele@169
|
86 function testrotatematrix
|
daniele@169
|
87 clear, clc, close all
|
daniele@170
|
88 n = 256; %ambient dimension
|
daniele@170
|
89 m = 512; %number of atoms
|
daniele@170
|
90 param.nIter = 300; %number of iterations
|
daniele@170
|
91 param.step = 0.001; %step size
|
daniele@170
|
92 param.mu = 0.01; %regularization factor (for tangent method)
|
daniele@184
|
93 methods = {'tangent','linesearchlie','conjgradlie'};
|
daniele@169
|
94
|
daniele@170
|
95 Phi = randn(n,m); %initial dictionary
|
daniele@170
|
96 Qtrue = expm(skew(randn(n))); %rotation matrix
|
daniele@170
|
97 D = Qtrue*Phi; %target dictionary
|
daniele@170
|
98
|
daniele@170
|
99 cost = zeros(param.nIter,length(methods));
|
daniele@184
|
100 times = zeros(param.nIter,length(methods));
|
daniele@170
|
101 for iIter=1:length(methods)
|
daniele@170
|
102 tic
|
daniele@170
|
103 [~, cost(:,iIter)] = rotatematrix(D,Phi,methods{iIter},param);
|
daniele@184
|
104 times(:,iIter) = linspace(0,toc,param.nIter);
|
daniele@184
|
105 sprintf('Method %s completed in %f seconds \n',methods{iIter},toc)
|
daniele@170
|
106 end
|
daniele@169
|
107
|
daniele@184
|
108 figure, plot(times,cost)
|
daniele@184
|
109 set(gca,'XScale','lin','Yscale','log')
|
daniele@170
|
110 legend(methods)
|
daniele@169
|
111 grid on
|
daniele@184
|
112 xlabel('time (sec)')
|
daniele@169
|
113 ylabel('J(W)')
|
daniele@169
|
114 end
|