Commit ef29d79ae93a759fe2bbec602c69afa5e7c7a948
1 parent
12d86d4e
Correct hessian method (now computes the Hessian as fully symmetric matrix).
Showing
1 changed file
with
109 additions
and
26 deletions
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& pars) | @@ -325,6 +327,11 @@ void GObservations::likelihood::eval(const GOptimizerPars& 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& pars) | @@ -361,6 +368,22 @@ void GObservations::likelihood::eval(const GOptimizerPars& 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& pars) | @@ -382,10 +405,11 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars& 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& pars) | @@ -402,30 +426,41 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars& 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& pars) | @@ -437,9 +472,14 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars& 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& pars) | @@ -450,12 +490,24 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars& 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& pars) | @@ -464,37 +516,68 @@ GMatrixSparse GObservations::likelihood::hessian(const GOptimizerPars& 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 | } |