optimize_cgfit_routines.f90 Source File


Files dependent on this one

sourcefile~~optimize_cgfit_routines.f90~~AfferentGraph sourcefile~optimize_cgfit_routines.f90 optimize_cgfit_routines.f90 sourcefile~sf_optimize.f90 SF_OPTIMIZE.f90 sourcefile~sf_optimize.f90->sourcefile~optimize_cgfit_routines.f90 sourcefile~scifor.f90 SCIFOR.f90 sourcefile~scifor.f90->sourcefile~sf_optimize.f90

Source Code

MODULE CGFIT_ROUTINES
  implicit none

  abstract interface
     function cgfit_func(a)
       real(8),dimension(:)  ::  a
       real(8)               ::  cgfit_func
     end function cgfit_func
     function cgfit_fjac(a)
       real(8),dimension(:)       :: a
       real(8),dimension(size(a)) :: cgfit_fjac
     end function cgfit_fjac
  end interface



  integer                        :: ncom
  real(8), dimension(:), pointer :: pcom,xicom
  procedure(cgfit_func),pointer  :: func
  procedure(cgfit_fjac),pointer  :: fjac

contains

  !+-------------------------------------------------------------------+
  ! PURPOSE  : Minimization routine
  ! Given an N dimensional point P and an N dimensional direction 
  ! XI, LINMIN moves and resets P to where the function FUNC(P) takes 
  ! on a minimum
  ! along the direction XI from P, and replaces XI by the actual vector
  ! displacement that P was moved.  Also returns FRET the value of 
  ! FUNC at the returned location P.  
  ! This is actually all accomplished by calling the routines 
  ! MNBRAK and BRENT.
  !+-------------------------------------------------------------------+
  SUBROUTINE linmin(p,xi,fret,ftol)
    real(8), intent(out)                         :: fret
    real(8), dimension(:), target, intent(inout) :: p,xi
    real(8),optional                             :: ftol
    real(8)                                      :: tol
    real(8)                                      :: ax,bx,fa,fb,fx,xmin,xx
    tol=1.d-6;if(present(ftol))tol=ftol
    ncom=size(p) ; if(ncom /= size(xi))stop "Error in LinMin"
    pcom=>p
    xicom=>xi
    ax=0.d0
    xx=1.d0
    call mnbrak(ax,xx,bx,fa,fx,fb,f1dim)
    fret=brent_(ax,xx,bx,f1dim,tol,xmin)
    xi=xmin*xi
    p=p+xi
    return
  end subroutine linmin

  SUBROUTINE dlinmin(p,xi,fret,ftol,itmax)
    real(8), intent(out)                         :: fret
    real(8), dimension(:), target, intent(inout) :: p,xi
    real(8),optional                             :: ftol
    integer,optional                             :: itmax
    real(8)                                      :: tol
    integer                                      :: itmax_
    real(8)                                      :: ax,bx,fa,fb,fx,xmin,xx
    tol=1.d-6;if(present(ftol))tol=ftol
    itmax_=100;if(present(itmax))itmax_=itmax
    ncom=size(p) ; if(ncom /= size(xi))stop "Error in DLinMin"
    pcom=>p
    xicom=>xi
    ax=0.d0
    xx=1.d0
    call mnbrak(ax,xx,bx,fa,fx,fb,f1dim)
    fret=dbrent_(ax,xx,bx,f1dim,df1dim,tol,xmin,itmax_)
    xi=xmin*xi
    p=p+xi
    return
  end subroutine dlinmin


  !+-------------------------------------------------------------------+
  !PURPOSE  : 
  !+-------------------------------------------------------------------+
  function f1dim(x)
    real(8), intent(in)                :: x
    real(8)                            :: f1dim
    real(8), dimension(:), allocatable :: xt
    allocate(xt(ncom))
    xt(:)=pcom(:)+x*xicom(:)
    f1dim=func(xt)
    deallocate(xt)
  end function f1dim

  function df1dim(x)
    real(8), intent(in)                :: x
    real(8)                            :: df1dim
    real(8), dimension(:), allocatable :: xt,df
    allocate(xt(ncom),df(ncom))
    xt(:)=pcom(:)+x*xicom(:)
    df(:)=fjac(xt)
    df1dim=dot_product(df,xicom)
    deallocate(xt,df)
  end function df1dim

  !+-------------------------------------------------------------------+
  !PURPOSE  : 
  !     Given a function FUNC, and given distinct initial points AX and BX,
  !     this routine searches in the downhill direction (defined by the 
  !     function as evaluated at the initial points) and returns new points
  !     AX, BX, CX which bracket a minimum of the function.  
  !     Also returned are the function values at the three points, 
  !     FA, FB, and FC.
  !+-------------------------------------------------------------------+
  subroutine mnbrak(ax,bx,cx,fa,fb,fc,func)
    real(8), intent(inout) :: ax,bx
    real(8), intent(out)   :: cx,fa,fb,fc
    !...the first parameter is the default ratio by which successive intervals
    !   are magnified; the second is the maximum magnification allowed for a
    !   parabolic-fit step
    real(8), parameter     :: gold=(1d0+sqrt(5d0))/2d0,glimit=10d0,tiny=1.d-20
    real(8)                :: fu,q,r,u,ulim
    interface
       function func(x)
         real(8), intent(in) :: x
         real(8)             :: func
       end function func
    end interface
    fa=func(ax)
    fb=func(bx)
    if (fb > fa) then           !switch the role of a and b to that we can go downhill
       call swap(ax,bx)
       call swap(fa,fb)
    end if
    cx=bx+GOLD*(bx-ax)
    fc=func(cx)
    ! do while (fb.ge.fc)
    do                          !do while”: keep returning here until we bracket.
       ! print*,"mnbrak count",ax,cx,bx
       if(isnan(ax).OR.isnan(cx).OR.isnan(bx))stop "MNBRAK error: ax/bx/cx are Nan!"
       if (fb < fc) RETURN
       r=(bx-ax)*(fb-fc)        !Compute u by parabolic extrapolation from a, b, c. TINY
       q=(bx-cx)*(fb-fa)        !is used to prevent any possible division by zero
       u=bx-((bx-cx)*q-(bx-ax)*r)/(2d0*sign(max(abs(q-r),TINY),q-r))
       ulim=bx+GLIMIT*(cx-bx)
       if ((bx-u)*(u-cx) > 0.d0) then !Parabolic u is between b and c: try it.
          fu=func(u)
          if (fu < fc) then     !Got a minimum between b and c.
             ax=bx
             fa=fb
             bx=u
             fb=fu
             RETURN
          else if (fu > fb) then !Got a minimum between between a and u.
             cx=u
             fc=fu
             RETURN
          end if
          u=cx+GOLD*(cx-bx)     !Parabolic fit was no use. Use default magnification.
          fu=func(u)
       else if ((cx-u)*(u-ulim) > 0.d0) then !Parabolic fit is between c and its allowed limit
          fu=func(u)
          if (fu < fc) then
             bx=cx
             cx=u
             u=cx+GOLD*(cx-bx)
             call shft(fb,fc,fu,func(u))
          end if
       else if ((u-ulim)*(ulim-cx) >= 0.d0) then !Limit parabolic u to maximum allowed value.
          u=ulim
          fu=func(u)
       else                     !Reject parabolic u, use default magnification.
          u=cx+GOLD*(cx-bx)
          fu=func(u)
       end if                   !Eliminate oldest point and continue.
       call shft(ax,bx,cx,u)
       call shft(fa,fb,fc,fu)
    end do
  contains
    subroutine swap(a,b)
      real(8), intent(inout) :: a,b
      real(8) :: dum
      dum=a
      a=b
      b=dum
    end subroutine swap
    !-------------------
    subroutine shft(a,b,c,d)
      real(8), intent(out) :: a
      real(8), intent(inout) :: b,c
      real(8), intent(in) :: d
      a=b
      b=c
      c=d
    end subroutine shft
  end subroutine mnbrak


  !+-------------------------------------------------------------------+
  !purpose  : 
  !     given a function f, and given a bracketing triplet of 
  !     abscissas ax, bx, cx (such that bx is between ax and cx, 
  !     and f(bx) is less than both f(ax) and f(cx)), this routine 
  !     isolates the minimum to a fractional precision of about tol 
  !     using brents method. the abscissa of the minimum is returned 
  !     as xmin, and the minimum function value is returned as brent, 
  !     the returned function value.
  !+-------------------------------------------------------------------+
  function brent_(ax,bx,cx,func,tol,xmin)
    real(8), intent(in)  :: ax,bx,cx,tol
    real(8), intent(out) :: xmin
    real(8)              :: brent_
    integer, parameter   :: itmax=100
    real(8), parameter   :: zeps=1d0-3d0*epsilon(ax)
    real(8), parameter   :: cgold=(3d0-sqrt(5d0))/2d0 !0.3819660d0
    integer              :: iter
    real(8)              :: a,b,d,e,etemp,fu,fv,fw,fx,p,q,r,tol1,tol2,u,v,w,x,xm
    interface
       function func(x)
         real(8), intent(in) :: x
         real(8)             :: func
       end function func
    end interface
    a=min(ax,cx)
    b=max(ax,cx)
    v=bx
    w=v
    x=v
    e=0.d0
    fx=func(x)
    fv=fx
    fw=fx
    do iter=1,itmax
       xm=0.5d0*(a+b)
       tol1=tol*abs(x)+zeps
       tol2=2.0*tol1
       if (abs(x-xm) <= (tol2-0.5d0*(b-a))) then
          xmin=x
          brent_=fx
          return
       end if
       if (abs(e) > tol1) then
          r=(x-w)*(fx-fv)
          q=(x-v)*(fx-fw)
          p=(x-v)*q-(x-w)*r
          q=2.d0*(q-r)
          if (q > 0.d0) p=-p
          q=abs(q)
          etemp=e
          e=d
          if (abs(p) >= abs(0.5d0*q*etemp) .or. &
               p <= q*(a-x) .or. p >= q*(b-x)) then
             e=merge(a-x,b-x, x >= xm )
             d=cgold*e
          else
             d=p/q
             u=x+d
             if (u-a < tol2 .or. b-u < tol2) d=sign(tol1,xm-x)
          end if
       else
          e=merge(a-x,b-x, x >= xm )
          d=cgold*e
       end if
       u=merge(x+d,x+sign(tol1,d), abs(d) >= tol1 )
       fu=func(u)
       if (fu <= fx) then
          if (u >= x) then
             a=x
          else
             b=x
          end if
          call shft(v,w,x,u)
          call shft(fv,fw,fx,fu)
       else
          if (u < x) then
             a=u
          else
             b=u
          end if
          if (fu <= fw .or. w == x) then
             v=w
             fv=fw
             w=u
             fw=fu
          else if (fu <= fv .or. v == x .or. v == w) then
             v=u
             fv=fu
          end if
       end if
    end do
    ! pause 'brent: exceed maximum iterations'
  contains
    subroutine shft(a,b,c,d)
      real(8), intent(out) :: a
      real(8), intent(inout) :: b,c
      real(8), intent(in) :: d
      a=b
      b=c
      c=d
    end subroutine shft
  end function brent_




  function dbrent_(ax,bx,cx,func,fjac,tol,xmin,itmax) result(dbrent)
    real(8),intent(in)  :: ax,bx,cx,tol
    real(8),intent(out) :: xmin
    real(8)             :: dbrent
    integer,optional    :: itmax
    integer             :: itmax_=100
    real(8), parameter  :: zeps=1.d-3*epsilon(ax)
    integer             :: iter
    real(8)             :: a,b,d,d1,d2,du,dv,dw,dx,e,fu,fv,fw,fx,olde,tol1,tol2
    real(8)             :: u,u1,u2,v,w,x,xm
    logical             :: ok1,ok2
    interface
       function func(x)
         real(8), intent(in) :: x
         real(8)             :: func
       end function func
       function fjac(x)
         real(8), intent(in) :: x
         real(8)             :: fjac
       end function fjac
    end interface
    itmax_=100;if(present(itmax))itmax_=itmax    
    a=min(ax,cx)
    b=max(ax,cx)
    v=bx
    w=v
    x=v
    e=0.d0
    fx=func(x)
    fv=fx
    fw=fx
    dx=fjac(x)
    dv=dx
    dw=dx
    do iter=1,ITMAX
       xm=0.5d0*(a+b)
       tol1=tol*abs(x)+ZEPS
       tol2=2.0d0*tol1
       if (abs(x-xm) <= (tol2-0.5d0*(b-a))) exit
       if (abs(e) > tol1) then
          d1=2.0d0*(b-a)
          d2=d1
          if (dw /= dx) d1=(w-x)*dx/(dx-dw)
          if (dv /= dx) d2=(v-x)*dx/(dx-dv)
          u1=x+d1
          u2=x+d2
          ok1=((a-u1)*(u1-b) > 0.d0) .and. (dx*d1 <= 0.d0)
          ok2=((a-u2)*(u2-b) > 0.d0) .and. (dx*d2 <= 0.d0)
          olde=e
          e=d
          if (ok1 .or. ok2) then
             if (ok1 .and. ok2) then
                d=merge(d1,d2, abs(d1) < abs(d2))
             else
                d=merge(d1,d2,ok1)
             end if
             if (abs(d) <= abs(0.5d0*olde)) then
                u=x+d
                if (u-a < tol2 .or. b-u < tol2) &
                     d=sign(tol1,xm-x)
             else
                e=merge(a,b, dx >= 0.d0)-x
                d=0.5d0*e
             end if
          else
             e=merge(a,b, dx >= 0.d0)-x
             d=0.5d0*e
          end if
       else
          e=merge(a,b, dx >= 0.d0)-x
          d=0.5d0*e
       end if
       if (abs(d) >= tol1) then
          u=x+d
          fu=func(u)
       else
          u=x+sign(tol1,d)
          fu=func(u)
          if (fu > fx) exit
       end if
       du=fjac(u)
       if (fu <= fx) then
          if (u >= x) then
             a=x
          else
             b=x
          end if
          call mov3(v,fv,dv,w,fw,dw)
          call mov3(w,fw,dw,x,fx,dx)
          call mov3(x,fx,dx,u,fu,du)
       else
          if (u < x) then
             a=u
          else
             b=u
          end if
          if (fu <= fw .or. w == x) then
             call mov3(v,fv,dv,w,fw,dw)
             call mov3(w,fw,dw,u,fu,du)
          else if (fu <= fv .or. v == x .or. v == w) then
             call mov3(v,fv,dv,u,fu,du)
          end if
       end if
    end do
    if (iter > ITMAX) stop 'dbrent: exceeded maximum iterations'
    xmin=x
    dbrent=fx
  contains
    !bl
    subroutine mov3(a,b,c,d,e,f)
      real(8), intent(in) :: d,e,f
      real(8), intent(out) :: a,b,c
      a=d
      b=e
      c=f
    end subroutine mov3
  end function dbrent_




  function isinfty(a) result(bool)
    real(8) :: a
    logical :: bool
    bool = (a-1 == a)
  end function isinfty

  function isnan(a) result(bool)
    real(8) :: a
    logical :: bool
    bool = (a /= a)
  end function isnan


end module CGFIT_ROUTINES