Actual source code: svdsolve.c

  1: /*
  2:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3:    SLEPc - Scalable Library for Eigenvalue Problem Computations
  4:    Copyright (c) 2002-, Universitat Politecnica de Valencia, Spain

  6:    This file is part of SLEPc.
  7:    SLEPc is distributed under a 2-clause BSD license (see LICENSE).
  8:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  9: */
 10: /*
 11:    SVD routines related to the solution process
 12: */

 14: #include <slepc/private/svdimpl.h>
 15: #include <slepc/private/bvimpl.h>

 17: /*
 18:   SVDComputeVectors_Left - Compute left singular vectors as U=A*V.
 19:   Only done if the leftbasis flag is false. Assumes V is available.
 20:  */
 21: PetscErrorCode SVDComputeVectors_Left(SVD svd)
 22: {
 23:   Vec                tl,omega2,u,v,w;
 24:   PetscInt           i,oldsize;
 25:   VecType            vtype;
 26:   const PetscScalar* varray;

 28:   PetscFunctionBegin;
 29:   if (!svd->leftbasis) {
 30:     /* generate left singular vectors on U */
 31:     if (!svd->U) PetscCall(SVDGetBV(svd,NULL,&svd->U));
 32:     PetscCall(BVGetSizes(svd->U,NULL,NULL,&oldsize));
 33:     if (!oldsize) {
 34:       if (!((PetscObject)svd->U)->type_name) PetscCall(BVSetType(svd->U,((PetscObject)svd->V)->type_name));
 35:       PetscCall(MatCreateVecsEmpty(svd->A,NULL,&tl));
 36:       PetscCall(BVSetSizesFromVec(svd->U,tl,svd->ncv));
 37:       PetscCall(VecDestroy(&tl));
 38:     }
 39:     PetscCall(BVSetActiveColumns(svd->V,0,svd->nconv));
 40:     PetscCall(BVSetActiveColumns(svd->U,0,svd->nconv));
 41:     if (!svd->ishyperbolic) PetscCall(BVMatMult(svd->V,svd->A,svd->U));
 42:     else if (svd->swapped) {  /* compute right singular vectors as V=A'*Omega*U */
 43:       PetscCall(MatCreateVecs(svd->A,&w,NULL));
 44:       for (i=0;i<svd->nconv;i++) {
 45:         PetscCall(BVGetColumn(svd->V,i,&v));
 46:         PetscCall(BVGetColumn(svd->U,i,&u));
 47:         PetscCall(VecPointwiseMult(w,v,svd->omega));
 48:         PetscCall(MatMult(svd->A,w,u));
 49:         PetscCall(BVRestoreColumn(svd->V,i,&v));
 50:         PetscCall(BVRestoreColumn(svd->U,i,&u));
 51:       }
 52:       PetscCall(VecDestroy(&w));
 53:     } else {  /* compute left singular vectors as usual U=A*V, and set-up Omega-orthogonalization of U */
 54:       PetscCall(BV_SetMatrixDiagonal(svd->U,svd->omega,svd->A));
 55:       PetscCall(BVMatMult(svd->V,svd->A,svd->U));
 56:     }
 57:     PetscCall(BVOrthogonalize(svd->U,NULL));
 58:     if (svd->ishyperbolic && !svd->swapped) {  /* store signature after Omega-orthogonalization */
 59:       PetscCall(MatGetVecType(svd->A,&vtype));
 60:       PetscCall(VecCreate(PETSC_COMM_SELF,&omega2));
 61:       PetscCall(VecSetSizes(omega2,svd->nconv,svd->nconv));
 62:       PetscCall(VecSetType(omega2,vtype));
 63:       PetscCall(BVGetSignature(svd->U,omega2));
 64:       PetscCall(VecGetArrayRead(omega2,&varray));
 65:       for (i=0;i<svd->nconv;i++) {
 66:         svd->sign[i] = PetscRealPart(varray[i]);
 67:         if (PetscRealPart(varray[i])<0.0) PetscCall(BVScaleColumn(svd->U,i,-1.0));
 68:       }
 69:       PetscCall(VecRestoreArrayRead(omega2,&varray));
 70:       PetscCall(VecDestroy(&omega2));
 71:     }
 72:   }
 73:   PetscFunctionReturn(PETSC_SUCCESS);
 74: }

 76: PetscErrorCode SVDComputeVectors(SVD svd)
 77: {
 78:   PetscFunctionBegin;
 79:   SVDCheckSolved(svd,1);
 80:   if (svd->state==SVD_STATE_SOLVED) PetscTryTypeMethod(svd,computevectors);
 81:   svd->state = SVD_STATE_VECTORS;
 82:   PetscFunctionReturn(PETSC_SUCCESS);
 83: }

 85: /*@
 86:    SVDSolve - Solves the singular value problem.

 88:    Collective

 90:    Input Parameter:
 91: .  svd - the singular value solver context

 93:    Options Database Keys:
 94: +  -svd_view             - print information about the solver used
 95: .  -svd_view_mat0        - view the first matrix ($A$)
 96: .  -svd_view_mat1        - view the second matrix ($B$)
 97: .  -svd_view_signature   - view the signature matrix ($\Omega$)
 98: .  -svd_view_vectors     - view the computed singular vectors
 99: .  -svd_view_values      - view the computed singular values
100: .  -svd_converged_reason - print reason for convergence/divergence, and number of iterations
101: .  -svd_error_absolute   - print absolute errors of each singular triplet
102: .  -svd_error_relative   - print relative errors of each singular triplet
103: -  -svd_error_norm       - print errors relative to the matrix norms of each singular triplet

105:    Notes:
106:    The problem matrices are specified with `SVDSetOperators()`.

108:    `SVDSolve()` will return without generating an error regardless of whether
109:    all requested solutions were computed or not. Call `SVDGetConverged()` to get the
110:    actual number of computed solutions, and `SVDGetConvergedReason()` to determine if
111:    the solver converged or failed and why.

113:    All the command-line options listed above admit an optional argument specifying
114:    the viewer type and options. For instance, use `-svd_view_mat0 binary:amatrix.bin`
115:    to save the $A$ matrix to a binary file, `-svd_view_values draw` to draw the computed
116:    singular values graphically, or `-svd_error_relative :myerr.m:ascii_matlab` to save
117:    the errors in a file that can be executed in Matlab.

119:    Level: beginner

121: .seealso: [](ch:svd), `SVDCreate()`, `SVDSetUp()`, `SVDDestroy()`, `SVDSetOperators()`, `SVDGetConverged()`, `SVDGetConvergedReason()`
122: @*/
123: PetscErrorCode SVDSolve(SVD svd)
124: {
125:   PetscInt       i,m,n,*workperm;

127:   PetscFunctionBegin;
129:   if (svd->state>=SVD_STATE_SOLVED) PetscFunctionReturn(PETSC_SUCCESS);
130:   PetscCall(PetscLogEventBegin(SVD_Solve,svd,0,0,0));

132:   /* call setup */
133:   PetscCall(SVDSetUp(svd));

135:   /* safeguard for matrices with zero rows or columns */
136:   PetscCall(MatGetSize(svd->OP,&m,&n));
137:   if (m == 0 || n == 0) {
138:     svd->nconv  = 0;
139:     svd->reason = SVD_CONVERGED_TOL;
140:     svd->state  = SVD_STATE_SOLVED;
141:     PetscFunctionReturn(PETSC_SUCCESS);
142:   }

144:   svd->its = 0;
145:   svd->nconv = 0;
146:   for (i=0;i<svd->ncv;i++) {
147:     svd->sigma[i]  = 0.0;
148:     svd->errest[i] = 0.0;
149:     svd->perm[i]   = i;
150:   }
151:   PetscCall(SVDViewFromOptions(svd,NULL,"-svd_view_pre"));

153:   switch (svd->problem_type) {
154:     case SVD_STANDARD:
155:       PetscUseTypeMethod(svd,solve);
156:       break;
157:     case SVD_GENERALIZED:
158:       PetscUseTypeMethod(svd,solveg);
159:       break;
160:     case SVD_HYPERBOLIC:
161:       PetscUseTypeMethod(svd,solveh);
162:       break;
163:   }
164:   svd->state = SVD_STATE_SOLVED;

166:   /* sort singular triplets */
167:   if (svd->which == SVD_SMALLEST) PetscCall(PetscSortRealWithPermutation(svd->nconv,svd->sigma,svd->perm));
168:   else {
169:     PetscCall(PetscMalloc1(svd->nconv,&workperm));
170:     for (i=0;i<svd->nconv;i++) workperm[i] = i;
171:     PetscCall(PetscSortRealWithPermutation(svd->nconv,svd->sigma,workperm));
172:     for (i=0;i<svd->nconv;i++) svd->perm[i] = workperm[svd->nconv-i-1];
173:     PetscCall(PetscFree(workperm));
174:   }
175:   PetscCall(PetscLogEventEnd(SVD_Solve,svd,0,0,0));

177:   /* various viewers */
178:   PetscCall(SVDViewFromOptions(svd,NULL,"-svd_view"));
179:   PetscCall(SVDConvergedReasonViewFromOptions(svd));
180:   PetscCall(SVDErrorViewFromOptions(svd));
181:   PetscCall(SVDValuesViewFromOptions(svd));
182:   PetscCall(SVDVectorsViewFromOptions(svd));
183:   PetscCall(MatViewFromOptions(svd->OP,(PetscObject)svd,"-svd_view_mat0"));
184:   if (svd->isgeneralized) PetscCall(MatViewFromOptions(svd->OPb,(PetscObject)svd,"-svd_view_mat1"));
185:   if (svd->ishyperbolic) PetscCall(VecViewFromOptions(svd->omega,(PetscObject)svd,"-svd_view_signature"));

187:   /* Remove the initial subspaces */
188:   svd->nini = 0;
189:   svd->ninil = 0;
190:   PetscFunctionReturn(PETSC_SUCCESS);
191: }

