Actual source code: fnbasic.c

slepc-3.21.0 2024-03-30
Report Typos and Errors
  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:    Basic FN routines
 12: */

 14: #include <slepc/private/fnimpl.h>
 15: #include <slepcblaslapack.h>

 17: PetscFunctionList FNList = NULL;
 18: PetscBool         FNRegisterAllCalled = PETSC_FALSE;
 19: PetscClassId      FN_CLASSID = 0;
 20: PetscLogEvent     FN_Evaluate = 0;
 21: static PetscBool  FNPackageInitialized = PETSC_FALSE;

 23: const char *FNParallelTypes[] = {"REDUNDANT","SYNCHRONIZED","FNParallelType","FN_PARALLEL_",NULL};

 25: /*@C
 26:    FNFinalizePackage - This function destroys everything in the Slepc interface
 27:    to the FN package. It is called from SlepcFinalize().

 29:    Level: developer

 31: .seealso: SlepcFinalize()
 32: @*/
 33: PetscErrorCode FNFinalizePackage(void)
 34: {
 35:   PetscFunctionBegin;
 36:   PetscCall(PetscFunctionListDestroy(&FNList));
 37:   FNPackageInitialized = PETSC_FALSE;
 38:   FNRegisterAllCalled  = PETSC_FALSE;
 39:   PetscFunctionReturn(PETSC_SUCCESS);
 40: }

 42: /*@C
 43:   FNInitializePackage - This function initializes everything in the FN package.
 44:   It is called from PetscDLLibraryRegister() when using dynamic libraries, and
 45:   on the first call to FNCreate() when using static libraries.

 47:   Level: developer

 49: .seealso: SlepcInitialize()
 50: @*/
 51: PetscErrorCode FNInitializePackage(void)
 52: {
 53:   char           logList[256];
 54:   PetscBool      opt,pkg;
 55:   PetscClassId   classids[1];

 57:   PetscFunctionBegin;
 58:   if (FNPackageInitialized) PetscFunctionReturn(PETSC_SUCCESS);
 59:   FNPackageInitialized = PETSC_TRUE;
 60:   /* Register Classes */
 61:   PetscCall(PetscClassIdRegister("Math Function",&FN_CLASSID));
 62:   /* Register Constructors */
 63:   PetscCall(FNRegisterAll());
 64:   /* Register Events */
 65:   PetscCall(PetscLogEventRegister("FNEvaluate",FN_CLASSID,&FN_Evaluate));
 66:   /* Process Info */
 67:   classids[0] = FN_CLASSID;
 68:   PetscCall(PetscInfoProcessClass("fn",1,&classids[0]));
 69:   /* Process summary exclusions */
 70:   PetscCall(PetscOptionsGetString(NULL,NULL,"-log_exclude",logList,sizeof(logList),&opt));
 71:   if (opt) {
 72:     PetscCall(PetscStrInList("fn",logList,',',&pkg));
 73:     if (pkg) PetscCall(PetscLogEventDeactivateClass(FN_CLASSID));
 74:   }
 75:   /* Register package finalizer */
 76:   PetscCall(PetscRegisterFinalize(FNFinalizePackage));
 77:   PetscFunctionReturn(PETSC_SUCCESS);
 78: }

 80: /*@
 81:    FNCreate - Creates an FN context.

 83:    Collective

 85:    Input Parameter:
 86: .  comm - MPI communicator

 88:    Output Parameter:
 89: .  newfn - location to put the FN context

 91:    Level: beginner

 93: .seealso: FNDestroy(), FN
 94: @*/
 95: PetscErrorCode FNCreate(MPI_Comm comm,FN *newfn)
 96: {
 97:   FN             fn;

 99:   PetscFunctionBegin;
100:   PetscAssertPointer(newfn,2);
101:   *newfn = NULL;
102:   PetscCall(FNInitializePackage());
103:   PetscCall(SlepcHeaderCreate(fn,FN_CLASSID,"FN","Math Function","FN",comm,FNDestroy,FNView));

105:   fn->alpha    = 1.0;
106:   fn->beta     = 1.0;
107:   fn->method   = 0;

109:   fn->nw       = 0;
110:   fn->cw       = 0;
111:   fn->data     = NULL;

113:   *newfn = fn;
114:   PetscFunctionReturn(PETSC_SUCCESS);
115: }

117: /*@C
118:    FNSetOptionsPrefix - Sets the prefix used for searching for all
119:    FN options in the database.

121:    Logically Collective

123:    Input Parameters:
124: +  fn - the math function context
125: -  prefix - the prefix string to prepend to all FN option requests

127:    Notes:
128:    A hyphen (-) must NOT be given at the beginning of the prefix name.
129:    The first character of all runtime options is AUTOMATICALLY the
130:    hyphen.

132:    Level: advanced

134: .seealso: FNAppendOptionsPrefix()
135: @*/
136: PetscErrorCode FNSetOptionsPrefix(FN fn,const char *prefix)
137: {
138:   PetscFunctionBegin;
140:   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)fn,prefix));
141:   PetscFunctionReturn(PETSC_SUCCESS);
142: }

144: /*@C
145:    FNAppendOptionsPrefix - Appends to the prefix used for searching for all
146:    FN options in the database.

148:    Logically Collective

150:    Input Parameters:
151: +  fn - the math function context
152: -  prefix - the prefix string to prepend to all FN option requests

154:    Notes:
155:    A hyphen (-) must NOT be given at the beginning of the prefix name.
156:    The first character of all runtime options is AUTOMATICALLY the hyphen.

158:    Level: advanced

160: .seealso: FNSetOptionsPrefix()
161: @*/
162: PetscErrorCode FNAppendOptionsPrefix(FN fn,const char *prefix)
163: {
164:   PetscFunctionBegin;
166:   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)fn,prefix));
167:   PetscFunctionReturn(PETSC_SUCCESS);
168: }

