/************************************************************************* This function prepares exact low-rank preconditioner for Hessian matrix H=D+W'*C*W, where: * H is a Hessian matrix, which is approximated by D/W/C * D is a diagonal matrix with positive entries * W is a rank-K correction * C is a diagonal factor of rank-K correction, positive semidefinite This preconditioner is exact but relatively slow - it requires O(N*K^2) time to be prepared and O(N*K) time to be applied. It is calculated with the help of Woodbury matrix identity. It should be used as follows: * PrepareLowRankPreconditioner() call PREPARES data structure * subsequent calls to ApplyLowRankPreconditioner() APPLY preconditioner to user-specified search direction. -- ALGLIB -- Copyright 30.06.2014 by Bochkanov Sergey *************************************************************************/ public static void preparelowrankpreconditioner(double[] d, double[] c, double[,] w, int n, int k, precbuflowrank buf) { int i = 0; int j = 0; double v = 0; bool b = new bool(); // // Check inputs // alglib.ap.assert(n>0, "PrepareLowRankPreconditioner: N<=0"); alglib.ap.assert(k>=0, "PrepareLowRankPreconditioner: N<=0"); for(i=0; i<=n-1; i++) { alglib.ap.assert((double)(d[i])>(double)(0), "PrepareLowRankPreconditioner: D[]<=0"); } for(i=0; i<=k-1; i++) { alglib.ap.assert((double)(c[i])>=(double)(0), "PrepareLowRankPreconditioner: C[]<0"); } // // Prepare buffer structure; skip zero entries of update. // apserv.rvectorsetlengthatleast(ref buf.d, n); apserv.rmatrixsetlengthatleast(ref buf.v, k, n); apserv.rvectorsetlengthatleast(ref buf.bufc, k); apserv.rmatrixsetlengthatleast(ref buf.bufw, k+1, n); buf.n = n; buf.k = 0; for(i=0; i<=k-1; i++) { // // Estimate magnitude of update row; skip zero rows (either W or C are zero) // v = 0.0; for(j=0; j<=n-1; j++) { v = v+w[i,j]*w[i,j]; } v = v*c[i]; if( (double)(v)==(double)(0) ) { continue; } alglib.ap.assert((double)(v)>(double)(0), "PrepareLowRankPreconditioner: internal error"); // // Copy non-zero update to buffer // buf.bufc[buf.k] = c[i]; for(j=0; j<=n-1; j++) { buf.v[buf.k,j] = w[i,j]; buf.bufw[buf.k,j] = w[i,j]; } apserv.inc(ref buf.k); } // // Reset K (for convenience) // k = buf.k; // // Prepare diagonal factor; quick exit for K=0 // for(i=0; i<=n-1; i++) { buf.d[i] = 1/d[i]; } if( k==0 ) { return; } // // Use Woodbury matrix identity // apserv.rmatrixsetlengthatleast(ref buf.bufz, k, k); for(i=0; i<=k-1; i++) { for(j=0; j<=k-1; j++) { buf.bufz[i,j] = 0.0; } } for(i=0; i<=k-1; i++) { buf.bufz[i,i] = 1/buf.bufc[i]; } for(j=0; j<=n-1; j++) { buf.bufw[k,j] = 1/Math.Sqrt(d[j]); } for(i=0; i<=k-1; i++) { for(j=0; j<=n-1; j++) { buf.bufw[i,j] = buf.bufw[i,j]*buf.bufw[k,j]; } } ablas.rmatrixgemm(k, k, n, 1.0, buf.bufw, 0, 0, 0, buf.bufw, 0, 0, 1, 1.0, buf.bufz, 0, 0); b = trfac.spdmatrixcholeskyrec(ref buf.bufz, 0, k, true, ref buf.tmp); alglib.ap.assert(b, "PrepareLowRankPreconditioner: internal error (Cholesky failure)"); ablas.rmatrixlefttrsm(k, n, buf.bufz, 0, 0, true, false, 1, buf.v, 0, 0); for(i=0; i<=k-1; i++) { for(j=0; j<=n-1; j++) { buf.v[i,j] = buf.v[i,j]*buf.d[j]; } } }
/************************************************************************* This function apply exact low-rank preconditioner prepared by PrepareLowRankPreconditioner function (see its comments for more information). -- ALGLIB -- Copyright 30.06.2014 by Bochkanov Sergey *************************************************************************/ public static void applylowrankpreconditioner(double[] s, precbuflowrank buf) { int n = 0; int k = 0; int i = 0; int j = 0; double v = 0; n = buf.n; k = buf.k; apserv.rvectorsetlengthatleast(ref buf.tmp, n); for(j=0; j<=n-1; j++) { buf.tmp[j] = buf.d[j]*s[j]; } for(i=0; i<=k-1; i++) { v = 0.0; for(j=0; j<=n-1; j++) { v = v+buf.v[i,j]*s[j]; } for(j=0; j<=n-1; j++) { buf.tmp[j] = buf.tmp[j]-v*buf.v[i,j]; } } for(i=0; i<=n-1; i++) { s[i] = buf.tmp[i]; } }
public override alglib.apobject make_copy() { precbuflowrank _result = new precbuflowrank(); _result.n = n; _result.k = k; _result.d = (double[])d.Clone(); _result.v = (double[,])v.Clone(); _result.bufc = (double[])bufc.Clone(); _result.bufz = (double[,])bufz.Clone(); _result.bufw = (double[,])bufw.Clone(); _result.tmp = (double[])tmp.Clone(); return _result; }