itp Subroutine

private subroutine itp(me, ax, bx, fax, fbx, xzero, fzero, iflag)

Compute the zero of the function f(x) in the interval ax,bx using the Interpolate Truncate and Project (ITP) method.

See also

  • Oliveira, I. F. D., Takahashi, R. H. C., "An Enhancement of the Bisection Method Average Performance Preserving Minmax Optimality", ACM Transactions on Mathematical Software. 47 (1): 5:1-5:24. (2020-12-06)

Notes

  • This implementation differs from the reference, in that it has an additional check to make sure the a,b values have changed at each iteration. If they haven't, it will perform a basic bisection.

Type Bound

itp_solver

Arguments

Type IntentOptional Attributes Name
class(itp_solver), intent(inout) :: me
real(kind=wp), intent(in) :: ax

left endpoint of initial interval

real(kind=wp), intent(in) :: bx

right endpoint of initial interval

real(kind=wp), intent(in) :: fax

f(ax)

real(kind=wp), intent(in) :: fbx

f(ax)

real(kind=wp), intent(out) :: xzero

abscissa approximating a zero of f in the interval ax,bx

real(kind=wp), intent(out) :: fzero

value of f at the root (f(xzero))

integer, intent(out) :: iflag

status flag (0=root found, -2=max iterations reached)


Calls

proc~~itp~~CallsGraph proc~itp root_module::itp_solver%itp f f proc~itp->f proc~bisect root_module::bisect proc~itp->proc~bisect proc~choose_best root_module::choose_best proc~itp->proc~choose_best proc~converged root_module::root_solver%converged proc~itp->proc~converged proc~solution root_module::root_solver%solution proc~itp->proc~solution

Source Code

    subroutine itp(me,ax,bx,fax,fbx,xzero,fzero,iflag)

    implicit none

    class(itp_solver),intent(inout) :: me
    real(wp),intent(in)    :: ax      !! left endpoint of initial interval
    real(wp),intent(in)    :: bx      !! right endpoint of initial interval
    real(wp),intent(in)    :: fax     !! `f(ax)`
    real(wp),intent(in)    :: fbx     !! `f(ax)`
    real(wp),intent(out)   :: xzero   !! abscissa approximating a zero of `f` in the interval `ax`,`bx`
    real(wp),intent(out)   :: fzero   !! value of `f` at the root (`f(xzero)`)
    integer,intent(out)    :: iflag   !! status flag (`0`=root found, `-2`=max iterations reached)

    real(wp) :: a,b,ya,yb,x12,r,d,xf,bma,sigma,xt,xitp,yitp,term,aprev,bprev,denom
    integer :: n12, nmax
    integer :: j !! iteration counter
    logical :: root_found !! convergence in x
    logical :: fail !! if we can't interpolate
    real(wp) :: factor !! factor to ensure f(ax)<f(bx)

    real(wp),parameter :: log2 = log(2.0_wp)

    ! initialize:
    if (fax < fbx) then  ! a requirement for the algorithm ?
        factor = 1.0_wp
    else
        ! flip the sign of the function to ensure fax < fbx
        ! note that a < b is already enforced by the calling routine.
        factor = -1.0_wp
    end if
    a  = ax
    b  = bx
    ya = factor*fax
    yb = factor*fbx
    iflag = 0
    term = (b-a)/(2.0_wp*me%rtol)
    n12 = ceiling ( log(abs(term)) / log2 ) ! ceiling(log2(term))
    nmax = n12 + me%n0
    aprev = huge(1.0_wp) ! initialize to unusual values
    bprev = huge(1.0_wp) !

    ! main loop
    do j = 0, min(nmax,me%maxiter)

        x12 = bisect(a,b)
        bma = b - a
        ! note: protect for r<0 as mentioned in paper
        r = max(me%rtol * 2.0_wp ** (nmax-j) - bma/2.0_wp, 0.0_wp)
        d = me%k1 * bma**me%k2

        ! interpolation:
        denom = yb - ya
        fail = abs(denom) <= tiny(1.0_wp)  ! check for divide by zero

        if (.not. fail) then
            xf = (yb*a - ya*b) / denom

            ! truncation:
            sigma = sign(1.0_wp, x12 - xf)
            if (d <= abs(x12-xf)) then
                xt = xf + sigma * d
            else
                xt = x12
            end if

            ! projection:
            if (abs(xt - x12) <= r) then
                xitp = xt
            else
                xitp = x12 - sigma * r
            end if

            ! updating interval:
            yitp = factor*me%f(xitp)
            if (me%solution(xitp,yitp,xzero,fzero)) return
            if (yitp > 0.0_wp) then
                b = xitp
                yb = yitp
            elseif (yitp < 0.0_wp) then
                a = xitp
                ya = yitp
            else
                a = xitp
                b = xitp
            end if

        end if

        if (fail .or. (a==aprev .and. b==bprev)) then
            ! if the interval hasn't changed, then it is stuck.
            ! [this can happen in the test cases].
            ! So just do a bisection
            xitp = bisect(a,b)
            yitp = factor*me%f(xitp)
            if (me%solution(xitp,yitp,xzero,fzero)) return
            if (ya*yitp<0.0_wp) then
                ! root lies between a and xitp
                b = xitp
                yb = yitp
            else
                ! root lies between xitp and b
                a = xitp
                ya = yitp
            end if
        end if
        aprev = a
        bprev = b

        ! check for convergence:
        root_found = me%converged(a,b)
        if (root_found .or. j==me%maxiter) then
            call choose_best(a,b,ya,yb,xzero,fzero)
            if (.not. root_found) iflag = -2  ! max iterations reached
            exit
        end if

    end do

    end subroutine itp