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,10 +46,12 @@
46 /* __ Macros _____________________________________________________________ */ 46 /* __ Macros _____________________________________________________________ */
47 47
48 /* __ Coding definitions _________________________________________________ */ 48 /* __ Coding definitions _________________________________________________ */
  49 +//#define G_USE_HESSIAN
49 50
50 /* __ Debug definitions __________________________________________________ */ 51 /* __ Debug definitions __________________________________________________ */
51 //#define G_EVAL_TIMING //!< Perform optimizer timing (0=no, 1=yes) 52 //#define G_EVAL_TIMING //!< Perform optimizer timing (0=no, 1=yes)
52 //#define G_EVAL_DEBUG //!< Perform optimizer debugging (0=no, 1=yes) 53 //#define G_EVAL_DEBUG //!< Perform optimizer debugging (0=no, 1=yes)
  54 +//#define G_HESSIAN //!< Debug Hessian computation
53 55
54 /* __ Prototypes _________________________________________________________ */ 56 /* __ Prototypes _________________________________________________________ */
55 57
@@ -325,6 +327,11 @@ void GObservations::likelihood::eval(const GOptimizerPars&amp; pars) @@ -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 // Optionally dump gradient and curvature matrix 335 // Optionally dump gradient and curvature matrix
329 #if defined(G_EVAL_DEBUG) 336 #if defined(G_EVAL_DEBUG)
330 std::cout << *m_gradient << std::endl; 337 std::cout << *m_gradient << std::endl;
@@ -361,6 +368,22 @@ void GObservations::likelihood::eval(const GOptimizerPars&amp; pars) @@ -361,6 +368,22 @@ void GObservations::likelihood::eval(const GOptimizerPars&amp; pars)
361 ***************************************************************************/ 368 ***************************************************************************/
362 GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars& pars) 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 // Create working copy of parameters 387 // Create working copy of parameters
365 GOptimizerPars wrk_pars = pars; 388 GOptimizerPars wrk_pars = pars;
366 389
@@ -382,10 +405,11 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars) @@ -382,10 +405,11 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars)
382 double f = value(); 405 double f = value();
383 406
384 // Compute aimsag 407 // Compute aimsag
385 - double aimsag = std::sqrt(eps2)*std::abs(f); 408 + double aimsag = std::sqrt(eps2) * std::abs(f);
386 409
387 // Diagonal elements 410 // Diagonal elements
388 std::vector<double> g2(npars, 0.0); 411 std::vector<double> g2(npars, 0.0);
  412 + std::vector<double> grd(npars, 0.0);
389 std::vector<double> dir(npars, 0.0); 413 std::vector<double> dir(npars, 0.0);
390 std::vector<double> yy(npars, 0.0); 414 std::vector<double> yy(npars, 0.0);
391 415
@@ -402,30 +426,41 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars) @@ -402,30 +426,41 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars)
402 } 426 }
403 427
404 // Setup step size 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 // Initialise 440 // Initialise
412 double sag = 0.0; 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 for (int multpy = 0; multpy < 5; ++multpy) { 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 eval(wrk_pars); 451 eval(wrk_pars);
422 fs1 = value(); 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 eval(wrk_pars); 456 eval(wrk_pars);
426 fs2 = value(); 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 // Break if sag is okay 465 // Break if sag is okay
431 if (std::abs(sag) > eps2 || sag == 0.0) { 466 if (std::abs(sag) > eps2 || sag == 0.0) {
@@ -437,9 +472,14 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars) @@ -437,9 +472,14 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars)
437 472
438 } // endfor 473 } // endfor
439 474
  475 + // Save old step size and second derivative
  476 + double dlast = d;
  477 + double g2bfor = g2[i];
  478 +
440 // Compute parameter derivatives and store step size and 479 // Compute parameter derivatives and store step size and
441 // function value 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 dir[i] = d; 483 dir[i] = d;
444 yy[i] = fs1; 484 yy[i] = fs1;
445 485
@@ -450,12 +490,24 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars) @@ -450,12 +490,24 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars)
450 if (d < dmin) { 490 if (d < dmin) {
451 d = dmin; 491 d = dmin;
452 } 492 }
  493 + /*
453 else if (par->factor_value()+d > par->factor_max()) { 494 else if (par->factor_value()+d > par->factor_max()) {
454 d = dmin; 495 d = dmin;
455 } 496 }
456 else if (par->factor_value()-d > par->factor_min()) { 497 else if (par->factor_value()-d > par->factor_min()) {
457 d = dmin; 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 } // endfor: cycles 512 } // endfor: cycles
461 513
@@ -464,37 +516,68 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars) @@ -464,37 +516,68 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars&amp; pars)
464 516
465 } // endfor: looped over all parameters 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 // Compute off-diagonal elements 529 // Compute off-diagonal elements
468 for (int i = 0; i < npars; ++i) { 530 for (int i = 0; i < npars; ++i) {
469 531
470 - // Get parameter 532 + // Get parameter 1
471 GOptimizerPar* par1 = wrk_pars[i]; 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 for (int j = i+1; j < npars; ++j) { 540 for (int j = i+1; j < npars; ++j) {
476 541
477 - // Get parameter 542 + // Get parameter 2
478 GOptimizerPar* par2 = wrk_pars[j]; 543 GOptimizerPar* par2 = wrk_pars[j];
  544 + double x2 = par2->factor_value();
479 545
480 // Interrupt if parameter is fixed 546 // Interrupt if parameter is fixed
481 if (par1->is_fixed() || par2->is_fixed()) { 547 if (par1->is_fixed() || par2->is_fixed()) {
482 hessian(i,j) = 0.0; 548 hessian(i,j) = 0.0;
  549 + hessian(j,i) = 0.0;
483 continue; 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 eval(wrk_pars); 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 } // endfor: looped over parameters 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 // Return Hessian 581 // Return Hessian
499 return hessian; 582 return hessian;
500 } 583 }