Actual source code: lsqr.c

  1: #define PETSCKSP_DLL

  3: /* lourens.vanzanen@shell.com contributed the standard error estimates of the solution, Jul 25, 2006 */
  4: /* Bas van't Hof contributed the preconditioned aspects Feb 10, 2010 */

  6: #define SWAP(a,b,c) { c = a; a = b; b = c; }

 8:  #include private/kspimpl.h
 9:  #include ../src/ksp/ksp/impls/lsqr/lsqr.h

 11: typedef struct {
 12:   PetscInt   nwork_n,nwork_m;
 13:   Vec        *vwork_m;  /* work vectors of length m, where the system is size m x n */
 14:   Vec        *vwork_n;  /* work vectors of length n */
 15:   Vec        se;        /* Optional standard error vector */
 16:   PetscTruth se_flg;   /* flag for -ksp_lsqr_set_standard_error */
 17:   PetscReal  arnorm;   /* Norm of the vector A.r */
 18:   PetscReal  anorm;    /* Frobenius norm of the matrix A */
 19:   PetscReal  rhs_norm; /* Norm of the right hand side */
 20: } KSP_LSQR;


 26: static PetscErrorCode KSPSetUp_LSQR(KSP ksp)
 27: {
 29:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;
 30:   PetscTruth     nopreconditioner;

 33:   PetscTypeCompare((PetscObject)ksp->pc,PCNONE,&nopreconditioner);
 34:   if (ksp->pc_side == PC_SYMMETRIC){
 35:     SETERRQ(PETSC_ERR_SUP,"no symmetric preconditioning for KSPLSQR");
 36:   } else if (ksp->pc_side == PC_RIGHT){
 37:     SETERRQ(PETSC_ERR_SUP,"no right preconditioning for KSPLSQR");
 38:   }
 39:   /*  nopreconditioner =PETSC_FALSE; */

 41:   lsqr->nwork_m = 2;
 42:   if (lsqr->vwork_m) {
 43:     VecDestroyVecs(lsqr->vwork_m,lsqr->nwork_m);
 44:   }
 45:   if (nopreconditioner) {
 46:      lsqr->nwork_n = 4;
 47:   } else {
 48:      lsqr->nwork_n = 5;
 49:   }
 50:   if (lsqr->vwork_n) {
 51:     VecDestroyVecs(lsqr->vwork_n,lsqr->nwork_n);
 52:   }
 53:   KSPGetVecs(ksp,lsqr->nwork_n,&lsqr->vwork_n,lsqr->nwork_m,&lsqr->vwork_m);
 54:   if (lsqr->se_flg && !lsqr->se){
 55:     /* lsqr->se is not set by user, get it from pmat */
 56:     Vec *se;
 57:     KSPGetVecs(ksp,1,&se,0,PETSC_NULL);
 58:     lsqr->se = *se;
 59:     PetscFree(se);
 60:   }
 61:   return(0);
 62: }

 66: static PetscErrorCode KSPSolve_LSQR(KSP ksp)
 67: {
 69:   PetscInt       i,size1,size2;
 70:   PetscScalar    rho,rhobar,phi,phibar,theta,c,s,tmp,tau,alphac;
 71:   PetscReal      beta,alpha,rnorm;
 72:   Vec            X,B,V,V1,U,U1,TMP,W,W2,SE,Z = PETSC_NULL;
 73:   Mat            Amat,Pmat;
 74:   MatStructure   pflag;
 75:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;
 76:   PetscTruth     diagonalscale,nopreconditioner;
 77: 
 79:   PCDiagonalScale(ksp->pc,&diagonalscale);
 80:   if (diagonalscale) SETERRQ1(PETSC_ERR_SUP,"Krylov method %s does not support diagonal scaling",((PetscObject)ksp)->type_name);

 82:   PCGetOperators(ksp->pc,&Amat,&Pmat,&pflag);
 83:   PetscTypeCompare((PetscObject)ksp->pc,PCNONE,&nopreconditioner);

 85:   /*  nopreconditioner =PETSC_FALSE; */
 86:   /* Calculate norm of right hand side */
 87:   VecNorm(ksp->vec_rhs,NORM_2,&lsqr->rhs_norm);

 89:   /* Calculate norm of matrix*/
 90:   MatNorm( Amat, NORM_FROBENIUS, &lsqr->anorm);

 92:   /* vectors of length m, where system size is mxn */
 93:   B        = ksp->vec_rhs;
 94:   U        = lsqr->vwork_m[0];
 95:   U1       = lsqr->vwork_m[1];

 97:   /* vectors of length n */
 98:   X        = ksp->vec_sol;
 99:   W        = lsqr->vwork_n[0];
100:   V        = lsqr->vwork_n[1];
101:   V1       = lsqr->vwork_n[2];
102:   W2       = lsqr->vwork_n[3];
103:   if (!nopreconditioner) {
104:      Z     = lsqr->vwork_n[4];
105:   }

107:   /* standard error vector */
108:   SE = lsqr->se;
109:   if (SE){
110:     VecGetSize(SE,&size1);
111:     VecGetSize(X ,&size2);
112:     if (size1 != size2) SETERRQ2(PETSC_ERR_ARG_SIZ,"Standard error vector (size %d) does not match solution vector (size %d)",size1,size2);
113:     VecSet(SE,0.0);
114:   }

116:   /* Compute initial residual, temporarily use work vector u */
117:   if (!ksp->guess_zero) {
118:     KSP_MatMult(ksp,Amat,X,U);       /*   u <- b - Ax     */
119:     VecAYPX(U,-1.0,B);
120:   } else {
121:     VecCopy(B,U);            /*   u <- b (x is 0) */
122:   }

124:   /* Test for nothing to do */
125:   VecNorm(U,NORM_2,&rnorm);
126:   PetscObjectTakeAccess(ksp);
127:   ksp->its   = 0;
128:   ksp->rnorm = rnorm;
129:   PetscObjectGrantAccess(ksp);
130:   KSPLogResidualHistory(ksp,rnorm);
131:   KSPMonitor(ksp,0,rnorm);
132:   (*ksp->converged)(ksp,0,rnorm,&ksp->reason,ksp->cnvP);
133:   if (ksp->reason) return(0);

135:   VecCopy(B,U);
136:   VecNorm(U,NORM_2,&beta);
137:   VecScale(U,1.0/beta);
138:   KSP_MatMultTranspose(ksp,Amat,U,V);
139:   if (nopreconditioner) {
140:      VecNorm(V,NORM_2,&alpha);
141:   } else {
142:     PCApply(ksp->pc,V,Z);
143:     VecDot(V,Z,&alphac);
144:     if (PetscRealPart(alphac) <= 0.0) {
145:       ksp->reason = KSP_DIVERGED_BREAKDOWN;
146:       return(0);
147:     }
148:     alpha = sqrt(PetscRealPart(alphac));
149:     VecScale(Z,1.0/alpha);
150:   }
151:   VecScale(V,1.0/alpha);

153:   if (nopreconditioner){
154:     VecCopy(V,W);
155:   } else {
156:     VecCopy(Z,W);
157:   }
158:   VecSet(X,0.0);

160:   lsqr->arnorm = alpha * beta;
161:   phibar = beta;
162:   rhobar = alpha;
163:   tau = -beta;
164:   i = 0;
165:   do {
166:     if (nopreconditioner) {
167:        KSP_MatMult(ksp,Amat,V,U1);
168:     } else {
169:        KSP_MatMult(ksp,Amat,Z,U1);
170:     }
171:     VecAXPY(U1,-alpha,U);
172:     VecNorm(U1,NORM_2,&beta);
173:     if (beta == 0.0){
174:       ksp->reason = KSP_DIVERGED_BREAKDOWN;
175:       break;
176:     }
177:     VecScale(U1,1.0/beta);

179:     KSP_MatMultTranspose(ksp,Amat,U1,V1);
180:     VecAXPY(V1,-beta,V);
181:     if (nopreconditioner) {
182:       VecNorm(V1,NORM_2,&alpha);
183:     } else {
184:       PCApply(ksp->pc,V1,Z);
185:       VecDot(V1,Z,&alphac);
186:       if (PetscRealPart(alphac) <= 0.0) {
187:         ksp->reason = KSP_DIVERGED_BREAKDOWN;
188:         break;
189:       }
190:       alpha = sqrt(PetscRealPart(alphac));
191:       VecScale(Z,1.0/alpha);
192:     }
193:     VecScale(V1,1.0/alpha);
194:     rho    = PetscSqrtScalar(rhobar*rhobar + beta*beta);
195:     c      = rhobar / rho;
196:     s      = beta / rho;
197:     theta  = s * alpha;
198:     rhobar = - c * alpha;
199:     phi    = c * phibar;
200:     phibar = s * phibar;
201:     tau    = s * phi;

203:     VecAXPY(X,phi/rho,W);  /*    x <- x + (phi/rho) w   */

205:     if (SE) {
206:       VecCopy(W,W2);
207:       VecSquare(W2);
208:       VecScale(W2,1.0/(rho*rho));
209:       VecAXPY(SE, 1.0, W2); /* SE <- SE + (w^2/rho^2) */
210:     }
211:     if (nopreconditioner) {
212:        VecAYPX(W,-theta/rho,V1); /* w <- v - (theta/rho) w */
213:     } else {
214:        VecAYPX(W,-theta/rho,Z);  /* w <- z - (theta/rho) w */
215:     }

217:     lsqr->arnorm = alpha*PetscAbsScalar(tau);
218:     rnorm = PetscRealPart(phibar);

220:     PetscObjectTakeAccess(ksp);
221:     ksp->its++;
222:     ksp->rnorm = rnorm;
223:     PetscObjectGrantAccess(ksp);
224:     KSPLogResidualHistory(ksp,rnorm);
225:     KSPMonitor(ksp,i+1,rnorm);
226:     (*ksp->converged)(ksp,i+1,rnorm,&ksp->reason,ksp->cnvP);
227:     if (ksp->reason) break;
228:     SWAP(U1,U,TMP);
229:     SWAP(V1,V,TMP);

231:     i++;
232:   } while (i<ksp->max_it);
233:   if (i >= ksp->max_it && !ksp->reason) {
234:     ksp->reason = KSP_DIVERGED_ITS;
235:   }

237:   /* Finish off the standard error estimates */
238:   if (SE) {
239:     tmp = 1.0;
240:     MatGetSize(Amat,&size1,&size2);
241:     if ( size1 > size2 ) tmp = size1 - size2;
242:     tmp = rnorm / PetscSqrtScalar(tmp);
243:     VecSqrt(SE);
244:     VecScale(SE,tmp);
245:   }

247:   return(0);
248: }