170: /*@C
171:    FNGetOptionsPrefix - Gets the prefix used for searching for all
172:    FN options in the database.

174:    Not Collective

176:    Input Parameters:
177: .  fn - the math function context

179:    Output Parameters:
180: .  prefix - pointer to the prefix string used is returned

182:    Note:
183:    On the Fortran side, the user should pass in a string 'prefix' of
184:    sufficient length to hold the prefix.

186:    Level: advanced

188: .seealso: FNSetOptionsPrefix(), FNAppendOptionsPrefix()
189: @*/
190: PetscErrorCode FNGetOptionsPrefix(FN fn,const char *prefix[])
191: {
192:   PetscFunctionBegin;
194:   PetscAssertPointer(prefix,2);
195:   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)fn,prefix));
196:   PetscFunctionReturn(PETSC_SUCCESS);
197: }

199: /*@C
200:    FNSetType - Selects the type for the FN object.

202:    Logically Collective

204:    Input Parameters:
205: +  fn   - the math function context
206: -  type - a known type

208:    Notes:
209:    The default is FNRATIONAL, which includes polynomials as a particular
210:    case as well as simple functions such as f(x)=x and f(x)=constant.

212:    Level: intermediate

214: .seealso: FNGetType()
215: @*/
216: PetscErrorCode FNSetType(FN fn,FNType type)
217: {
218:   PetscErrorCode (*r)(FN);
219:   PetscBool      match;

221:   PetscFunctionBegin;
223:   PetscAssertPointer(type,2);

225:   PetscCall(PetscObjectTypeCompare((PetscObject)fn,type,&match));
226:   if (match) PetscFunctionReturn(PETSC_SUCCESS);

228:   PetscCall(PetscFunctionListFind(FNList,type,&r));
229:   PetscCheck(r,PetscObjectComm((PetscObject)fn),PETSC_ERR_ARG_UNKNOWN_TYPE,"Unable to find requested FN type %s",type);

231:   PetscTryTypeMethod(fn,destroy);
232:   PetscCall(PetscMemzero(fn->ops,sizeof(struct _FNOps)));

234:   PetscCall(PetscObjectChangeTypeName((PetscObject)fn,type));
235:   PetscCall((*r)(fn));
236:   PetscFunctionReturn(PETSC_SUCCESS);
237: }

239: /*@C
240:    FNGetType - Gets the FN type name (as a string) from the FN context.

242:    Not Collective

244:    Input Parameter:
245: .  fn - the math function context

247:    Output Parameter:
248: .  type - name of the math function

250:    Level: intermediate

252: .seealso: FNSetType()
253: @*/
254: PetscErrorCode FNGetType(FN fn,FNType *type)
255: {
256:   PetscFunctionBegin;
258:   PetscAssertPointer(type,2);
259:   *type = ((PetscObject)fn)->type_name;
260:   PetscFunctionReturn(PETSC_SUCCESS);
261: }

263: /*@
264:    FNSetScale - Sets the scaling parameters that define the matematical function.

266:    Logically Collective

268:    Input Parameters:
269: +  fn    - the math function context
270: .  alpha - inner scaling (argument)
271: -  beta  - outer scaling (result)

273:    Notes:
274:    Given a function f(x) specified by the FN type, the scaling parameters can
275:    be used to realize the function beta*f(alpha*x). So when these values are given,
276:    the procedure for function evaluation will first multiply the argument by alpha,
277:    then evaluate the function itself, and finally scale the result by beta.
278:    Likewise, these values are also considered when evaluating the derivative.

280:    If you want to provide only one of the two scaling factors, set the other
281:    one to 1.0.

283:    Level: intermediate

285: .seealso: FNGetScale(), FNEvaluateFunction()
286: @*/
287: PetscErrorCode FNSetScale(FN fn,PetscScalar alpha,PetscScalar beta)
288: {
289:   PetscFunctionBegin;
293:   PetscCheck(PetscAbsScalar(alpha)!=0.0 && PetscAbsScalar(beta)!=0.0,PetscObjectComm((PetscObject)fn),PETSC_ERR_ARG_WRONG,"Scaling factors must be nonzero");
294:   fn->alpha = alpha;
295:   fn->beta  = beta;
296:   PetscFunctionReturn(PETSC_SUCCESS);
297: }

299: /*@
300:    FNGetScale - Gets the scaling parameters that define the matematical function.

302:    Not Collective

304:    Input Parameter:
305: .  fn    - the math function context

307:    Output Parameters:
308: +  alpha - inner scaling (argument)
309: -  beta  - outer scaling (result)

311:    Level: intermediate

313: .seealso: FNSetScale()
314: @*/
315: PetscErrorCode FNGetScale(FN fn,PetscScalar *alpha,PetscScalar *beta)
316: {
317:   PetscFunctionBegin;
319:   if (alpha) *alpha = fn->alpha;
320:   if (beta)  *beta  = fn->beta;
321:   PetscFunctionReturn(PETSC_SUCCESS);
322: }

