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*/