Actual source code: ex1.c

      1: /* Program usage: mpiexec -n 2 ./ex1 [-help] [all TAO options] */

      3: /* ----------------------------------------------------------------------
      4: min f(x) = (x0 - 2)^2 + (x1 - 2)^2 - 2*(x0 + x1)
      5: s.t.  x0^2 + x1 - 2 = 0
      6:       0  <= x0^2 - x1 <= 1
      7:       -1 <= x0, x1 <= 2
      8: -->
      9:       g(x)  = 0
     10:       h(x) >= 0
     11:       -1 <= x0, x1 <= 2
     12: where
     13:       g(x) = x0^2 + x1 - 2
     14:       h(x) = [x0^2 - x1
     15:               1 -(x0^2 - x1)]
     16: ---------------------------------------------------------------------- */

     18: #include <petsctao.h>

     20: static  char help[]= "Solves constrained optimiztion problem using pdipm.\n\
     21: Input parameters include:\n\
     22:   -tao_type pdipm    : sets Tao solver\n\
     23:   -no_eq             : removes the equaility constraints from the problem\n\
     24:   -init_view         : view initial object setup\n\
     25:   -snes_fd           : snes with finite difference Jacobian (needed for pdipm)\n\
     26:   -snes_compare_explicit : compare user Jacobian with finite difference Jacobian \n\
     27:   -tao_cmonitor      : convergence monitor with constraint norm \n\
     28:   -tao_view_solution : view exact solution at each iteration\n\
     29:   Note: external package MUMPS is required to run pdipm in parallel. This is designed for a maximum of 2 processors, the code will error if size > 2.\n";

     31: /*
     32:    User-defined application context - contains data needed by the application
     33: */
     34: typedef struct {
     35:   PetscInt   n;  /* Global length of x */
     36:   PetscInt   ne; /* Global number of equality constraints */
     37:   PetscInt   ni; /* Global number of inequality constraints */
     38:   PetscBool  noeqflag, initview;
     39:   Vec        x,xl,xu;
     40:   Vec        ce,ci,bl,bu,Xseq;
     41:   Mat        Ae,Ai,H;
     42:   VecScatter scat;
     43: } AppCtx;

     45: /* -------- User-defined Routines --------- */
     46: PetscErrorCode InitializeProblem(AppCtx *);
     47: PetscErrorCode DestroyProblem(AppCtx *);
     48: PetscErrorCode FormFunctionGradient(Tao,Vec,PetscReal *,Vec,void *);
     49: PetscErrorCode FormHessian(Tao,Vec,Mat,Mat, void*);
     50: PetscErrorCode FormInequalityConstraints(Tao,Vec,Vec,void*);
     51: PetscErrorCode FormEqualityConstraints(Tao,Vec,Vec,void*);
     52: PetscErrorCode FormInequalityJacobian(Tao,Vec,Mat,Mat, void*);
     53: PetscErrorCode FormEqualityJacobian(Tao,Vec,Mat,Mat, void*);

     55: PetscErrorCode main(int argc,char **argv)
     56: {
     57:   Tao            tao;
     58:   KSP            ksp;
     59:   PC             pc;
     60:   AppCtx         user;  /* application context */
     61:   Vec            x,G,CI,CE;
     62:   PetscMPIInt    size;
     63:   TaoType        type;
     64:   PetscReal      f;
     65:   PetscBool      pdipm;

     67:   PetscInitialize(&argc,&argv,(char*)0,help);
     68:   MPI_Comm_size(PETSC_COMM_WORLD,&size);

     71:   PetscPrintf(PETSC_COMM_WORLD,"---- Constrained Problem -----\n");
     72:   InitializeProblem(&user); /* sets up problem, function below */
     73:   TaoCreate(PETSC_COMM_WORLD,&tao);
     74:   TaoSetType(tao,TAOPDIPM);
     75:   TaoSetSolution(tao,user.x); /* gets solution vector from problem */
     76:   TaoSetVariableBounds(tao,user.xl,user.xu); /* sets lower upper bounds from given solution */
     77:   TaoSetObjectiveAndGradient(tao,NULL,FormFunctionGradient,(void*)&user);

     79:   if (!user.noeqflag) {
     80:     TaoSetEqualityConstraintsRoutine(tao,user.ce,FormEqualityConstraints,(void*)&user);
     81:   }
     82:   TaoSetInequalityConstraintsRoutine(tao,user.ci,FormInequalityConstraints,(void*)&user);
     83:   if (!user.noeqflag) {
     84:     TaoSetJacobianEqualityRoutine(tao,user.Ae,user.Ae,FormEqualityJacobian,(void*)&user); /* equality jacobian */
     85:   }
     86:   TaoSetJacobianInequalityRoutine(tao,user.Ai,user.Ai,FormInequalityJacobian,(void*)&user); /* inequality jacobian */
     87:   TaoSetTolerances(tao,1.e-6,1.e-6,1.e-6);
     88:   TaoSetConstraintTolerances(tao,1.e-6,1.e-6);

     90:   TaoGetKSP(tao,&ksp);
     91:   KSPGetPC(ksp,&pc);
     92:   PCSetType(pc,PCCHOLESKY);
     93:   /*
     94:       This algorithm produces matrices with zeros along the diagonal therefore we use
     95:     MUMPS which provides solver for indefinite matrices
     96:   */
     97: #if defined(PETSC_HAVE_MUMPS)
     98:   PCFactorSetMatSolverType(pc,MATSOLVERMUMPS);  /* requires mumps to solve pdipm */
     99: #else
    101: #endif
    102:   KSPSetType(ksp,KSPPREONLY);
    103:   KSPSetFromOptions(ksp);

    105:   TaoSetFromOptions(tao);
    106:   TaoGetType(tao,&type);
    107:   PetscObjectTypeCompare((PetscObject)tao,TAOPDIPM,&pdipm);
    108:   if (pdipm) {
    109:     TaoSetHessian(tao,user.H,user.H,FormHessian,(void*)&user);
    110:     if (user.initview) {
    111:       TaoSetUp(tao);
    112:       VecDuplicate(user.x, &G);
    113:       FormFunctionGradient(tao, user.x, &f, G, (void*)&user);
    114:       FormHessian(tao, user.x, user.H, user.H, (void*)&user);
    115:       PetscViewerASCIIPushTab(PETSC_VIEWER_STDOUT_WORLD);
    116:       PetscPrintf(PETSC_COMM_WORLD,"\nInitial point X:\n",f);
    117:       VecView(user.x, PETSC_VIEWER_STDOUT_WORLD);
    118:       PetscPrintf(PETSC_COMM_WORLD,"\nInitial objective f(x) = %g\n",f);
    119:       PetscPrintf(PETSC_COMM_WORLD,"\nInitial gradient and Hessian:\n",f);
    120:       VecView(G, PETSC_VIEWER_STDOUT_WORLD);
    121:       MatView(user.H, PETSC_VIEWER_STDOUT_WORLD);
    122:       VecDestroy(&G);
    123:       FormInequalityJacobian(tao, user.x, user.Ai, user.Ai, (void*)&user);
    124:       MatCreateVecs(user.Ai, NULL, &CI);
    125:       FormInequalityConstraints(tao, user.x, CI, (void*)&user);
    126:       PetscPrintf(PETSC_COMM_WORLD,"\nInitial inequality constraints and Jacobian:\n",f);
    127:       VecView(CI, PETSC_VIEWER_STDOUT_WORLD);
    128:       MatView(user.Ai, PETSC_VIEWER_STDOUT_WORLD);
    129:       VecDestroy(&CI);
    130:       if (!user.noeqflag) {
    131:         FormEqualityJacobian(tao, user.x, user.Ae, user.Ae, (void*)&user);
    132:         MatCreateVecs(user.Ae, NULL, &CE);
    133:         FormEqualityConstraints(tao, user.x, CE, (void*)&user);
    134:         PetscPrintf(PETSC_COMM_WORLD,"\nInitial equality constraints and Jacobian:\n",f);
    135:         VecView(CE, PETSC_VIEWER_STDOUT_WORLD);
    136:         MatView(user.Ae, PETSC_VIEWER_STDOUT_WORLD);
    137:         VecDestroy(&CE);
    138:       }
    139:       PetscPrintf(PETSC_COMM_WORLD, "\n");
    140:       PetscViewerASCIIPopTab(PETSC_VIEWER_STDOUT_WORLD);
    141:     }
    142:   }

    144:   TaoSolve(tao);
    145:   TaoGetSolution(tao,&x);
    146:   VecView(x,PETSC_VIEWER_STDOUT_WORLD);

    148:   /* Free objects */
    149:   DestroyProblem(&user);
    150:   TaoDestroy(&tao);
    151:   PetscFinalize();
    152:   return 0;
    153: }

    155: PetscErrorCode InitializeProblem(AppCtx *user)
    156: {
    157:   PetscMPIInt    size;
    158:   PetscMPIInt    rank;
    159:   PetscInt       nloc,neloc,niloc;

    161:   MPI_Comm_size(PETSC_COMM_WORLD,&size);
    162:   MPI_Comm_rank(PETSC_COMM_WORLD,&rank);
    163:   user->noeqflag = PETSC_FALSE;
    164:   PetscOptionsGetBool(NULL,NULL,"-no_eq",&user->noeqflag,NULL);
    165:   user->initview = PETSC_FALSE;
    166:   PetscOptionsGetBool(NULL,NULL,"-init_view",&user->initview,NULL);

    168:   if (!user->noeqflag) {
    169:     /* Tell user the correct solution, not an error checking */
    170:     PetscPrintf(PETSC_COMM_WORLD,"Solution should be f(1,1)=-2\n");
    171:   }

    173:   /* create vector x and set initial values */
    174:   user->n = 2; /* global length */
    175:   nloc = (size==1)?user->n:1;
    176:   VecCreate(PETSC_COMM_WORLD,&user->x);
    177:   VecSetSizes(user->x,nloc,user->n);
    178:   VecSetFromOptions(user->x);
    179:   VecSet(user->x,0);

    181:   /* create and set lower and upper bound vectors */
    182:   VecDuplicate(user->x,&user->xl);
    183:   VecDuplicate(user->x,&user->xu);
    184:   VecSet(user->xl,-1.0);
    185:   VecSet(user->xu,2.0);

    187:   /* create scater to zero */
    188:   VecScatterCreateToZero(user->x,&user->scat,&user->Xseq);

    190:   user->ne = 1;
    191:   user->ni = 2;
    192:   neloc = (rank==0)?user->ne:0;
    193:   niloc = (size==1)?user->ni:1;

    195:   if (!user->noeqflag) {
    196:     VecCreate(PETSC_COMM_WORLD,&user->ce); /* a 1x1 vec for equality constraints */
    197:     VecSetSizes(user->ce,neloc,user->ne);
    198:     VecSetFromOptions(user->ce);
    199:     VecSetUp(user->ce);
    200:   }

    202:   VecCreate(PETSC_COMM_WORLD,&user->ci); /* a 2x1 vec for inequality constraints */
    203:   VecSetSizes(user->ci,niloc,user->ni);
    204:   VecSetFromOptions(user->ci);
    205:   VecSetUp(user->ci);

    207:   /* nexn & nixn matricies for equally and inequalty constraints */
    208:   if (!user->noeqflag) {
    209:     MatCreate(PETSC_COMM_WORLD,&user->Ae);
    210:     MatSetSizes(user->Ae,neloc,nloc,user->ne,user->n);
    211:     MatSetFromOptions(user->Ae);
    212:     MatSetUp(user->Ae);
    213:   }

    215:   MatCreate(PETSC_COMM_WORLD,&user->Ai);
    216:   MatSetSizes(user->Ai,niloc,nloc,user->ni,user->n);
    217:   MatSetFromOptions(user->Ai);
    218:   MatSetUp(user->Ai);

    220:   MatCreate(PETSC_COMM_WORLD,&user->H);
    221:   MatSetSizes(user->H,nloc,nloc,user->n,user->n);
    222:   MatSetFromOptions(user->H);
    223:   MatSetUp(user->H);
    224:   return 0;
    225: }

    227: PetscErrorCode DestroyProblem(AppCtx *user)
    228: {
    229:   if (!user->noeqflag) {
    230:     MatDestroy(&user->Ae);
    231:   }
    232:   MatDestroy(&user->Ai);
    233:   MatDestroy(&user->H);

    235:   VecDestroy(&user->x);
    236:   if (!user->noeqflag) {
    237:     VecDestroy(&user->ce);
    238:   }
    239:   VecDestroy(&user->ci);
    240:   VecDestroy(&user->xl);
    241:   VecDestroy(&user->xu);
    242:   VecDestroy(&user->Xseq);
    243:   VecScatterDestroy(&user->scat);
    244:   return 0;
    245: }

    247: /* Evaluate
    248:    f(x) = (x0 - 2)^2 + (x1 - 2)^2 - 2*(x0 + x1)
    249:    G = grad f = [2*(x0 - 2) - 2;
    250:                  2*(x1 - 2) - 2]
    251: */
    252: PetscErrorCode FormFunctionGradient(Tao tao, Vec X, PetscReal *f, Vec G, void *ctx)
    253: {
    254:   PetscScalar       g;
    255:   const PetscScalar *x;
    256:   MPI_Comm          comm;
    257:   PetscMPIInt       rank;
    258:   PetscReal         fin;
    259:   AppCtx            *user=(AppCtx*)ctx;
    260:   Vec               Xseq=user->Xseq;
    261:   VecScatter        scat=user->scat;

    263:   PetscObjectGetComm((PetscObject)tao,&comm);
    264:   MPI_Comm_rank(comm,&rank);

    266:   VecScatterBegin(scat,X,Xseq,INSERT_VALUES,SCATTER_FORWARD);
    267:   VecScatterEnd(scat,X,Xseq,INSERT_VALUES,SCATTER_FORWARD);

    269:   fin = 0.0;
    270:   if (rank == 0) {
    271:     VecGetArrayRead(Xseq,&x);
    272:     fin = (x[0]-2.0)*(x[0]-2.0) + (x[1]-2.0)*(x[1]-2.0) - 2.0*(x[0]+x[1]);
    273:     g = 2.0*(x[0]-2.0) - 2.0;
    274:     VecSetValue(G,0,g,INSERT_VALUES);
    275:     g = 2.0*(x[1]-2.0) - 2.0;
    276:     VecSetValue(G,1,g,INSERT_VALUES);
    277:     VecRestoreArrayRead(Xseq,&x);
    278:   }
    279:   MPI_Allreduce(&fin,f,1,MPIU_REAL,MPIU_SUM,comm);
    280:   VecAssemblyBegin(G);
    281:   VecAssemblyEnd(G);
    282:   return 0;
    283: }

    285: /* Evaluate
    286:    H = fxx + grad (grad g^T*DI) - grad (grad h^T*DE)]
    287:      = [ 2*(1+de[0]-di[0]+di[1]), 0;
    288:                    0,             2]
    289: */
    290: PetscErrorCode FormHessian(Tao tao, Vec x,Mat H, Mat Hpre, void *ctx)
    291: {
    292:   AppCtx            *user=(AppCtx*)ctx;
    293:   Vec               DE,DI;
    294:   const PetscScalar *de,*di;
    295:   PetscInt          zero=0,one=1;
    296:   PetscScalar       two=2.0;
    297:   PetscScalar       val=0.0;
    298:   Vec               Deseq,Diseq;
    299:   VecScatter        Descat,Discat;
    300:   PetscMPIInt       rank;
    301:   MPI_Comm          comm;

    303:   TaoGetDualVariables(tao,&DE,&DI);

    305:   PetscObjectGetComm((PetscObject)tao,&comm);
    306:   MPI_Comm_rank(comm,&rank);

    308:   if (!user->noeqflag) {
    309:    VecScatterCreateToZero(DE,&Descat,&Deseq);
    310:    VecScatterBegin(Descat,DE,Deseq,INSERT_VALUES,SCATTER_FORWARD);
    311:    VecScatterEnd(Descat,DE,Deseq,INSERT_VALUES,SCATTER_FORWARD);
    312:   }
    313:   VecScatterCreateToZero(DI,&Discat,&Diseq);
    314:   VecScatterBegin(Discat,DI,Diseq,INSERT_VALUES,SCATTER_FORWARD);
    315:   VecScatterEnd(Discat,DI,Diseq,INSERT_VALUES,SCATTER_FORWARD);

    317:   if (rank == 0) {
    318:     if (!user->noeqflag) {
    319:       VecGetArrayRead(Deseq,&de);  /* places equality constraint dual into array */
    320:     }
    321:     VecGetArrayRead(Diseq,&di);  /* places inequality constraint dual into array */

    323:     if (!user->noeqflag) {
    324:       val = 2.0 * (1 + de[0] - di[0] + di[1]);
    325:       VecRestoreArrayRead(Deseq,&de);
    326:       VecRestoreArrayRead(Diseq,&di);
    327:     } else {
    328:       val = 2.0 * (1 - di[0] + di[1]);
    329:     }
    330:     VecRestoreArrayRead(Diseq,&di);
    331:     MatSetValues(H,1,&zero,1,&zero,&val,INSERT_VALUES);
    332:     MatSetValues(H,1,&one,1,&one,&two,INSERT_VALUES);
    333:   }
    334:   MatAssemblyBegin(H,MAT_FINAL_ASSEMBLY);
    335:   MatAssemblyEnd(H,MAT_FINAL_ASSEMBLY);
    336:   if (!user->noeqflag) {
    337:     VecScatterDestroy(&Descat);
    338:     VecDestroy(&Deseq);
    339:   }
    340:   VecScatterDestroy(&Discat);
    341:   VecDestroy(&Diseq);
    342:   return 0;
    343: }

    345: /* Evaluate
    346:    h = [ x0^2 - x1;
    347:          1 -(x0^2 - x1)]
    348: */
    349: PetscErrorCode FormInequalityConstraints(Tao tao,Vec X,Vec CI,void *ctx)
    350: {
    351:   const PetscScalar *x;
    352:   PetscScalar       ci;
    353:   MPI_Comm          comm;
    354:   PetscMPIInt       rank;
    355:   AppCtx            *user=(AppCtx*)ctx;
    356:   Vec               Xseq=user->Xseq;
    357:   VecScatter        scat=user->scat;

    359:   PetscObjectGetComm((PetscObject)tao,&comm);
    360:   MPI_Comm_rank(comm,&rank);

    362:   VecScatterBegin(scat,X,Xseq,INSERT_VALUES,SCATTER_FORWARD);
    363:   VecScatterEnd(scat,X,Xseq,INSERT_VALUES,SCATTER_FORWARD);

    365:   if (rank == 0) {
    366:     VecGetArrayRead(Xseq,&x);
    367:     ci = x[0]*x[0] - x[1];
    368:     VecSetValue(CI,0,ci,INSERT_VALUES);
    369:     ci = -x[0]*x[0] + x[1] + 1.0;
    370:     VecSetValue(CI,1,ci,INSERT_VALUES);
    371:     VecRestoreArrayRead(Xseq,&x);
    372:   }
    373:   VecAssemblyBegin(CI);
    374:   VecAssemblyEnd(CI);
    375:   return 0;
    376: }

    378: /* Evaluate
    379:    g = [ x0^2 + x1 - 2]
    380: */
    381: PetscErrorCode FormEqualityConstraints(Tao tao,Vec X,Vec CE,void *ctx)
    382: {
    383:   const PetscScalar *x;
    384:   PetscScalar       ce;
    385:   MPI_Comm          comm;
    386:   PetscMPIInt       rank;
    387:   AppCtx            *user=(AppCtx*)ctx;
    388:   Vec               Xseq=user->Xseq;
    389:   VecScatter        scat=user->scat;

    391:   PetscObjectGetComm((PetscObject)tao,&comm);
    392:   MPI_Comm_rank(comm,&rank);

    394:   VecScatterBegin(scat,X,Xseq,INSERT_VALUES,SCATTER_FORWARD);
    395:   VecScatterEnd(scat,X,Xseq,INSERT_VALUES,SCATTER_FORWARD);

    397:   if (rank == 0) {
    398:     VecGetArrayRead(Xseq,&x);
    399:     ce = x[0]*x[0] + x[1] - 2.0;
    400:     VecSetValue(CE,0,ce,INSERT_VALUES);
    401:     VecRestoreArrayRead(Xseq,&x);
    402:   }
    403:   VecAssemblyBegin(CE);
    404:   VecAssemblyEnd(CE);
    405:   return 0;
    406: }

    408: /*
    409:   grad h = [  2*x0, -1;
    410:              -2*x0,  1]
    411: */
    412: PetscErrorCode FormInequalityJacobian(Tao tao, Vec X, Mat JI, Mat JIpre,  void *ctx)
    413: {
    414:   AppCtx            *user=(AppCtx*)ctx;
    415:   PetscInt          zero=0,one=1,cols[2];
    416:   PetscScalar       vals[2];
    417:   const PetscScalar *x;
    418:   Vec               Xseq=user->Xseq;
    419:   VecScatter        scat=user->scat;
    420:   MPI_Comm          comm;
    421:   PetscMPIInt       rank;

    423:   PetscObjectGetComm((PetscObject)tao,&comm);
    424:   MPI_Comm_rank(comm,&rank);
    425:   VecScatterBegin(scat,X,Xseq,INSERT_VALUES,SCATTER_FORWARD);
    426:   VecScatterEnd(scat,X,Xseq,INSERT_VALUES,SCATTER_FORWARD);

    428:   VecGetArrayRead(Xseq,&x);
    429:   if (!rank) {
    430:     cols[0] = 0; cols[1] = 1;
    431:     vals[0] = 2*x[0]; vals[1] = -1.0;
    432:     MatSetValues(JI,1,&zero,2,cols,vals,INSERT_VALUES);
    433:     vals[0] = -2*x[0]; vals[1] = 1.0;
    434:     MatSetValues(JI,1,&one,2,cols,vals,INSERT_VALUES);
    435:   }
    436:   VecRestoreArrayRead(Xseq,&x);
    437:   MatAssemblyBegin(JI,MAT_FINAL_ASSEMBLY);
    438:   MatAssemblyEnd(JI,MAT_FINAL_ASSEMBLY);
    439:   return 0;
    440: }

    442: /*
    443:   grad g = [2*x0
    444:              1.0 ]
    445: */
    446: PetscErrorCode FormEqualityJacobian(Tao tao,Vec X,Mat JE,Mat JEpre,void *ctx)
    447: {
    448:   PetscInt          zero=0,cols[2];
    449:   PetscScalar       vals[2];
    450:   const PetscScalar *x;
    451:   PetscMPIInt       rank;
    452:   MPI_Comm          comm;

    454:   PetscObjectGetComm((PetscObject)tao,&comm);
    455:   MPI_Comm_rank(comm,&rank);

    457:   if (rank == 0) {
    458:     VecGetArrayRead(X,&x);
    459:     cols[0] = 0;       cols[1] = 1;
    460:     vals[0] = 2*x[0];  vals[1] = 1.0;
    461:     MatSetValues(JE,1,&zero,2,cols,vals,INSERT_VALUES);
    462:     VecRestoreArrayRead(X,&x);
    463:   }
    464:   MatAssemblyBegin(JE,MAT_FINAL_ASSEMBLY);
    465:   MatAssemblyEnd(JE,MAT_FINAL_ASSEMBLY);
    466:   return 0;
    467: }

    469: /*TEST

    471:    build:
    472:       requires: !complex !defined(PETSC_USE_CXX)

    474:    test:
    475:       args: -tao_converged_reason -tao_pdipm_kkt_shift_pd
    476:       requires: mumps

    478:    test:
    479:       suffix: 2
    480:       nsize: 2
    481:       args: -tao_converged_reason -tao_pdipm_kkt_shift_pd
    482:       requires: mumps

    484:    test:
    485:       suffix: 3
    486:       args: -tao_converged_reason -no_eq
    487:       requires: mumps

    489:    test:
    490:       suffix: 4
    491:       nsize: 2
    492:       args: -tao_converged_reason -no_eq
    493:       requires: mumps

    495:    test:
    496:       suffix: 5
    497:       args: -tao_cmonitor -tao_type almm
    498:       requires: mumps

    500:    test:
    501:       suffix: 6
    502:       args: -tao_cmonitor -tao_type almm -tao_almm_type phr
    503:       requires: mumps

    505:    test:
    506:       suffix: 7
    507:       nsize: 2
    508:       requires: mumps
    509:       args: -tao_cmonitor -tao_type almm

    511:    test:
    512:       suffix: 8
    513:       nsize: 2
    514:       requires: cuda mumps
    515:       args: -tao_cmonitor -tao_type almm -vec_type cuda -mat_type aijcusparse

    517:    test:
    518:       suffix: 9
    519:       nsize: 2
    520:       args: -tao_cmonitor -tao_type almm -no_eq
    521:       requires: mumps

    523:    test:
    524:       suffix: 10
    525:       nsize: 2
    526:       args: -tao_cmonitor -tao_type almm -tao_almm_type phr -no_eq
    527:       requires: mumps

    529: TEST*/