Commit ef29d79ae93a759fe2bbec602c69afa5e7c7a948

Authored by Jürgen Knödlseder
1 parent 12d86d4e

Correct hessian method (now computes the Hessian as fully symmetric matrix).

src/obs/GObservations_likelihood.cpp
... ... @@ -46,10 +46,12 @@
46 46 /* __ Macros _____________________________________________________________ */
47 47  
48 48 /* __ Coding definitions _________________________________________________ */
  49 +//#define G_USE_HESSIAN
49 50  
50 51 /* __ Debug definitions __________________________________________________ */
51 52 //#define G_EVAL_TIMING //!< Perform optimizer timing (0=no, 1=yes)
52 53 //#define G_EVAL_DEBUG //!< Perform optimizer debugging (0=no, 1=yes)
  54 +//#define G_HESSIAN //!< Debug Hessian computation
53 55  
54 56 /* __ Prototypes _________________________________________________________ */
55 57  
... ... @@ -325,6 +327,11 @@ void GObservations::likelihood::eval(const GOptimizerPars&amp; pars)
325 327 }
326 328 }
327 329  
  330 + // Optionally use Hessian instead of curvature matrix
  331 + #if defined(G_USE_HESSIAN)
  332 + *m_curvature = hessian(pars);
  333 + #endif
  334 +
328 335 // Optionally dump gradient and curvature matrix
329 336 #if defined(G_EVAL_DEBUG)
330 337 std::cout << *m_gradient << std::endl;
... ... @@ -361,6 +368,22 @@ void GObservations::likelihood::eval(const GOptimizerPars&amp; pars)
361 368 ***************************************************************************/
362 369 GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars& pars)
363 370 {
  371 + // Set strategy constants (low)
  372 + //const int ncyles = 3;
  373 + //const double step_tolerance = 0.5;
  374 + //const double gradient_tolerance = 0.1;
  375 +
  376 + // Set strategy constants (medium)
  377 + const int ncyles = 5;
  378 + const double step_tolerance = 0.3;
  379 + const double gradient_tolerance = 0.05;
  380 +
  381 + // Set strategy constants (high)
  382 + //const int ncyles = 7;
  383 + //const double step_tolerance = 0.1;
  384 + //const double gradient_tolerance = 0.02;
  385 +
  386 +
364 387 // Create working copy of parameters
365 388 GOptimizerPars wrk_pars = pars;
366 389  
... ... @@ -382,10 +405,11 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars)
382 405 double f = value();
383 406  
384 407 // Compute aimsag
385   - double aimsag = std::sqrt(eps2)*std::abs(f);
  408 + double aimsag = std::sqrt(eps2) * std::abs(f);
386 409  
387 410 // Diagonal elements
388 411 std::vector<double> g2(npars, 0.0);
  412 + std::vector<double> grd(npars, 0.0);
389 413 std::vector<double> dir(npars, 0.0);
390 414 std::vector<double> yy(npars, 0.0);
391 415  
... ... @@ -402,30 +426,41 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars)
402 426 }
403 427  
404 428 // Setup step size
405   - double dmin = 0.0002;
406   - double d = dmin;
  429 + double xtf = par->factor_value();
  430 + double dmin = 8.0 * eps2 * std::abs(xtf);
  431 + double d = 0.000001;
  432 + if (d < dmin) {
  433 + d = dmin;
  434 + }
