xref: /petsc/src/ksp/pc/impls/svd/svd.c (revision db31f6defe33e3dfd9985704e6b8f54a7ffb6553)
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,V;
11   PetscInt    nzero;
12   PetscReal   zerosing;         /* measure of smallest singular value treated as nonzero */
13   PetscInt    essrank;          /* essential rank of operator */
14   PetscViewer monitor;
15 } PC_SVD;
16 
17 
18 /* -------------------------------------------------------------------------- */
19 /*
20    PCSetUp_SVD - Prepares for the use of the SVD preconditioner
21                     by setting data structures and options.
22 
23    Input Parameter:
24 .  pc - the preconditioner context
25 
26    Application Interface Routine: PCSetUp()
27 
28    Notes:
29    The interface routine PCSetUp() is not usually called directly by
30    the user, but instead is called by PCApply() if necessary.
31 */
32 #undef __FUNCT__
33 #define __FUNCT__ "PCSetUp_SVD"
34 static PetscErrorCode PCSetUp_SVD(PC pc)
35 {
36 #if defined(PETSC_MISSING_LAPACK_GESVD)
37   SETERRQ(((PetscObject)pc)->comm,PETSC_ERR_SUP,"GESVD - Lapack routine is unavailable\nNot able to provide singular value estimates.");
38 #else
39   PC_SVD         *jac = (PC_SVD*)pc->data;
40   PetscErrorCode ierr;
41   PetscScalar    *a,*u,*v,*d,*work;
42   PetscBLASInt   nb,lwork;
43   PetscInt       i,n;
44 
45   PetscFunctionBegin;
46   if (!jac->diag) {
47     /* assume square matrices */
48     ierr = MatGetVecs(pc->pmat,&jac->diag,&jac->work);CHKERRQ(ierr);
49   }
50   ierr = MatDestroy(&jac->A);CHKERRQ(ierr);
51   ierr = MatConvert(pc->pmat,MATSEQDENSE,MAT_INITIAL_MATRIX,&jac->A);CHKERRQ(ierr);
52   if (!jac->U) {
53     ierr = MatDuplicate(jac->A,MAT_DO_NOT_COPY_VALUES,&jac->U);CHKERRQ(ierr);
54     ierr = MatDuplicate(jac->A,MAT_DO_NOT_COPY_VALUES,&jac->V);CHKERRQ(ierr);
55   }
56   ierr = MatGetSize(pc->pmat,&n,PETSC_NULL);CHKERRQ(ierr);
57   nb    = PetscBLASIntCast(n);
58   lwork = 5*nb;
59   ierr  = PetscMalloc(lwork*sizeof(PetscScalar),&work);CHKERRQ(ierr);
60   ierr  = MatGetArray(jac->A,&a);CHKERRQ(ierr);
61   ierr  = MatGetArray(jac->U,&u);CHKERRQ(ierr);
62   ierr  = MatGetArray(jac->V,&v);CHKERRQ(ierr);
63   ierr  = VecGetArray(jac->diag,&d);CHKERRQ(ierr);
64 #if !defined(PETSC_USE_COMPLEX)
65   {
66     PetscBLASInt lierr;
67     ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr);
68     LAPACKgesvd_("A","A",&nb,&nb,a,&nb,d,u,&nb,v,&nb,work,&lwork,&lierr);
69     if (lierr) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"gesv() error %d",lierr);
70     ierr = PetscFPTrapPop();CHKERRQ(ierr);
71   }
72 #else
73   SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Not coded for complex");
74 #endif
75   ierr  = MatRestoreArray(jac->A,&a);CHKERRQ(ierr);
76   ierr  = MatRestoreArray(jac->U,&u);CHKERRQ(ierr);
77   ierr  = MatRestoreArray(jac->V,&v);CHKERRQ(ierr);
78   for (i=n-1; i>=0; i--) if (PetscRealPart(d[i]) > jac->zerosing) break;
79   jac->nzero = n-1-i;
80   if (jac->monitor) {
81     ierr = PetscViewerASCIIAddTab(jac->monitor,((PetscObject)pc)->tablevel);CHKERRQ(ierr);
82     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);
83     if (n >= 10) {              /* print 5 smallest and 5 largest */
84       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);
85       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);
86     } else {                    /* print all singular values */
87       char buf[256],*p;
88       size_t left = sizeof buf,used;
89       PetscInt thisline;
90       for (p=buf,i=n-1,thisline=1; i>=0; i--,thisline++) {
91         ierr = PetscSNPrintfCount(p,left," %14.12e",&used,(double)PetscRealPart(d[i]));CHKERRQ(ierr);
92         left -= used;
93         p += used;
94         if (thisline > 4 || i==0) {
95           ierr = PetscViewerASCIIPrintf(jac->monitor,"    SVD: singular values:%s\n",buf);CHKERRQ(ierr);
96           p = buf;
97           thisline = 0;
98         }
99       }
100     }
101     ierr = PetscViewerASCIISubtractTab(jac->monitor,((PetscObject)pc)->tablevel);CHKERRQ(ierr);
102   }
103   ierr = PetscInfo2(pc,"Largest and smallest singular values %14.12e %14.12e\n",(double)PetscRealPart(d[0]),(double)PetscRealPart(d[n-1]));
104   for (i=0; i<n-jac->nzero; i++) d[i] = 1.0/d[i];
105   for (; i<n; i++) d[i] = 0.0;
106   if (jac->essrank > 0) for (i=0; i<n-jac->nzero-jac->essrank; i++) d[i] = 0.0; /* Skip all but essrank eigenvalues */
107   ierr = PetscInfo1(pc,"Number of zero or nearly singular values %D\n",jac->nzero);
108   ierr = VecRestoreArray(jac->diag,&d);CHKERRQ(ierr);
109 #if defined(foo)
110 {
111   PetscViewer viewer;
112   ierr = PetscViewerBinaryOpen(PETSC_COMM_SELF,"joe",FILE_MODE_WRITE,&viewer);CHKERRQ(ierr);
113   ierr = MatView(jac->A,viewer);CHKERRQ(ierr);
114   ierr = MatView(jac->U,viewer);CHKERRQ(ierr);
115   ierr = MatView(jac->V,viewer);CHKERRQ(ierr);
116   ierr = VecView(jac->diag,viewer);CHKERRQ(ierr);
117   ierr = PetscViewerDestroy(viewer);CHKERRQ(ierr);
118  }
119 #endif
120   ierr = PetscFree(work);
121   PetscFunctionReturn(0);
122 #endif
123 }
124 
125 /* -------------------------------------------------------------------------- */
126 /*
127    PCApply_SVD - Applies the SVD preconditioner to a vector.
128 
129    Input Parameters:
130 .  pc - the preconditioner context
131 .  x - input vector
132 
133    Output Parameter:
134 .  y - output vector
135 
136    Application Interface Routine: PCApply()
137  */
138 #undef __FUNCT__
139 #define __FUNCT__ "PCApply_SVD"
140 static PetscErrorCode PCApply_SVD(PC pc,Vec x,Vec y)
141 {
142   PC_SVD         *jac = (PC_SVD*)pc->data;
143   Vec            work = jac->work;
144   PetscErrorCode ierr;
145 
146   PetscFunctionBegin;
147   ierr = MatMultTranspose(jac->U,x,work);CHKERRQ(ierr);
148   ierr = VecPointwiseMult(work,work,jac->diag);CHKERRQ(ierr);
149   ierr = MatMultTranspose(jac->V,work,y);CHKERRQ(ierr);
150   PetscFunctionReturn(0);
151 }
152 
153 #undef __FUNCT__
154 #define __FUNCT__ "PCReset_SVD"
155 static PetscErrorCode PCReset_SVD(PC pc)
156 {
157   PC_SVD         *jac = (PC_SVD*)pc->data;
158   PetscErrorCode ierr;
159 
160   PetscFunctionBegin;
161   ierr = MatDestroy(&jac->A);CHKERRQ(ierr);
162   ierr = MatDestroy(&jac->U);CHKERRQ(ierr);
163   ierr = MatDestroy(&jac->V);CHKERRQ(ierr);
164   ierr = VecDestroy(&jac->diag);CHKERRQ(ierr);
165   ierr = VecDestroy(&jac->work);CHKERRQ(ierr);
166   PetscFunctionReturn(0);
167 }
168 
169 /* -------------------------------------------------------------------------- */
170 /*
171    PCDestroy_SVD - Destroys the private context for the SVD preconditioner
172    that was created with PCCreate_SVD().
173 
174    Input Parameter:
175 .  pc - the preconditioner context
176 
177    Application Interface Routine: PCDestroy()
178 */
179 #undef __FUNCT__
180 #define __FUNCT__ "PCDestroy_SVD"
181 static PetscErrorCode PCDestroy_SVD(PC pc)
182 {
183   PC_SVD         *jac = (PC_SVD*)pc->data;
184   PetscErrorCode ierr;
185 
186   PetscFunctionBegin;
187   ierr = PCReset_SVD(pc);CHKERRQ(ierr);
188   ierr = PetscViewerDestroy(&jac->monitor);CHKERRQ(ierr);
189   ierr = PetscFree(pc->data);CHKERRQ(ierr);
190   PetscFunctionReturn(0);
191 }
192 
193 #undef __FUNCT__
194 #define __FUNCT__ "PCSetFromOptions_SVD"
195 static PetscErrorCode PCSetFromOptions_SVD(PC pc)
196 {
197   PetscErrorCode ierr;
198   PC_SVD         *jac = (PC_SVD*)pc->data;
199   PetscBool      flg,set;
200 
201   PetscFunctionBegin;
202   ierr = PetscOptionsHead("SVD options");CHKERRQ(ierr);
203   ierr = PetscOptionsReal("-pc_svd_zero_sing","Singular values smaller than this treated as zero","None",jac->zerosing,&jac->zerosing,PETSC_NULL);CHKERRQ(ierr);
204   ierr = PetscOptionsInt("-pc_svd_ess_rank","Essential rank of operator (0 to use entire operator)","None",jac->essrank,&jac->essrank,PETSC_NULL);CHKERRQ(ierr);
205   ierr = PetscOptionsBool("-pc_svd_monitor","Monitor the conditioning, and extremal singular values","None",jac->monitor?PETSC_TRUE:PETSC_FALSE,&flg,&set);CHKERRQ(ierr);
206   if (set) {                    /* Should make PCSVDSetMonitor() */
207     if (flg && !jac->monitor) {
208       ierr = PetscViewerASCIIOpen(((PetscObject)pc)->comm,"stdout",&jac->monitor);CHKERRQ(ierr);
209     } else if (!flg) {
210       ierr = PetscViewerDestroy(&jac->monitor);CHKERRQ(ierr);
211     }
212   }
213   ierr = PetscOptionsTail();CHKERRQ(ierr);
214   PetscFunctionReturn(0);
215 }
216 
217 /* -------------------------------------------------------------------------- */
218 /*
219    PCCreate_SVD - Creates a SVD preconditioner context, PC_SVD,
220    and sets this as the private data within the generic preconditioning
221    context, PC, that was created within PCCreate().
222 
223    Input Parameter:
224 .  pc - the preconditioner context
225 
226    Application Interface Routine: PCCreate()
227 */
228 
229 /*MC
230      PCSVD - Use pseudo inverse defined by SVD of operator
231 
232    Level: advanced
233 
234   Concepts: SVD
235 
236   Options Database:
237 -  -pc_svd_zero_sing <rtol> Singular values smaller than this are treated as zero
238 +  -pc_svd_monitor  Print information on the extreme singular values of the operator
239 
240 .seealso:  PCCreate(), PCSetType(), PCType (for list of available types), PC
241 M*/
242 
243 EXTERN_C_BEGIN
244 #undef __FUNCT__
245 #define __FUNCT__ "PCCreate_SVD"
246 PetscErrorCode PCCreate_SVD(PC pc)
247 {
248   PC_SVD         *jac;
249   PetscErrorCode ierr;
250 
251   PetscFunctionBegin;
252   /*
253      Creates the private data structure for this preconditioner and
254      attach it to the PC object.
255   */
256   ierr          = PetscNewLog(pc,PC_SVD,&jac);CHKERRQ(ierr);
257   jac->zerosing = 1.e-12;
258   pc->data      = (void*)jac;
259 
260   /*
261       Set the pointers for the functions that are provided above.
262       Now when the user-level routines (such as PCApply(), PCDestroy(), etc.)
263       are called, they will automatically call these functions.  Note we
264       choose not to provide a couple of these functions since they are
265       not needed.
266   */
267   pc->ops->apply               = PCApply_SVD;
268   pc->ops->applytranspose      = PCApply_SVD;
269   pc->ops->setup               = PCSetUp_SVD;
270   pc->ops->reset               = PCReset_SVD;
271   pc->ops->destroy             = PCDestroy_SVD;
272   pc->ops->setfromoptions      = PCSetFromOptions_SVD;
273   pc->ops->view                = 0;
274   pc->ops->applyrichardson     = 0;
275   PetscFunctionReturn(0);
276 }
277 EXTERN_C_END
278 
279