Actual source code: fncombine.c

slepc-main 2024-11-22
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:    A function that is obtained by combining two other functions (either by
 12:    addition, multiplication, division or composition)

 14:       addition:          f(x) = f1(x)+f2(x)
 15:       multiplication:    f(x) = f1(x)*f2(x)
 16:       division:          f(x) = f1(x)/f2(x)      f(A) = f2(A)\f1(A)
 17:       composition:       f(x) = f2(f1(x))
 18: */

 20: #include <slepc/private/fnimpl.h>

 22: typedef struct {
 23:   FN            f1,f2;    /* functions */
 24:   FNCombineType comb;     /* how the functions are combined */
 25: } FN_COMBINE;

 27: static PetscErrorCode FNEvaluateFunction_Combine(FN fn,PetscScalar x,PetscScalar *y)
 28: {
 29:   FN_COMBINE     *ctx = (FN_COMBINE*)fn->data;
 30:   PetscScalar    a,b;

 32:   PetscFunctionBegin;
 33:   PetscCall(FNEvaluateFunction(ctx->f1,x,&a));
 34:   switch (ctx->comb) {
 35:     case FN_COMBINE_ADD:
 36:       PetscCall(FNEvaluateFunction(ctx->f2,x,&b));
 37:       *y = a+b;
 38:       break;
 39:     case FN_COMBINE_MULTIPLY:
 40:       PetscCall(FNEvaluateFunction(ctx->f2,x,&b));
 41:       *y = a*b;
 42:       break;
 43:     case FN_COMBINE_DIVIDE:
 44:       PetscCall(FNEvaluateFunction(ctx->f2,x,&b));
 45:       PetscCheck(b!=0.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Function not defined in the requested value");
 46:       *y = a/b;
 47:       break;
 48:     case FN_COMBINE_COMPOSE:
 49:       PetscCall(FNEvaluateFunction(ctx->f2,a,y));
 50:       break;
 51:   }
 52:   PetscFunctionReturn(PETSC_SUCCESS);
 53: }

 55: static PetscErrorCode FNEvaluateDerivative_Combine(FN fn,PetscScalar x,PetscScalar *yp)
 56: {
 57:   FN_COMBINE     *ctx = (FN_COMBINE*)fn->data;
 58:   PetscScalar    a,b,ap,bp;

 60:   PetscFunctionBegin;
 61:   switch (ctx->comb) {
 62:     case FN_COMBINE_ADD:
 63:       PetscCall(FNEvaluateDerivative(ctx->f1,x,&ap));
 64:       PetscCall(FNEvaluateDerivative(ctx->f2,x,&bp));
 65:       *yp = ap+bp;
 66:       break;
 67:     case FN_COMBINE_MULTIPLY:
 68:       PetscCall(FNEvaluateDerivative(ctx->f1,x,&ap));
 69:       PetscCall(FNEvaluateDerivative(ctx->f2,x,&bp));
 70:       PetscCall(FNEvaluateFunction(ctx->f1,x,&a));
 71:       PetscCall(FNEvaluateFunction(ctx->f2,x,&b));
 72:       *yp = ap*b+a*bp;
 73:       break;
 74:     case FN_COMBINE_DIVIDE:
 75:       PetscCall(FNEvaluateDerivative(ctx->f1,x,&ap));
 76:       PetscCall(FNEvaluateDerivative(ctx->f2,x,&bp));
 77:       PetscCall(FNEvaluateFunction(ctx->f1,x,&a));
 78:       PetscCall(FNEvaluateFunction(ctx->f2,x,&b));
 79:       PetscCheck(b!=0.0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Derivative not defined in the requested value");
 80:       *yp = (ap*b-a*bp)/(b*b);
 81:       break;
 82:     case FN_COMBINE_COMPOSE:
 83:       PetscCall(FNEvaluateFunction(ctx->f1,x,&a));
 84:       PetscCall(FNEvaluateDerivative(ctx->f1,x,&ap));
 85:       PetscCall(FNEvaluateDerivative(ctx->f2,a,yp));
 86:       *yp *= ap;
 87:       break;
 88:   }
 89:   PetscFunctionReturn(PETSC_SUCCESS);
 90: }

 92: static PetscErrorCode FNEvaluateFunctionMat_Combine(FN fn,Mat A,Mat B)
 93: {
 94:   FN_COMBINE   *ctx = (FN_COMBINE*)fn->data;
 95:   Mat          W,Z,F;
 96:   PetscBool    iscuda;

 98:   PetscFunctionBegin;
 99:   PetscCall(FN_AllocateWorkMat(fn,A,&W));
100:   switch (ctx->comb) {
101:     case FN_COMBINE_ADD:
102:       PetscCall(FNEvaluateFunctionMat_Private(ctx->f1,A,W,PETSC_FALSE));
103:       PetscCall(FNEvaluateFunctionMat_Private(ctx->f2,A,B,PETSC_FALSE));
104:       PetscCall(MatAXPY(B,1.0,W,SAME_NONZERO_PATTERN));
105:       break;
106:     case FN_COMBINE_MULTIPLY:
107:       PetscCall(FN_AllocateWorkMat(fn,A,&Z));
108:       PetscCall(FNEvaluateFunctionMat_Private(ctx->f1,A,W,PETSC_FALSE));
109:       PetscCall(FNEvaluateFunctionMat_Private(ctx->f2,A,Z,PETSC_FALSE));
110:       PetscCall(MatMatMult(W,Z,MAT_REUSE_MATRIX,PETSC_DEFAULT,&B));
111:       PetscCall(FN_FreeWorkMat(fn,&Z));
112:       break;
113:     case FN_COMBINE_DIVIDE:
114:       PetscCall(FNEvaluateFunctionMat_Private(ctx->f2,A,W,PETSC_FALSE));
115:       PetscCall(FNEvaluateFunctionMat_Private(ctx->f1,A,B,PETSC_FALSE));
116:       PetscCall(PetscObjectTypeCompare((PetscObject)A,MATSEQDENSECUDA,&iscuda));
117:       PetscCall(MatGetFactor(W,iscuda?MATSOLVERCUDA:MATSOLVERPETSC,MAT_FACTOR_LU,&F));
118:       PetscCall(MatLUFactorSymbolic(F,W,NULL,NULL,NULL));
119:       PetscCall(MatLUFactorNumeric(F,W,NULL));
120:       PetscCall(MatMatSolve(F,B,B));
121:       PetscCall(MatDestroy(&F));
122:       break;
123:     case FN_COMBINE_COMPOSE:
124:       PetscCall(FNEvaluateFunctionMat_Private(ctx->f1,A,W,PETSC_FALSE));
125:       PetscCall(FNEvaluateFunctionMat_Private(ctx->f2,W,B,PETSC_FALSE));
126:       break;
127:   }
128:   PetscCall(FN_FreeWorkMat(fn,&W));
129:   PetscFunctionReturn(PETSC_SUCCESS);
130: }

132: static PetscErrorCode FNEvaluateFunctionMatVec_Combine(FN fn,Mat A,Vec v)
133: {
134:   FN_COMBINE     *ctx = (FN_COMBINE*)fn->data;
135:   PetscBool      iscuda;
136:   Mat            Z,F;
137:   Vec            w;

139:   PetscFunctionBegin;
140:   switch (ctx->comb) {
141:     case FN_COMBINE_ADD:
142:       PetscCall(VecDuplicate(v,&w));
143:       PetscCall(FNEvaluateFunctionMatVec_Private(ctx->f1,A,w,PETSC_FALSE));
144:       PetscCall(FNEvaluateFunctionMatVec_Private(ctx->f2,A,v,PETSC_FALSE));
145:       PetscCall(VecAXPY(v,1.0,w));
146:       PetscCall(VecDestroy(&w));
147:       break;
148:     case FN_COMBINE_MULTIPLY:
149:       PetscCall(VecDuplicate(v,&w));
150:       PetscCall(FN_AllocateWorkMat(fn,A,&Z));
151:       PetscCall(FNEvaluateFunctionMat_Private(ctx->f1,A,Z,PETSC_FALSE));
152:       PetscCall(FNEvaluateFunctionMatVec_Private(ctx->f2,A,w,PETSC_FALSE));
153:       PetscCall(MatMult(Z,w,v));
154:       PetscCall(FN_FreeWorkMat(fn,&Z));
155:       PetscCall(VecDestroy(&w));
156:       break;
157:     case FN_COMBINE_DIVIDE:
158:       PetscCall(VecDuplicate(v,&w));
159:       PetscCall(FN_AllocateWorkMat(fn,A,&Z));
160:       PetscCall(FNEvaluateFunctionMat_Private(ctx->f2,A,Z,PETSC_FALSE));
161:       PetscCall(FNEvaluateFunctionMatVec_Private(ctx->f1,A,w,PETSC_FALSE));
162:       PetscCall(PetscObjectTypeCompare((PetscObject)A,MATSEQDENSECUDA,&iscuda));
163:       PetscCall(MatGetFactor(Z,iscuda?MATSOLVERCUDA:MATSOLVERPETSC,MAT_FACTOR_LU,&F));
164:       PetscCall(MatLUFactorSymbolic(F,Z,NULL,NULL,NULL));
165:       PetscCall(MatLUFactorNumeric(F,Z,NULL));
166:       PetscCall(MatSolve(F,w,v));
167:       PetscCall(MatDestroy(&F));
168:       PetscCall(FN_FreeWorkMat(fn,&Z));
169:       PetscCall(VecDestroy(&w));
170:       break;
171:     case FN_COMBINE_COMPOSE:
172:       PetscCall(FN_AllocateWorkMat(fn,A,&Z));
173:       PetscCall(FNEvaluateFunctionMat_Private(ctx->f1,A,Z,PETSC_FALSE));
174:       PetscCall(FNEvaluateFunctionMatVec_Private(ctx->f2,Z,v,PETSC_FALSE));
175:       PetscCall(FN_FreeWorkMat(fn,&Z));
176:       break;
177:   }
178:   PetscFunctionReturn(PETSC_SUCCESS);
179: }

181: static PetscErrorCode FNView_Combine(FN fn,PetscViewer viewer)
182: {
183:   FN_COMBINE     *ctx = (FN_COMBINE*)fn->data;
184:   PetscBool      isascii;

186:   PetscFunctionBegin;
187:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
188:   if (isascii) {
189:     switch (ctx->comb) {
190:       case FN_COMBINE_ADD:
191:         PetscCall(PetscViewerASCIIPrintf(viewer,"  two added functions f1+f2\n"));
192:         break;
193:       case FN_COMBINE_MULTIPLY:
194:         PetscCall(PetscViewerASCIIPrintf(viewer,"  two multiplied functions f1*f2\n"));
195:         break;
196:       case FN_COMBINE_DIVIDE:
197:         PetscCall(PetscViewerASCIIPrintf(viewer,"  a quotient of two functions f1/f2\n"));
198:         break;
199:       case FN_COMBINE_COMPOSE:
200:         PetscCall(PetscViewerASCIIPrintf(viewer,"  two composed functions f2(f1(.))\n"));
201:         break;
202:     }
203:     PetscCall(PetscViewerASCIIPushTab(viewer));
204:     PetscCall(FNView(ctx->f1,viewer));
205:     PetscCall(FNView(ctx->f2,viewer));
206:     PetscCall(PetscViewerASCIIPopTab(viewer));
207:   }
208:   PetscFunctionReturn(PETSC_SUCCESS);
209: }

211: static PetscErrorCode FNCombineSetChildren_Combine(FN fn,FNCombineType comb,FN f1,FN f2)
212: {
213:   FN_COMBINE     *ctx = (FN_COMBINE*)fn->data;

215:   PetscFunctionBegin;
216:   ctx->comb = comb;
217:   PetscCall(PetscObjectReference((PetscObject)f1));
218:   PetscCall(FNDestroy(&ctx->f1));
219:   ctx->f1 = f1;
220:   PetscCall(PetscObjectReference((PetscObject)f2));
221:   PetscCall(FNDestroy(&ctx->f2));
222:   ctx->f2 = f2;
223:   PetscFunctionReturn(PETSC_SUCCESS);
224: }

226: /*@
227:    FNCombineSetChildren - Sets the two child functions that constitute this
228:    combined function, and the way they must be combined.

230:    Logically Collective

232:    Input Parameters:
233: +  fn   - the math function context
234: .  comb - how to combine the functions (addition, multiplication, division or composition)
235: .  f1   - first function
236: -  f2   - second function

238:    Level: intermediate

240: .seealso: FNCombineGetChildren()
241: @*/
242: PetscErrorCode FNCombineSetChildren(FN fn,FNCombineType comb,FN f1,FN f2)
243: {
244:   PetscFunctionBegin;
249:   PetscTryMethod(fn,"FNCombineSetChildren_C",(FN,FNCombineType,FN,FN),(fn,comb,f1,f2));
250:   PetscFunctionReturn(PETSC_SUCCESS);
251: }

253: static PetscErrorCode FNCombineGetChildren_Combine(FN fn,FNCombineType *comb,FN *f1,FN *f2)
254: {
255:   FN_COMBINE     *ctx = (FN_COMBINE*)fn->data;

257:   PetscFunctionBegin;
258:   if (comb) *comb = ctx->comb;
259:   if (f1) {
260:     if (!ctx->f1) PetscCall(FNCreate(PetscObjectComm((PetscObject)fn),&ctx->f1));
261:     *f1 = ctx->f1;
262:   }
263:   if (f2) {
264:     if (!ctx->f2) PetscCall(FNCreate(PetscObjectComm((PetscObject)fn),&ctx->f2));
265:     *f2 = ctx->f2;
266:   }
267:   PetscFunctionReturn(PETSC_SUCCESS);
268: }

270: /*@
271:    FNCombineGetChildren - Gets the two child functions that constitute this
272:    combined function, and the way they are combined.

274:    Not Collective

276:    Input Parameter:
277: .  fn   - the math function context

279:    Output Parameters:
280: +  comb - how to combine the functions (addition, multiplication, division or composition)
281: .  f1   - first function
282: -  f2   - second function

284:    Level: intermediate

286: .seealso: FNCombineSetChildren()
287: @*/
288: PetscErrorCode FNCombineGetChildren(FN fn,FNCombineType *comb,FN *f1,FN *f2)
289: {
290:   PetscFunctionBegin;
292:   PetscUseMethod(fn,"FNCombineGetChildren_C",(FN,FNCombineType*,FN*,FN*),(fn,comb,f1,f2));
293:   PetscFunctionReturn(PETSC_SUCCESS);
294: }

296: static PetscErrorCode FNDuplicate_Combine(FN fn,MPI_Comm comm,FN *newfn)
297: {
298:   FN_COMBINE     *ctx = (FN_COMBINE*)fn->data,*ctx2 = (FN_COMBINE*)(*newfn)->data;

300:   PetscFunctionBegin;
301:   ctx2->comb = ctx->comb;
302:   PetscCall(FNDuplicate(ctx->f1,comm,&ctx2->f1));
303:   PetscCall(FNDuplicate(ctx->f2,comm,&ctx2->f2));
304:   PetscFunctionReturn(PETSC_SUCCESS);
305: }

307: static PetscErrorCode FNDestroy_Combine(FN fn)
308: {
309:   FN_COMBINE     *ctx = (FN_COMBINE*)fn->data;

311:   PetscFunctionBegin;
312:   PetscCall(FNDestroy(&ctx->f1));
313:   PetscCall(FNDestroy(&ctx->f2));
314:   PetscCall(PetscFree(fn->data));
315:   PetscCall(PetscObjectComposeFunction((PetscObject)fn,"FNCombineSetChildren_C",NULL));
316:   PetscCall(PetscObjectComposeFunction((PetscObject)fn,"FNCombineGetChildren_C",NULL));
317:   PetscFunctionReturn(PETSC_SUCCESS);
318: }

320: SLEPC_EXTERN PetscErrorCode FNCreate_Combine(FN fn)
321: {
322:   FN_COMBINE     *ctx;

324:   PetscFunctionBegin;
325:   PetscCall(PetscNew(&ctx));
326:   fn->data = (void*)ctx;

328:   fn->ops->evaluatefunction          = FNEvaluateFunction_Combine;
329:   fn->ops->evaluatederivative        = FNEvaluateDerivative_Combine;
330:   fn->ops->evaluatefunctionmat[0]    = FNEvaluateFunctionMat_Combine;
331:   fn->ops->evaluatefunctionmatvec[0] = FNEvaluateFunctionMatVec_Combine;
332: #if defined(PETSC_HAVE_CUDA)
333:   fn->ops->evaluatefunctionmatcuda[0]    = FNEvaluateFunctionMat_Combine;
334:   fn->ops->evaluatefunctionmatveccuda[0] = FNEvaluateFunctionMatVec_Combine;
335: #endif
336:   fn->ops->view                      = FNView_Combine;
337:   fn->ops->duplicate                 = FNDuplicate_Combine;
338:   fn->ops->destroy                   = FNDestroy_Combine;
339:   PetscCall(PetscObjectComposeFunction((PetscObject)fn,"FNCombineSetChildren_C",FNCombineSetChildren_Combine));
340:   PetscCall(PetscObjectComposeFunction((PetscObject)fn,"FNCombineGetChildren_C",FNCombineGetChildren_Combine));
341:   PetscFunctionReturn(PETSC_SUCCESS);
342: }