xref: /petsc/src/mat/impls/shell/shell.c (revision 7087cfbefd1a42b179f217f9994fb6cb0d0c1824)
1 #define PETSCMAT_DLL
2 
3 /*
4    This provides a simple shell for Fortran (and C programmers) to
5   create a very simple matrix class for use with KSP without coding
6   much of anything.
7 */
8 
9 #include "private/matimpl.h"        /*I "petscmat.h" I*/
10 #include "private/vecimpl.h"
11 
12 typedef struct {
13   PetscErrorCode (*destroy)(Mat);
14   PetscErrorCode (*mult)(Mat,Vec,Vec);
15   PetscErrorCode (*multtranspose)(Mat,Vec,Vec);
16   PetscErrorCode (*getdiagonal)(Mat,Vec);
17   PetscBool      scale,shift;
18   PetscScalar    vscale,vshift;
19   void           *ctx;
20 } Mat_Shell;
21 
22 #undef __FUNCT__
23 #define __FUNCT__ "MatShellGetContext"
24 /*@C
25     MatShellGetContext - Returns the user-provided context associated with a shell matrix.
26 
27     Not Collective
28 
29     Input Parameter:
30 .   mat - the matrix, should have been created with MatCreateShell()
31 
32     Output Parameter:
33 .   ctx - the user provided context
34 
35     Level: advanced
36 
37     Notes:
38     This routine is intended for use within various shell matrix routines,
39     as set with MatShellSetOperation().
40 
41 .keywords: matrix, shell, get, context
42 
43 .seealso: MatCreateShell(), MatShellSetOperation(), MatShellSetContext()
44 @*/
45 PetscErrorCode  MatShellGetContext(Mat mat,void **ctx)
46 {
47   PetscErrorCode ierr;
48   PetscBool      flg;
49 
50   PetscFunctionBegin;
51   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
52   PetscValidPointer(ctx,2);
53   ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
54   if (!flg) *ctx = 0;
55   else      *ctx = ((Mat_Shell*)(mat->data))->ctx;
56   PetscFunctionReturn(0);
57 }
58 
59 #undef __FUNCT__
60 #define __FUNCT__ "MatDestroy_Shell"
61 PetscErrorCode MatDestroy_Shell(Mat mat)
62 {
63   PetscErrorCode ierr;
64   Mat_Shell      *shell;
65 
66   PetscFunctionBegin;
67   shell = (Mat_Shell*)mat->data;
68   if (shell->destroy) {ierr = (*shell->destroy)(mat);CHKERRQ(ierr);}
69   ierr = PetscFree(shell);CHKERRQ(ierr);
70   PetscFunctionReturn(0);
71 }
72 
73 #undef __FUNCT__
74 #define __FUNCT__ "MatMult_Shell"
75 PetscErrorCode MatMult_Shell(Mat A,Vec x,Vec y)
76 {
77   Mat_Shell      *shell = (Mat_Shell*)A->data;
78   PetscErrorCode ierr;
79 
80   PetscFunctionBegin;
81   ierr = (*shell->mult)(A,x,y);CHKERRQ(ierr);
82   if (shell->shift && shell->scale) {
83     ierr = VecAXPBY(y,shell->vshift,shell->vscale,x);CHKERRQ(ierr);
84   } else if (shell->scale) {
85     ierr = VecScale(y,shell->vscale);CHKERRQ(ierr);
86   } else {
87     ierr = VecAXPY(y,shell->vshift,x);CHKERRQ(ierr);
88   }
89   PetscFunctionReturn(0);
90 }
91 
92 #undef __FUNCT__
93 #define __FUNCT__ "MatMultTranspose_Shell"
94 PetscErrorCode MatMultTranspose_Shell(Mat A,Vec x,Vec y)
95 {
96   Mat_Shell      *shell = (Mat_Shell*)A->data;
97   PetscErrorCode ierr;
98 
99   PetscFunctionBegin;
100   ierr = (*shell->multtranspose)(A,x,y);CHKERRQ(ierr);
101   if (shell->shift && shell->scale) {
102     ierr = VecAXPBY(y,shell->vshift,shell->vscale,x);CHKERRQ(ierr);
103   } else if (shell->scale) {
104     ierr = VecScale(y,shell->vscale);CHKERRQ(ierr);
105   } else {
106     ierr = VecAXPY(y,shell->vshift,x);CHKERRQ(ierr);
107   }
108   PetscFunctionReturn(0);
109 }
110 
111 #undef __FUNCT__
112 #define __FUNCT__ "MatGetDiagonal_Shell"
113 PetscErrorCode MatGetDiagonal_Shell(Mat A,Vec v)
114 {
115   Mat_Shell      *shell = (Mat_Shell*)A->data;
116   PetscErrorCode ierr;
117 
118   PetscFunctionBegin;
119   ierr = (*shell->getdiagonal)(A,v);CHKERRQ(ierr);
120   if (shell->scale) {
121     ierr = VecScale(v,shell->vscale);CHKERRQ(ierr);
122   }
123   if (shell->shift) {
124     ierr = VecShift(v,shell->vshift);CHKERRQ(ierr);
125   }
126   PetscFunctionReturn(0);
127 }
128 
129 #undef __FUNCT__
130 #define __FUNCT__ "MatShift_Shell"
131 PetscErrorCode MatShift_Shell(Mat Y,PetscScalar a)
132 {
133   Mat_Shell *shell = (Mat_Shell*)Y->data;
134 
135   PetscFunctionBegin;
136   if (shell->scale || shell->shift) {
137     shell->vshift += a;
138   } else {
139     shell->mult  = Y->ops->mult;
140     Y->ops->mult = MatMult_Shell;
141     if (Y->ops->multtranspose) {
142       shell->multtranspose  = Y->ops->multtranspose;
143       Y->ops->multtranspose = MatMultTranspose_Shell;
144     }
145     if (Y->ops->getdiagonal) {
146       shell->getdiagonal  = Y->ops->getdiagonal;
147       Y->ops->getdiagonal = MatGetDiagonal_Shell;
148     }
149     shell->vshift = a;
150   }
151   shell->shift  =  PETSC_TRUE;
152   PetscFunctionReturn(0);
153 }
154 
155 #undef __FUNCT__
156 #define __FUNCT__ "MatScale_Shell"
157 PetscErrorCode MatScale_Shell(Mat Y,PetscScalar a)
158 {
159   Mat_Shell *shell = (Mat_Shell*)Y->data;
160 
161   PetscFunctionBegin;
162   if (shell->scale || shell->shift) {
163     shell->vscale *= a;
164   } else {
165     shell->mult  = Y->ops->mult;
166     Y->ops->mult = MatMult_Shell;
167     if (Y->ops->multtranspose) {
168       shell->multtranspose  = Y->ops->multtranspose;
169       Y->ops->multtranspose = MatMultTranspose_Shell;
170     }
171     if (Y->ops->getdiagonal) {
172       shell->getdiagonal  = Y->ops->getdiagonal;
173       Y->ops->getdiagonal = MatGetDiagonal_Shell;
174     }
175     shell->vscale = a;
176   }
177   shell->scale  =  PETSC_TRUE;
178   PetscFunctionReturn(0);
179 }
180 
181 #undef __FUNCT__
182 #define __FUNCT__ "MatAssemblyEnd_Shell"
183 PetscErrorCode MatAssemblyEnd_Shell(Mat Y,MatAssemblyType t)
184 {
185   Mat_Shell *shell = (Mat_Shell*)Y->data;
186 
187   PetscFunctionBegin;
188   if ((shell->shift || shell->scale) && t == MAT_FINAL_ASSEMBLY) {
189     shell->scale  = PETSC_FALSE;
190     shell->shift  = PETSC_FALSE;
191     shell->vshift = 0.0;
192     shell->vscale = 1.0;
193     Y->ops->mult          = shell->mult;
194     Y->ops->multtranspose = shell->multtranspose;
195     Y->ops->getdiagonal   = shell->getdiagonal;
196   }
197   PetscFunctionReturn(0);
198 }
199 
200 extern PetscErrorCode MatConvert_Shell(Mat, const MatType,MatReuse,Mat*);
201 
202 #undef __FUNCT__
203 #define __FUNCT__ "MatSetBlockSize_Shell"
204 PetscErrorCode MatSetBlockSize_Shell(Mat A,PetscInt bs)
205 {
206   PetscErrorCode ierr;
207 
208   PetscFunctionBegin;
209   ierr = PetscLayoutSetBlockSize(A->rmap,bs);CHKERRQ(ierr);
210   ierr = PetscLayoutSetBlockSize(A->cmap,bs);CHKERRQ(ierr);
211   PetscFunctionReturn(0);
212 }
213 
214 static struct _MatOps MatOps_Values = {0,
215        0,
216        0,
217        0,
218 /* 4*/ 0,
219        0,
220        0,
221        0,
222        0,
223        0,
224 /*10*/ 0,
225        0,
226        0,
227        0,
228        0,
229 /*15*/ 0,
230        0,
231        0,
232        0,
233        0,
234 /*20*/ 0,
235        MatAssemblyEnd_Shell,
236        0,
237        0,
238 /*24*/ 0,
239        0,
240        0,
241        0,
242        0,
243 /*29*/ 0,
244        0,
245        0,
246        0,
247        0,
248 /*34*/ 0,
249        0,
250        0,
251        0,
252        0,
253 /*39*/ 0,
254        0,
255        0,
256        0,
257        0,
258 /*44*/ 0,
259        MatScale_Shell,
260        MatShift_Shell,
261        0,
262        0,
263 /*49*/ MatSetBlockSize_Shell,
264        0,
265        0,
266        0,
267        0,
268 /*54*/ 0,
269        0,
270        0,
271        0,
272        0,
273 /*59*/ 0,
274        MatDestroy_Shell,
275        0,
276        0,
277        0,
278 /*64*/ 0,
279        0,
280        0,
281        0,
282        0,
283 /*69*/ 0,
284        0,
285        MatConvert_Shell,
286        0,
287        0,
288 /*74*/ 0,
289        0,
290        0,
291        0,
292        0,
293 /*79*/ 0,
294        0,
295        0,
296        0,
297        0,
298 /*84*/ 0,
299        0,
300        0,
301        0,
302        0,
303 /*89*/ 0,
304        0,
305        0,
306        0,
307        0,
308 /*94*/ 0,
309        0,
310        0,
311        0};
312 
313 /*MC
314    MATSHELL - MATSHELL = "shell" - A matrix type to be used to define your own matrix type -- perhaps matrix free.
315 
316   Level: advanced
317 
318 .seealso: MatCreateShell
319 M*/
320 
321 EXTERN_C_BEGIN
322 #undef __FUNCT__
323 #define __FUNCT__ "MatCreate_Shell"
324 PetscErrorCode  MatCreate_Shell(Mat A)
325 {
326   Mat_Shell      *b;
327   PetscErrorCode ierr;
328 
329   PetscFunctionBegin;
330   ierr = PetscMemcpy(A->ops,&MatOps_Values,sizeof(struct _MatOps));CHKERRQ(ierr);
331 
332   ierr = PetscNewLog(A,Mat_Shell,&b);CHKERRQ(ierr);
333   A->data = (void*)b;
334 
335   ierr = PetscLayoutSetBlockSize(A->rmap,1);CHKERRQ(ierr);
336   ierr = PetscLayoutSetBlockSize(A->cmap,1);CHKERRQ(ierr);
337   ierr = PetscLayoutSetUp(A->rmap);CHKERRQ(ierr);
338   ierr = PetscLayoutSetUp(A->cmap);CHKERRQ(ierr);
339 
340   b->ctx           = 0;
341   b->scale         = PETSC_FALSE;
342   b->shift         = PETSC_FALSE;
343   b->vshift        = 0.0;
344   b->vscale        = 1.0;
345   b->mult          = 0;
346   b->multtranspose = 0;
347   b->getdiagonal   = 0;
348   A->assembled     = PETSC_TRUE;
349   A->preallocated  = PETSC_FALSE;
350   ierr = PetscObjectChangeTypeName((PetscObject)A,MATSHELL);CHKERRQ(ierr);
351   PetscFunctionReturn(0);
352 }
353 EXTERN_C_END
354 
355 #undef __FUNCT__
356 #define __FUNCT__ "MatCreateShell"
357 /*@C
358    MatCreateShell - Creates a new matrix class for use with a user-defined
359    private data storage format.
360 
361   Collective on MPI_Comm
362 
363    Input Parameters:
364 +  comm - MPI communicator
365 .  m - number of local rows (must be given)
366 .  n - number of local columns (must be given)
367 .  M - number of global rows (may be PETSC_DETERMINE)
368 .  N - number of global columns (may be PETSC_DETERMINE)
369 -  ctx - pointer to data needed by the shell matrix routines
370 
371    Output Parameter:
372 .  A - the matrix
373 
374    Level: advanced
375 
376   Usage:
377 $    extern int mult(Mat,Vec,Vec);
378 $    MatCreateShell(comm,m,n,M,N,ctx,&mat);
379 $    MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
380 $    [ Use matrix for operations that have been set ]
381 $    MatDestroy(mat);
382 
383    Notes:
384    The shell matrix type is intended to provide a simple class to use
385    with KSP (such as, for use with matrix-free methods). You should not
386    use the shell type if you plan to define a complete matrix class.
387 
388    Fortran Notes: The context can only be an integer or a PetscObject
389       unfortunately it cannot be a Fortran array or derived type.
390 
391    PETSc requires that matrices and vectors being used for certain
392    operations are partitioned accordingly.  For example, when
393    creating a shell matrix, A, that supports parallel matrix-vector
394    products using MatMult(A,x,y) the user should set the number
395    of local matrix rows to be the number of local elements of the
396    corresponding result vector, y. Note that this is information is
397    required for use of the matrix interface routines, even though
398    the shell matrix may not actually be physically partitioned.
399    For example,
400 
401 $
402 $     Vec x, y
403 $     extern int mult(Mat,Vec,Vec);
404 $     Mat A
405 $
406 $     VecCreateMPI(comm,PETSC_DECIDE,M,&y);
407 $     VecCreateMPI(comm,PETSC_DECIDE,N,&x);
408 $     VecGetLocalSize(y,&m);
409 $     VecGetLocalSize(x,&n);
410 $     MatCreateShell(comm,m,n,M,N,ctx,&A);
411 $     MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
412 $     MatMult(A,x,y);
413 $     MatDestroy(A);
414 $     VecDestroy(y); VecDestroy(x);
415 $
416 
417 .keywords: matrix, shell, create
418 
419 .seealso: MatShellSetOperation(), MatHasOperation(), MatShellGetContext(), MatShellSetContext()
420 @*/
421 PetscErrorCode  MatCreateShell(MPI_Comm comm,PetscInt m,PetscInt n,PetscInt M,PetscInt N,void *ctx,Mat *A)
422 {
423   PetscErrorCode ierr;
424 
425   PetscFunctionBegin;
426   ierr = MatCreate(comm,A);CHKERRQ(ierr);
427   ierr = MatSetSizes(*A,m,n,M,N);CHKERRQ(ierr);
428 
429   ierr = MatSetType(*A,MATSHELL);CHKERRQ(ierr);
430   ierr = MatShellSetContext(*A,ctx);CHKERRQ(ierr);
431   PetscFunctionReturn(0);
432 }
433 
434 #undef __FUNCT__
435 #define __FUNCT__ "MatShellSetContext"
436 /*@
437     MatShellSetContext - sets the context for a shell matrix
438 
439    Logically Collective on Mat
440 
441     Input Parameters:
442 +   mat - the shell matrix
443 -   ctx - the context
444 
445    Level: advanced
446 
447    Fortran Notes: The context can only be an integer or a PetscObject
448       unfortunately it cannot be a Fortran array or derived type.
449 
450 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation()
451 @*/
452 PetscErrorCode  MatShellSetContext(Mat mat,void *ctx)
453 {
454   Mat_Shell      *shell = (Mat_Shell*)mat->data;
455   PetscErrorCode ierr;
456   PetscBool      flg;
457 
458   PetscFunctionBegin;
459   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
460   ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
461   if (flg) {
462     shell->ctx = ctx;
463   }
464   PetscFunctionReturn(0);
465 }
466 
467 #undef __FUNCT__
468 #define __FUNCT__ "MatShellSetOperation"
469 /*@C
470     MatShellSetOperation - Allows user to set a matrix operation for
471                            a shell matrix.
472 
473    Logically Collective on Mat
474 
475     Input Parameters:
476 +   mat - the shell matrix
477 .   op - the name of the operation
478 -   f - the function that provides the operation.
479 
480    Level: advanced
481 
482     Usage:
483 $      extern PetscErrorCode usermult(Mat,Vec,Vec);
484 $      ierr = MatCreateShell(comm,m,n,M,N,ctx,&A);
485 $      ierr = MatShellSetOperation(A,MATOP_MULT,(void(*)(void))usermult);
486 
487     Notes:
488     See the file include/petscmat.h for a complete list of matrix
489     operations, which all have the form MATOP_<OPERATION>, where
490     <OPERATION> is the name (in all capital letters) of the
491     user interface routine (e.g., MatMult() -> MATOP_MULT).
492 
493     All user-provided functions should have the same calling
494     sequence as the usual matrix interface routines, since they
495     are intended to be accessed via the usual matrix interface
496     routines, e.g.,
497 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
498 
499     In particular each function MUST return an error code of 0 on success and
500     nonzero on failure.
501 
502     Within each user-defined routine, the user should call
503     MatShellGetContext() to obtain the user-defined context that was
504     set by MatCreateShell().
505 
506     Fortran Notes: For MatGetVecs() the user code should check if the input left or right matrix is -1 and in that case not
507        generate a matrix. See src/mat/examples/tests/ex120f.F
508 
509 .keywords: matrix, shell, set, operation
510 
511 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext()
512 @*/
513 PetscErrorCode  MatShellSetOperation(Mat mat,MatOperation op,void (*f)(void))
514 {
515   PetscErrorCode ierr;
516   PetscBool      flg;
517 
518   PetscFunctionBegin;
519   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
520   if (op == MATOP_DESTROY) {
521     ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
522     if (flg) {
523        Mat_Shell *shell = (Mat_Shell*)mat->data;
524        shell->destroy                 = (PetscErrorCode (*)(Mat)) f;
525     } else mat->ops->destroy          = (PetscErrorCode (*)(Mat)) f;
526   }
527   else if (op == MATOP_VIEW) mat->ops->view  = (PetscErrorCode (*)(Mat,PetscViewer)) f;
528   else                       (((void(**)(void))mat->ops)[op]) = f;
529 
530   PetscFunctionReturn(0);
531 }
532 
533 #undef __FUNCT__
534 #define __FUNCT__ "MatShellGetOperation"
535 /*@C
536     MatShellGetOperation - Gets a matrix function for a shell matrix.
537 
538     Not Collective
539 
540     Input Parameters:
541 +   mat - the shell matrix
542 -   op - the name of the operation
543 
544     Output Parameter:
545 .   f - the function that provides the operation.
546 
547     Level: advanced
548 
549     Notes:
550     See the file include/petscmat.h for a complete list of matrix
551     operations, which all have the form MATOP_<OPERATION>, where
552     <OPERATION> is the name (in all capital letters) of the
553     user interface routine (e.g., MatMult() -> MATOP_MULT).
554 
555     All user-provided functions have the same calling
556     sequence as the usual matrix interface routines, since they
557     are intended to be accessed via the usual matrix interface
558     routines, e.g.,
559 $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)
560 
561     Within each user-defined routine, the user should call
562     MatShellGetContext() to obtain the user-defined context that was
563     set by MatCreateShell().
564 
565 .keywords: matrix, shell, set, operation
566 
567 .seealso: MatCreateShell(), MatShellGetContext(), MatShellSetOperation(), MatShellSetContext()
568 @*/
569 PetscErrorCode  MatShellGetOperation(Mat mat,MatOperation op,void(**f)(void))
570 {
571   PetscErrorCode ierr;
572   PetscBool      flg;
573 
574   PetscFunctionBegin;
575   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
576   if (op == MATOP_DESTROY) {
577     ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr);
578     if (flg) {
579       Mat_Shell *shell = (Mat_Shell*)mat->data;
580       *f = (void(*)(void))shell->destroy;
581     } else {
582       *f = (void(*)(void))mat->ops->destroy;
583     }
584   } else if (op == MATOP_VIEW) {
585     *f = (void(*)(void))mat->ops->view;
586   } else {
587     *f = (((void(**)(void))mat->ops)[op]);
588   }
589 
590   PetscFunctionReturn(0);
591 }
592 
593