324: /*@
325:    FNSetMethod - Selects the method to be used to evaluate functions of matrices.

327:    Logically Collective

329:    Input Parameters:
330: +  fn   - the math function context
331: -  meth - an index identifying the method

333:    Options Database Key:
334: .  -fn_method <meth> - Sets the method

336:    Notes:
337:    In some FN types there are more than one algorithms available for computing
338:    matrix functions. In that case, this function allows choosing the wanted method.

340:    If meth is currently set to 0 (the default) and the input argument A of
341:    FNEvaluateFunctionMat() is a symmetric/Hermitian matrix, then the computation
342:    is done via the eigendecomposition of A, rather than with the general algorithm.

344:    Level: intermediate

346: .seealso: FNGetMethod(), FNEvaluateFunctionMat()
347: @*/
348: PetscErrorCode FNSetMethod(FN fn,PetscInt meth)
349: {
350:   PetscFunctionBegin;
353:   PetscCheck(meth>=0,PetscObjectComm((PetscObject)fn),PETSC_ERR_ARG_OUTOFRANGE,"The method must be a non-negative integer");
354:   PetscCheck(meth<=FN_MAX_SOLVE,PetscObjectComm((PetscObject)fn),PETSC_ERR_ARG_OUTOFRANGE,"Too large value for the method");
355:   fn->method = meth;
356:   PetscFunctionReturn(PETSC_SUCCESS);
357: }

359: /*@
360:    FNGetMethod - Gets the method currently used in the FN.

362:    Not Collective

364:    Input Parameter:
365: .  fn - the math function context

367:    Output Parameter:
368: .  meth - identifier of the method

370:    Level: intermediate

372: .seealso: FNSetMethod()
373: @*/
374: PetscErrorCode FNGetMethod(FN fn,PetscInt *meth)
375: {
376:   PetscFunctionBegin;
378:   PetscAssertPointer(meth,2);
379:   *meth = fn->method;
380:   PetscFunctionReturn(PETSC_SUCCESS);
381: }

383: /*@
384:    FNSetParallel - Selects the mode of operation in parallel runs.

386:    Logically Collective

388:    Input Parameters:
389: +  fn    - the math function context
390: -  pmode - the parallel mode

392:    Options Database Key:
393: .  -fn_parallel <mode> - Sets the parallel mode, either 'redundant' or 'synchronized'

395:    Notes:
396:    This is relevant only when the function is evaluated on a matrix, with
397:    either FNEvaluateFunctionMat() or FNEvaluateFunctionMatVec().

399:    In the 'redundant' parallel mode, all processes will make the computation
400:    redundantly, starting from the same data, and producing the same result.
401:    This result may be slightly different in the different processes if using a
402:    multithreaded BLAS library, which may cause issues in ill-conditioned problems.

404:    In the 'synchronized' parallel mode, only the first MPI process performs the
405:    computation and then the computed matrix is broadcast to the other
406:    processes in the communicator. This communication is done automatically at
407:    the end of FNEvaluateFunctionMat() or FNEvaluateFunctionMatVec().

409:    Level: advanced

411: .seealso: FNEvaluateFunctionMat() or FNEvaluateFunctionMatVec(), FNGetParallel()
412: @*/
413: PetscErrorCode FNSetParallel(FN fn,FNParallelType pmode)
414: {
415:   PetscFunctionBegin;
418:   fn->pmode = pmode;
419:   PetscFunctionReturn(PETSC_SUCCESS);
420: }

422: /*@
423:    FNGetParallel - Gets the mode of operation in parallel runs.

425:    Not Collective

427:    Input Parameter:
428: .  fn - the math function context

430:    Output Parameter:
431: .  pmode - the parallel mode

433:    Level: advanced

435: .seealso: FNSetParallel()
436: @*/
437: PetscErrorCode FNGetParallel(FN fn,FNParallelType *pmode)
438: {
439:   PetscFunctionBegin;
441:   PetscAssertPointer(pmode,2);
442:   *pmode = fn->pmode;
443:   PetscFunctionReturn(PETSC_SUCCESS);
444: }

446: /*@
447:    FNEvaluateFunction - Computes the value of the function f(x) for a given x.

449:    Not Collective

451:    Input Parameters:
452: +  fn - the math function context
453: -  x  - the value where the function must be evaluated

455:    Output Parameter:
456: .  y  - the result of f(x)

458:    Note:
459:    Scaling factors are taken into account, so the actual function evaluation
460:    will return beta*f(alpha*x).

462:    Level: intermediate

464: .seealso: FNEvaluateDerivative(), FNEvaluateFunctionMat(), FNSetScale()
465: @*/
466: PetscErrorCode FNEvaluateFunction(FN fn,PetscScalar x,PetscScalar *y)
467: {
468:   PetscScalar    xf,yf;

470:   PetscFunctionBegin;
473:   PetscAssertPointer(y,3);
474:   PetscCall(PetscLogEventBegin(FN_Evaluate,fn,0,0,0));
475:   xf = fn->alpha*x;
476:   PetscUseTypeMethod(fn,evaluatefunction,xf,&yf);
477:   *y = fn->beta*yf;
478:   PetscCall(PetscLogEventEnd(FN_Evaluate,fn,0,0,0));
479:   PetscFunctionReturn(PETSC_SUCCESS);
480: }

