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