Actual source code: ex40.c
slepc-3.22.1 2024-10-28
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: */
11: static char help[] = "Checking the definite property in quadratic symmetric eigenproblem.\n\n"
12: "The command line options are:\n"
13: " -n <n> ... dimension of the matrices.\n"
14: " -transform... whether to transform to a hyperbolic problem or not.\n"
15: " -nonhyperbolic... to test with a modified (definite) problem that is not hyperbolic.\n\n";
17: #include <slepcpep.h>
19: /*
20: This example is based on spring.c, for fixed values mu=1,tau=10,kappa=5
22: The transformations are based on the method proposed in [Niendorf and Voss, LAA 2010].
23: */
25: PetscErrorCode QEPDefiniteTransformGetMatrices(PEP,PetscBool,PetscReal,PetscReal,Mat[3]);
26: PetscErrorCode QEPDefiniteTransformMap(PetscBool,PetscReal,PetscReal,PetscInt,PetscScalar*,PetscBool);
27: PetscErrorCode QEPDefiniteCheckError(Mat*,PEP,PetscBool,PetscReal,PetscReal);
28: PetscErrorCode TransformMatricesMoebius(Mat[3],MatStructure,PetscReal,PetscReal,PetscReal,PetscReal,Mat[3]);
30: int main(int argc,char **argv)
31: {
32: Mat M,C,K,*Op,A[3],At[3],B[3]; /* problem matrices */
33: PEP pep; /* polynomial eigenproblem solver context */
34: ST st; /* spectral transformation context */
35: KSP ksp;
36: PC pc;
37: PEPProblemType type;
38: PetscBool terse,transform=PETSC_FALSE,nohyp=PETSC_FALSE;
39: PetscInt n=100,Istart,Iend,i,def=0,hyp;
40: PetscReal muu=1,tau=10,kappa=5,inta,intb;
41: PetscReal alpha,beta,xi,mu,at[2]={0.0,0.0},c=.857,s;
42: PetscScalar target,targett,ats[2];
44: PetscFunctionBeginUser;
45: PetscCall(SlepcInitialize(&argc,&argv,NULL,help));
47: PetscCall(PetscOptionsGetInt(NULL,NULL,"-n",&n,NULL));
48: PetscCall(PetscPrintf(PETSC_COMM_WORLD,"\nPEP example that checks definite property, n=%" PetscInt_FMT "\n\n",n));
50: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
51: Compute the matrices that define the eigensystem, (k^2*M+k*C+K)x=0
52: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
54: /* K is a tridiagonal */
55: PetscCall(MatCreate(PETSC_COMM_WORLD,&K));
56: PetscCall(MatSetSizes(K,PETSC_DECIDE,PETSC_DECIDE,n,n));
57: PetscCall(MatSetFromOptions(K));
59: PetscCall(MatGetOwnershipRange(K,&Istart,&Iend));
60: for (i=Istart;i<Iend;i++) {
61: if (i>0) PetscCall(MatSetValue(K,i,i-1,-kappa,INSERT_VALUES));
62: PetscCall(MatSetValue(K,i,i,kappa*3.0,INSERT_VALUES));
63: if (i<n-1) PetscCall(MatSetValue(K,i,i+1,-kappa,INSERT_VALUES));
64: }
66: PetscCall(MatAssemblyBegin(K,MAT_FINAL_ASSEMBLY));
67: PetscCall(MatAssemblyEnd(K,MAT_FINAL_ASSEMBLY));
69: /* C is a tridiagonal */
70: PetscCall(MatCreate(PETSC_COMM_WORLD,&C));
71: PetscCall(MatSetSizes(C,PETSC_DECIDE,PETSC_DECIDE,n,n));
72: PetscCall(MatSetFromOptions(C));
74: PetscCall(MatGetOwnershipRange(C,&Istart,&Iend));
75: for (i=Istart;i<Iend;i++) {
76: if (i>0) PetscCall(MatSetValue(C,i,i-1,-tau,INSERT_VALUES));
77: PetscCall(MatSetValue(C,i,i,tau*3.0,INSERT_VALUES));
78: if (i<n-1) PetscCall(MatSetValue(C,i,i+1,-tau,INSERT_VALUES));
79: }
81: PetscCall(MatAssemblyBegin(C,MAT_FINAL_ASSEMBLY));
82: PetscCall(MatAssemblyEnd(C,MAT_FINAL_ASSEMBLY));
84: /* M is a diagonal matrix */
85: PetscCall(MatCreate(PETSC_COMM_WORLD,&M));
86: PetscCall(MatSetSizes(M,PETSC_DECIDE,PETSC_DECIDE,n,n));
87: PetscCall(MatSetFromOptions(M));
88: PetscCall(MatGetOwnershipRange(M,&Istart,&Iend));
89: for (i=Istart;i<Iend;i++) PetscCall(MatSetValue(M,i,i,muu,INSERT_VALUES));
90: PetscCall(MatAssemblyBegin(M,MAT_FINAL_ASSEMBLY));
91: PetscCall(MatAssemblyEnd(M,MAT_FINAL_ASSEMBLY));
93: PetscCall(PetscOptionsGetBool(NULL,NULL,"-nonhyperbolic",&nohyp,NULL));
94: A[0] = K; A[1] = C; A[2] = M;
95: if (nohyp) {
96: s = c*.6;
97: PetscCall(TransformMatricesMoebius(A,UNKNOWN_NONZERO_PATTERN,c,s,-s,c,At));
98: for (i=0;i<3;i++) PetscCall(MatDestroy(&A[i]));
99: Op = At;
100: } else Op = A;
102: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
103: Create the eigensolver and solve the problem
104: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
106: /*
107: Create eigensolver context
108: */
109: PetscCall(PEPCreate(PETSC_COMM_WORLD,&pep));
110: PetscCall(PEPSetProblemType(pep,PEP_HERMITIAN));
111: PetscCall(PEPSetType(pep,PEPSTOAR));
112: /*
113: Set operators and set problem type
114: */
115: PetscCall(PEPSetOperators(pep,3,Op));
117: /*
118: Set shift-and-invert with Cholesky; select MUMPS if available
119: */
120: PetscCall(PEPGetST(pep,&st));
121: PetscCall(STGetKSP(st,&ksp));
122: PetscCall(KSPSetType(ksp,KSPPREONLY));
123: PetscCall(KSPGetPC(ksp,&pc));
124: PetscCall(PCSetType(pc,PCCHOLESKY));
126: /*
127: Use MUMPS if available.
128: Note that in complex scalars we cannot use MUMPS for spectrum slicing,
129: because MatGetInertia() is not available in that case.
130: */
131: #if defined(PETSC_HAVE_MUMPS) && !defined(PETSC_USE_COMPLEX)
132: PetscCall(PCFactorSetMatSolverType(pc,MATSOLVERMUMPS));
133: /*
134: Add several MUMPS options (see ex43.c for a better way of setting them in program):
135: '-st_mat_mumps_icntl_13 1': turn off ScaLAPACK for matrix inertia
136: */
137: PetscCall(PetscOptionsInsertString(NULL,"-st_mat_mumps_icntl_13 1 -st_mat_mumps_icntl_24 1 -st_mat_mumps_cntl_3 1e-12"));
138: #endif
140: /*
141: Set solver parameters at runtime
142: */
143: PetscCall(PEPSetFromOptions(pep));
145: PetscCall(PetscOptionsGetBool(NULL,NULL,"-transform",&transform,NULL));
146: if (transform) {
147: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
148: Check if the problem is definite
149: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
150: PetscCall(PEPCheckDefiniteQEP(pep,&xi,&mu,&def,&hyp));
151: switch (def) {
152: case 1:
153: if (hyp==1) PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Hyperbolic Problem xi=%g\n",(double)xi));
154: else PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Definite Problem xi=%g mu=%g\n",(double)xi,(double)mu));
155: break;
156: case -1:
157: PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Not Definite Problem\n"));
158: break;
159: default:
160: PetscCall(PetscPrintf(PETSC_COMM_WORLD,"Cannot determine definiteness\n"));
161: break;
162: }
164: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
165: Transform the QEP to have a definite inner product in the linearization
166: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
167: if (def==1) {
168: PetscCall(QEPDefiniteTransformGetMatrices(pep,hyp==1?PETSC_TRUE:PETSC_FALSE,xi,mu,B));
169: PetscCall(PEPSetOperators(pep,3,B));
170: PetscCall(PEPGetTarget(pep,&target));
171: targett = target;
172: PetscCall(QEPDefiniteTransformMap(hyp==1?PETSC_TRUE:PETSC_FALSE,xi,mu,1,&targett,PETSC_FALSE));
173: PetscCall(PEPSetTarget(pep,targett));
174: PetscCall(PEPGetProblemType(pep,&type));
175: PetscCall(PEPSetProblemType(pep,PEP_HYPERBOLIC));
176: PetscCall(PEPSTOARGetLinearization(pep,&alpha,&beta));
177: PetscCall(PEPSTOARSetLinearization(pep,1.0,0.0));
178: PetscCall(PEPGetInterval(pep,&inta,&intb));
179: if (inta!=intb) {
180: ats[0] = inta; ats[1] = intb;
181: PetscCall(QEPDefiniteTransformMap(hyp==1?PETSC_TRUE:PETSC_FALSE,xi,mu,2,ats,PETSC_FALSE));
182: at[0] = PetscRealPart(ats[0]); at[1] = PetscRealPart(ats[1]);
183: if (at[0]<at[1]) PetscCall(PEPSetInterval(pep,at[0],at[1]));
184: else PetscCall(PEPSetInterval(pep,PETSC_MIN_REAL,at[1]));
185: }
186: }
187: }
189: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
190: Solve the eigensystem
191: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
192: PetscCall(PEPSolve(pep));
194: /* show detailed info unless -terse option is given by user */
195: if (def!=1) {
196: PetscCall(PetscOptionsHasName(NULL,NULL,"-terse",&terse));
197: if (terse) PetscCall(PEPErrorView(pep,PEP_ERROR_BACKWARD,NULL));
198: else {
199: PetscCall(PetscViewerPushFormat(PETSC_VIEWER_STDOUT_WORLD,PETSC_VIEWER_ASCII_INFO_DETAIL));
200: PetscCall(PEPConvergedReasonView(pep,PETSC_VIEWER_STDOUT_WORLD));
201: PetscCall(PEPErrorView(pep,PEP_ERROR_RELATIVE,PETSC_VIEWER_STDOUT_WORLD));
202: PetscCall(PetscViewerPopFormat(PETSC_VIEWER_STDOUT_WORLD));
203: }
204: } else {
205: /* Map the solution */
206: PetscCall(PEPConvergedReasonView(pep,PETSC_VIEWER_STDOUT_WORLD));
207: PetscCall(QEPDefiniteCheckError(Op,pep,hyp==1?PETSC_TRUE:PETSC_FALSE,xi,mu));
208: for (i=0;i<3;i++) PetscCall(MatDestroy(B+i));
209: }
210: if (at[0]>at[1]) {
211: PetscCall(PEPSetInterval(pep,at[0],PETSC_MAX_REAL));
212: PetscCall(PEPSolve(pep));
213: PetscCall(PEPConvergedReasonView(pep,PETSC_VIEWER_STDOUT_WORLD));
214: /* Map the solution */
215: PetscCall(QEPDefiniteCheckError(Op,pep,hyp==1?PETSC_TRUE:PETSC_FALSE,xi,mu));
216: }
217: if (def==1) {
218: PetscCall(PEPSetTarget(pep,target));
219: PetscCall(PEPSetProblemType(pep,type));
220: PetscCall(PEPSTOARSetLinearization(pep,alpha,beta));
221: if (inta!=intb) PetscCall(PEPSetInterval(pep,inta,intb));
222: }
224: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
225: Clean up
226: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
227: PetscCall(PEPDestroy(&pep));
228: for (i=0;i<3;i++) PetscCall(MatDestroy(Op+i));
229: PetscCall(SlepcFinalize());
230: return 0;
231: }
233: /* ------------------------------------------------------------------- */
234: /*
235: QEPDefiniteTransformMap_Initial - map a scalar value with a certain Moebius transform
237: a theta + b
238: lambda = --------------
239: c theta + d
241: Input:
242: xi,mu: real values such that Q(xi)<0 and Q(mu)>0
243: hyperbolic: if true the problem is assumed hyperbolic (mu is not used)
244: Input/Output:
245: val (array of length n)
246: if backtransform=true returns lambda from theta, else returns theta from lambda
247: */
248: static PetscErrorCode QEPDefiniteTransformMap_Initial(PetscBool hyperbolic,PetscReal xi,PetscReal mu,PetscInt n,PetscScalar *val,PetscBool backtransform)
249: {
250: PetscInt i;
251: PetscReal a,b,c,d,s;
253: PetscFunctionBegin;
254: if (hyperbolic) { a = 1.0; b = xi; c =0.0; d = 1.0; }
255: else { a = mu; b = mu*xi-1; c = 1.0; d = xi+mu; }
256: if (!backtransform) { s = a; a = -d; d = -s; }
257: for (i=0;i<n;i++) {
258: if (PetscRealPart(val[i]) >= PETSC_MAX_REAL || PetscRealPart(val[i]) <= PETSC_MIN_REAL) val[i] = a/c;
259: else if (val[i] == -d/c) val[i] = PETSC_MAX_REAL;
260: else val[i] = (a*val[i]+b)/(c*val[i]+d);
261: }
262: PetscFunctionReturn(PETSC_SUCCESS);
263: }
265: /* ------------------------------------------------------------------- */
266: /*
267: QEPDefiniteTransformMap - perform the mapping if the problem is hyperbolic, otherwise
268: modify the value of xi in advance
269: */
270: PetscErrorCode QEPDefiniteTransformMap(PetscBool hyperbolic,PetscReal xi,PetscReal mu,PetscInt n,PetscScalar *val,PetscBool backtransform)
271: {
272: PetscReal xit;
273: PetscScalar alpha;
275: PetscFunctionBegin;
276: xit = xi;
277: if (!hyperbolic) {
278: alpha = xi;
279: PetscCall(QEPDefiniteTransformMap_Initial(PETSC_FALSE,0.0,mu,1,&alpha,PETSC_FALSE));
280: xit = PetscRealPart(alpha);
281: }
282: PetscCall(QEPDefiniteTransformMap_Initial(hyperbolic,xit,mu,n,val,backtransform));
283: PetscFunctionReturn(PETSC_SUCCESS);
284: }
286: /* ------------------------------------------------------------------- */
287: /*
288: TransformMatricesMoebius - transform the coefficient matrices of a QEP
290: Input:
291: A: coefficient matrices of the original QEP
292: a,b,c,d: parameters of the Moebius transform
293: str: structure flag for MatAXPY operations
294: Output:
295: B: transformed matrices
296: */
297: PetscErrorCode TransformMatricesMoebius(Mat A[3],MatStructure str,PetscReal a,PetscReal b,PetscReal c,PetscReal d,Mat B[3])
298: {
299: PetscInt i,k;
300: PetscReal cf[9];
302: PetscFunctionBegin;
303: for (i=0;i<3;i++) PetscCall(MatDuplicate(A[2],MAT_COPY_VALUES,&B[i]));
304: /* Ct = b*b*A+b*d*B+d*d*C */
305: cf[0] = d*d; cf[1] = b*d; cf[2] = b*b;
306: /* Bt = 2*a*b*A+(b*c+a*d)*B+2*c*d*C*/
307: cf[3] = 2*c*d; cf[4] = b*c+a*d; cf[5] = 2*a*b;
308: /* At = a*a*A+a*c*B+c*c*C */
309: cf[6] = c*c; cf[7] = a*c; cf[8] = a*a;
310: for (k=0;k<3;k++) {
311: PetscCall(MatScale(B[k],cf[k*3+2]));
312: for (i=0;i<2;i++) PetscCall(MatAXPY(B[k],cf[3*k+i],A[i],str));
313: }
314: PetscFunctionReturn(PETSC_SUCCESS);
315: }
317: /* ------------------------------------------------------------------- */
318: /*
319: QEPDefiniteTransformGetMatrices - given a PEP of degree 2, transform the three
320: matrices with TransformMatricesMoebius
322: Input:
323: pep: polynomial eigenproblem to be transformed, with Q(.) being the quadratic polynomial
324: xi,mu: real values such that Q(xi)<0 and Q(mu)>0
325: hyperbolic: if true the problem is assumed hyperbolic (mu is not used)
326: Output:
327: T: coefficient matrices of the transformed polynomial
328: */
329: PetscErrorCode QEPDefiniteTransformGetMatrices(PEP pep,PetscBool hyperbolic,PetscReal xi,PetscReal mu,Mat T[3])
330: {
331: MatStructure str;
332: ST st;
333: PetscInt i;
334: PetscReal a,b,c,d;
335: PetscScalar xit;
336: Mat A[3];
338: PetscFunctionBegin;
339: for (i=2;i>=0;i--) PetscCall(PEPGetOperators(pep,i,&A[i]));
340: if (hyperbolic) { a = 1.0; b = xi; c =0.0; d = 1.0; }
341: else {
342: xit = xi;
343: PetscCall(QEPDefiniteTransformMap_Initial(PETSC_FALSE,0.0,mu,1,&xit,PETSC_FALSE));
344: a = mu; b = mu*PetscRealPart(xit)-1.0; c = 1.0; d = PetscRealPart(xit)+mu;
345: }
346: PetscCall(PEPGetST(pep,&st));
347: PetscCall(STGetMatStructure(st,&str));
348: PetscCall(TransformMatricesMoebius(A,str,a,b,c,d,T));
349: PetscFunctionReturn(PETSC_SUCCESS);
350: }
352: /* ------------------------------------------------------------------- */
353: /*
354: Auxiliary function to compute the residual norm of an eigenpair of a QEP defined
355: by coefficient matrices A
356: */
357: static PetscErrorCode PEPResidualNorm(Mat *A,PetscScalar kr,PetscScalar ki,Vec xr,Vec xi,Vec *z,PetscReal *norm)
358: {
359: PetscInt i,nmat=3;
360: PetscScalar vals[3];
361: Vec u,w;
362: #if !defined(PETSC_USE_COMPLEX)
363: Vec ui,wi;
364: PetscReal ni;
365: PetscBool imag;
366: PetscScalar ivals[3];
367: #endif
369: PetscFunctionBegin;
370: u = z[0]; w = z[1];
371: PetscCall(VecSet(u,0.0));
372: #if !defined(PETSC_USE_COMPLEX)
373: ui = z[2]; wi = z[3];
374: #endif
375: vals[0] = 1.0;
376: vals[1] = kr;
377: vals[2] = kr*kr-ki*ki;
378: #if !defined(PETSC_USE_COMPLEX)
379: ivals[0] = 0.0;
380: ivals[1] = ki;
381: ivals[2] = 2.0*kr*ki;
382: if (ki == 0 || PetscAbsScalar(ki) < PetscAbsScalar(kr*PETSC_MACHINE_EPSILON))
383: imag = PETSC_FALSE;
384: else {
385: imag = PETSC_TRUE;
386: PetscCall(VecSet(ui,0.0));
387: }
388: #endif
389: for (i=0;i<nmat;i++) {
390: if (vals[i]!=0.0) {
391: PetscCall(MatMult(A[i],xr,w));
392: PetscCall(VecAXPY(u,vals[i],w));
393: }
394: #if !defined(PETSC_USE_COMPLEX)
395: if (imag) {
396: if (ivals[i]!=0 || vals[i]!=0) {
397: PetscCall(MatMult(A[i],xi,wi));
398: if (vals[i]==0) PetscCall(MatMult(A[i],xr,w));
399: }
400: if (ivals[i]!=0) {
401: PetscCall(VecAXPY(u,-ivals[i],wi));
402: PetscCall(VecAXPY(ui,ivals[i],w));
403: }
404: if (vals[i]!=0) PetscCall(VecAXPY(ui,vals[i],wi));
405: }
406: #endif
407: }
408: PetscCall(VecNorm(u,NORM_2,norm));
409: #if !defined(PETSC_USE_COMPLEX)
410: if (imag) {
411: PetscCall(VecNorm(ui,NORM_2,&ni));
412: *norm = SlepcAbsEigenvalue(*norm,ni);
413: }
414: #endif
415: PetscFunctionReturn(PETSC_SUCCESS);
416: }
418: /* ------------------------------------------------------------------- */
419: /*
420: QEPDefiniteCheckError - check and print the residual norm of a transformed PEP
422: Input:
423: A: coefficient matrices of the original problem
424: pep: solver containing the computed solution of the transformed problem
425: xi,mu,hyperbolic: parameters used in transformation
426: */
427: PetscErrorCode QEPDefiniteCheckError(Mat *A,PEP pep,PetscBool hyperbolic,PetscReal xi,PetscReal mu)
428: {
429: PetscScalar er,ei;
430: PetscReal re,im,error;
431: Vec vr,vi,w[4];
432: PetscInt i,nconv;
433: BV bv;
434: char ex[30],sep[]=" ---------------------- --------------------\n";
436: PetscFunctionBegin;
437: PetscCall(PetscSNPrintf(ex,sizeof(ex),"||P(k)x||/||kx||"));
438: PetscCall(PetscPrintf(PETSC_COMM_WORLD,"%s k %s\n%s",sep,ex,sep));
439: PetscCall(PEPGetConverged(pep,&nconv));
440: PetscCall(PEPGetBV(pep,&bv));
441: PetscCall(BVCreateVec(bv,w));
442: PetscCall(VecDuplicate(w[0],&vr));
443: PetscCall(VecDuplicate(w[0],&vi));
444: for (i=1;i<4;i++) PetscCall(VecDuplicate(w[0],w+i));
445: for (i=0;i<nconv;i++) {
446: PetscCall(PEPGetEigenpair(pep,i,&er,&ei,vr,vi));
447: PetscCall(QEPDefiniteTransformMap(hyperbolic,xi,mu,1,&er,PETSC_TRUE));
448: PetscCall(PEPResidualNorm(A,er,0.0,vr,vi,w,&error));
449: error /= SlepcAbsEigenvalue(er,0.0);
450: #if defined(PETSC_USE_COMPLEX)
451: re = PetscRealPart(er);
452: im = PetscImaginaryPart(ei);
453: #else
454: re = er;
455: im = ei;
456: #endif
457: if (im!=0.0) PetscCall(PetscPrintf(PETSC_COMM_WORLD," % 9f%+9fi %12g\n",(double)re,(double)im,(double)error));
458: else PetscCall(PetscPrintf(PETSC_COMM_WORLD," % 12f %12g\n",(double)re,(double)error));
459: }
460: PetscCall(PetscPrintf(PETSC_COMM_WORLD,"%s",sep));
461: for (i=0;i<4;i++) PetscCall(VecDestroy(w+i));
462: PetscCall(VecDestroy(&vi));
463: PetscCall(VecDestroy(&vr));
464: PetscFunctionReturn(PETSC_SUCCESS);
465: }
467: /*TEST
469: testset:
470: requires: !single
471: args: -pep_nev 3 -nonhyperbolic -pep_target 2
472: output_file: output/ex40_1.out
473: filter: grep -v "Definite" | sed -e "s/iterations [0-9]\([0-9]*\)/iterations xx/g" | sed -e "s/[0-9]\.[0-9]*e[+-]\([0-9]*\)/removed/g"
474: test:
475: suffix: 1
476: requires: !complex
477: test:
478: suffix: 1_complex
479: requires: complex !mumps
480: test:
481: suffix: 1_transform
482: requires: !complex
483: args: -transform
484: test:
485: suffix: 1_transform_complex
486: requires: complex !mumps
487: args: -transform
489: TEST*/