482: /*@
483:    FNEvaluateDerivative - Computes the value of the derivative f'(x) for a given x.

485:    Not Collective

487:    Input Parameters:
488: +  fn - the math function context
489: -  x  - the value where the derivative must be evaluated

491:    Output Parameter:
492: .  y  - the result of f'(x)

494:    Note:
495:    Scaling factors are taken into account, so the actual derivative evaluation will
496:    return alpha*beta*f'(alpha*x).

498:    Level: intermediate

500: .seealso: FNEvaluateFunction()
501: @*/
502: PetscErrorCode FNEvaluateDerivative(FN fn,PetscScalar x,PetscScalar *y)
503: {
504:   PetscScalar    xf,yf;

506:   PetscFunctionBegin;
509:   PetscAssertPointer(y,3);
510:   PetscCall(PetscLogEventBegin(FN_Evaluate,fn,0,0,0));
511:   xf = fn->alpha*x;
512:   PetscUseTypeMethod(fn,evaluatederivative,xf,&yf);
513:   *y = fn->alpha*fn->beta*yf;
514:   PetscCall(PetscLogEventEnd(FN_Evaluate,fn,0,0,0));
515:   PetscFunctionReturn(PETSC_SUCCESS);
516: }

518: static PetscErrorCode FNEvaluateFunctionMat_Sym_Private(FN fn,const PetscScalar *As,PetscScalar *Bs,PetscInt m,PetscBool firstonly)
519: {
520:   PetscInt       i,j;
521:   PetscBLASInt   n,k,ld,lwork,info;
522:   PetscScalar    *Q,*W,*work,adummy,a,x,y,one=1.0,zero=0.0;
523:   PetscReal      *eig,dummy;
524: #if defined(PETSC_USE_COMPLEX)
525:   PetscReal      *rwork,rdummy;
526: #endif

528:   PetscFunctionBegin;
529:   PetscCall(PetscBLASIntCast(m,&n));
530:   ld = n;
531:   k = firstonly? 1: n;

533:   /* workspace query and memory allocation */
534:   lwork = -1;
535: #if defined(PETSC_USE_COMPLEX)
536:   PetscCallBLAS("LAPACKsyev",LAPACKsyev_("V","L",&n,&adummy,&ld,&dummy,&a,&lwork,&rdummy,&info));
537:   PetscCall(PetscBLASIntCast((PetscInt)PetscRealPart(a),&lwork));
538:   PetscCall(PetscMalloc5(m,&eig,m*m,&Q,m*k,&W,lwork,&work,PetscMax(1,3*m-2),&rwork));
539: #else
540:   PetscCallBLAS("LAPACKsyev",LAPACKsyev_("V","L",&n,&adummy,&ld,&dummy,&a,&lwork,&info));
541:   PetscCall(PetscBLASIntCast((PetscInt)a,&lwork));
542:   PetscCall(PetscMalloc4(m,&eig,m*m,&Q,m*k,&W,lwork,&work));
543: #endif

545:   /* compute eigendecomposition */
546:   for (j=0;j<n;j++) for (i=j;i<n;i++) Q[i+j*ld] = As[i+j*ld];
547: #if defined(PETSC_USE_COMPLEX)
548:   PetscCallBLAS("LAPACKsyev",LAPACKsyev_("V","L",&n,Q,&ld,eig,work,&lwork,rwork,&info));
549: #else
550:   PetscCallBLAS("LAPACKsyev",LAPACKsyev_("V","L",&n,Q,&ld,eig,work,&lwork,&info));
551: #endif
552:   SlepcCheckLapackInfo("syev",info);

554:   /* W = f(Lambda)*Q' */
555:   for (i=0;i<n;i++) {
556:     x = fn->alpha*eig[i];
557:     PetscUseTypeMethod(fn,evaluatefunction,x,&y);  /* y = f(x) */
558:     for (j=0;j<k;j++) W[i+j*ld] = PetscConj(Q[j+i*ld])*fn->beta*y;
559:   }
560:   /* Bs = Q*W */
561:   PetscCallBLAS("BLASgemm",BLASgemm_("N","N",&n,&k,&n,&one,Q,&ld,W,&ld,&zero,Bs,&ld));
562: #if defined(PETSC_USE_COMPLEX)
563:   PetscCall(PetscFree5(eig,Q,W,work,rwork));
564: #else
565:   PetscCall(PetscFree4(eig,Q,W,work));
566: #endif
567:   PetscCall(PetscLogFlops(9.0*n*n*n+2.0*n*n*n));
568:   PetscFunctionReturn(PETSC_SUCCESS);
569: }

571: /*
572:    FNEvaluateFunctionMat_Sym_Default - given a symmetric matrix A,
573:    compute the matrix function as f(A)=Q*f(D)*Q' where the spectral
574:    decomposition of A is A=Q*D*Q'
575: */
576: static PetscErrorCode FNEvaluateFunctionMat_Sym_Default(FN fn,Mat A,Mat B)
577: {
578:   PetscInt          m;
579:   const PetscScalar *As;
580:   PetscScalar       *Bs;

582:   PetscFunctionBegin;
583:   PetscCall(MatDenseGetArrayRead(A,&As));
584:   PetscCall(MatDenseGetArray(B,&Bs));
585:   PetscCall(MatGetSize(A,&m,NULL));
586:   PetscCall(FNEvaluateFunctionMat_Sym_Private(fn,As,Bs,m,PETSC_FALSE));
587:   PetscCall(MatDenseRestoreArrayRead(A,&As));
588:   PetscCall(MatDenseRestoreArray(B,&Bs));
589:   PetscFunctionReturn(PETSC_SUCCESS);
590: }

