function [X,y] = CorMatWnorm(G,W,b,tau) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%% This code is for computing %%%%%% "the W-weighted version of the nearest correlation matrix problem" %%%%%% based on %%%%%% Houduo Qi and Defeng Sun, "A Quadratically Convergent Newton Method %%%%%% for Computing the Nearest Correlation Matrix" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% for solving %% min 0.5 ||W^1/2*(X - G)*W^1/2 ||^2 %% s.t. X_ii =1, i=1,2,..., n %% X >= tau*I (symmetric and positive semi-definite; tau can be zero) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Input: % G: the estimated potentially inconsistent correlation matrix (n by n) % W: the weight matrix for G % b: = ones(n,1) % tau: the lower bound for the smallest eigevalue of X0 (can be zero) % Output: % X: the calibrated correlation matrix % y: Lagrangian dual variable corresponding to X_ii =1 % Warning: accuracy may not be guaranteed %%%%%%%%%%%%%%%%%%%%%%%% %%%%% Last modified on April 5, 2008 %%%%%%%%%%%%%%%%%%%%% fprintf('\n --- Semimsooth Newton method with diagonal preconditioner--- \n') t0 = cputime; n = length(G); G = (G + G')/2; % make G symmetric G0 = G; W = real(W); W = (W + W')/2; % make W symmetric if nargin == 4 G = G - tau*eye(n); % reset G end if nargin == 3 tau =0; end if nargin == 2 tau = 0; b = ones(n) end if nargin == 1 tau = 0; b = ones(n); W = eye(n); end b0 =b; b0 = b0-tau*ones(n,1); eig_time =0; t1 = cputime; [P,D] = eig(W); eig_time = eig_time + cputime - t1; lambda = diag(D); P = real(P); lambda = real(lambda); if max(lambda)<=0 disp('Warning: Warning: Warning: W is not positive definite: Stop Stop Stop') return; end W_half = P'; W_half_inv =P'; i=1; while (i error_tol & k< Iter_Whole) % update approximate preconditioner t1 = cputime; c = precond_matrix(Omega,W0P); prec_time = prec_time + cputime - t1; t2 = cputime; [d,flag,relres,iterk] = pre_cg(b,tol,maxit,c,Omega,W0P); CG_time = CG_time + cputime - t2; CG_num = CG_num + iterk; if (flag~=0); % if CG is unsuccessful, use the negative gradient direction disp('..... Not a full Newton step......') end slope = (Fy-b0)'*d; y = x0 + d; C = W_half_inv; i=1; while (i 1.0e-8 ) k_inner = k_inner+1; y = x0 + 0.5^k_inner*d; % backtracking C = W_half_inv; i=1; while (i=1) f = f + (max(lambda(i),0))^2; i = i-1; end f = 0.5*f - b0'*y; return %%%%% end of gradient.m %%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%% To generate the first -order difference of d %%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function Omega = omega_mat(P,lambda,n) %We compute omega only for 1<=|idx|<=n-1 idx.idp = find(lambda>0); idx.idm = setdiff([1:n],idx.idp); n =length(lambda); r = length(idx.idp); Omega = zeros(n); if ~isempty(idx.idp) if (r == n) Omega = ones(n,n); else s = n-r; if idx.idp(1)< idx.idm(1) dp = lambda(1:r); dn = lambda(r+1:n); Omega12 = (dp*ones(1,s))./(abs(dp)*ones(1,s) + ones(r,1)*abs(dn')); Omega12 = max(1e-15,Omega12); Omega =[ones(r) Omega12;Omega12' zeros(s)]; else dp = lambda(s+1:n); dn = lambda(1:s); Omega12 = (dp*ones(1,s))./(abs(dp)*ones(1,s) + ones(r,1)*abs(dn')); Omega12 = max(1e-15,Omega12); Omega =[zeros(s) Omega12';Omega12 ones(r)]; end end end %%***** perturbation ***** return %%%% end of omega_mat.m %%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%% PCG method %%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%% This is exactly the algorithm by Hestenes and Stiefel (1952) %%%%%%% An iterative method to solve A(x) =b %%%%%%% The symmetric positive definite matrix M is a %%%%%%% preconditioner for A. %%%%%%% See Pages 527 and 534 of Golub and va Loan (1996) function [p,flag,relres,iterk] = pre_cg(b,tol,maxit,c,Omega,W0P) n = length(W0P); % Initializations r = b; % We take the initial guess x0=0 to save time in calculating A(x0) n2b =norm(b); % norm of b tolb = tol * n2b; % relative tolerance p = zeros(n,1); flag=1; iterk =0; relres=1000; % To give a big value on relres % Precondition z =r./c; % z = M\r; here M =diag(c); if M is not the identity matrix rz1 = r'*z; rz2 = 1; d = z; % CG iteration for k = 1:maxit if k > 1 beta = rz1/rz2; d = z + beta*d; end w = Jacobian_matrix(d,Omega,W0P); % W =A(d) denom = d'*w; iterk =k; relres =norm(r)/n2b; %relative residue =norm(r) / norm(b) if denom <= 0 sssss=0 p = d/norm(d); % d is not a descent direction break % exit else alpha = rz1/denom; p = p + alpha*d; r = r - alpha*w; end z = r./c; % z = M\r; here M =diag(c); if M is not the identity matrix ; if norm(r) <= tolb % Exit if Hp=b solved within the relative tolerance iterk =k; relres =norm(r)/n2b; % relative residue =norm(z) / norm(b) flag =0; break end rz2 = rz1; rz1 = r'*z; end return %%%%%%%% end of pre_cg.m %%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%% To generate the Jacobain product with x: F'(y)(x) %%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function Ax = Jacobian_matrix(x,Omega,W0P) n = length(W0P); Ax = zeros(n,1); H = W0P; i=1; while (i<=n) H(i,:) = x(i)*H(i,:); i=i+1; end H = W0P'*H; H = Omega.*H; % H =[Omega o ( P^T*(W^(-1/2)*diag(x)*W^(-1/2))*P )] H = H*W0P'; i=1; while (i<=n) Ax(i) = W0P(i,:)*H(:,i); Ax(i) = Ax(i) + 1.0e-10*x(i); % add a small perturbation i=i+1; end return %%%%%% end of Jacobian_matrix.m %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%% To generate the diagonal preconditioner %%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % We chooose e1-en to compute the diagonal elements of Jacobian matrix function c = precond_matrix(Omega,W0P) n = length(W0P); c = ones(n,1); H = W0P'; H = H.*H; Omega = Omega*H; H = H'; for i=1:n c(i) = H(i,:)*Omega(:,i); if c(i) <= 1.0e-8 c(i) =1.0e-8; end end return %%% end of precond_matrix.m