407 435  
408   - // Loop
409   - for (int icyc = 0; icyc < 5; ++icyc) {
  436 + // Loop over cycles
  437 + for (int icyc = 0; icyc < ncyles; ++icyc) {
  438 + //for (int icyc = 0; icyc < 1; ++icyc) {
410 439  
411 440 // Initialise
412 441 double sag = 0.0;
413   - double fs1 = 0.0; //right-hand side
414   - double fs2 = 0.0; //left-hand side
  442 + double fs1 = 0.0; // right-hand side
  443 + double fs2 = 0.0; // left-hand side
415 444  
416   - // ...
  445 + // Compute gradient
417 446 for (int multpy = 0; multpy < 5; ++multpy) {
  447 + //for (int multpy = 0; multpy < 1; ++multpy) {
418 448  
419   - GOptimizerPar current = *par;
420   - par->factor_value(par->factor_value()+d);
  449 + // Compute right-hand side
  450 + par->factor_value(xtf + d);
421 451 eval(wrk_pars);
422 452 fs1 = value();
423   - *par = current;
424   - par->factor_value(par->factor_value()-d);
  453 +
  454 + // Compute left-hand side
  455 + par->factor_value(xtf - d);
425 456 eval(wrk_pars);
426 457 fs2 = value();
427   - *par = current;
428   - sag = 0.5*(fs1-2.0*f+fs2);
  458 +
  459 + // Recover current value
  460 + par->factor_value(xtf);
  461 +
  462 + // Compute sag
  463 + sag = 0.5 * (fs1 + fs2 - 2.0*f);
429 464  
430 465 // Break if sag is okay
431 466 if (std::abs(sag) > eps2 || sag == 0.0) {
... ... @@ -437,9 +472,14 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars)
437 472  
438 473 } // endfor
439 474  
  475 + // Save old step size and second derivative
  476 + double dlast = d;
  477 + double g2bfor = g2[i];
  478 +
440 479 // Compute parameter derivatives and store step size and
441 480 // function value
442   - g2[i] = 2.0*sag/(d*d);
  481 + g2[i] = 2.0 * sag/(d*d);
  482 + grd[i] = (fs1-fs2)/(2.*d);
443 483 dir[i] = d;
444 484 yy[i] = fs1;
445 485  
... ... @@ -450,12 +490,24 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars)
450 490 if (d < dmin) {
451 491 d = dmin;
452 492 }
  493 + /*
453 494 else if (par->factor_value()+d > par->factor_max()) {
454 495 d = dmin;
455 496 }
456 497 else if (par->factor_value()-d > par->factor_min()) {
457 498 d = dmin;
458 499 }
  500 + */
  501 +
  502 + // Check if converged
  503 + if (std::abs((d-dlast)/d) < step_tolerance) {
  504 + break;
  505 + }
  506 + if (std::abs((g2[i]-g2bfor)/g2[i]) < gradient_tolerance) {
  507 + break;
  508 + }
  509 + d = std::min(d, 10.*dlast);
  510 + d = std::max(d, 0.1*dlast);
459 511  
460 512 } // endfor: cycles
461 513  
... ... @@ -464,37 +516,68 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars)
464 516  
465 517 } // endfor: looped over all parameters
466 518  
  519 + // Debug dump
  520 + #if defined(G_HESSIAN)
  521 + std::cout << "GObservations::likelihood::hessian: ";
  522 + std::cout << "deltas and gradients:" << std::endl;
  523 + for (int i = 0; i < npars; ++i) {
  524 + std::cout << dir[i] << " ";
  525 + std::cout << grd[i] << std::endl;
  526 + }
  527 + #endif
  528 +
467 529 // Compute off-diagonal elements
468 530 for (int i = 0; i < npars; ++i) {
469 531  
470   - // Get parameter
  532 + // Get parameter 1
471 533 GOptimizerPar* par1 = wrk_pars[i];
472   - par1->factor_value(par1->factor_value()+dir[i]);
  534 + double x1 = par1->factor_value();
  535 +
  536 + // Increment parameter 1
  537 + par1->factor_value(x1 + dir[i]);
473 538  
474   - // Loop
  539 + // Loop over columns
475 540 for (int j = i+1; j < npars; ++j) {
476 541  
477   - // Get parameter
  542 + // Get parameter 2
478 543 GOptimizerPar* par2 = wrk_pars[j];
  544 + double x2 = par2->factor_value();
479 545  
480 546 // Interrupt if parameter is fixed
481 547 if (par1->is_fixed() || par2->is_fixed()) {
482 548 hessian(i,j) = 0.0;
  549 + hessian(j,i) = 0.0;
483 550 continue;
484 551 }
485 552  
486   - par2->factor_value(par2->factor_value()+dir[j]);
  553 + // Increment parameter 2
  554 + par2->factor_value(x2 + dir[j]);
  555 +
  556 + // Evaluate Hessian element
487 557 eval(wrk_pars);
488   - double fs1 = value();
489   - hessian(i,j) = (fs1 + f - yy[i] - yy[j])/(dir[i]*dir[j]);
490   - par2->factor_value(par2->factor_value()-dir[j]);
491   - }
  558 + double fs1 = value();
  559 + double element = (fs1 + f - yy[i] - yy[j])/(dir[i]*dir[j]);
  560 +
  561 + // Store Hessian element
  562 + hessian(i,j) = element;
  563 + hessian(j,i) = element;
  564 +
  565 + // Restore parameter 2
  566 + par2->factor_value(x2);
  567 +
  568 + } // endfor: looped over columns
492 569  
493   - // ...
494   - par1->factor_value(par1->factor_value()-dir[i]);
  570 + // Restore parameter 1
  571 + par1->factor_value(x1);
495 572  
496 573 } // endfor: looped over parameters
497 574  
  575 + // Debug dump
  576 + #if defined(G_HESSIAN)
  577 + std::cout << "GObservations::likelihood::hessian: " << std::endl;
  578 + std::cout << hessian << std::endl;
  579 + #endif
  580 +
498 581 // Return Hessian
499 582 return hessian;
500 583 }
... ...