592: static PetscErrorCode FNEvaluateFunctionMat_Basic(FN fn,Mat A,Mat F)
593: {
594:   PetscBool iscuda;

596:   PetscFunctionBegin;
597:   PetscCall(PetscObjectTypeCompare((PetscObject)A,MATSEQDENSECUDA,&iscuda));
598:   if (iscuda && !fn->ops->evaluatefunctionmatcuda[fn->method]) PetscCall(PetscInfo(fn,"The method %" PetscInt_FMT " is not implemented for CUDA, falling back to CPU version\n",fn->method));
599:   if (iscuda && fn->ops->evaluatefunctionmatcuda[fn->method]) PetscUseTypeMethod(fn,evaluatefunctionmatcuda[fn->method],A,F);
600:   else if (fn->ops->evaluatefunctionmat[fn->method]) PetscUseTypeMethod(fn,evaluatefunctionmat[fn->method],A,F);
601:   else {
602:     PetscCheck(fn->method,PetscObjectComm((PetscObject)fn),PETSC_ERR_SUP,"Matrix functions not implemented in this FN type");
603:     PetscCheck(!fn->method,PetscObjectComm((PetscObject)fn),PETSC_ERR_ARG_OUTOFRANGE,"The specified method number does not exist for this FN type");
604:   }
605:   PetscFunctionReturn(PETSC_SUCCESS);
606: }

608: PetscErrorCode FNEvaluateFunctionMat_Private(FN fn,Mat A,Mat B,PetscBool sync)
609: {
610:   PetscBool      set,flg,symm=PETSC_FALSE,iscuda,hasspecificmeth;
611:   PetscInt       m,n;
612:   PetscMPIInt    size,rank;
613:   PetscScalar    *pF;
614:   Mat            M,F;

616:   PetscFunctionBegin;
617:   /* destination matrix */
618:   F = B?B:A;

620:   /* check symmetry of A */
621:   PetscCall(MatIsHermitianKnown(A,&set,&flg));
622:   symm = set? flg: PETSC_FALSE;

624:   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)fn),&size));
625:   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)fn),&rank));
626:   if (size==1 || fn->pmode==FN_PARALLEL_REDUNDANT || (fn->pmode==FN_PARALLEL_SYNCHRONIZED && !rank)) {
627:     PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
628:     PetscCall(PetscObjectTypeCompare((PetscObject)A,MATSEQDENSECUDA,&iscuda));
629:     hasspecificmeth = ((iscuda && fn->ops->evaluatefunctionmatcuda[fn->method]) || (!iscuda && fn->method && fn->ops->evaluatefunctionmat[fn->method]))? PETSC_TRUE: PETSC_FALSE;
630:     if (!hasspecificmeth && symm && !fn->method) {  /* prefer diagonalization */
631:       PetscCall(PetscInfo(fn,"Computing matrix function via diagonalization\n"));
632:       PetscCall(FNEvaluateFunctionMat_Sym_Default(fn,A,F));
633:     } else {
634:       /* scale argument */
635:       if (fn->alpha!=(PetscScalar)1.0) {
636:         PetscCall(FN_AllocateWorkMat(fn,A,&M));
637:         PetscCall(MatScale(M,fn->alpha));
638:       } else M = A;
639:       PetscCall(FNEvaluateFunctionMat_Basic(fn,M,F));
640:       if (fn->alpha!=(PetscScalar)1.0) PetscCall(FN_FreeWorkMat(fn,&M));
641:       /* scale result */
642:       PetscCall(MatScale(F,fn->beta));
643:     }
644:     PetscCall(PetscFPTrapPop());
645:   }
646:   if (size>1 && fn->pmode==FN_PARALLEL_SYNCHRONIZED && sync) {  /* synchronize */
647:     PetscCall(MatGetSize(A,&m,&n));
648:     PetscCall(MatDenseGetArray(F,&pF));
649:     PetscCallMPI(MPI_Bcast(pF,n*n,MPIU_SCALAR,0,PetscObjectComm((PetscObject)fn)));
650:     PetscCall(MatDenseRestoreArray(F,&pF));
651:   }
652:   PetscFunctionReturn(PETSC_SUCCESS);
653: }

655: /*@
656:    FNEvaluateFunctionMat - Computes the value of the function f(A) for a given
657:    matrix A, where the result is also a matrix.

659:    Logically Collective

661:    Input Parameters:
662: +  fn - the math function context
663: -  A  - matrix on which the function must be evaluated

665:    Output Parameter:
666: .  B  - (optional) matrix resulting from evaluating f(A)

668:    Notes:
669:    Matrix A must be a square sequential dense Mat, with all entries equal on
670:    all processes (otherwise each process will compute different results).
671:    If matrix B is provided, it must also be a square sequential dense Mat, and
672:    both matrices must have the same dimensions. If B is NULL (or B=A) then the
673:    function will perform an in-place computation, overwriting A with f(A).

675:    If A is known to be real symmetric or complex Hermitian then it is
676:    recommended to set the appropriate flag with MatSetOption(), because
677:    symmetry can sometimes be exploited by the algorithm.

679:    Scaling factors are taken into account, so the actual function evaluation
680:    will return beta*f(alpha*A).

682:    Level: advanced

684: .seealso: FNEvaluateFunction(), FNEvaluateFunctionMatVec(), FNSetMethod()
685: @*/
686: PetscErrorCode FNEvaluateFunctionMat(FN fn,Mat A,Mat B)
687: {
688:   PetscBool      inplace=PETSC_FALSE;
689:   PetscInt       m,n,n1;
690:   MatType        type;

692:   PetscFunctionBegin;
697:   if (B) {
700:   } else inplace = PETSC_TRUE;
701:   PetscCheckTypeNames(A,MATSEQDENSE,MATSEQDENSECUDA);
702:   PetscCall(MatGetSize(A,&m,&n));
703:   PetscCheck(m==n,PetscObjectComm((PetscObject)fn),PETSC_ERR_ARG_SIZ,"Mat A is not square (has %" PetscInt_FMT " rows, %" PetscInt_FMT " cols)",m,n);
704:   if (!inplace) {
705:     PetscCall(MatGetType(A,&type));
706:     PetscCheckTypeName(B,type);
707:     n1 = n;
708:     PetscCall(MatGetSize(B,&m,&n));
709:     PetscCheck(m==n,PetscObjectComm((PetscObject)fn),PETSC_ERR_ARG_SIZ,"Mat B is not square (has %" PetscInt_FMT " rows, %" PetscInt_FMT " cols)",m,n);
710:     PetscCheck(n1==n,PetscObjectComm((PetscObject)fn),PETSC_ERR_ARG_SIZ,"Matrices A and B must have the same dimension");
711:   }

713:   /* evaluate matrix function */
714:   PetscCall(PetscLogEventBegin(FN_Evaluate,fn,0,0,0));
715:   PetscCall(FNEvaluateFunctionMat_Private(fn,A,B,PETSC_TRUE));
716:   PetscCall(PetscLogEventEnd(FN_Evaluate,fn,0,0,0));
717:   PetscFunctionReturn(PETSC_SUCCESS);
718: }

