Extension: Matrix functions
#include "dg/matrix/matrix.h"
Loading...
Searching...
No Matches
optimise.h
Go to the documentation of this file.
1#pragma once
2#include "dg/algorithm.h"
3#include "tridiaginv.h" // lapack wrapper
4
5namespace dg{
6namespace mat{
80template<class Gradient, class InvHessian, class ContainerType>
81unsigned newton( Gradient grad, InvHessian invhess,
82 ContainerType& x0, double tol = 1e-5, unsigned max_iter = 1000)
83{
84 ContainerType jj(x0), p(x0), test(x0);
85 for ( unsigned i=0; i<max_iter; i++)
86 {
87 grad( x0, jj);
88 invhess( x0, jj, p);
89 double alpha = 1.;
90 dg::blas1::axpby( -alpha, p, 1., x0);
91 double err = sqrt(dg::blas1::dot( jj, jj));
92 if (err < tol)
93 return i;
94 }
95 return max_iter;
96}
97
315template<class Func, class Jacobian, class ContainerType0, class ContainerType1>
316unsigned levenberg_marquardt( Func fun, Jacobian jac,
317 ContainerType0& x0,
318 const ContainerType1& copyable, // size of return of fun
319 double tol = 1e-8, unsigned max_iter = 1000)
320{
321 unsigned num_p = x0.size();
322 auto x1 = x0, W = x0;
323 auto rs(copyable), rs1(rs);
324 std::vector<ContainerType1> jacs(num_p, copyable);
325 dg::SquareMatrix<double> HH(num_p, 0.), evHH( HH), evHH_T(HH), WW(HH), syWW(WW);
326 thrust::host_vector<double> evs( num_p), grad(num_p), gradbar(num_p),
327 pk(num_p), pkbar(num_p);
328 thrust::host_vector<double> work( 3*num_p-1);
329 // init loop
330 fun( x0, rs);
331 jac( x0, jacs);
332 double delta = 0;
333 for( unsigned p=0; p<num_p; p++)
334 {
335 WW(p,p) = W[p] = dg::blas1::dot( jacs[p], jacs[p]);
336 }
337 for( unsigned p=0; p<num_p; p++)
338 {
339 grad[p] = dg::blas1::dot( jacs[p], rs);
340 delta += grad[p]*grad[p]/W[p];
341 }
342 //std::cout << "Norm gT g/W^2 " << sqrt(delta)<<"\n";
343 double f0 = dg::blas1::dot( rs, rs);
344 delta = 0.25*f0/sqrt(delta);
345 //std::cout << "initial delta " << delta<<"\n";
346 double normx0 = sqrt(dg::blas1::dot( x0, x0));
347 for ( unsigned k=0; k<max_iter; k++)
348 {
349 // 1. Solve (J^T J + lambda W)p = -J^T r with lambda : ||p||_W leq Delta
350 // W[p] = max{ W_{k-1}, H_pp}
351 // In: jacs, rs; Out: pk, normpk, lambda
352 for( unsigned l=0; l<num_p; l++)
353 {
354 for( unsigned j=l; j<num_p; j++)
355 HH(j,l) = HH(l,j) = dg::blas1::dot( jacs[l], jacs[j]);
356 WW(l,l) = std::max( WW(l,l), HH(l,l)); // After Mor´e the weights must not decrease
357 }
358 // !!! sygv destroys WW on output (so use copy syWW instead) !!!
359 syWW = WW;
360 lapack::sygv( 1, 'V', 'U', num_p, HH.data(), num_p, syWW.data(), num_p, evs, work);
361 evHH_T = HH;
362 evHH = HH.transpose();
363 //std::cout << "#########Iteration "<<k<<"\n";
364 //std::cout << "Eigenvalues are \n";
365 //for( unsigned p=0; p<num_p; p++)
366 // std::cout << "p EV "<<evs[p]<<"\n";
367 //std::cout << "Weights are \n";
368 //for( unsigned p=0; p<num_p; p++)
369 // std::cout << "W "<<WW(p,p)<<"\n";
370 dg::blas2::gemv( evHH_T, grad, gradbar); // !! sygv gives A = WE_A Lambda E_A^TW
371 double normp=0;
372 auto target = [&]( double lambda)
373 {
374 // safeguard against 0 Eigenvalue!
375 for( unsigned p=0; p<num_p; p++)
376 pkbar[p]= -gradbar[p]/(evs[p] +lambda == 0 ? 1e-16 : evs[p]+lambda);
377 normp = sqrt(dg::blas1::dot( pkbar, pkbar));
378 return 1./delta - 1./normp;
379 };
380 auto dtarget = [&]( double lambda)
381 {
382 // safeguard against 0 Eigenvalue!
383 double dnorm =0;
384 for( unsigned p=0; p<num_p; p++)
385 dnorm += pkbar[p]*pkbar[p]/(evs[p]+lambda == 0 ? 1e-16 : evs[p]+lambda);
386 return - dnorm/normp/normp/normp;
387 };
388 double lambda = 0;
389 const double sigma = 1e-4; // tolerance for Newton algorithm
390 const unsigned max_newton = 100;
391 double phi = target(lambda);
392 if( phi > 0) // positive lambda only exist if phi(0) > 0 since dlambda < 0 for all lambda >= 0
393 {
394 for( unsigned i=0; i<max_newton; i++) // Safeguard
395 {
396 // if ||p|| leq delta ( 1+sigma)
397 if ( fabs(phi) <= sigma/normp || i == max_newton-1)
398 break;
399 // The first step is always to the right ... (phi > 0 , lambda < 0)
400 lambda += - phi/dtarget(lambda);
401 phi = target(lambda);
402 }
403 }
404 dg::blas2::gemv(evHH, pkbar, pk);
405 // target(lambda) updates grad and normp
406 // 2. Check termination
407 //std::cout << "Real Norm p "<<sqrt(dg::blas1::dot( pk, pk))<<" normx0 "<<normx0<<"\n";
408 if( sqrt(dg::blas1::dot( pk,pk)) <= tol*(normx0 + 1.))
409 {
410 dg::blas1::axpby( 1., pk , 1., x0);
411 return k;
412 }
413 // 3. Compute Ratio rhok
414 // don't overwrite (x0, rs) because we might reject
415 dg::blas1::axpby( 1., pk , 1., x0, x1);
416 //for( unsigned l=0; l<num_p; l++)
417 // std::cout << "x0 "<<l<<" "<<x0[l]<<"\n";
418 //for( unsigned l=0; l<num_p; l++)
419 // std::cout << "x1 "<<l<<" "<<x1[l]<<"\n";
420 double f1 = 0;
421 try{
422 fun( x1, rs1);
423 f1 = dg::blas1::dot( rs1, rs1);
424 }
425 catch( dg::Error& e)
426 {
427 // The only reason to throw an Error here is if there is a NaN or Inf
428 // In this case will rs1 with huge values such the step is rejected
429 dg::blas1::copy( 1e300, rs1);
430 f1 = 1e300;
431 //std::cout << "############################# STEP CONTAINS NAN OR INF! REJECT!\n";
432 }
433 // compute (Jp)^2
434 double jp = dg::blas2::dot( pkbar, evs, pkbar);
435 //std::cout<< "f0 "<<f0<<" f1 "<<f1<<"\n";
436 double rhok = (1.-f1/f0)/( jp/f0 + 2*lambda*normp*normp/f0);
437 //std::cout << "Actual reduction "<<f0-f1<<"\n";
438 //std::cout << "Predicted reduction "<<( jp + 2*lambda*normp*normp)<<"\n";
439 //std::cout << "Ratio "<<rhok<<"\n";
440 // Algorithm 4.1 from Nocedal & Wright
441 if( rhok < 0.25)
442 delta = 0.25*delta;
443 else
444 {
445 // Mor´e has slightly different conditions
446 if( rhok > 0.75 && lambda > 0) // steps lies on the trust region boundary
447 delta = 2*delta;
448 // else delta remains unchanged
449 }
450 const double eta = 1e-4; // when step is rejected
451 if ( rhok > eta)
452 {
453 x0 = x1;
454 // update all quantities
455 f0 = f1;
456 using std::swap;
457 swap( rs, rs1);
458 jac( x0, jacs);
459 normx0 = sqrt(dg::blas1::dot( x0, x0));
460 for( unsigned l=0; l<num_p; l++)
461 {
462 grad[l] = dg::blas1::dot( jacs[l], rs);
463 //std::cout << "grad "<<l<<" "<<grad[l]<<"\n";
464 }
465 }
466 else
467 {
468 // std::cout << "REJECTED\n";
469 // else step is rejected
470 }
471 }
472 return max_iter;
473}
474} //namespace mat
475} //namespace dg
const std::vector< value_type > & data() const
void copy(const ContainerTypeIn &source, ContainerTypeOut &target)
void axpby(value_type alpha, const ContainerType1 &x, value_type1 beta, ContainerType &y)
auto dot(const ContainerType1 &x, const ContainerType2 &y)
void gemv(get_value_type< ContainerType1 > alpha, MatrixType &&M, const ContainerType1 &x, get_value_type< ContainerType1 > beta, ContainerType2 &y)
auto dot(const ContainerType1 &x, const MatrixType &m, const ContainerType2 &y)
unsigned levenberg_marquardt(Func fun, Jacobian jac, ContainerType0 &x0, const ContainerType1 &copyable, double tol=1e-8, unsigned max_iter=1000)
The Levenberg Marquardt algorithm.
Definition optimise.h:316
unsigned newton(Gradient grad, InvHessian invhess, ContainerType &x0, double tol=1e-5, unsigned max_iter=1000)
Newton iteration.
Definition optimise.h:81
const double alpha
Definition lanczos_b.cpp:11
Functions for optimizing Contours.