Date: 2025-03-28T21:11:09+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 line search, the solve is successful.

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

     21: Example usage:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    168:   /*
    169:      Compute Jacobian entries and insert into matrix.
    170:       - Since this is such a small problem, we set all entries for
    171:         the matrix at once.
    172:   */
    173:   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.;

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

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

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

    194: /*TEST

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

    211: TEST*/