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 | 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& 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& 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& 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& 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& 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& 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& 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 | } | ... | ... |