Hi everyone, I am wondering whether it is possible to create an “interface” or “pointer” on the objective function that the algorithm wants to minimize.
The following is my implementation of golden section search. I am writing a dynamic programming problem, which requires the following algorithm to be given all expectation, idx, xdval, xuval
four variables inside a for
loop, and then using this implementation to compute the minimum (or maximum).
The part that I want to delegate for a user to modify is the interface
block and somehow inside the gss_1d
function I can somehow point
or refer to whatever user-defined and calculate the value of the objective function.
module goldenSectionSearch
! ----------------------------------------------------- !
! modify the interface based on your objective function !
! (adjust numbers of arguments, intent and optional) !
! and also whenever the corresponding gss subroutine !
! calls the Obj procedure !
! ----------------------------------------------------- !
use iso_Fortran_env, only: rk => real64, ik => int32
implicit none
private
public :: gss
real(rk), parameter :: goldenRatio = (3.0_rk - dsqrt(5.0_rk)) / 2.0_rk
interface gss
module procedure gss_1d
end interface
interface
function gssObj(x, expectation, idx, xdval, xuval) result(f)
import :: rk, ik
! class(*), intent(in), optional :: func_data
integer(ik), intent(in) :: idx
real(rk), dimension(:, :, :), intent(in) :: expectation
real(rk), intent(in), optional :: xdval, xuval
real(rk), intent(in) :: x
real(rk) :: f
end function gssObj
end interface
contains
subroutine gss_1d(fout, xout, Obj, LB, UB, expectation, idx, xdval, xuval, tol, maxiter, findmax, show)
procedure(gssObj) :: Obj
real(rk), intent(out) :: fout, xout
real(rk), intent(in) :: LB, UB
integer(ik), intent(in) :: idx
real(rk), dimension(:, :, :), intent(in):: expectation
real(rk), intent(in), optional :: xdval, xuval
real(rk), intent(in), optional :: tol
integer(ik), intent(in), optional :: maxiter
logical, intent(in), optional :: findmax
logical, intent(in), optional :: show
! class(*), intent(in), optional :: func_data
character(len=128) :: tmp
integer(ik) :: iter, gssMaxIter
logical :: isFindMax, isShow
real(rk) :: a, b, c, d, z, fval, fc, fd, gssTol, gssDist
! ------- !
! default !
! ------- !
iter = 0_ik
gssTol = 1D-12
gssMaxIter = 500_ik
isFindMax = .true.
isShow = .false.
if (present(tol)) gssTol = tol
if (present(maxiter)) gssMaxIter = maxiter
if (present(findmax)) isFindMax = findmax
if (present(show)) isShow = show
! --------------------- !
! golden section search !
! --------------------- !
gssDist = 2.0_rk*gssTol
a = LB
b = UB
c = a + goldenRatio*(b-a);
d = a + (1.0_rk-goldenRatio)*(b-a);
fval = Obj(c, expectation, idx, xdval, xuval); fc = merge(-fval, fval, isFindMax);
fval = Obj(d, expectation, idx, xdval, xuval); fd = merge(-fval, fval, isFindMax);
do while (gssDist > gssTol .and. iter <= gssMaxIter)
iter = iter + 1
if (fc >= fd) then
z = c + (1.0_rk-goldenRatio)*(b-c)
! case 1 [a c d b] <--- [c d z b]
a = c
c = d
fc = fd
d = z
fval = Obj(d, expectation, idx, xdval, xuval); fd = merge(-fval, fval, isFindMax);
else
z = a + goldenRatio*(d-a)
! case 2 [a c d b] <--- [a z c d]
b = d
d = c
fd = fc
c = z
fval = Obj(c, expectation, idx, xdval, xuval); fc = merge(-fval, fval, isFindMax);
endif
gssDist = b - a
if (iter == 1 .and. isShow) then
write(*, *) ''
write(*, '(a4, 5(a20))') 'iter', '||b-a||', 'a', 'c', 'd', 'b'
write(tmp, '(a4, 5(a20))') 'iter', '||b-a||', 'a', 'c', 'd', 'b'
write(*, '(a)') repeat('=', len_trim(tmp))
endif
if (isShow) write (*, '(i4, 5(ES20.6))') iter, gssDist, a, c, d, b
if (iter > gssMaxIter) write(*, *) "Warning: Max Iter reached."
enddo
if (z < LB) then
z = LB
fout = Obj(z, expectation, idx, xdval, xuval);
xout = z
elseif (z > UB) then
z = UB
fout = Obj(z, expectation, idx, xdval, xuval);
xout = z
else
fout = fval
xout = z
endif
end subroutine gss_1d
end module goldenSectionSearch