193: /*@
194:    SVDGetIterationNumber - Gets the current iteration number. If the
195:    call to `SVDSolve()` is complete, then it returns the number of iterations
196:    carried out by the solution method.

198:    Not Collective

200:    Input Parameter:
201: .  svd - the singular value solver context

203:    Output Parameter:
204: .  its - number of iterations

206:    Note:
207:    During the $i$-th iteration this call returns $i-1$. If `SVDSolve()` is
208:    complete, then parameter `its` contains either the iteration number at
209:    which convergence was successfully reached, or failure was detected.
210:    Call `SVDGetConvergedReason()` to determine if the solver converged or
211:    failed and why.

213:    Level: intermediate

215: .seealso: [](ch:svd), `SVDGetConvergedReason()`, `SVDSetTolerances()`
216: @*/
217: PetscErrorCode SVDGetIterationNumber(SVD svd,PetscInt *its)
218: {
219:   PetscFunctionBegin;
221:   PetscAssertPointer(its,2);
222:   *its = svd->its;
223:   PetscFunctionReturn(PETSC_SUCCESS);
224: }

226: /*@
227:    SVDGetConvergedReason - Gets the reason why the `SVDSolve()` iteration was
228:    stopped.

230:    Not Collective

232:    Input Parameter:
233: .  svd - the singular value solver context

235:    Output Parameter:
236: .  reason - negative value indicates diverged, positive value converged, see
237:    `SVDConvergedReason` for the possible values

239:    Options Database Key:
240: .  -svd_converged_reason - print reason for convergence/divergence, and number of iterations

242:    Note:
243:    If this routine is called before or doing the `SVDSolve()` the value of
244:    `SVD_CONVERGED_ITERATING` is returned.

246:    Level: intermediate

248: .seealso: [](ch:svd), `SVDSetTolerances()`, `SVDSolve()`, `SVDConvergedReason`
249: @*/
250: PetscErrorCode SVDGetConvergedReason(SVD svd,SVDConvergedReason *reason)
251: {
252:   PetscFunctionBegin;
254:   PetscAssertPointer(reason,2);
255:   SVDCheckSolved(svd,1);
256:   *reason = svd->reason;
257:   PetscFunctionReturn(PETSC_SUCCESS);
258: }

