1 2 #include <petsc/private/pcimpl.h> /*I "petscpc.h" I*/ 3 #include <petscblaslapack.h> 4 5 /* 6 Private context (data structure) for the SVD preconditioner. 7 */ 8 typedef struct { 9 Vec diag, work; 10 Mat A, U, Vt; 11 PetscInt nzero; 12 PetscReal zerosing; /* measure of smallest singular value treated as nonzero */ 13 PetscInt essrank; /* essential rank of operator */ 14 VecScatter left2red, right2red; 15 Vec leftred, rightred; 16 PetscViewer monitor; 17 PetscViewerFormat monitorformat; 18 } PC_SVD; 19 20 typedef enum { 21 READ = 1, 22 WRITE = 2, 23 READ_WRITE = 3 24 } AccessMode; 25 26 /* 27 PCSetUp_SVD - Prepares for the use of the SVD preconditioner 28 by setting data structures and options. 29 30 Input Parameter: 31 . pc - the preconditioner context 32 33 Application Interface Routine: PCSetUp() 34 35 Note: 36 The interface routine PCSetUp() is not usually called directly by 37 the user, but instead is called by PCApply() if necessary. 38 */ 39 static PetscErrorCode PCSetUp_SVD(PC pc) { 40 PC_SVD *jac = (PC_SVD *)pc->data; 41 PetscScalar *a, *u, *v, *d, *work; 42 PetscBLASInt nb, lwork; 43 PetscInt i, n; 44 PetscMPIInt size; 45 46 PetscFunctionBegin; 47 PetscCall(MatDestroy(&jac->A)); 48 PetscCallMPI(MPI_Comm_size(((PetscObject)pc->pmat)->comm, &size)); 49 if (size > 1) { 50 Mat redmat; 51 52 PetscCall(MatCreateRedundantMatrix(pc->pmat, size, PETSC_COMM_SELF, MAT_INITIAL_MATRIX, &redmat)); 53 PetscCall(MatConvert(redmat, MATSEQDENSE, MAT_INITIAL_MATRIX, &jac->A)); 54 PetscCall(MatDestroy(&redmat)); 55 } else { 56 PetscCall(MatConvert(pc->pmat, MATSEQDENSE, MAT_INITIAL_MATRIX, &jac->A)); 57 } 58 if (!jac->diag) { /* assume square matrices */ 59 PetscCall(MatCreateVecs(jac->A, &jac->diag, &jac->work)); 60 } 61 if (!jac->U) { 62 PetscCall(MatDuplicate(jac->A, MAT_DO_NOT_COPY_VALUES, &jac->U)); 63 PetscCall(MatDuplicate(jac->A, MAT_DO_NOT_COPY_VALUES, &jac->Vt)); 64 } 65 PetscCall(MatGetSize(jac->A, &n, NULL)); 66 if (!n) { 67 PetscCall(PetscInfo(pc, "Matrix has zero rows, skipping svd\n")); 68 PetscFunctionReturn(0); 69 } 70 PetscCall(PetscBLASIntCast(n, &nb)); 71 lwork = 5 * nb; 72 PetscCall(PetscMalloc1(lwork, &work)); 73 PetscCall(MatDenseGetArray(jac->A, &a)); 74 PetscCall(MatDenseGetArray(jac->U, &u)); 75 PetscCall(MatDenseGetArray(jac->Vt, &v)); 76 PetscCall(VecGetArray(jac->diag, &d)); 77 #if !defined(PETSC_USE_COMPLEX) 78 { 79 PetscBLASInt lierr; 80 PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF)); 81 PetscCallBLAS("LAPACKgesvd", LAPACKgesvd_("A", "A", &nb, &nb, a, &nb, d, u, &nb, v, &nb, work, &lwork, &lierr)); 82 PetscCheck(!lierr, PETSC_COMM_SELF, PETSC_ERR_LIB, "gesv() error %" PetscBLASInt_FMT, lierr); 83 PetscCall(PetscFPTrapPop()); 84 } 85 #else 86 { 87 PetscBLASInt lierr; 88 PetscReal *rwork, *dd; 89 PetscCall(PetscMalloc1(5 * nb, &rwork)); 90 PetscCall(PetscMalloc1(nb, &dd)); 91 PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF)); 92 PetscCallBLAS("LAPACKgesvd", LAPACKgesvd_("A", "A", &nb, &nb, a, &nb, dd, u, &nb, v, &nb, work, &lwork, rwork, &lierr)); 93 PetscCheck(!lierr, PETSC_COMM_SELF, PETSC_ERR_LIB, "gesv() error %" PetscBLASInt_FMT, lierr); 94 PetscCall(PetscFree(rwork)); 95 for (i = 0; i < n; i++) d[i] = dd[i]; 96 PetscCall(PetscFree(dd)); 97 PetscCall(PetscFPTrapPop()); 98 } 99 #endif 100 PetscCall(MatDenseRestoreArray(jac->A, &a)); 101 PetscCall(MatDenseRestoreArray(jac->U, &u)); 102 PetscCall(MatDenseRestoreArray(jac->Vt, &v)); 103 for (i = n - 1; i >= 0; i--) 104 if (PetscRealPart(d[i]) > jac->zerosing) break; 105 jac->nzero = n - 1 - i; 106 if (jac->monitor) { 107 PetscCall(PetscViewerASCIIAddTab(jac->monitor, ((PetscObject)pc)->tablevel)); 108 PetscCall(PetscViewerASCIIPrintf(jac->monitor, " SVD: condition number %14.12e, %" PetscInt_FMT " of %" PetscInt_FMT " singular values are (nearly) zero\n", (double)PetscRealPart(d[0] / d[n - 1]), jac->nzero, n)); 109 if (n < 10 || jac->monitorformat == PETSC_VIEWER_ALL) { 110 PetscCall(PetscViewerASCIIPrintf(jac->monitor, " SVD: singular values:\n")); 111 for (i = 0; i < n; i++) { 112 if (i % 5 == 0) { 113 if (i != 0) PetscCall(PetscViewerASCIIPrintf(jac->monitor, "\n")); 114 PetscCall(PetscViewerASCIIPrintf(jac->monitor, " ")); 115 } 116 PetscCall(PetscViewerASCIIPrintf(jac->monitor, " %14.12e", (double)PetscRealPart(d[i]))); 117 } 118 PetscCall(PetscViewerASCIIPrintf(jac->monitor, "\n")); 119 } else { /* print 5 smallest and 5 largest */ 120 PetscCall(PetscViewerASCIIPrintf(jac->monitor, " SVD: smallest singular values: %14.12e %14.12e %14.12e %14.12e %14.12e\n", (double)PetscRealPart(d[n - 1]), (double)PetscRealPart(d[n - 2]), (double)PetscRealPart(d[n - 3]), (double)PetscRealPart(d[n - 4]), (double)PetscRealPart(d[n - 5]))); 121 PetscCall(PetscViewerASCIIPrintf(jac->monitor, " SVD: largest singular values : %14.12e %14.12e %14.12e %14.12e %14.12e\n", (double)PetscRealPart(d[4]), (double)PetscRealPart(d[3]), (double)PetscRealPart(d[2]), (double)PetscRealPart(d[1]), (double)PetscRealPart(d[0]))); 122 } 123 PetscCall(PetscViewerASCIISubtractTab(jac->monitor, ((PetscObject)pc)->tablevel)); 124 } 125 PetscCall(PetscInfo(pc, "Largest and smallest singular values %14.12e %14.12e\n", (double)PetscRealPart(d[0]), (double)PetscRealPart(d[n - 1]))); 126 for (i = 0; i < n - jac->nzero; i++) d[i] = 1.0 / d[i]; 127 for (; i < n; i++) d[i] = 0.0; 128 if (jac->essrank > 0) 129 for (i = 0; i < n - jac->nzero - jac->essrank; i++) d[i] = 0.0; /* Skip all but essrank eigenvalues */ 130 PetscCall(PetscInfo(pc, "Number of zero or nearly singular values %" PetscInt_FMT "\n", jac->nzero)); 131 PetscCall(VecRestoreArray(jac->diag, &d)); 132 PetscCall(PetscFree(work)); 133 PetscFunctionReturn(0); 134 } 135 136 static PetscErrorCode PCSVDGetVec(PC pc, PCSide side, AccessMode amode, Vec x, Vec *xred) { 137 PC_SVD *jac = (PC_SVD *)pc->data; 138 PetscMPIInt size; 139 140 PetscFunctionBegin; 141 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size)); 142 *xred = NULL; 143 switch (side) { 144 case PC_LEFT: 145 if (size == 1) *xred = x; 146 else { 147 if (!jac->left2red) PetscCall(VecScatterCreateToAll(x, &jac->left2red, &jac->leftred)); 148 if (amode & READ) { 149 PetscCall(VecScatterBegin(jac->left2red, x, jac->leftred, INSERT_VALUES, SCATTER_FORWARD)); 150 PetscCall(VecScatterEnd(jac->left2red, x, jac->leftred, INSERT_VALUES, SCATTER_FORWARD)); 151 } 152 *xred = jac->leftred; 153 } 154 break; 155 case PC_RIGHT: 156 if (size == 1) *xred = x; 157 else { 158 if (!jac->right2red) PetscCall(VecScatterCreateToAll(x, &jac->right2red, &jac->rightred)); 159 if (amode & READ) { 160 PetscCall(VecScatterBegin(jac->right2red, x, jac->rightred, INSERT_VALUES, SCATTER_FORWARD)); 161 PetscCall(VecScatterEnd(jac->right2red, x, jac->rightred, INSERT_VALUES, SCATTER_FORWARD)); 162 } 163 *xred = jac->rightred; 164 } 165 break; 166 default: SETERRQ(PetscObjectComm((PetscObject)pc), PETSC_ERR_PLIB, "Side must be LEFT or RIGHT"); 167 } 168 PetscFunctionReturn(0); 169 } 170 171 static PetscErrorCode PCSVDRestoreVec(PC pc, PCSide side, AccessMode amode, Vec x, Vec *xred) { 172 PC_SVD *jac = (PC_SVD *)pc->data; 173 PetscMPIInt size; 174 175 PetscFunctionBegin; 176 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size)); 177 switch (side) { 178 case PC_LEFT: 179 if (size != 1 && amode & WRITE) { 180 PetscCall(VecScatterBegin(jac->left2red, jac->leftred, x, INSERT_VALUES, SCATTER_REVERSE)); 181 PetscCall(VecScatterEnd(jac->left2red, jac->leftred, x, INSERT_VALUES, SCATTER_REVERSE)); 182 } 183 break; 184 case PC_RIGHT: 185 if (size != 1 && amode & WRITE) { 186 PetscCall(VecScatterBegin(jac->right2red, jac->rightred, x, INSERT_VALUES, SCATTER_REVERSE)); 187 PetscCall(VecScatterEnd(jac->right2red, jac->rightred, x, INSERT_VALUES, SCATTER_REVERSE)); 188 } 189 break; 190 default: SETERRQ(PetscObjectComm((PetscObject)pc), PETSC_ERR_PLIB, "Side must be LEFT or RIGHT"); 191 } 192 *xred = NULL; 193 PetscFunctionReturn(0); 194 } 195 196 /* 197 PCApply_SVD - Applies the SVD preconditioner to a vector. 198 199 Input Parameters: 200 . pc - the preconditioner context 201 . x - input vector 202 203 Output Parameter: 204 . y - output vector 205 206 Application Interface Routine: PCApply() 207 */ 208 static PetscErrorCode PCApply_SVD(PC pc, Vec x, Vec y) { 209 PC_SVD *jac = (PC_SVD *)pc->data; 210 Vec work = jac->work, xred, yred; 211 212 PetscFunctionBegin; 213 PetscCall(PCSVDGetVec(pc, PC_RIGHT, READ, x, &xred)); 214 PetscCall(PCSVDGetVec(pc, PC_LEFT, WRITE, y, &yred)); 215 #if !defined(PETSC_USE_COMPLEX) 216 PetscCall(MatMultTranspose(jac->U, xred, work)); 217 #else 218 PetscCall(MatMultHermitianTranspose(jac->U, xred, work)); 219 #endif 220 PetscCall(VecPointwiseMult(work, work, jac->diag)); 221 #if !defined(PETSC_USE_COMPLEX) 222 PetscCall(MatMultTranspose(jac->Vt, work, yred)); 223 #else 224 PetscCall(MatMultHermitianTranspose(jac->Vt, work, yred)); 225 #endif 226 PetscCall(PCSVDRestoreVec(pc, PC_RIGHT, READ, x, &xred)); 227 PetscCall(PCSVDRestoreVec(pc, PC_LEFT, WRITE, y, &yred)); 228 PetscFunctionReturn(0); 229 } 230 231 static PetscErrorCode PCMatApply_SVD(PC pc, Mat X, Mat Y) { 232 PC_SVD *jac = (PC_SVD *)pc->data; 233 Mat W; 234 235 PetscFunctionBegin; 236 PetscCall(MatTransposeMatMult(jac->U, X, MAT_INITIAL_MATRIX, PETSC_DEFAULT, &W)); 237 PetscCall(MatDiagonalScale(W, jac->diag, NULL)); 238 PetscCall(MatTransposeMatMult(jac->Vt, W, MAT_REUSE_MATRIX, PETSC_DEFAULT, &Y)); 239 PetscCall(MatDestroy(&W)); 240 PetscFunctionReturn(0); 241 } 242 243 static PetscErrorCode PCApplyTranspose_SVD(PC pc, Vec x, Vec y) { 244 PC_SVD *jac = (PC_SVD *)pc->data; 245 Vec work = jac->work, xred, yred; 246 247 PetscFunctionBegin; 248 PetscCall(PCSVDGetVec(pc, PC_LEFT, READ, x, &xred)); 249 PetscCall(PCSVDGetVec(pc, PC_RIGHT, WRITE, y, &yred)); 250 PetscCall(MatMult(jac->Vt, xred, work)); 251 PetscCall(VecPointwiseMult(work, work, jac->diag)); 252 PetscCall(MatMult(jac->U, work, yred)); 253 PetscCall(PCSVDRestoreVec(pc, PC_LEFT, READ, x, &xred)); 254 PetscCall(PCSVDRestoreVec(pc, PC_RIGHT, WRITE, y, &yred)); 255 PetscFunctionReturn(0); 256 } 257 258 static PetscErrorCode PCReset_SVD(PC pc) { 259 PC_SVD *jac = (PC_SVD *)pc->data; 260 261 PetscFunctionBegin; 262 PetscCall(MatDestroy(&jac->A)); 263 PetscCall(MatDestroy(&jac->U)); 264 PetscCall(MatDestroy(&jac->Vt)); 265 PetscCall(VecDestroy(&jac->diag)); 266 PetscCall(VecDestroy(&jac->work)); 267 PetscCall(VecScatterDestroy(&jac->right2red)); 268 PetscCall(VecScatterDestroy(&jac->left2red)); 269 PetscCall(VecDestroy(&jac->rightred)); 270 PetscCall(VecDestroy(&jac->leftred)); 271 PetscFunctionReturn(0); 272 } 273 274 /* 275 PCDestroy_SVD - Destroys the private context for the SVD preconditioner 276 that was created with PCCreate_SVD(). 277 278 Input Parameter: 279 . pc - the preconditioner context 280 281 Application Interface Routine: PCDestroy() 282 */ 283 static PetscErrorCode PCDestroy_SVD(PC pc) { 284 PC_SVD *jac = (PC_SVD *)pc->data; 285 286 PetscFunctionBegin; 287 PetscCall(PCReset_SVD(pc)); 288 PetscCall(PetscViewerDestroy(&jac->monitor)); 289 PetscCall(PetscFree(pc->data)); 290 PetscFunctionReturn(0); 291 } 292 293 static PetscErrorCode PCSetFromOptions_SVD(PC pc, PetscOptionItems *PetscOptionsObject) { 294 PC_SVD *jac = (PC_SVD *)pc->data; 295 PetscBool flg; 296 297 PetscFunctionBegin; 298 PetscOptionsHeadBegin(PetscOptionsObject, "SVD options"); 299 PetscCall(PetscOptionsReal("-pc_svd_zero_sing", "Singular values smaller than this treated as zero", "None", jac->zerosing, &jac->zerosing, NULL)); 300 PetscCall(PetscOptionsInt("-pc_svd_ess_rank", "Essential rank of operator (0 to use entire operator)", "None", jac->essrank, &jac->essrank, NULL)); 301 PetscCall(PetscOptionsViewer("-pc_svd_monitor", "Monitor the conditioning, and extremal singular values", "None", &jac->monitor, &jac->monitorformat, &flg)); 302 PetscOptionsHeadEnd(); 303 PetscFunctionReturn(0); 304 } 305 306 static PetscErrorCode PCView_SVD(PC pc, PetscViewer viewer) { 307 PC_SVD *svd = (PC_SVD *)pc->data; 308 PetscBool iascii; 309 310 PetscFunctionBegin; 311 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii)); 312 if (iascii) { 313 PetscCall(PetscViewerASCIIPrintf(viewer, " All singular values smaller than %g treated as zero\n", (double)svd->zerosing)); 314 PetscCall(PetscViewerASCIIPrintf(viewer, " Provided essential rank of the matrix %" PetscInt_FMT " (all other eigenvalues are zeroed)\n", svd->essrank)); 315 } 316 PetscFunctionReturn(0); 317 } 318 319 /* 320 PCCreate_SVD - Creates a SVD preconditioner context, PC_SVD, 321 and sets this as the private data within the generic preconditioning 322 context, PC, that was created within PCCreate(). 323 324 Input Parameter: 325 . pc - the preconditioner context 326 327 Application Interface Routine: PCCreate() 328 */ 329 330 /*MC 331 PCSVD - Use pseudo inverse defined by SVD of operator 332 333 Level: advanced 334 335 Options Database Keys: 336 + -pc_svd_zero_sing <rtol> - Singular values smaller than this are treated as zero 337 - -pc_svd_monitor - Print information on the extreme singular values of the operator 338 339 Developer Note: 340 This implementation automatically creates a redundant copy of the 341 matrix on each process and uses a sequential SVD solve. Why does it do this instead 342 of using the composable `PCREDUNDANT` object? 343 344 .seealso: `PCCreate()`, `PCSetType()`, `PCType`, `PC`, `PCREDUNDANT` 345 M*/ 346 347 PETSC_EXTERN PetscErrorCode PCCreate_SVD(PC pc) { 348 PC_SVD *jac; 349 PetscMPIInt size = 0; 350 351 PetscFunctionBegin; 352 /* 353 Creates the private data structure for this preconditioner and 354 attach it to the PC object. 355 */ 356 PetscCall(PetscNew(&jac)); 357 jac->zerosing = 1.e-12; 358 pc->data = (void *)jac; 359 360 /* 361 Set the pointers for the functions that are provided above. 362 Now when the user-level routines (such as PCApply(), PCDestroy(), etc.) 363 are called, they will automatically call these functions. Note we 364 choose not to provide a couple of these functions since they are 365 not needed. 366 */ 367 368 #if defined(PETSC_HAVE_COMPLEX) 369 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size)); 370 #endif 371 if (size == 1) pc->ops->matapply = PCMatApply_SVD; 372 pc->ops->apply = PCApply_SVD; 373 pc->ops->applytranspose = PCApplyTranspose_SVD; 374 pc->ops->setup = PCSetUp_SVD; 375 pc->ops->reset = PCReset_SVD; 376 pc->ops->destroy = PCDestroy_SVD; 377 pc->ops->setfromoptions = PCSetFromOptions_SVD; 378 pc->ops->view = PCView_SVD; 379 pc->ops->applyrichardson = NULL; 380 PetscFunctionReturn(0); 381 } 382