Date: 2015-10-02T22:21:28+00:00

Actual source code: lsqr.c
petsc-3.6.2 2015-10-02
Report Typos and Errors

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

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

      7: #include <petsc/private/kspimpl.h>
      8: #include <../src/ksp/ksp/impls/lsqr/lsqr.h>

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

     21: extern PetscErrorCode  VecSquare(Vec);

     25: static PetscErrorCode KSPSetUp_LSQR(KSP ksp)
     26: {
     28:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;
     29:   PetscBool      nopreconditioner;

     32:   PetscObjectTypeCompare((PetscObject)ksp->pc,PCNONE,&nopreconditioner);
     33:   /*  nopreconditioner =PETSC_FALSE; */

     35:   lsqr->nwork_m = 2;
     36:   if (lsqr->vwork_m) {
     37:     VecDestroyVecs(lsqr->nwork_m,&lsqr->vwork_m);
     38:   }
     39:   if (nopreconditioner) lsqr->nwork_n = 4;
     40:   else lsqr->nwork_n = 5;

     42:   if (lsqr->vwork_n) {
     43:     VecDestroyVecs(lsqr->nwork_n,&lsqr->vwork_n);
     44:   }
     45:   KSPCreateVecs(ksp,lsqr->nwork_n,&lsqr->vwork_n,lsqr->nwork_m,&lsqr->vwork_m);
     46:   if (lsqr->se_flg && !lsqr->se) {
     47:     /* lsqr->se is not set by user, get it from pmat */
     48:     Vec *se;
     49:     KSPCreateVecs(ksp,1,&se,0,NULL);
     50:     lsqr->se = *se;
     51:     PetscFree(se);
     52:   }
     53:   return(0);
     54: }

     58: static PetscErrorCode KSPSolve_LSQR(KSP ksp)
     59: {
     61:   PetscInt       i,size1,size2;
     62:   PetscScalar    rho,rhobar,phi,phibar,theta,c,s,tmp,tau;
     63:   PetscReal      beta,alpha,rnorm;
     64:   Vec            X,B,V,V1,U,U1,TMP,W,W2,SE,Z = NULL;
     65:   Mat            Amat,Pmat;
     66:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;
     67:   PetscBool      diagonalscale,nopreconditioner;

     70:   PCGetDiagonalScale(ksp->pc,&diagonalscale);
     71:   if (diagonalscale) SETERRQ1(PetscObjectComm((PetscObject)ksp),PETSC_ERR_SUP,"Krylov method %s does not support diagonal scaling",((PetscObject)ksp)->type_name);

     73:   PCGetOperators(ksp->pc,&Amat,&Pmat);
     74:   PetscObjectTypeCompare((PetscObject)ksp->pc,PCNONE,&nopreconditioner);

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

     80:   /* mark norm of matrix with negative number to indicate it has not yet been computed */
     81:   lsqr->anorm = -1.0;

     83:   /* vectors of length m, where system size is mxn */
     84:   B  = ksp->vec_rhs;
     85:   U  = lsqr->vwork_m[0];
     86:   U1 = lsqr->vwork_m[1];

     88:   /* vectors of length n */
     89:   X  = ksp->vec_sol;
     90:   W  = lsqr->vwork_n[0];
     91:   V  = lsqr->vwork_n[1];
     92:   V1 = lsqr->vwork_n[2];
     93:   W2 = lsqr->vwork_n[3];
     94:   if (!nopreconditioner) Z = lsqr->vwork_n[4];

     96:   /* standard error vector */
     97:   SE = lsqr->se;
     98:   if (SE) {
     99:     VecGetSize(SE,&size1);
    100:     VecGetSize(X,&size2);
    101:     if (size1 != size2) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Standard error vector (size %d) does not match solution vector (size %d)",size1,size2);
    102:     VecSet(SE,0.0);
    103:   }

    105:   /* Compute initial residual, temporarily use work vector u */
    106:   if (!ksp->guess_zero) {
    107:     KSP_MatMult(ksp,Amat,X,U);       /*   u <- b - Ax     */
    108:     VecAYPX(U,-1.0,B);
    109:   } else {
    110:     VecCopy(B,U);            /*   u <- b (x is 0) */
    111:   }

    113:   /* Test for nothing to do */
    114:   VecNorm(U,NORM_2,&rnorm);
    115:   PetscObjectSAWsTakeAccess((PetscObject)ksp);
    116:   ksp->its   = 0;
    117:   ksp->rnorm = rnorm;
    118:   PetscObjectSAWsGrantAccess((PetscObject)ksp);
    119:   KSPLogResidualHistory(ksp,rnorm);
    120:   KSPMonitor(ksp,0,rnorm);
    121:   (*ksp->converged)(ksp,0,rnorm,&ksp->reason,ksp->cnvP);
    122:   if (ksp->reason) return(0);

    124:   beta = rnorm;
    125:   VecScale(U,1.0/beta);
    126:   KSP_MatMultTranspose(ksp,Amat,U,V);
    127:   if (nopreconditioner) {
    128:     VecNorm(V,NORM_2,&alpha);
    129:   } else {
    130:     PCApply(ksp->pc,V,Z);
    131:     VecDotRealPart(V,Z,&alpha);
    132:     if (alpha <= 0.0) {
    133:       ksp->reason = KSP_DIVERGED_BREAKDOWN;
    134:       return(0);
    135:     }
    136:     alpha = PetscSqrtReal(alpha);
    137:     VecScale(Z,1.0/alpha);
    138:   }
    139:   VecScale(V,1.0/alpha);

    141:   if (nopreconditioner) {
    142:     VecCopy(V,W);
    143:   } else {
    144:     VecCopy(Z,W);
    145:   }

    147:   lsqr->arnorm = alpha * beta;
    148:   phibar       = beta;
    149:   rhobar       = alpha;
    150:   i            = 0;
    151:   do {
    152:     if (nopreconditioner) {
    153:       KSP_MatMult(ksp,Amat,V,U1);
    154:     } else {
    155:       KSP_MatMult(ksp,Amat,Z,U1);
    156:     }
    157:     VecAXPY(U1,-alpha,U);
    158:     VecNorm(U1,NORM_2,&beta);
    159:     if (beta > 0.0) {
    160:       VecScale(U1,1.0/beta); /* beta*U1 = Amat*V - alpha*U */
    161:     }

    163:     KSP_MatMultTranspose(ksp,Amat,U1,V1);
    164:     VecAXPY(V1,-beta,V);
    165:     if (nopreconditioner) {
    166:       VecNorm(V1,NORM_2,&alpha);
    167:     } else {
    168:       PCApply(ksp->pc,V1,Z);
    169:       VecDotRealPart(V1,Z,&alpha);
    170:       if (alpha <= 0.0) {
    171:         ksp->reason = KSP_DIVERGED_BREAKDOWN;
    172:         break;
    173:       }
    174:       alpha = PetscSqrtReal(alpha);
    175:       VecScale(Z,1.0/alpha);
    176:     }
    177:     VecScale(V1,1.0/alpha); /* alpha*V1 = Amat^T*U1 - beta*V */
    178:     rho    = PetscSqrtScalar(rhobar*rhobar + beta*beta);
    179:     c      = rhobar / rho;
    180:     s      = beta / rho;
    181:     theta  = s * alpha;
    182:     rhobar = -c * alpha;
    183:     phi    = c * phibar;
    184:     phibar = s * phibar;
    185:     tau    = s * phi;

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

    189:     if (SE) {
    190:       VecCopy(W,W2);
    191:       VecSquare(W2);
    192:       VecScale(W2,1.0/(rho*rho));
    193:       VecAXPY(SE, 1.0, W2); /* SE <- SE + (w^2/rho^2) */
    194:     }
    195:     if (nopreconditioner) {
    196:       VecAYPX(W,-theta/rho,V1);  /* w <- v - (theta/rho) w */
    197:     } else {
    198:       VecAYPX(W,-theta/rho,Z);   /* w <- z - (theta/rho) w */
    199:     }

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

    204:     PetscObjectSAWsTakeAccess((PetscObject)ksp);
    205:     ksp->its++;
    206:     ksp->rnorm = rnorm;
    207:     PetscObjectSAWsGrantAccess((PetscObject)ksp);
    208:     KSPLogResidualHistory(ksp,rnorm);
    209:     KSPMonitor(ksp,i+1,rnorm);
    210:     (*ksp->converged)(ksp,i+1,rnorm,&ksp->reason,ksp->cnvP);
    211:     if (ksp->reason) break;
    212:     SWAP(U1,U,TMP);
    213:     SWAP(V1,V,TMP);

    215:     i++;
    216:   } while (i<ksp->max_it);
    217:   if (i >= ksp->max_it && !ksp->reason) ksp->reason = KSP_DIVERGED_ITS;

    219:   /* Finish off the standard error estimates */
    220:   if (SE) {
    221:     tmp  = 1.0;
    222:     MatGetSize(Amat,&size1,&size2);
    223:     if (size1 > size2) tmp = size1 - size2;
    224:     tmp  = rnorm / PetscSqrtScalar(tmp);
    225:     VecSqrtAbs(SE);
    226:     VecScale(SE,tmp);
    227:   }
    228:   return(0);
    229: }

    234: PetscErrorCode KSPDestroy_LSQR(KSP ksp)
    235: {
    236:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;

    240:   /* Free work vectors */
    241:   if (lsqr->vwork_n) {
    242:     VecDestroyVecs(lsqr->nwork_n,&lsqr->vwork_n);
    243:   }
    244:   if (lsqr->vwork_m) {
    245:     VecDestroyVecs(lsqr->nwork_m,&lsqr->vwork_m);
    246:   }
    247:   if (lsqr->se_flg) {
    248:     VecDestroy(&lsqr->se);
    249:   }
    250:   PetscFree(ksp->data);
    251:   return(0);
    252: }

    256: PetscErrorCode  KSPLSQRSetStandardErrorVec(KSP ksp, Vec se)
    257: {
    258:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;

    262:   VecDestroy(&lsqr->se);
    263:   lsqr->se = se;
    264:   return(0);
    265: }

    269: PetscErrorCode  KSPLSQRGetStandardErrorVec(KSP ksp,Vec *se)
    270: {
    271:   KSP_LSQR *lsqr = (KSP_LSQR*)ksp->data;

    274:   *se = lsqr->se;
    275:   return(0);
    276: }

    280: PetscErrorCode  KSPLSQRGetArnorm(KSP ksp,PetscReal *arnorm, PetscReal *rhs_norm, PetscReal *anorm)
    281: {
    282:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;

    286:   *arnorm = lsqr->arnorm;
    287:   if (anorm) {
    288:     if (lsqr->anorm < 0.0) {
    289:       PC  pc;
    290:       Mat Amat;
    291:       KSPGetPC(ksp,&pc);
    292:       PCGetOperators(pc,&Amat,NULL);
    293:       MatNorm(Amat,NORM_FROBENIUS,&lsqr->anorm);
    294:     }
    295:     *anorm = lsqr->anorm;
    296:   }
    297:   if (rhs_norm) *rhs_norm = lsqr->rhs_norm;
    298:   return(0);
    299: }

    303: /*@C
    304:    KSPLSQRMonitorDefault - Print the residual norm at each iteration of the LSQR method and the norm of the residual of the normal equations A'*A x = A' b

    306:    Collective on KSP

    308:    Input Parameters:
    309: +  ksp   - iterative context
    310: .  n     - iteration number
    311: .  rnorm - 2-norm (preconditioned) residual value (may be estimated).
    312: -  dummy - unused monitor context

    314:    Level: intermediate

    316: .keywords: KSP, default, monitor, residual

    318: .seealso: KSPMonitorSet(), KSPMonitorTrueResidualNorm(), KSPMonitorLGResidualNormCreate(), KSPMonitorDefault()
    319: @*/
    320: PetscErrorCode  KSPLSQRMonitorDefault(KSP ksp,PetscInt n,PetscReal rnorm,void *dummy)
    321: {
    323:   PetscViewer    viewer = dummy ? (PetscViewer) dummy : PETSC_VIEWER_STDOUT_(PetscObjectComm((PetscObject)ksp));
    324:   KSP_LSQR       *lsqr  = (KSP_LSQR*)ksp->data;

    327:   PetscViewerASCIIAddTab(viewer,((PetscObject)ksp)->tablevel);
    328:   if (((PetscObject)ksp)->prefix) {
    329:     PetscViewerASCIIPrintf(viewer,"  Residual norm and norm of normal equations for %s solve.\n",((PetscObject)ksp)->prefix);
    330:   }
    331:   if (!n) {
    332:     PetscViewerASCIIPrintf(viewer,"%3D KSP Residual norm %14.12e\n",n,rnorm);
    333:   } else {
    334:     PetscViewerASCIIPrintf(viewer,"%3D KSP Residual norm %14.12e Residual norm normal equations %14.12e\n",n,rnorm,lsqr->arnorm);
    335:   }
    336:   PetscViewerASCIISubtractTab(viewer,((PetscObject)ksp)->tablevel);
    337:   return(0);
    338: }

    342: PetscErrorCode KSPSetFromOptions_LSQR(PetscOptions *PetscOptionsObject,KSP ksp)
    343: {
    345:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;
    346:   char           monfilename[PETSC_MAX_PATH_LEN];
    347:   PetscViewer    monviewer;
    348:   PetscBool      flg;

    351:   PetscOptionsHead(PetscOptionsObject,"KSP LSQR Options");
    352:   PetscOptionsName("-ksp_lsqr_set_standard_error","Set Standard Error Estimates of Solution","KSPLSQRSetStandardErrorVec",&lsqr->se_flg);
    353:   PetscOptionsString("-ksp_lsqr_monitor","Monitor residual norm and norm of residual of normal equations","KSPMonitorSet","stdout",monfilename,PETSC_MAX_PATH_LEN,&flg);
    354:   if (flg) {
    355:     PetscViewerASCIIOpen(PetscObjectComm((PetscObject)ksp),monfilename,&monviewer);
    356:     KSPMonitorSet(ksp,KSPLSQRMonitorDefault,monviewer,(PetscErrorCode (*)(void**))PetscViewerDestroy);
    357:   }
    358:   PetscOptionsTail();
    359:   return(0);
    360: }

    364: PetscErrorCode KSPView_LSQR(KSP ksp,PetscViewer viewer)
    365: {
    366:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;
    368:   PetscBool      iascii;

    371:   PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);
    372:   if (iascii) {
    373:     if (lsqr->se) {
    374:       PetscReal rnorm;
    375:       KSPLSQRGetStandardErrorVec(ksp,&lsqr->se);
    376:       VecNorm(lsqr->se,NORM_2,&rnorm);
    377:       PetscViewerASCIIPrintf(viewer,"  Norm of Standard Error %g, Iterations %D\n",(double)rnorm,ksp->its);
    378:     }
    379:   }
    380:   return(0);
    381: }

    385: /*@C
    386:    KSPLSQRDefaultConverged - Determines convergence of the LSQR Krylov method. This calls KSPConvergedDefault() and if that does not determine convergence then checks
    387:       convergence for the least squares problem.

    389:    Collective on KSP

    391:    Input Parameters:
    392: +  ksp   - iterative context
    393: .  n     - iteration number
    394: .  rnorm - 2-norm residual value (may be estimated)
    395: -  ctx - convergence context which must be created by KSPConvergedDefaultCreate()

    397:    reason is set to:
    398: +   positive - if the iteration has converged;
    399: .   negative - if residual norm exceeds divergence threshold;
    400: -   0 - otherwise.

    402:    Notes:
    403:       Possible convergence for the least squares problem (which is based on the residual of the normal equations) are KSP_CONVERGED_RTOL_NORMAL norm and KSP_CONVERGED_ATOL_NORMAL.

    405:    Level: intermediate

    407: .keywords: KSP, default, convergence, residual

    409: .seealso: KSPSetConvergenceTest(), KSPSetTolerances(), KSPConvergedSkip(), KSPConvergedReason, KSPGetConvergedReason(),
    410:           KSPConvergedDefaultSetUIRNorm(), KSPConvergedDefaultSetUMIRNorm(), KSPConvergedDefaultCreate(), KSPConvergedDefaultDestroy(), KSPConvergedDefault()
    411: @*/
    412: PetscErrorCode  KSPLSQRDefaultConverged(KSP ksp,PetscInt n,PetscReal rnorm,KSPConvergedReason *reason,void *ctx)
    413: {
    415:   KSP_LSQR       *lsqr = (KSP_LSQR*)ksp->data;

    418:   KSPConvergedDefault(ksp,n,rnorm,reason,ctx);
    419:   if (!n || *reason) return(0);
    420:   if (lsqr->arnorm/lsqr->rhs_norm < ksp->rtol) *reason = KSP_CONVERGED_RTOL_NORMAL;
    421:   if (lsqr->arnorm < ksp->abstol) *reason = KSP_CONVERGED_ATOL_NORMAL;
    422:   return(0);
    423: }

    427: /*MC
    428:      KSPLSQR - This implements LSQR

    430:    Options Database Keys:
    431: +   -ksp_lsqr_set_standard_error  - Set Standard Error Estimates of Solution see KSPLSQRSetStandardErrorVec()
    432: .   -ksp_lsqr_monitor - Monitor residual norm and norm of residual of normal equations
    433: -   see KSPSolve()

    435:    Level: beginner

    437:    Notes:
    438:      This varient, when applied with no preconditioning is identical to the original algorithm in exact arithematic; however, in practice, with no preconditioning
    439:      due to inexact arithematic, it can converge differently. Hence when no preconditioner is used (PCType PCNONE) it automatically reverts to the original algorithm.

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

    444:      Supports only left preconditioning.

    446:    References:The original unpreconditioned algorithm can be found in Paige and Saunders, ACM Transactions on Mathematical Software, Vol 8, pp 43-71, 1982.
    447:      In exact arithmetic the LSQR method (with no preconditioning) is identical to the KSPCG algorithm applied to the normal equations.
    448:      The preconditioned varient was implemented by Bas van't Hof and is essentially a left preconditioning for the Normal Equations. It appears the implementation with preconditioner
    449:      track the true norm of the residual and uses that in the convergence test.

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

    455:    For least squares problems without a zero to A*x = b, there are additional convergence tests for the residual of the normal equations, A'*(b - Ax), see KSPLSQRDefaultConverged()

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

    459: M*/
    462: PETSC_EXTERN PetscErrorCode KSPCreate_LSQR(KSP ksp)
    463: {
    464:   KSP_LSQR       *lsqr;

    468:   PetscNewLog(ksp,&lsqr);
    469:   lsqr->se     = NULL;
    470:   lsqr->se_flg = PETSC_FALSE;
    471:   lsqr->arnorm = 0.0;
    472:   ksp->data    = (void*)lsqr;
    473:   KSPSetSupportedNorm(ksp,KSP_NORM_UNPRECONDITIONED,PC_LEFT,3);

    475:   ksp->ops->setup          = KSPSetUp_LSQR;
    476:   ksp->ops->solve          = KSPSolve_LSQR;
    477:   ksp->ops->destroy        = KSPDestroy_LSQR;
    478:   ksp->ops->buildsolution  = KSPBuildSolutionDefault;
    479:   ksp->ops->buildresidual  = KSPBuildResidualDefault;
    480:   ksp->ops->setfromoptions = KSPSetFromOptions_LSQR;
    481:   ksp->ops->view           = KSPView_LSQR;
    482:   ksp->converged           = KSPLSQRDefaultConverged;
    483:   return(0);
    484: }

    488: PetscErrorCode  VecSquare(Vec v)
    489: {
    491:   PetscScalar    *x;
    492:   PetscInt       i, n;

    495:   VecGetLocalSize(v, &n);
    496:   VecGetArray(v, &x);
    497:   for (i = 0; i < n; i++) x[i] *= PetscConj(x[i]);
    498:   VecRestoreArray(v, &x);
    499:   return(0);
    500: }