260: /*@
261:    SVDGetConverged - Gets the number of converged singular values.

263:    Not Collective

265:    Input Parameter:
266: .  svd - the singular value solver context

268:    Output Parameter:
269: .  nconv - number of converged singular values

271:    Notes:
272:    This function should be called after `SVDSolve()` has finished.

274:    The value `nconv` may be different from the number of requested solutions
275:    `nsv`, but not larger than `ncv`, see `SVDSetDimensions()`.

277:    Level: beginner

279: .seealso: [](ch:svd), `SVDSetDimensions()`, `SVDSolve()`, `SVDGetSingularTriplet()`
280: @*/
281: PetscErrorCode SVDGetConverged(SVD svd,PetscInt *nconv)
282: {
283:   PetscFunctionBegin;
285:   PetscAssertPointer(nconv,2);
286:   SVDCheckSolved(svd,1);
287:   *nconv = svd->nconv;
288:   PetscFunctionReturn(PETSC_SUCCESS);
289: }

291: /*@
292:    SVDGetSingularTriplet - Gets the `i`-th triplet of the singular value decomposition
293:    as computed by `SVDSolve()`. The solution consists in the singular value and its left
294:    and right singular vectors.

296:    Collective

298:    Input Parameters:
299: +  svd - the singular value solver context
300: -  i   - index of the solution

302:    Output Parameters:
303: +  sigma - singular value
304: .  u     - left singular vector
305: -  v     - right singular vector

307:    Note:
308:    Both `u` or `v` can be `NULL` if singular vectors are not required.
309:    Otherwise, the caller must provide valid `Vec` objects, i.e.,
310:    they must be created by the calling program with e.g. `MatCreateVecs()`.

312:    The index `i` should be a value between 0 and `nconv`-1 (see `SVDGetConverged()`).
313:    Singular triplets are indexed according to the ordering criterion established
314:    with `SVDSetWhichSingularTriplets()`.

316:    In the case of GSVD, the solution consists in three vectors $u$, $v$, $x$ that are
317:    returned as follows. Vector $x$ is returned in the right singular vector
318:    (argument `v`) and has length equal to the number of columns of $A$ and $B$.
319:    The other two vectors are returned stacked on top of each other $[u^*,v^*]^*$ in
320:    the left singular vector argument `u`, with length equal to $m+n$ (number of rows
321:    of $A$ plus number of rows of $B$).

323:    Level: beginner

325: .seealso: [](ch:svd), `SVDSolve()`, `SVDGetConverged()`, `SVDSetWhichSingularTriplets()`
326: @*/
327: PetscErrorCode SVDGetSingularTriplet(SVD svd,PetscInt i,PetscReal *sigma,Vec u,Vec v)
328: {
329:   PetscInt       M,N;
330:   Vec            w;

332:   PetscFunctionBegin;
335:   SVDCheckSolved(svd,1);
338:   PetscCheck(i>=0,PetscObjectComm((PetscObject)svd),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
339:   PetscCheck(i<svd->nconv,PetscObjectComm((PetscObject)svd),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see SVDGetConverged()");
340:   if (sigma) *sigma = svd->sigma[svd->perm[i]];
341:   if (u || v) {
342:     if (!svd->isgeneralized) {
343:       PetscCall(MatGetSize(svd->OP,&M,&N));
344:       if (M<N) { w = u; u = v; v = w; }
345:     }
346:     PetscCall(SVDComputeVectors(svd));
347:     if (u) PetscCall(BVCopyVec(svd->U,svd->perm[i],u));
348:     if (v) PetscCall(BVCopyVec(svd->V,svd->perm[i],v));
349:   }
350:   PetscFunctionReturn(PETSC_SUCCESS);
351: }

353: /*
354:    SVDComputeResidualNorms_Standard - Computes the norms of the left and
355:    right residuals associated with the i-th computed singular triplet.

357:    Input Parameters:
358:      sigma - singular value
359:      u,v   - singular vectors
360:      x,y   - two work vectors with the same dimensions as u,v
361: */
362: static PetscErrorCode SVDComputeResidualNorms_Standard(SVD svd,PetscReal sigma,Vec u,Vec v,Vec x,Vec y,PetscReal *norm1,PetscReal *norm2)
363: {
364:   PetscInt       M,N;

366:   PetscFunctionBegin;
367:   /* norm1 = ||A*v-sigma*u||_2 */
368:   if (norm1) {
369:     PetscCall(MatMult(svd->OP,v,x));
370:     PetscCall(VecAXPY(x,-sigma,u));
371:     PetscCall(VecNorm(x,NORM_2,norm1));
372:   }
373:   /* norm2 = ||A^T*u-sigma*v||_2 */
374:   if (norm2) {
375:     PetscCall(MatGetSize(svd->OP,&M,&N));
376:     if (M<N) PetscCall(MatMult(svd->A,u,y));
377:     else PetscCall(MatMult(svd->AT,u,y));
378:     PetscCall(VecAXPY(y,-sigma,v));
379:     PetscCall(VecNorm(y,NORM_2,norm2));
380:   }
381:   PetscFunctionReturn(PETSC_SUCCESS);
382: }

384: /*
385:    SVDComputeResidualNorms_Generalized - In GSVD, compute the residual norms
386:    norm1 = ||s^2*A'*u-c*B'*B*x||_2 and norm2 = ||c^2*B'*v-s*A'*A*x||_2.

388:    Input Parameters:
389:      sigma - singular value
390:      uv    - left singular vectors [u;v]
391:      x     - right singular vector
392:      y,z   - two work vectors with the same dimension as x
393: */
394: static PetscErrorCode SVDComputeResidualNorms_Generalized(SVD svd,PetscReal sigma,Vec uv,Vec x,Vec y,Vec z,PetscReal *norm1,PetscReal *norm2)
395: {
396:   Vec            u,v,ax,bx,nest,aux[2];
397:   PetscReal      c,s;

399:   PetscFunctionBegin;
400:   PetscCall(MatCreateVecs(svd->OP,NULL,&u));
401:   PetscCall(MatCreateVecs(svd->OPb,NULL,&v));
402:   aux[0] = u;
403:   aux[1] = v;
404:   PetscCall(VecCreateNest(PetscObjectComm((PetscObject)svd),2,NULL,aux,&nest));
405:   PetscCall(VecCopy(uv,nest));

407:   s = 1.0/PetscSqrtReal(1.0+sigma*sigma);
408:   c = sigma*s;

410:   /* norm1 = ||s^2*A'*u-c*B'*B*x||_2 */
411:   if (norm1) {
412:     PetscCall(VecDuplicate(v,&bx));
413:     PetscCall(MatMultHermitianTranspose(svd->OP,u,z));
414:     PetscCall(MatMult(svd->OPb,x,bx));
415:     PetscCall(MatMultHermitianTranspose(svd->OPb,bx,y));
416:     PetscCall(VecAXPBY(y,s*s,-c,z));
417:     PetscCall(VecNorm(y,NORM_2,norm1));
418:     PetscCall(VecDestroy(&bx));
419:   }
420:   /* norm2 = ||c^2*B'*v-s*A'*A*x||_2 */
421:   if (norm2) {
422:     PetscCall(VecDuplicate(u,&ax));
423:     PetscCall(MatMultHermitianTranspose(svd->OPb,v,z));
424:     PetscCall(MatMult(svd->OP,x,ax));
425:     PetscCall(MatMultHermitianTranspose(svd->OP,ax,y));
426:     PetscCall(VecAXPBY(y,c*c,-s,z));
427:     PetscCall(VecNorm(y,NORM_2,norm2));
428:     PetscCall(VecDestroy(&ax));
429:   }

431:   PetscCall(VecDestroy(&v));
432:   PetscCall(VecDestroy(&u));
433:   PetscCall(VecDestroy(&nest));
434:   PetscFunctionReturn(PETSC_SUCCESS);
435: }

437: /*
438:    SVDComputeResidualNorms_Hyperbolic - Computes the norms of the left and
439:    right residuals associated with the i-th computed singular triplet.

441:    Input Parameters:
442:      sigma - singular value
443:      sign  - corresponding element of the signature Omega2
444:      u,v   - singular vectors
445:      x,y,z - three work vectors with the same dimensions as u,v,u
446: */
447: static PetscErrorCode SVDComputeResidualNorms_Hyperbolic(SVD svd,PetscReal sigma,PetscReal sign,Vec u,Vec v,Vec x,Vec y,Vec z,PetscReal *norm1,PetscReal *norm2)
448: {
449:   PetscInt       M,N;

451:   PetscFunctionBegin;
452:   /* norm1 = ||A*v-sigma*u||_2 */
453:   if (norm1) {
454:     PetscCall(MatMult(svd->OP,v,x));
455:     PetscCall(VecAXPY(x,-sigma,u));
456:     PetscCall(VecNorm(x,NORM_2,norm1));
457:   }
458:   /* norm2 = ||A^T*Omega*u-sigma*sign*v||_2 */
459:   if (norm2) {
460:     PetscCall(MatGetSize(svd->OP,&M,&N));
461:     PetscCall(VecPointwiseMult(z,u,svd->omega));
462:     if (M<N) PetscCall(MatMult(svd->A,z,y));
463:     else PetscCall(MatMult(svd->AT,z,y));
464:     PetscCall(VecAXPY(y,-sigma*sign,v));
465:     PetscCall(VecNorm(y,NORM_2,norm2));
466:   }
467:   PetscFunctionReturn(PETSC_SUCCESS);
468: }

470: /*@
471:    SVDComputeError - Computes the error (based on the residual norm) associated
472:    with the `i`-th singular triplet.

474:    Collective

476:    Input Parameters:
477: +  svd  - the singular value solver context
478: .  i    - the solution index
479: -  type - the type of error to compute, see `SVDErrorType`

481:    Output Parameter:
482: .  error - the error

484:    Notes:
485:    The error can be computed in various ways, all of them based on the residual
486:    norm obtained as $\sqrt{\eta_1^2+\eta_2^2}$ with $\eta_1 = \|Av-\sigma u\|_2$ and
487:    $\eta_2 = \|A^*u-\sigma v\|_2$, where $(\sigma,u,v)$ is the approximate singular
488:    triplet.

490:    In the case of the GSVD, the two components of the residual norm are
491:    $\eta_1 = \|s^2 A^*u-cB^*Bx\|_2$ and $\eta_2 = ||c^2 B^*v-sA^*Ax||_2$, where
492:    $(\sigma,u,v,x)$ is the approximate generalized singular quadruple, with
493:    $\sigma=c/s$.

495:    Level: beginner

497: .seealso: [](ch:svd), `SVDErrorType`, `SVDSolve()`
498: @*/
499: PetscErrorCode SVDComputeError(SVD svd,PetscInt i,SVDErrorType type,PetscReal *error)
500: {
501:   PetscReal      sigma,norm1,norm2,c,s;
502:   Vec            u=NULL,v=NULL,x=NULL,y=NULL,z=NULL;
503:   PetscReal      vecnorm=1.0;

505:   PetscFunctionBegin;
509:   PetscAssertPointer(error,4);
510:   SVDCheckSolved(svd,1);

512:   /* allocate work vectors */
513:   switch (svd->problem_type) {
514:     case SVD_STANDARD:
515:       PetscCall(SVDSetWorkVecs(svd,2,2));
516:       u = svd->workl[0];
517:       v = svd->workr[0];
518:       x = svd->workl[1];
519:       y = svd->workr[1];
520:       break;
521:     case SVD_GENERALIZED:
522:       PetscCall(SVDSetWorkVecs(svd,1,3));
523:       u = svd->workl[0];
524:       v = svd->workr[0];
525:       x = svd->workr[1];
526:       y = svd->workr[2];
527:       break;
528:     case SVD_HYPERBOLIC:
529:       PetscCall(SVDSetWorkVecs(svd,3,2));
530:       u = svd->workl[0];
531:       v = svd->workr[0];
532:       x = svd->workl[1];
533:       y = svd->workr[1];
534:       z = svd->workl[2];
535:       break;
536:   }

538:   /* compute residual norm */
539:   PetscCall(SVDGetSingularTriplet(svd,i,&sigma,u,v));
540:   switch (svd->problem_type) {
541:     case SVD_STANDARD:
542:       PetscCall(SVDComputeResidualNorms_Standard(svd,sigma,u,v,x,y,&norm1,&norm2));
543:       break;
544:     case SVD_GENERALIZED:
545:       PetscCall(SVDComputeResidualNorms_Generalized(svd,sigma,u,v,x,y,&norm1,&norm2));
546:       break;
547:     case SVD_HYPERBOLIC:
548:       PetscCall(SVDComputeResidualNorms_Hyperbolic(svd,sigma,svd->sign[svd->perm[i]],u,v,x,y,z,&norm1,&norm2));
549:       break;
550:   }
551:   *error = SlepcAbs(norm1,norm2);

553:   /* compute 2-norm of eigenvector of the cyclic form */
554:   if (type!=SVD_ERROR_ABSOLUTE) {
555:     switch (svd->problem_type) {
556:       case SVD_STANDARD:
557:         vecnorm = PETSC_SQRT2;
558:         break;
559:       case SVD_GENERALIZED:
560:         PetscCall(VecNorm(v,NORM_2,&vecnorm));
561:         vecnorm = PetscSqrtReal(1.0+vecnorm*vecnorm);
562:         break;
563:       case SVD_HYPERBOLIC:
564:         PetscCall(VecNorm(u,NORM_2,&vecnorm));
565:         vecnorm = PetscSqrtReal(1.0+vecnorm*vecnorm);
566:         break;
567:     }
568:   }

570:   /* compute error */
571:   switch (type) {
572:     case SVD_ERROR_ABSOLUTE:
573:       break;
574:     case SVD_ERROR_RELATIVE:
575:       if (svd->isgeneralized) {
576:         s = 1.0/PetscSqrtReal(1.0+sigma*sigma);
577:         c = sigma*s;
578:         norm1 /= c*vecnorm;
579:         norm2 /= s*vecnorm;
580:         *error = PetscMax(norm1,norm2);
581:       } else *error /= sigma*vecnorm;
582:       break;
583:     case SVD_ERROR_NORM:
584:       if (!svd->nrma) PetscCall(MatNorm(svd->OP,NORM_INFINITY,&svd->nrma));
585:       if (svd->isgeneralized && !svd->nrmb) PetscCall(MatNorm(svd->OPb,NORM_INFINITY,&svd->nrmb));
586:       *error /= PetscMax(svd->nrma,svd->nrmb)*vecnorm;
587:       break;
588:     default:
589:       SETERRQ(PetscObjectComm((PetscObject)svd),PETSC_ERR_ARG_OUTOFRANGE,"Invalid error type");
590:   }
591:   PetscFunctionReturn(PETSC_SUCCESS);
592: }