1 #include <petsc-private/snesimpl.h> /*I "petscsnes.h" I*/ 2 3 #define H(i,j) qn->dXdFmat[i*qn->m + j] 4 5 const char *const SNESQNScaleTypes[] = {"NONE","SHANNO","LINESEARCH","JACOBIAN","SNESQNScaleType","SNES_QN_SCALING_",0}; 6 const char *const SNESQNRestartTypes[] = {"NONE","POWELL","PERIODIC","SNESQNRestartType","SNES_QN_RESTART_",0}; 7 const char *const SNESQNTypes[] = {"LBFGS","BROYDEN","BADBROYDEN","SNESQNType","SNES_QN_RESTART_",0}; 8 9 typedef enum {SNES_QN_LBFGS = 0, 10 SNES_QN_BROYDEN = 1, 11 SNES_QN_BADBROYDEN = 2 12 } SNESQNType; 13 14 typedef struct { 15 Vec *U; /* Stored past states (vary from method to method) */ 16 Vec *V; /* Stored past states (vary from method to method) */ 17 PetscInt m; /* The number of kept previous steps */ 18 PetscScalar *alpha, *beta; 19 PetscScalar *dXtdF, *dFtdX, *YtdX; 20 PetscBool singlereduction; /* Aggregated reduction implementation */ 21 PetscScalar *dXdFmat; /* A matrix of values for dX_i dot dF_j */ 22 PetscViewer monitor; 23 PetscReal powell_gamma; /* Powell angle restart condition */ 24 PetscReal powell_downhill; /* Powell descent restart condition */ 25 PetscReal scaling; /* scaling of H0 */ 26 SNESQNType type; /* the type of quasi-newton method used */ 27 SNESQNScaleType scale_type; /* the type of scaling used */ 28 SNESQNRestartType restart_type; /* determine the frequency and type of restart conditions */ 29 } SNES_QN; 30 31 #undef __FUNCT__ 32 #define __FUNCT__ "SNESQNApply_Broyden" 33 PetscErrorCode SNESQNApply_Broyden(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold, Vec D, Vec Dold) 34 { 35 PetscErrorCode ierr; 36 SNES_QN *qn = (SNES_QN*)snes->data; 37 Vec W = snes->work[3]; 38 Vec *U = qn->U; 39 Vec *V = qn->V; 40 KSPConvergedReason kspreason; 41 PetscInt k,i,lits; 42 PetscInt m = qn->m; 43 PetscScalar gdot; 44 PetscInt l = m; 45 46 PetscFunctionBegin; 47 if (it < m) l = it; 48 if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) { 49 ierr = KSPSolve(snes->ksp,D,W);CHKERRQ(ierr); 50 ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr); 51 if (kspreason < 0) { 52 if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) { 53 ierr = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr); 54 snes->reason = SNES_DIVERGED_LINEAR_SOLVE; 55 PetscFunctionReturn(0); 56 } 57 } 58 ierr = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr); 59 snes->linear_its += lits; 60 ierr = VecCopy(W,Y);CHKERRQ(ierr); 61 } else { 62 ierr = VecCopy(D,Y);CHKERRQ(ierr); 63 ierr = VecScale(Y,qn->scaling);CHKERRQ(ierr); 64 } 65 66 /* inward recursion starting at the first update and working forward */ 67 if (it > 1) { 68 for (i = 0; i < l-1; i++) { 69 k = (it+i-l)%l; 70 ierr = VecDot(U[k],Y,&gdot);CHKERRQ(ierr); 71 ierr = VecAXPY(Y,gdot,V[k]);CHKERRQ(ierr); 72 if (qn->monitor) { 73 ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 74 ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr); 75 ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 76 } 77 } 78 } 79 if (it < m) l = it; 80 81 /* set W to be the last step, post-linesearch */ 82 ierr = VecCopy(Xold,W);CHKERRQ(ierr); 83 ierr = VecAXPY(W,-1.0,X);CHKERRQ(ierr); 84 85 if (l > 0) { 86 k = (it-1)%l; 87 ierr = VecCopy(W,U[k]);CHKERRQ(ierr); 88 ierr = VecAXPY(W,-1.0,Y);CHKERRQ(ierr); 89 ierr = VecDot(U[k],W,&gdot);CHKERRQ(ierr); 90 ierr = VecCopy(Y,V[k]);CHKERRQ(ierr); 91 ierr = VecScale(V[k],1.0/gdot);CHKERRQ(ierr); 92 ierr = VecDot(U[k],Y,&gdot);CHKERRQ(ierr); 93 ierr = VecAXPY(Y,gdot,V[k]);CHKERRQ(ierr); 94 if (qn->monitor) { 95 ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 96 ierr = PetscViewerASCIIPrintf(qn->monitor, "update: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr); 97 ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 98 } 99 } 100 PetscFunctionReturn(0); 101 } 102 103 #undef __FUNCT__ 104 #define __FUNCT__ "SNESQNApply_BadBroyden" 105 PetscErrorCode SNESQNApply_BadBroyden(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold,Vec D,Vec Dold) 106 { 107 PetscErrorCode ierr; 108 SNES_QN *qn = (SNES_QN*)snes->data; 109 Vec W = snes->work[3]; 110 Vec *U = qn->U; 111 Vec *T = qn->V; 112 113 /* ksp thing for jacobian scaling */ 114 KSPConvergedReason kspreason; 115 PetscInt k, i, lits; 116 PetscInt m = qn->m; 117 PetscScalar gdot; 118 PetscInt l = m; 119 120 PetscFunctionBegin; 121 if (it < m) l = it; 122 ierr = VecCopy(D,Y);CHKERRQ(ierr); 123 if (l > 0) { 124 k = (it-1)%l; 125 ierr = VecCopy(Dold,U[k]);CHKERRQ(ierr); 126 ierr = VecAXPY(U[k],-1.0,D);CHKERRQ(ierr); 127 ierr = VecDot(U[k],U[k],&gdot);CHKERRQ(ierr); 128 ierr = VecCopy(D,T[k]);CHKERRQ(ierr); 129 ierr = VecScale(T[k],1.0/gdot);CHKERRQ(ierr); 130 ierr = VecDot(U[k],Y,&gdot);CHKERRQ(ierr); 131 ierr = VecAXPY(Y,gdot,T[k]);CHKERRQ(ierr); 132 if (qn->monitor) { 133 ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 134 ierr = PetscViewerASCIIPrintf(qn->monitor, "update: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr); 135 ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 136 } 137 } 138 139 /* inward recursion starting at the first update and working forward */ 140 if (it > 1) { 141 for (i = 0; i < l-1; i++) { 142 k = (it+i-l)%l; 143 ierr = VecDot(U[k],Y,&gdot);CHKERRQ(ierr); 144 ierr = VecAXPY(Y,gdot,T[k]);CHKERRQ(ierr); 145 if (qn->monitor) { 146 ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 147 ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr); 148 ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 149 } 150 } 151 } 152 153 if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) { 154 ierr = KSPSolve(snes->ksp,Y,W);CHKERRQ(ierr); 155 ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr); 156 if (kspreason < 0) { 157 if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) { 158 ierr = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr); 159 snes->reason = SNES_DIVERGED_LINEAR_SOLVE; 160 PetscFunctionReturn(0); 161 } 162 } 163 ierr = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr); 164 snes->linear_its += lits; 165 ierr = VecCopy(W,Y);CHKERRQ(ierr); 166 } else { 167 ierr = VecScale(Y,qn->scaling);CHKERRQ(ierr); 168 } 169 PetscFunctionReturn(0); 170 } 171 172 #undef __FUNCT__ 173 #define __FUNCT__ "SNESQNApply_LBFGS" 174 PetscErrorCode SNESQNApply_LBFGS(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold,Vec D,Vec Dold) 175 { 176 PetscErrorCode ierr; 177 SNES_QN *qn = (SNES_QN*)snes->data; 178 Vec W = snes->work[3]; 179 Vec *dX = qn->U; 180 Vec *dF = qn->V; 181 PetscScalar *alpha = qn->alpha; 182 PetscScalar *beta = qn->beta; 183 PetscScalar *dXtdF = qn->dXtdF; 184 PetscScalar *dFtdX = qn->dFtdX; 185 PetscScalar *YtdX = qn->YtdX; 186 187 /* ksp thing for jacobian scaling */ 188 KSPConvergedReason kspreason; 189 PetscInt k,i,j,g,lits; 190 PetscInt m = qn->m; 191 PetscScalar t; 192 PetscInt l = m; 193 194 PetscFunctionBegin; 195 if (it < m) l = it; 196 ierr = VecCopy(D,Y);CHKERRQ(ierr); 197 if (it > 0) { 198 k = (it - 1) % l; 199 ierr = VecCopy(D,dF[k]);CHKERRQ(ierr); 200 ierr = VecAXPY(dF[k], -1.0, Dold);CHKERRQ(ierr); 201 ierr = VecCopy(X, dX[k]);CHKERRQ(ierr); 202 ierr = VecAXPY(dX[k], -1.0, Xold);CHKERRQ(ierr); 203 if (qn->singlereduction) { 204 PetscScalar dFtdF; 205 ierr = VecMDotBegin(dF[k],l,dX,dXtdF);CHKERRQ(ierr); 206 ierr = VecMDotBegin(dX[k],l,dF,dFtdX);CHKERRQ(ierr); 207 ierr = VecMDotBegin(Y,l,dX,YtdX);CHKERRQ(ierr); 208 if (qn->scale_type == SNES_QN_SCALE_SHANNO) {ierr = VecDotBegin(dF[k],dF[k],&dFtdF);CHKERRQ(ierr);} 209 ierr = VecMDotEnd(dF[k],l,dX,dXtdF);CHKERRQ(ierr); 210 ierr = VecMDotEnd(dX[k],l,dF,dFtdX);CHKERRQ(ierr); 211 ierr = VecMDotEnd(Y,l,dX,YtdX);CHKERRQ(ierr); 212 if (qn->scale_type == SNES_QN_SCALE_SHANNO) { 213 ierr = VecDotEnd(dF[k],dF[k],&dFtdF);CHKERRQ(ierr); 214 qn->scaling = PetscRealPart(dXtdF[k]) / PetscRealPart(dFtdF); 215 } 216 for (j = 0; j < l; j++) { 217 H(k, j) = dFtdX[j]; 218 H(j, k) = dXtdF[j]; 219 } 220 /* copy back over to make the computation of alpha and beta easier */ 221 for (j = 0; j < l; j++) dXtdF[j] = H(j, j); 222 } else { 223 ierr = VecDot(dX[k], dF[k], &dXtdF[k]);CHKERRQ(ierr); 224 if (qn->scale_type == SNES_QN_SCALE_SHANNO) { 225 PetscReal dFtdF; 226 ierr = VecDotRealPart(dF[k],dF[k],&dFtdF);CHKERRQ(ierr); 227 qn->scaling = PetscRealPart(dXtdF[k])/dFtdF; 228 } 229 } 230 if (qn->scale_type == SNES_QN_SCALE_LINESEARCH) { 231 ierr = SNESLineSearchGetLambda(snes->linesearch,&qn->scaling);CHKERRQ(ierr); 232 } 233 } 234 235 /* outward recursion starting at iteration k's update and working back */ 236 for (i=0; i<l; i++) { 237 k = (it-i-1)%l; 238 if (qn->singlereduction) { 239 /* construct t = dX[k] dot Y as Y_0 dot dX[k] + sum(-alpha[j]dX[k]dF[j]) */ 240 t = YtdX[k]; 241 for (j=0; j<i; j++) { 242 g = (it-j-1)%l; 243 t -= alpha[g]*H(k, g); 244 } 245 alpha[k] = t/H(k,k); 246 } else { 247 ierr = VecDot(dX[k],Y,&t);CHKERRQ(ierr); 248 alpha[k] = t/dXtdF[k]; 249 } 250 if (qn->monitor) { 251 ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 252 ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d alpha: %14.12e\n", it, k, PetscRealPart(alpha[k]));CHKERRQ(ierr); 253 ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 254 } 255 ierr = VecAXPY(Y,-alpha[k],dF[k]);CHKERRQ(ierr); 256 } 257 258 if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) { 259 ierr = KSPSolve(snes->ksp,Y,W);CHKERRQ(ierr); 260 ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr); 261 if (kspreason < 0) { 262 if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) { 263 ierr = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr); 264 snes->reason = SNES_DIVERGED_LINEAR_SOLVE; 265 PetscFunctionReturn(0); 266 } 267 } 268 ierr = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr); 269 snes->linear_its += lits; 270 ierr = VecCopy(W, Y);CHKERRQ(ierr); 271 } else { 272 ierr = VecScale(Y, qn->scaling);CHKERRQ(ierr); 273 } 274 if (qn->singlereduction) { 275 ierr = VecMDot(Y,l,dF,YtdX);CHKERRQ(ierr); 276 } 277 /* inward recursion starting at the first update and working forward */ 278 for (i = 0; i < l; i++) { 279 k = (it + i - l) % l; 280 if (qn->singlereduction) { 281 t = YtdX[k]; 282 for (j = 0; j < i; j++) { 283 g = (it + j - l) % l; 284 t += (alpha[g] - beta[g])*H(g, k); 285 } 286 beta[k] = t / H(k, k); 287 } else { 288 ierr = VecDot(dF[k], Y, &t);CHKERRQ(ierr); 289 beta[k] = t / dXtdF[k]; 290 } 291 ierr = VecAXPY(Y, (alpha[k] - beta[k]), dX[k]);CHKERRQ(ierr); 292 if (qn->monitor) { 293 ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 294 ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d alpha - beta: %14.12e\n", it, k, PetscRealPart(alpha[k] - beta[k]));CHKERRQ(ierr); 295 ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 296 } 297 } 298 PetscFunctionReturn(0); 299 } 300 301 #undef __FUNCT__ 302 #define __FUNCT__ "SNESSolve_QN" 303 static PetscErrorCode SNESSolve_QN(SNES snes) 304 { 305 PetscErrorCode ierr; 306 SNES_QN *qn = (SNES_QN*) snes->data; 307 Vec X,Xold; 308 Vec F; 309 Vec Y,D,Dold; 310 PetscInt i, i_r; 311 PetscReal fnorm,xnorm,ynorm,gnorm; 312 PetscBool lssucceed,powell,periodic; 313 PetscScalar DolddotD,DolddotDold; 314 MatStructure flg = DIFFERENT_NONZERO_PATTERN; 315 SNESConvergedReason reason; 316 317 /* basically just a regular newton's method except for the application of the jacobian */ 318 319 PetscFunctionBegin; 320 F = snes->vec_func; /* residual vector */ 321 Y = snes->vec_sol_update; /* search direction generated by J^-1D*/ 322 323 X = snes->vec_sol; /* solution vector */ 324 Xold = snes->work[0]; 325 326 /* directions generated by the preconditioned problem with F_pre = F or x - M(x, b) */ 327 D = snes->work[1]; 328 Dold = snes->work[2]; 329 330 snes->reason = SNES_CONVERGED_ITERATING; 331 332 ierr = PetscObjectAMSTakeAccess((PetscObject)snes);CHKERRQ(ierr); 333 snes->iter = 0; 334 snes->norm = 0.; 335 ierr = PetscObjectAMSGrantAccess((PetscObject)snes);CHKERRQ(ierr); 336 337 if (snes->pc && snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_PRECONDITIONED) { 338 ierr = SNESApplyPC(snes,X,NULL,NULL,F);CHKERRQ(ierr); 339 ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr); 340 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 341 snes->reason = SNES_DIVERGED_INNER; 342 PetscFunctionReturn(0); 343 } 344 ierr = VecNorm(F,NORM_2,&fnorm);CHKERRQ(ierr); 345 } else { 346 if (!snes->vec_func_init_set) { 347 ierr = SNESComputeFunction(snes,X,F);CHKERRQ(ierr); 348 if (snes->domainerror) { 349 snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN; 350 PetscFunctionReturn(0); 351 } 352 } else snes->vec_func_init_set = PETSC_FALSE; 353 if (!snes->norm_init_set) { 354 /* convergence test */ 355 ierr = VecNorm(F,NORM_2,&fnorm);CHKERRQ(ierr); 356 if (PetscIsInfOrNanReal(fnorm)) { 357 snes->reason = SNES_DIVERGED_FNORM_NAN; 358 PetscFunctionReturn(0); 359 } 360 } else { 361 fnorm = snes->norm_init; 362 snes->norm_init_set = PETSC_FALSE; 363 } 364 } 365 if (snes->pc && snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) { 366 ierr = SNESApplyPC(snes,X,F,&fnorm,D);CHKERRQ(ierr); 367 ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr); 368 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 369 snes->reason = SNES_DIVERGED_INNER; 370 PetscFunctionReturn(0); 371 } 372 } else { 373 ierr = VecCopy(F,D);CHKERRQ(ierr); 374 } 375 376 ierr = PetscObjectAMSTakeAccess((PetscObject)snes);CHKERRQ(ierr); 377 snes->norm = fnorm; 378 ierr = PetscObjectAMSGrantAccess((PetscObject)snes);CHKERRQ(ierr); 379 ierr = SNESLogConvergenceHistory(snes,fnorm,0);CHKERRQ(ierr); 380 ierr = SNESMonitor(snes,0,fnorm);CHKERRQ(ierr); 381 382 /* set parameter for default relative tolerance convergence test */ 383 snes->ttol = fnorm*snes->rtol; 384 /* test convergence */ 385 ierr = (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr); 386 if (snes->reason) PetscFunctionReturn(0); 387 388 if (snes->pc && snes->pcside == PC_RIGHT) { 389 ierr = PetscLogEventBegin(SNES_NPCSolve,snes->pc,X,0,0);CHKERRQ(ierr); 390 ierr = SNESSolve(snes->pc,snes->vec_rhs,X);CHKERRQ(ierr); 391 ierr = PetscLogEventEnd(SNES_NPCSolve,snes->pc,X,0,0);CHKERRQ(ierr); 392 ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr); 393 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 394 snes->reason = SNES_DIVERGED_INNER; 395 PetscFunctionReturn(0); 396 } 397 ierr = SNESGetPCFunction(snes,F,&fnorm);CHKERRQ(ierr); 398 ierr = VecCopy(F,D);CHKERRQ(ierr); 399 } 400 401 /* scale the initial update */ 402 if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) { 403 ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr); 404 ierr = KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);CHKERRQ(ierr); 405 } 406 407 for (i = 0, i_r = 0; i < snes->max_its; i++, i_r++) { 408 switch (qn->type) { 409 case SNES_QN_BADBROYDEN: 410 ierr = SNESQNApply_BadBroyden(snes,i_r,Y,X,Xold,D,Dold);CHKERRQ(ierr); 411 break; 412 case SNES_QN_BROYDEN: 413 ierr = SNESQNApply_Broyden(snes,i_r,Y,X,Xold,D,Dold);CHKERRQ(ierr); 414 break; 415 case SNES_QN_LBFGS: 416 SNESQNApply_LBFGS(snes,i_r,Y,X,Xold,D,Dold);CHKERRQ(ierr); 417 break; 418 } 419 /* line search for lambda */ 420 ynorm = 1; gnorm = fnorm; 421 ierr = VecCopy(D, Dold);CHKERRQ(ierr); 422 ierr = VecCopy(X, Xold);CHKERRQ(ierr); 423 ierr = SNESLineSearchApply(snes->linesearch, X, F, &fnorm, Y);CHKERRQ(ierr); 424 if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break; 425 if (snes->domainerror) { 426 snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN; 427 PetscFunctionReturn(0); 428 } 429 ierr = SNESLineSearchGetSuccess(snes->linesearch, &lssucceed);CHKERRQ(ierr); 430 if (!lssucceed) { 431 if (++snes->numFailures >= snes->maxFailures) { 432 snes->reason = SNES_DIVERGED_LINE_SEARCH; 433 break; 434 } 435 } 436 ierr = SNESLineSearchGetNorms(snes->linesearch, &xnorm, &fnorm, &ynorm);CHKERRQ(ierr); 437 if (qn->scale_type == SNES_QN_SCALE_LINESEARCH) { 438 ierr = SNESLineSearchGetLambda(snes->linesearch, &qn->scaling);CHKERRQ(ierr); 439 } 440 441 /* convergence monitoring */ 442 ierr = PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",(double)fnorm,(double)gnorm,(double)ynorm,(int)lssucceed);CHKERRQ(ierr); 443 444 if (snes->pc && snes->pcside == PC_RIGHT) { 445 ierr = PetscLogEventBegin(SNES_NPCSolve,snes->pc,X,0,0);CHKERRQ(ierr); 446 ierr = SNESSolve(snes->pc,snes->vec_rhs,X);CHKERRQ(ierr); 447 ierr = PetscLogEventEnd(SNES_NPCSolve,snes->pc,X,0,0);CHKERRQ(ierr); 448 ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr); 449 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 450 snes->reason = SNES_DIVERGED_INNER; 451 PetscFunctionReturn(0); 452 } 453 ierr = SNESGetPCFunction(snes,F,&fnorm);CHKERRQ(ierr); 454 } 455 456 ierr = SNESSetIterationNumber(snes, i+1);CHKERRQ(ierr); 457 ierr = SNESSetFunctionNorm(snes, fnorm);CHKERRQ(ierr); 458 459 ierr = SNESLogConvergenceHistory(snes,snes->norm,snes->iter);CHKERRQ(ierr); 460 ierr = SNESMonitor(snes,snes->iter,snes->norm);CHKERRQ(ierr); 461 /* set parameter for default relative tolerance convergence test */ 462 ierr = (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr); 463 if (snes->reason) PetscFunctionReturn(0); 464 if (snes->pc && snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) { 465 ierr = SNESApplyPC(snes,X,F,&fnorm,D);CHKERRQ(ierr); 466 ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr); 467 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 468 snes->reason = SNES_DIVERGED_INNER; 469 PetscFunctionReturn(0); 470 } 471 } else { 472 ierr = VecCopy(F, D);CHKERRQ(ierr); 473 } 474 475 powell = PETSC_FALSE; 476 if (qn->restart_type == SNES_QN_RESTART_POWELL) { 477 /* check restart by Powell's Criterion: |F^T H_0 Fold| > 0.2 * |Fold^T H_0 Fold| */ 478 ierr = VecDotBegin(Dold, Dold, &DolddotDold);CHKERRQ(ierr); 479 ierr = VecDotBegin(Dold, D, &DolddotD);CHKERRQ(ierr); 480 ierr = VecDotEnd(Dold, Dold, &DolddotDold);CHKERRQ(ierr); 481 ierr = VecDotEnd(Dold, D, &DolddotD);CHKERRQ(ierr); 482 if (PetscAbs(PetscRealPart(DolddotD)) > qn->powell_gamma*PetscAbs(PetscRealPart(DolddotDold))) powell = PETSC_TRUE; 483 } 484 periodic = PETSC_FALSE; 485 if (qn->restart_type == SNES_QN_RESTART_PERIODIC) { 486 if (i_r>qn->m-1) periodic = PETSC_TRUE; 487 } 488 /* restart if either powell or periodic restart is satisfied. */ 489 if (powell || periodic) { 490 if (qn->monitor) { 491 ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 492 ierr = PetscViewerASCIIPrintf(qn->monitor, "restart! |%14.12e| > %4.2f*|%14.12e| or i_r = %d\n", PetscRealPart(DolddotD), qn->powell_gamma, PetscRealPart(DolddotDold), i_r);CHKERRQ(ierr); 493 ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr); 494 } 495 i_r = -1; 496 /* general purpose update */ 497 if (snes->ops->update) { 498 ierr = (*snes->ops->update)(snes, snes->iter);CHKERRQ(ierr); 499 } 500 if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) { 501 ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr); 502 ierr = KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);CHKERRQ(ierr); 503 } 504 } 505 /* general purpose update */ 506 if (snes->ops->update) { 507 ierr = (*snes->ops->update)(snes, snes->iter);CHKERRQ(ierr); 508 } 509 } 510 if (i == snes->max_its) { 511 ierr = PetscInfo1(snes, "Maximum number of iterations has been reached: %D\n", snes->max_its);CHKERRQ(ierr); 512 if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT; 513 } 514 PetscFunctionReturn(0); 515 } 516 517 #undef __FUNCT__ 518 #define __FUNCT__ "SNESSetUp_QN" 519 static PetscErrorCode SNESSetUp_QN(SNES snes) 520 { 521 SNES_QN *qn = (SNES_QN*)snes->data; 522 PetscErrorCode ierr; 523 524 PetscFunctionBegin; 525 ierr = VecDuplicateVecs(snes->vec_sol, qn->m, &qn->U);CHKERRQ(ierr); 526 ierr = VecDuplicateVecs(snes->vec_sol, qn->m, &qn->V);CHKERRQ(ierr); 527 ierr = PetscMalloc3(qn->m, PetscScalar, &qn->alpha, 528 qn->m, PetscScalar, &qn->beta, 529 qn->m, PetscScalar, &qn->dXtdF);CHKERRQ(ierr); 530 531 if (qn->singlereduction) { 532 ierr = PetscMalloc3(qn->m*qn->m, PetscScalar, &qn->dXdFmat, 533 qn->m, PetscScalar, &qn->dFtdX, 534 qn->m, PetscScalar, &qn->YtdX);CHKERRQ(ierr); 535 } 536 ierr = SNESSetWorkVecs(snes,4);CHKERRQ(ierr); 537 538 /* set up the line search */ 539 if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) { 540 ierr = SNESSetUpMatrices(snes);CHKERRQ(ierr); 541 } 542 543 if (snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_DEFAULT) {snes->functype = SNES_FUNCTION_UNPRECONDITIONED;} 544 545 PetscFunctionReturn(0); 546 } 547 548 #undef __FUNCT__ 549 #define __FUNCT__ "SNESReset_QN" 550 static PetscErrorCode SNESReset_QN(SNES snes) 551 { 552 PetscErrorCode ierr; 553 SNES_QN *qn; 554 555 PetscFunctionBegin; 556 if (snes->data) { 557 qn = (SNES_QN*)snes->data; 558 if (qn->U) { 559 ierr = VecDestroyVecs(qn->m, &qn->U);CHKERRQ(ierr); 560 } 561 if (qn->V) { 562 ierr = VecDestroyVecs(qn->m, &qn->V);CHKERRQ(ierr); 563 } 564 if (qn->singlereduction) { 565 ierr = PetscFree3(qn->dXdFmat, qn->dFtdX, qn->YtdX);CHKERRQ(ierr); 566 } 567 ierr = PetscFree3(qn->alpha, qn->beta, qn->dXtdF);CHKERRQ(ierr); 568 } 569 PetscFunctionReturn(0); 570 } 571 572 #undef __FUNCT__ 573 #define __FUNCT__ "SNESDestroy_QN" 574 static PetscErrorCode SNESDestroy_QN(SNES snes) 575 { 576 PetscErrorCode ierr; 577 578 PetscFunctionBegin; 579 ierr = SNESReset_QN(snes);CHKERRQ(ierr); 580 ierr = PetscFree(snes->data);CHKERRQ(ierr); 581 ierr = PetscObjectComposeFunction((PetscObject)snes,"",NULL);CHKERRQ(ierr); 582 PetscFunctionReturn(0); 583 } 584 585 #undef __FUNCT__ 586 #define __FUNCT__ "SNESSetFromOptions_QN" 587 static PetscErrorCode SNESSetFromOptions_QN(SNES snes) 588 { 589 590 PetscErrorCode ierr; 591 SNES_QN *qn = (SNES_QN*)snes->data; 592 PetscBool monflg = PETSC_FALSE,flg; 593 SNESLineSearch linesearch; 594 SNESQNRestartType rtype = qn->restart_type; 595 SNESQNScaleType stype = qn->scale_type; 596 597 PetscFunctionBegin; 598 ierr = PetscOptionsHead("SNES QN options");CHKERRQ(ierr); 599 ierr = PetscOptionsInt("-snes_qn_m","Number of past states saved for L-BFGS methods","SNESQN",qn->m,&qn->m,NULL);CHKERRQ(ierr); 600 ierr = PetscOptionsReal("-snes_qn_powell_gamma","Powell angle tolerance", "SNESQN", qn->powell_gamma, &qn->powell_gamma, NULL);CHKERRQ(ierr); 601 ierr = PetscOptionsReal("-snes_qn_powell_downhill","Powell descent tolerance", "SNESQN", qn->powell_downhill, &qn->powell_downhill, NULL);CHKERRQ(ierr); 602 ierr = PetscOptionsBool("-snes_qn_monitor", "Monitor for the QN methods", "SNESQN", monflg, &monflg, NULL);CHKERRQ(ierr); 603 ierr = PetscOptionsBool("-snes_qn_single_reduction", "Aggregate reductions", "SNESQN", qn->singlereduction, &qn->singlereduction, NULL);CHKERRQ(ierr); 604 ierr = PetscOptionsEnum("-snes_qn_scale_type","Scaling type","SNESQNSetScaleType",SNESQNScaleTypes,(PetscEnum)stype,(PetscEnum*)&stype,&flg);CHKERRQ(ierr); 605 if (flg) ierr = SNESQNSetScaleType(snes,stype);CHKERRQ(ierr); 606 607 ierr = PetscOptionsEnum("-snes_qn_restart_type","Restart type","SNESQNSetRestartType",SNESQNRestartTypes,(PetscEnum)rtype,(PetscEnum*)&rtype,&flg);CHKERRQ(ierr); 608 if (flg) ierr = SNESQNSetRestartType(snes,rtype);CHKERRQ(ierr); 609 610 ierr = PetscOptionsEnum("-snes_qn_type","Quasi-Newton update type","",SNESQNTypes, 611 (PetscEnum)qn->type,(PetscEnum*)&qn->type,NULL);CHKERRQ(ierr); 612 ierr = PetscOptionsTail();CHKERRQ(ierr); 613 if (!snes->linesearch) { 614 ierr = SNESGetLineSearch(snes, &linesearch);CHKERRQ(ierr); 615 ierr = SNESLineSearchSetType(linesearch, SNESLINESEARCHCP);CHKERRQ(ierr); 616 } 617 if (monflg) { 618 qn->monitor = PETSC_VIEWER_STDOUT_(PetscObjectComm((PetscObject)snes));CHKERRQ(ierr); 619 } 620 PetscFunctionReturn(0); 621 } 622 623 624 #undef __FUNCT__ 625 #define __FUNCT__ "SNESQNSetRestartType" 626 /*@ 627 SNESQNSetRestartType - Sets the restart type for SNESQN. 628 629 Logically Collective on SNES 630 631 Input Parameters: 632 + snes - the iterative context 633 - rtype - restart type 634 635 Options Database: 636 + -snes_qn_restart_type<powell,periodic,none> - set the restart type 637 - -snes_qn_restart[10] - sets the number of iterations before restart for periodic 638 639 Level: intermediate 640 641 SNESQNRestartTypes: 642 + SNES_QN_RESTART_NONE - never restart 643 . SNES_QN_RESTART_POWELL - restart based upon descent criteria 644 - SNES_QN_RESTART_PERIODIC - restart after a fixed number of iterations 645 646 Notes: 647 The default line search used is the L2 line search and it requires two additional function evaluations. 648 649 .keywords: SNES, SNESQN, restart, type, set SNESLineSearch 650 @*/ 651 PetscErrorCode SNESQNSetRestartType(SNES snes, SNESQNRestartType rtype) 652 { 653 PetscErrorCode ierr; 654 655 PetscFunctionBegin; 656 PetscValidHeaderSpecific(snes,SNES_CLASSID,1); 657 ierr = PetscTryMethod(snes,"SNESQNSetRestartType_C",(SNES,SNESQNRestartType),(snes,rtype));CHKERRQ(ierr); 658 PetscFunctionReturn(0); 659 } 660 661 #undef __FUNCT__ 662 #define __FUNCT__ "SNESQNSetScaleType" 663 /*@ 664 SNESQNSetScaleType - Sets the scaling type for the inner inverse jacobian in SNESQN. 665 666 Logically Collective on SNES 667 668 Input Parameters: 669 + snes - the iterative context 670 - stype - scale type 671 672 Options Database: 673 . -snes_qn_scale_type<shanno,none,linesearch,jacobian> 674 675 Level: intermediate 676 677 SNESQNSelectTypes: 678 + SNES_QN_SCALE_NONE - don't scale the problem 679 . SNES_QN_SCALE_SHANNO - use shanno scaling 680 . SNES_QN_SCALE_LINESEARCH - scale based upon line search lambda 681 - SNES_QN_SCALE_JACOBIAN - scale by inverting a previously computed Jacobian. 682 683 .keywords: SNES, SNESQN, scaling, type, set SNESLineSearch 684 @*/ 685 686 PetscErrorCode SNESQNSetScaleType(SNES snes, SNESQNScaleType stype) 687 { 688 PetscErrorCode ierr; 689 690 PetscFunctionBegin; 691 PetscValidHeaderSpecific(snes,SNES_CLASSID,1); 692 ierr = PetscTryMethod(snes,"SNESQNSetScaleType_C",(SNES,SNESQNScaleType),(snes,stype));CHKERRQ(ierr); 693 PetscFunctionReturn(0); 694 } 695 696 #undef __FUNCT__ 697 #define __FUNCT__ "SNESQNSetScaleType_QN" 698 PetscErrorCode SNESQNSetScaleType_QN(SNES snes, SNESQNScaleType stype) 699 { 700 SNES_QN *qn = (SNES_QN*)snes->data; 701 702 PetscFunctionBegin; 703 qn->scale_type = stype; 704 PetscFunctionReturn(0); 705 } 706 707 #undef __FUNCT__ 708 #define __FUNCT__ "SNESQNSetRestartType_QN" 709 PetscErrorCode SNESQNSetRestartType_QN(SNES snes, SNESQNRestartType rtype) 710 { 711 SNES_QN *qn = (SNES_QN*)snes->data; 712 713 PetscFunctionBegin; 714 qn->restart_type = rtype; 715 PetscFunctionReturn(0); 716 } 717 718 /* -------------------------------------------------------------------------- */ 719 /*MC 720 SNESQN - Limited-Memory Quasi-Newton methods for the solution of nonlinear systems. 721 722 Options Database: 723 724 + -snes_qn_m - Number of past states saved for the L-Broyden methods. 725 . -snes_qn_powell_angle - Angle condition for restart. 726 . -snes_qn_powell_descent - Descent condition for restart. 727 . -snes_linesearch_type <cp, l2, basic> - Type of line search. 728 - -snes_qn_monitor - Monitors the quasi-newton jacobian. 729 730 Notes: This implements the L-BFGS, Broyden, and "Bad" Broyden algorithms for the solution of F(x) = b using 731 previous change in F(x) and x to form the approximate inverse Jacobian using a series of multiplicative rank-one 732 updates. 733 734 When using a nonlinear preconditioner, one has two options as to how the preconditioner is applied. The first of 735 these options, sequential, uses the preconditioner to generate a new solution and function and uses those at this 736 iteration as the current iteration's values when constructing the approximate jacobian. The second, composed, 737 perturbs the problem the jacobian represents to be P(x, b) - x = 0, where P(x, b) is the preconditioner. 738 739 References: 740 741 L-Broyden Methods: a generalization of the L-BFGS method to the limited memory Broyden family, M. B. Reed, 742 International Journal of Computer Mathematics, vol. 86, 2009. 743 744 Experiments with Quasi-Newton Methods in Solving Stiff ODE Systems, Peter N. Brown, Alan C. Hindmarsh, Homer F. Walker 745 SIAM J. Sci. Stat. Comput. Vol 6(2), April 1985. 746 747 748 Level: beginner 749 750 .seealso: SNESCreate(), SNES, SNESSetType(), SNESNEWTONLS, SNESNEWTONTR 751 752 M*/ 753 #undef __FUNCT__ 754 #define __FUNCT__ "SNESCreate_QN" 755 PETSC_EXTERN PetscErrorCode SNESCreate_QN(SNES snes) 756 { 757 PetscErrorCode ierr; 758 SNES_QN *qn; 759 760 PetscFunctionBegin; 761 snes->ops->setup = SNESSetUp_QN; 762 snes->ops->solve = SNESSolve_QN; 763 snes->ops->destroy = SNESDestroy_QN; 764 snes->ops->setfromoptions = SNESSetFromOptions_QN; 765 snes->ops->view = 0; 766 snes->ops->reset = SNESReset_QN; 767 768 snes->pcside = PC_LEFT; 769 770 snes->usespc = PETSC_TRUE; 771 snes->usesksp = PETSC_FALSE; 772 773 if (!snes->tolerancesset) { 774 snes->max_funcs = 30000; 775 snes->max_its = 10000; 776 } 777 778 ierr = PetscNewLog(snes,SNES_QN,&qn);CHKERRQ(ierr); 779 snes->data = (void*) qn; 780 qn->m = 10; 781 qn->scaling = 1.0; 782 qn->U = NULL; 783 qn->V = NULL; 784 qn->dXtdF = NULL; 785 qn->dFtdX = NULL; 786 qn->dXdFmat = NULL; 787 qn->monitor = NULL; 788 qn->singlereduction = PETSC_TRUE; 789 qn->powell_gamma = 0.9999; 790 qn->scale_type = SNES_QN_SCALE_SHANNO; 791 qn->restart_type = SNES_QN_RESTART_POWELL; 792 qn->type = SNES_QN_LBFGS; 793 794 ierr = PetscObjectComposeFunction((PetscObject)snes,"SNESQNSetScaleType_C",SNESQNSetScaleType_QN);CHKERRQ(ierr); 795 ierr = PetscObjectComposeFunction((PetscObject)snes,"SNESQNSetRestartType_C",SNESQNSetRestartType_QN);CHKERRQ(ierr); 796 PetscFunctionReturn(0); 797 } 798