720: /*
721:    FNEvaluateFunctionMatVec_Default - computes the full matrix f(A)
722:    and then copies the first column.
723: */
724: static PetscErrorCode FNEvaluateFunctionMatVec_Default(FN fn,Mat A,Vec v)
725: {
726:   Mat            F;

728:   PetscFunctionBegin;
729:   PetscCall(FN_AllocateWorkMat(fn,A,&F));
730:   PetscCall(FNEvaluateFunctionMat_Basic(fn,A,F));
731:   PetscCall(MatGetColumnVector(F,v,0));
732:   PetscCall(FN_FreeWorkMat(fn,&F));
733:   PetscFunctionReturn(PETSC_SUCCESS);
734: }

736: /*
737:    FNEvaluateFunctionMatVec_Sym_Default - given a symmetric matrix A,
738:    compute the matrix function as f(A)=Q*f(D)*Q' where the spectral
739:    decomposition of A is A=Q*D*Q'. Only the first column is computed.
740: */
741: static PetscErrorCode FNEvaluateFunctionMatVec_Sym_Default(FN fn,Mat A,Vec v)
742: {
743:   PetscInt          m;
744:   const PetscScalar *As;
745:   PetscScalar       *vs;

747:   PetscFunctionBegin;
748:   PetscCall(MatDenseGetArrayRead(A,&As));
749:   PetscCall(VecGetArray(v,&vs));
750:   PetscCall(MatGetSize(A,&m,NULL));
751:   PetscCall(FNEvaluateFunctionMat_Sym_Private(fn,As,vs,m,PETSC_TRUE));
752:   PetscCall(MatDenseRestoreArrayRead(A,&As));
753:   PetscCall(VecRestoreArray(v,&vs));
754:   PetscFunctionReturn(PETSC_SUCCESS);
755: }

757: PetscErrorCode FNEvaluateFunctionMatVec_Private(FN fn,Mat A,Vec v,PetscBool sync)
758: {
759:   PetscBool      set,flg,symm=PETSC_FALSE,iscuda,hasspecificmeth;
760:   PetscInt       m,n;
761:   Mat            M;
762:   PetscMPIInt    size,rank;
763:   PetscScalar    *pv;

765:   PetscFunctionBegin;
766:   /* check symmetry of A */
767:   PetscCall(MatIsHermitianKnown(A,&set,&flg));
768:   symm = set? flg: PETSC_FALSE;

770:   /* evaluate matrix function */
771:   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)fn),&size));
772:   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)fn),&rank));
773:   if (size==1 || fn->pmode==FN_PARALLEL_REDUNDANT || (fn->pmode==FN_PARALLEL_SYNCHRONIZED && !rank)) {
774:     PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
775:     PetscCall(PetscObjectTypeCompare((PetscObject)A,MATSEQDENSECUDA,&iscuda));
776:     hasspecificmeth = ((iscuda && fn->ops->evaluatefunctionmatcuda[fn->method]) || (!iscuda && fn->method && fn->ops->evaluatefunctionmat[fn->method]))? PETSC_TRUE: PETSC_FALSE;
777:     if (!hasspecificmeth && symm && !fn->method) {  /* prefer diagonalization */
778:       PetscCall(PetscInfo(fn,"Computing matrix function via diagonalization\n"));
779:       PetscCall(FNEvaluateFunctionMatVec_Sym_Default(fn,A,v));
780:     } else {
781:       /* scale argument */
782:       if (fn->alpha!=(PetscScalar)1.0) {
783:         PetscCall(FN_AllocateWorkMat(fn,A,&M));
784:         PetscCall(MatScale(M,fn->alpha));
785:       } else M = A;
786:       if (iscuda && fn->ops->evaluatefunctionmatveccuda[fn->method]) PetscUseTypeMethod(fn,evaluatefunctionmatveccuda[fn->method],M,v);
787:       else if (fn->ops->evaluatefunctionmatvec[fn->method]) PetscUseTypeMethod(fn,evaluatefunctionmatvec[fn->method],M,v);
788:       else PetscCall(FNEvaluateFunctionMatVec_Default(fn,M,v));
789:       if (fn->alpha!=(PetscScalar)1.0) PetscCall(FN_FreeWorkMat(fn,&M));
790:       /* scale result */
791:       PetscCall(VecScale(v,fn->beta));
792:     }
793:     PetscCall(PetscFPTrapPop());
794:   }