253: PetscErrorCode KSPDestroy_LSQR(KSP ksp)
254: {
255:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;


260:   /* Free work vectors */
261:   if (lsqr->vwork_n) {
262:     VecDestroyVecs(lsqr->vwork_n,lsqr->nwork_n);
263:   }
264:   if (lsqr->vwork_m) {
265:     VecDestroyVecs(lsqr->vwork_m,lsqr->nwork_m);
266:   }
267:   if (lsqr->se_flg && lsqr->se){
268:     VecDestroy(lsqr->se);
269:   }
270:   PetscFree(ksp->data);
271:   return(0);
272: }

276: PetscErrorCode  KSPLSQRSetStandardErrorVec( KSP ksp, Vec se )
277: {
278:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;

282:   if (lsqr->se) {
283:     VecDestroy(lsqr->se);
284:   }
285:   lsqr->se = se;
286:   return(0);
287: }

291: PetscErrorCode  KSPLSQRGetStandardErrorVec( KSP ksp,Vec *se )
292: {
293:   KSP_LSQR *lsqr = (KSP_LSQR*)ksp->data;

296:   *se = lsqr->se;
297:   return(0);
298: }

302: PetscErrorCode  KSPLSQRGetArnorm( KSP ksp,PetscReal *arnorm, PetscReal *rhs_norm , PetscReal *anorm)
303: {
304:   KSP_LSQR *lsqr = (KSP_LSQR*)ksp->data;

307:   *arnorm   = lsqr->arnorm;
308:   *anorm    = lsqr->anorm;
309:   *rhs_norm = lsqr->rhs_norm;
310:   return(0);
311: }

