Date: 2026-03-31T13:23:09+00:00

Actual source code: rsvd.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:    SLEPc singular value solver: "randomized"

     13:    Method: RSVD

     15:    Algorithm:

     17:        Randomized singular value decomposition.

     19:    References:

     21:        [1] N. Halko, P.-G. Martinsson, and J. A. Tropp, "Finding
     22:            structure with randomness: Probabilistic algorithms for
     23:            constructing approximate matrix decompositions", SIAM Rev.,
     24:            53(2):217-288, 2011.
     25: */

     27: #include <slepc/private/svdimpl.h>

     29: static PetscErrorCode SVDSetUp_Randomized(SVD svd)
     30: {
     31:   PetscInt       N;

     33:   PetscFunctionBegin;
     34:   SVDCheckStandard(svd);
     35:   SVDCheckDefinite(svd);
     36:   SVDCheckUnsupported(svd,SVD_FEATURE_THRESHOLD);
     37:   PetscCheck(svd->which==SVD_LARGEST,PetscObjectComm((PetscObject)svd),PETSC_ERR_SUP,"This solver supports only largest singular values");
     38:   PetscCall(MatGetSize(svd->A,NULL,&N));
     39:   PetscCall(SVDSetDimensions_Default(svd));
     40:   PetscCheck(svd->ncv>=svd->nsv,PetscObjectComm((PetscObject)svd),PETSC_ERR_USER_INPUT,"The value of ncv must not be smaller than nsv");
     41:   if (svd->max_it==PETSC_DETERMINE) svd->max_it = PetscMax(N/svd->ncv,100);
     42:   svd->leftbasis = PETSC_TRUE;
     43:   svd->mpd = svd->ncv;
     44:   PetscCall(SVDAllocateSolution(svd,0));
     45:   PetscCall(DSSetType(svd->ds,DSSVD));
     46:   PetscCall(DSAllocate(svd->ds,svd->ncv));
     47:   PetscCall(SVDSetWorkVecs(svd,1,1));
     48:   PetscFunctionReturn(PETSC_SUCCESS);
     49: }

     51: static PetscErrorCode SVDRandomizedResidualNorm(SVD svd,PetscInt i,PetscScalar sigma,PetscReal *res)
     52: {
     53:   PetscReal      norm1,norm2;
     54:   Vec            u,v,wu,wv;

     56:   PetscFunctionBegin;
     57:   *res = 1.0;
     58:   if (svd->conv!=SVD_CONV_MAXIT) {
     59:     wu = svd->swapped? svd->workr[0]: svd->workl[0];
     60:     wv = svd->swapped? svd->workl[0]: svd->workr[0];
     61:     PetscCall(BVGetColumn(svd->V,i,&v));
     62:     PetscCall(BVGetColumn(svd->U,i,&u));
     63:     /* norm1 = ||A*v-sigma*u||_2 */
     64:     PetscCall(MatMult(svd->A,v,wu));
     65:     PetscCall(VecAXPY(wu,-sigma,u));
     66:     PetscCall(VecNorm(wu,NORM_2,&norm1));
     67:     /* norm2 = ||A^T*u-sigma*v||_2 */
     68:     PetscCall(MatMult(svd->AT,u,wv));
     69:     PetscCall(VecAXPY(wv,-sigma,v));
     70:     PetscCall(VecNorm(wv,NORM_2,&norm2));
     71:     PetscCall(BVRestoreColumn(svd->V,i,&v));
     72:     PetscCall(BVRestoreColumn(svd->U,i,&u));
     73:     *res = SlepcAbs(norm1,norm2);
     74:   }
     75:   PetscFunctionReturn(PETSC_SUCCESS);
     76: }

     78: /* If A is a virtual Hermitian transpose, then BVMatMult will fail if PRODUCT_AhB is not implemented */
     79: static PetscErrorCode BlockMatMult(BV V,Mat A,BV Y,Mat AT)
     80: {
     81:   PetscFunctionBegin;
     82:   if (!PetscDefined(USE_COMPLEX)) PetscCall(BVMatMult(V,A,Y));
     83:   else {
     84:     PetscBool flg=PETSC_FALSE;
     85:     PetscCall(PetscObjectTypeCompare((PetscObject)A,MATHERMITIANTRANSPOSEVIRTUAL,&flg));
     86:     if (flg) PetscCall(BVMatMultHermitianTranspose(V,AT,Y));
     87:     else PetscCall(BVMatMult(V,A,Y));
     88:   }
     89:   PetscFunctionReturn(PETSC_SUCCESS);
     90: }

     92: static PetscErrorCode SVDSolve_Randomized(SVD svd)
     93: {
     94:   PetscScalar    *w;
     95:   PetscReal      res=1.0;
     96:   PetscInt       i,k=0;
     97:   Mat            A,U,V;

     99:   PetscFunctionBegin;
    100:   /* Form random matrix, G. Complete the initial basis with random vectors */
    101:   PetscCall(BVSetActiveColumns(svd->V,svd->nini,svd->ncv));
    102:   PetscCall(BVSetRandomNormal(svd->V));
    103:   PetscCall(PetscCalloc1(svd->ncv,&w));

    105:   /* Subspace Iteration */
    106:   do {
    107:     svd->its++;
    108:     PetscCall(BVSetActiveColumns(svd->V,svd->nconv,svd->ncv));
    109:     PetscCall(BVSetActiveColumns(svd->U,svd->nconv,svd->ncv));
    110:     /* Form AG */
    111:     PetscCall(BlockMatMult(svd->V,svd->A,svd->U,svd->AT));
    112:     /* Orthogonalization Q=qr(AG)*/
    113:     PetscCall(BVOrthogonalize(svd->U,NULL));
    114:     /* Form B^*= AQ */
    115:     PetscCall(BlockMatMult(svd->U,svd->AT,svd->V,svd->A));

    117:     PetscCall(DSSetDimensions(svd->ds,svd->ncv,svd->nconv,svd->ncv));
    118:     PetscCall(DSSVDSetDimensions(svd->ds,svd->ncv));
    119:     PetscCall(DSGetMat(svd->ds,DS_MAT_A,&A));
    120:     PetscCall(MatZeroEntries(A));
    121:     PetscCall(BVOrthogonalize(svd->V,A));
    122:     PetscCall(DSRestoreMat(svd->ds,DS_MAT_A,&A));
    123:     PetscCall(DSSetState(svd->ds,DS_STATE_RAW));
    124:     PetscCall(DSSolve(svd->ds,w,NULL));
    125:     PetscCall(DSSort(svd->ds,w,NULL,NULL,NULL,NULL));
    126:     PetscCall(DSSynchronize(svd->ds,w,NULL));
    127:     PetscCall(DSGetMat(svd->ds,DS_MAT_U,&U));
    128:     PetscCall(DSGetMat(svd->ds,DS_MAT_V,&V));
    129:     PetscCall(BVMultInPlace(svd->U,V,svd->nconv,svd->ncv));
    130:     PetscCall(BVMultInPlace(svd->V,U,svd->nconv,svd->ncv));
    131:     PetscCall(DSRestoreMat(svd->ds,DS_MAT_U,&U));
    132:     PetscCall(DSRestoreMat(svd->ds,DS_MAT_V,&V));
    133:     /* Check convergence */
    134:     k = 0;
    135:     for (i=svd->nconv;i<svd->ncv;i++) {
    136:       PetscCall(SVDRandomizedResidualNorm(svd,i,w[i],&res));
    137:       svd->sigma[i] = PetscRealPart(w[i]);
    138:       PetscCall((*svd->converged)(svd,svd->sigma[i],res,&svd->errest[i],svd->convergedctx));
    139:       if (svd->errest[i] < svd->tol) k++;
    140:       else break;
    141:     }
    142:     if (svd->conv == SVD_CONV_MAXIT && svd->its >= svd->max_it) {
    143:       k = svd->nsv;
    144:       for (i=0;i<svd->ncv;i++) svd->sigma[i] = PetscRealPart(w[i]);
    145:     }
    146:     PetscCall((*svd->stopping)(svd,svd->its,svd->max_it,svd->nconv+k,svd->nsv,&svd->reason,svd->stoppingctx));
    147:     svd->nconv += k;
    148:     PetscCall(SVDMonitor(svd,svd->its,svd->nconv,svd->sigma,svd->errest,svd->ncv));
    149:   } while (svd->reason == SVD_CONVERGED_ITERATING);
    150:   PetscCall(PetscFree(w));
    151:   PetscFunctionReturn(PETSC_SUCCESS);
    152: }

    154: /*MC
    155:    SVDRANDOMIZED - SVDRANDOMIZED = "randomized" - A basic randomization-based
    156:    singular value solver.

    158:    Notes:
    159:    Only available for standard SVD problems.

    161:    This solver relies on randomization techniques described in {cite:p}`Hal11`.
    162:    It is different from other solvers in the sense that it assumes that the
    163:    input matrix has low rank, in which case the computation will be very fast.
    164:    Otherwise, the solver will proceed iteratively as a subspace iteration
    165:    solver (which may have slow convergence), until the required accuracy is reached.
    166:    Alternatively, it is possible to set a limited number of iterations (e.g., 1)
    167:    and the solver will return approximate bases of the dominant singular spaces.
    168:    Note that in this latter case, the individual singular vectors are not good
    169:    approximations, so residuals are expected to be large.

    171:    Level: beginner

    173: .seealso: [](ch:svd), [](#sec:svdback), `SVD`, `SVDType`, `SVDSetType()`, `SVDSetProblemType()`, `SVDTRLANCZOS`
    174: M*/
    175: SLEPC_EXTERN PetscErrorCode SVDCreate_Randomized(SVD svd)
    176: {
    177:   PetscFunctionBegin;
    178:   svd->ops->setup          = SVDSetUp_Randomized;
    179:   svd->ops->solve          = SVDSolve_Randomized;
    180:   PetscFunctionReturn(PETSC_SUCCESS);
    181: }