Date: 2025-03-29T05:58:41+00:00

Actual source code: ex99.c

      1: static const char help[] = "Attempts to solve for root of a function with multiple local minima.\n\
      2: With the proper initial guess, a backtracking line-search fails because Newton's method gets\n\
      3: stuck in a local minimum. However, a critical point line-search or Newton's method without a\n\
      4: line search succeeds.\n";

      6: /* Solve 1D problem f(x) = 8 * exp(-4 * (x - 2)^2) * (x - 2) + 2 * x = 0

      8: This problem is based on the example given here: https://scicomp.stackexchange.com/a/2446/24756
      9: Originally an optimization problem to find the minimum of the function

     11: g(x) = x^2 - exp(-4 * (x - 2)^2)

     13: it has been reformulated to solve dg(x)/dx = f(x) = 0. The reformulated problem has several local
     14: minima that can cause problems for some global Newton root-finding methods. In this particular
     15: example, an initial guess of x0 = 2.5 generates an initial search direction (-df/dx is positive)
     16: away from the root and towards a local minimum in which a back-tracking line search gets trapped.
     17: However, omitting a line-search or using a critical point or bisection line search, the solve is
     18: successful.

     20: The test outputs the final result for x and f(x).

     22: Example usage:

     24: Get help:
     25:   ./ex99 -help

     27: Monitor run (with default back-tracking line search; solve fails):
     28:   ./ex99 -snes_converged_reason -snes_monitor -snes_linesearch_monitor -ksp_converged_reason -ksp_monitor

     30: Run without a line search; solve succeeds:
     31:   ./ex99 -snes_linesearch_type basic

     33: Run with a critical point line search; solve succeeds:
     34:   ./ex99 -snes_linesearch_type cp
     35: */

     37: #include <math.h>
     38: #include <petscsnes.h>

     40: extern PetscErrorCode FormJacobian(SNES, Vec, Mat, Mat, void *);
     41: extern PetscErrorCode FormFunction(SNES, Vec, Vec, void *);

     43: int main(int argc, char **argv)
     44: {
     45:   SNES        snes; /* nonlinear solver context */
     46:   KSP         ksp;  /* linear solver context */
     47:   PC          pc;   /* preconditioner context */
     48:   Vec         x, r; /* solution, residual vectors */
     49:   Mat         J;    /* Jacobian matrix */
     50:   PetscMPIInt size;

     52:   PetscFunctionBeginUser;
     53:   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
     54:   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
     55:   PetscCheck(size == 1, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "Example is only for sequential runs");

     57:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     58:      Create nonlinear solver context
     59:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
     60:   PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));

     62:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     63:      Create matrix and vector data structures; set corresponding routines
     64:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
     65:   /*
     66:      Create vectors for solution and nonlinear function
     67:   */
     68:   PetscCall(VecCreate(PETSC_COMM_WORLD, &x));
     69:   PetscCall(VecSetSizes(x, PETSC_DECIDE, 1));
     70:   PetscCall(VecSetFromOptions(x));
     71:   PetscCall(VecDuplicate(x, &r));

     73:   /*
     74:      Create Jacobian matrix data structure
     75:   */
     76:   PetscCall(MatCreate(PETSC_COMM_WORLD, &J));
     77:   PetscCall(MatSetSizes(J, PETSC_DECIDE, PETSC_DECIDE, 1, 1));
     78:   PetscCall(MatSetFromOptions(J));
     79:   PetscCall(MatSetUp(J));

     81:   PetscCall(SNESSetFunction(snes, r, FormFunction, NULL));
     82:   PetscCall(SNESSetJacobian(snes, J, J, FormJacobian, NULL));

     84:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     85:      Customize nonlinear solver; set runtime options
     86:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
     87:   /*
     88:      Set linear solver defaults for this problem. By extracting the
     89:      KSP and PC contexts from the SNES context, we can then
     90:      directly call any KSP and PC routines to set various options.
     91:   */
     92:   PetscCall(SNESGetKSP(snes, &ksp));
     93:   PetscCall(KSPGetPC(ksp, &pc));
     94:   PetscCall(PCSetType(pc, PCNONE));
     95:   PetscCall(KSPSetTolerances(ksp, 1.e-4, PETSC_CURRENT, PETSC_CURRENT, 20));

     97:   /*
     98:      Set SNES/KSP/KSP/PC runtime options, e.g.,
     99:          -snes_view -snes_monitor -ksp_type <ksp> -pc_type <pc>
    100:      These options will override those specified above as long as
    101:      SNESSetFromOptions() is called _after_ any other customization
    102:      routines.
    103:   */
    104:   PetscCall(SNESSetFromOptions(snes));

    106:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    107:      Evaluate initial guess; then solve nonlinear system
    108:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    109:   PetscCall(VecSet(x, 2.5));

    111:   PetscCall(SNESSolve(snes, NULL, x));

    113:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    114:      Output x and f(x)
    115:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    116:   PetscCall(VecView(x, PETSC_VIEWER_STDOUT_WORLD));
    117:   PetscCall(VecView(r, PETSC_VIEWER_STDOUT_WORLD));

    119:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    120:      Free work space.  All PETSc objects should be destroyed when they
    121:      are no longer needed.
    122:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    124:   PetscCall(VecDestroy(&x));
    125:   PetscCall(VecDestroy(&r));
    126:   PetscCall(MatDestroy(&J));
    127:   PetscCall(SNESDestroy(&snes));
    128:   PetscCall(PetscFinalize());
    129:   return 0;
    130: }

    132: PetscErrorCode FormFunction(SNES snes, Vec x, Vec f, void *ctx)
    133: {
    134:   const PetscScalar *xx;
    135:   PetscScalar       *ff;

    137:   PetscFunctionBeginUser;
    138:   /*
    139:    Get pointers to vector data.
    140:       - For default PETSc vectors, VecGetArray() returns a pointer to
    141:         the data array.  Otherwise, the routine is implementation dependent.
    142:       - You MUST call VecRestoreArray() when you no longer need access to
    143:         the array.
    144:    */
    145:   PetscCall(VecGetArrayRead(x, &xx));
    146:   PetscCall(VecGetArray(f, &ff));

    148:   /* Compute function */
    149:   ff[0] = 8. * PetscExpScalar(-4. * (xx[0] - 2.) * (xx[0] - 2.)) * (xx[0] - 2.) + 2. * xx[0];

    151:   /* Restore vectors */
    152:   PetscCall(VecRestoreArrayRead(x, &xx));
    153:   PetscCall(VecRestoreArray(f, &ff));
    154:   PetscFunctionReturn(PETSC_SUCCESS);
    155: }

    157: PetscErrorCode FormJacobian(SNES snes, Vec x, Mat jac, Mat B, void *dummy)
    158: {
    159:   const PetscScalar *xx;
    160:   PetscScalar        A[1];
    161:   PetscInt           idx[1] = {0};

    163:   PetscFunctionBeginUser;
    164:   /*
    165:      Get pointer to vector data
    166:   */
    167:   PetscCall(VecGetArrayRead(x, &xx));

    169:   /*
    170:      Compute Jacobian entries and insert into matrix.
    171:       - Since this is such a small problem, we set all entries for
    172:         the matrix at once.
    173:   */
    174:   A[0] = 8. * ((xx[0] - 2.) * (PetscExpScalar(-4. * (xx[0] - 2.) * (xx[0] - 2.)) * -8. * (xx[0] - 2.)) + PetscExpScalar(-4. * (xx[0] - 2.) * (xx[0] - 2.))) + 2.;

    176:   PetscCall(MatSetValues(B, 1, idx, 1, idx, A, INSERT_VALUES));

    178:   /*
    179:      Restore vector
    180:   */
    181:   PetscCall(VecRestoreArrayRead(x, &xx));

    183:   /*
    184:      Assemble matrix
    185:   */
    186:   PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
    187:   PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));
    188:   if (jac != B) {
    189:     PetscCall(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY));
    190:     PetscCall(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY));
    191:   }
    192:   PetscFunctionReturn(PETSC_SUCCESS);
    193: }

    195: /*TEST

    197:    test:
    198:       suffix: 1
    199:       args: -snes_linesearch_type cp
    200:    test:
    201:       suffix: 2
    202:       args: -snes_linesearch_type basic
    203:    test:
    204:       suffix: 3
    205:    test:
    206:       suffix: 4
    207:       args: -snes_type newtontrdc
    208:    test:
    209:       suffix: 5
    210:       args: -snes_type newtontrdc -snes_trdc_use_cauchy false
    211:    test:
    212:       suffix: 6
    213:       args: -snes_linesearch_type bisection

    215: TEST*/