315: PetscErrorCode KSPSetFromOptions_LSQR(KSP ksp)
316: {
318:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;

321:   PetscOptionsHead("KSP LSQR Options");
322:   PetscOptionsName("-ksp_LSQR_set_standard_error","Set Standard Error Estimates of Solution","KSPLSQRSetStandardErrorVec",&lsqr->se_flg);
323:   PetscOptionsTail();
324:   return(0);
325: }

329: PetscErrorCode KSPView_LSQR(KSP ksp,PetscViewer viewer)
330: {
331:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;

335:   if (lsqr->se) {
336:     PetscReal rnorm;
337:     KSPLSQRGetStandardErrorVec(ksp,&lsqr->se);
338:     VecNorm(lsqr->se,NORM_2,&rnorm);
339:     PetscPrintf(PETSC_COMM_WORLD,"  Norm of Standard Error %A, Iterations %D\n",rnorm,ksp->its);
340:   }
341:   return(0);
342: }

344: /*MC
345:      KSPLSQR - This implements LSQR

347:    Options Database Keys:
348: .   see KSPSolve()

350:    Level: beginner

352:    Notes:  The original unpreconditioned algorithm can be found in Paige and Saunders, ACM Transactions on Mathematical Software, Vol 8, pp 43-71, 1982. 
353:      In exact arithmetic the LSQR method (with no preconditioning) is identical to the KSPCG algorithm applied to the normal equations.
354:      The preconditioned varient was implemented by Bas van't Hof and is essentially a left preconditioning for the Normal Equations. 
355:      This varient, when applied with no preconditioning is identical to the original algorithm in exact arithematic; however, in practice, with no preconditioning
356:      due to inexact arithematic, it can converge differently. Hence when no preconditioner is used (PCType PCNONE) it automatically reverts to the original algorithm.

358:      With the PETSc built-in preconditioners, such as ICC, one should call KSPSetOperators(ksp,A,A'*A,...) since the preconditioner needs to work 
359:      for the normal equations A'*A.

361:    Developer Notes: How is this related to the KSPCGNE implementation? One difference is that KSPCGNE applies the preconditioner transpose times the preconditioner, 
362:       so one does not need to pass A'*A as the third argument to KSPSetOperators().

364: .seealso:  KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP

366: M*/
370: PetscErrorCode  KSPCreate_LSQR(KSP ksp)
371: {
372:   KSP_LSQR       *lsqr;

376:   PetscNewLog(ksp,KSP_LSQR,&lsqr);
377:   lsqr->se     = PETSC_NULL;
378:   lsqr->se_flg = PETSC_FALSE;
379:   lsqr->arnorm = 0.0;
380:   ksp->data                      = (void*)lsqr;
381:   ksp->pc_side                   = PC_LEFT;
382:   ksp->ops->setup                = KSPSetUp_LSQR;
383:   ksp->ops->solve                = KSPSolve_LSQR;
384:   ksp->ops->destroy              = KSPDestroy_LSQR;
385:   ksp->ops->buildsolution        = KSPDefaultBuildSolution;
386:   ksp->ops->buildresidual        = KSPDefaultBuildResidual;
387:   ksp->ops->setfromoptions       = KSPSetFromOptions_LSQR;
388:   ksp->ops->view                 = KSPView_LSQR;
389:   return(0);
390: }

395: PetscErrorCode  VecSquare(Vec v)
396: {
398:   PetscScalar    *x;
399:   PetscInt       i, n;

402:   VecGetLocalSize(v, &n);
403:   VecGetArray(v, &x);
404:   for(i = 0; i < n; i++) {
405:     x[i] *= x[i];
406:   }
407:   VecRestoreArray(v, &x);
408:   return(0);
409: }