796:   /* synchronize */
797:   if (size>1 && fn->pmode==FN_PARALLEL_SYNCHRONIZED && sync) {
798:     PetscCall(MatGetSize(A,&m,&n));
799:     PetscCall(VecGetArray(v,&pv));
800:     PetscCallMPI(MPI_Bcast(pv,n,MPIU_SCALAR,0,PetscObjectComm((PetscObject)fn)));
801:     PetscCall(VecRestoreArray(v,&pv));
802:   }
803:   PetscFunctionReturn(PETSC_SUCCESS);
804: }

806: /*@
807:    FNEvaluateFunctionMatVec - Computes the first column of the matrix f(A)
808:    for a given matrix A.

810:    Logically Collective

812:    Input Parameters:
813: +  fn - the math function context
814: -  A  - matrix on which the function must be evaluated

816:    Output Parameter:
817: .  v  - vector to hold the first column of f(A)

819:    Notes:
820:    This operation is similar to FNEvaluateFunctionMat() but returns only
821:    the first column of f(A), hence saving computations in most cases.

823:    Level: advanced

825: .seealso: FNEvaluateFunction(), FNEvaluateFunctionMat(), FNSetMethod()
826: @*/
827: PetscErrorCode FNEvaluateFunctionMatVec(FN fn,Mat A,Vec v)
828: {
829:   PetscInt       m,n;
830:   PetscBool      iscuda;

832:   PetscFunctionBegin;
839:   PetscCheckTypeNames(A,MATSEQDENSE,MATSEQDENSECUDA);
840:   PetscCall(MatGetSize(A,&m,&n));
841:   PetscCheck(m==n,PetscObjectComm((PetscObject)fn),PETSC_ERR_ARG_SIZ,"Mat A is not square (has %" PetscInt_FMT " rows, %" PetscInt_FMT " cols)",m,n);
842:   PetscCall(PetscObjectTypeCompare((PetscObject)A,MATSEQDENSECUDA,&iscuda));
843:   PetscCheckTypeName(v,iscuda?VECSEQCUDA:VECSEQ);
844:   PetscCall(VecGetSize(v,&m));
845:   PetscCheck(m==n,PetscObjectComm((PetscObject)fn),PETSC_ERR_ARG_SIZ,"Matrix A and vector v must have the same size");
846:   PetscCall(PetscLogEventBegin(FN_Evaluate,fn,0,0,0));
847:   PetscCall(FNEvaluateFunctionMatVec_Private(fn,A,v,PETSC_TRUE));
848:   PetscCall(PetscLogEventEnd(FN_Evaluate,fn,0,0,0));
849:   PetscFunctionReturn(PETSC_SUCCESS);
850: }

852: /*@
853:    FNSetFromOptions - Sets FN options from the options database.

855:    Collective

857:    Input Parameters:
858: .  fn - the math function context

860:    Notes:
861:    To see all options, run your program with the -help option.

863:    Level: beginner

865: .seealso: FNSetOptionsPrefix()
866: @*/
867: PetscErrorCode FNSetFromOptions(FN fn)
868: {
869:   char           type[256];
870:   PetscScalar    array[2];
871:   PetscInt       k,meth;
872:   PetscBool      flg;
873:   FNParallelType pmode;

875:   PetscFunctionBegin;
877:   PetscCall(FNRegisterAll());
878:   PetscObjectOptionsBegin((PetscObject)fn);
879:     PetscCall(PetscOptionsFList("-fn_type","Math function type","FNSetType",FNList,(char*)(((PetscObject)fn)->type_name?((PetscObject)fn)->type_name:FNRATIONAL),type,sizeof(type),&flg));
880:     if (flg) PetscCall(FNSetType(fn,type));
881:     else if (!((PetscObject)fn)->type_name) PetscCall(FNSetType(fn,FNRATIONAL));

883:     k = 2;
884:     array[0] = 0.0; array[1] = 0.0;
885:     PetscCall(PetscOptionsScalarArray("-fn_scale","Scale factors (one or two scalar values separated with a comma without spaces)","FNSetScale",array,&k,&flg));
886:     if (flg) {
887:       if (k<2) array[1] = 1.0;
888:       PetscCall(FNSetScale(fn,array[0],array[1]));
889:     }

891:     PetscCall(PetscOptionsInt("-fn_method","Method to be used for computing matrix functions","FNSetMethod",fn->method,&meth,&flg));
892:     if (flg) PetscCall(FNSetMethod(fn,meth));

894:     PetscCall(PetscOptionsEnum("-fn_parallel","Operation mode in parallel runs","FNSetParallel",FNParallelTypes,(PetscEnum)fn->pmode,(PetscEnum*)&pmode,&flg));
895:     if (flg) PetscCall(FNSetParallel(fn,pmode));

897:     PetscTryTypeMethod(fn,setfromoptions,PetscOptionsObject);
898:     PetscCall(PetscObjectProcessOptionsHandlers((PetscObject)fn,PetscOptionsObject));
899:   PetscOptionsEnd();
900:   PetscFunctionReturn(PETSC_SUCCESS);
901: }

903: /*@C
904:    FNView - Prints the FN data structure.

906:    Collective

908:    Input Parameters:
909: +  fn - the math function context
910: -  viewer - optional visualization context

912:    Note:
913:    The available visualization contexts include
914: +     PETSC_VIEWER_STDOUT_SELF - standard output (default)
915: -     PETSC_VIEWER_STDOUT_WORLD - synchronized standard
916:          output where only the first processor opens
917:          the file.  All other processors send their
918:          data to the first processor to print.

920:    The user can open an alternative visualization context with
921:    PetscViewerASCIIOpen() - output to a specified file.

923:    Level: beginner

925: .seealso: FNCreate()
926: @*/
927: PetscErrorCode FNView(FN fn,PetscViewer viewer)
928: {
929:   PetscBool      isascii;
930:   PetscMPIInt    size;

932:   PetscFunctionBegin;
934:   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)fn),&viewer));
936:   PetscCheckSameComm(fn,1,viewer,2);
937:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
938:   if (isascii) {
939:     PetscCall(PetscObjectPrintClassNamePrefixType((PetscObject)fn,viewer));
940:     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)fn),&size));
941:     if (size>1) PetscCall(PetscViewerASCIIPrintf(viewer,"  parallel operation mode: %s\n",FNParallelTypes[fn->pmode]));
942:     PetscCall(PetscViewerASCIIPushTab(viewer));
943:     PetscTryTypeMethod(fn,view,viewer);
944:     PetscCall(PetscViewerASCIIPopTab(viewer));
945:   }
946:   PetscFunctionReturn(PETSC_SUCCESS);
947: }

949: /*@C
950:    FNViewFromOptions - View from options

952:    Collective

954:    Input Parameters:
955: +  fn   - the math function context
956: .  obj  - optional object
957: -  name - command line option

959:    Level: intermediate

961: .seealso: FNView(), FNCreate()
962: @*/
963: PetscErrorCode FNViewFromOptions(FN fn,PetscObject obj,const char name[])
964: {
965:   PetscFunctionBegin;
967:   PetscCall(PetscObjectViewFromOptions((PetscObject)fn,obj,name));
968:   PetscFunctionReturn(PETSC_SUCCESS);
969: }

971: /*@
972:    FNDuplicate - Duplicates a math function, copying all parameters, possibly with a
973:    different communicator.

975:    Collective

977:    Input Parameters:
978: +  fn   - the math function context
979: -  comm - MPI communicator

981:    Output Parameter:
982: .  newfn - location to put the new FN context

984:    Note:
985:    In order to use the same MPI communicator as in the original object,
986:    use PetscObjectComm((PetscObject)fn).

988:    Level: developer

990: .seealso: FNCreate()
991: @*/
992: PetscErrorCode FNDuplicate(FN fn,MPI_Comm comm,FN *newfn)
993: {
994:   FNType         type;
995:   PetscScalar    alpha,beta;
996:   PetscInt       meth;
997:   FNParallelType ptype;

999:   PetscFunctionBegin;
1002:   PetscAssertPointer(newfn,3);
1003:   PetscCall(FNCreate(comm,newfn));
1004:   PetscCall(FNGetType(fn,&type));
1005:   PetscCall(FNSetType(*newfn,type));
1006:   PetscCall(FNGetScale(fn,&alpha,&beta));
1007:   PetscCall(FNSetScale(*newfn,alpha,beta));
1008:   PetscCall(FNGetMethod(fn,&meth));
1009:   PetscCall(FNSetMethod(*newfn,meth));
1010:   PetscCall(FNGetParallel(fn,&ptype));
1011:   PetscCall(FNSetParallel(*newfn,ptype));
1012:   PetscTryTypeMethod(fn,duplicate,comm,newfn);
1013:   PetscFunctionReturn(PETSC_SUCCESS);
1014: }

1016: /*@C
1017:    FNDestroy - Destroys FN context that was created with FNCreate().

1019:    Collective

1021:    Input Parameter:
1022: .  fn - the math function context

1024:    Level: beginner

1026: .seealso: FNCreate()
1027: @*/
1028: PetscErrorCode FNDestroy(FN *fn)
1029: {
1030:   PetscInt       i;

1032:   PetscFunctionBegin;
1033:   if (!*fn) PetscFunctionReturn(PETSC_SUCCESS);
1035:   if (--((PetscObject)*fn)->refct > 0) { *fn = NULL; PetscFunctionReturn(PETSC_SUCCESS); }
1036:   PetscTryTypeMethod(*fn,destroy);
1037:   for (i=0;i<(*fn)->nw;i++) PetscCall(MatDestroy(&(*fn)->W[i]));
1038:   PetscCall(PetscHeaderDestroy(fn));
1039:   PetscFunctionReturn(PETSC_SUCCESS);
1040: }

1042: /*@C
1043:    FNRegister - Adds a mathematical function to the FN package.

1045:    Not Collective

1047:    Input Parameters:
1048: +  name - name of a new user-defined FN
1049: -  function - routine to create context

1051:    Notes:
1052:    FNRegister() may be called multiple times to add several user-defined functions.

1054:    Level: advanced

1056: .seealso: FNRegisterAll()
1057: @*/
1058: PetscErrorCode FNRegister(const char *name,PetscErrorCode (*function)(FN))
1059: {
1060:   PetscFunctionBegin;
1061:   PetscCall(FNInitializePackage());
1062:   PetscCall(PetscFunctionListAdd(&FNList,name,function));
1063:   PetscFunctionReturn(PETSC_SUCCESS);
1064: }