perturb_and_evaluate Subroutine

private subroutine perturb_and_evaluate(me, x, vm, xp, fp, nfcnev, ier, first)

Perturb the x vector and evaluate the function.

If the function evaluation is not valid, it will perturb and try again. Until a valid function is obtained or the maximum number of function evaluations is reached.

get the inputs to send to the user function for parallel evaluation there must be at least one

send the perturbed x values to the user function for parallel evaluation get the function values and status code back from the user function for parallel evaluation

Type Bound

simulated_annealing_type

Arguments

Type IntentOptional Attributes Name
class(simulated_annealing_type), intent(inout) :: me
real(kind=wp), intent(in), dimension(:) :: x

input optimization variable vector to perturb

real(kind=wp), intent(in), dimension(:) :: vm

step length vector

real(kind=wp), intent(out), dimension(:) :: xp

the perturbed x value

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

the value of the user function at xp

integer, intent(inout) :: nfcnev

total number of function evaluations

integer, intent(inout) :: ier

status output code

logical, intent(in), optional :: first

to use the input x the first time


Calls

proc~~perturb_and_evaluate~~CallsGraph proc~perturb_and_evaluate simulated_annealing_type%perturb_and_evaluate proc~perturb_variable simulated_annealing_type%perturb_variable proc~perturb_and_evaluate->proc~perturb_variable proc~bipareto bipareto proc~perturb_variable->proc~bipareto proc~cauchy cauchy proc~perturb_variable->proc~cauchy proc~triangular_dist triangular_dist proc~perturb_variable->proc~triangular_dist proc~truncated_normal truncated_normal proc~perturb_variable->proc~truncated_normal proc~uniform uniform proc~perturb_variable->proc~uniform proc~bipareto->proc~uniform proc~uniform_random_number uniform_random_number proc~bipareto->proc~uniform_random_number proc~cauchy->proc~uniform_random_number proc~triangular_dist->proc~uniform_random_number proc~truncated_normal->proc~uniform proc~normal normal proc~truncated_normal->proc~normal proc~uniform->proc~uniform_random_number proc~normal->proc~uniform_random_number

Called by

proc~~perturb_and_evaluate~~CalledByGraph proc~perturb_and_evaluate simulated_annealing_type%perturb_and_evaluate proc~sa simulated_annealing_type%sa proc~sa->proc~perturb_and_evaluate proc~solve_simulated_annealing solve_simulated_annealing proc~solve_simulated_annealing->proc~sa

Source Code

   subroutine perturb_and_evaluate(me,x,vm,xp,fp,nfcnev,ier,first)

      class(simulated_annealing_type),intent(inout) :: me
      real(wp),dimension(:),intent(in)  :: x       !! input optimization variable vector to perturb
      real(wp),dimension(:),intent(in)  :: vm      !! step length vector
      real(wp),dimension(:),intent(out) :: xp      !! the perturbed `x` value
      real(wp),intent(out)              :: fp      !! the value of the user function at `xp`
      integer,intent(inout)             :: nfcnev  !! total number of function evaluations
      integer,intent(inout)             :: ier     !! status output code
      logical,intent(in),optional       :: first   !! to use the input `x` the first time

      integer :: i          !! counter
      integer :: istat      !! user function status code
      logical :: first_try  !! local copy of `first`
      integer :: n_inputs   !! number of inputs to send to the user function for parallel evaluation
      logical :: reallocate !! whether to reallocate `xp_mat` for parallel evaluation
      real(wp),dimension(:,:),allocatable :: xp_mat !! array of `xp` vectors for parallel evaluation

      if (present(first)) then
         first_try = first
      else
         first_try = .false.
      end if

      do  ! we must return at least one that doesn't fail (or we exceed the max number of function evaluations)

         if (me%parallel_mode) then
            ! parallel mode: generate and send multiple x vectors
            call me%n_inputs_to_send(n_inputs)  !! get the inputs to send to the user function for parallel evaluation
            n_inputs = max(1, n_inputs)  !! there must be at least one
            ! do we need to reallocate xp_mat? (if n_inputs has changed):
            reallocate = .true.
            if (allocated(xp_mat)) then
               if (size(xp_mat,1) == me%n .and. size(xp_mat,2) == n_inputs) then
                  reallocate = .false.
               end if
            end if
            if (reallocate) then
               if (allocated(xp_mat)) deallocate(xp_mat)
               allocate(xp_mat(me%n, n_inputs))
            end if
            do i = 1, n_inputs
               xp_mat(:,i) = get_xp()  ! get a perturbed `x` value for each input
            end do
            call me%fcn_parallel_input(xp_mat)  !! send the perturbed `x` values to the user function for parallel evaluation
            call me%fcn_parallel_output(xp, fp, istat)  !! get the function values and status code back from the user function for parallel evaluation
         else
            ! serial mode: generate and send a single x vector
            xp = get_xp()  ! single funciton evaluation
            call me%fcn(xp, fp, istat)  ! evaluate the function with the trial point xp and return as fp.
         end if

         if (istat==0 .and. (me%ireport == 1 .or. me%ireport == 3)) then ! report this value to the user
            call me%report(xp, fp, istat=1) ! convert f back to user's sign if necessary
         end if

         nfcnev = nfcnev + 1  ! function eval counter (note: for parallel runs,
                              ! only count one evaluation per returned values.
                              ! others can still be running)

         ! if too many function evaluations occur, terminate the algorithm.
         if (nfcnev > me%maxevl) then
            if (me%iprint>0) then
               write(me%iunit, '(A)') ' too many function evaluations; consider'
               write(me%iunit, '(A)') ' increasing maxevl or eps, or decreasing'
               write(me%iunit, '(A)') ' nt or rt. these results are likely to be'
               write(me%iunit, '(A)') ' poor.'
            end if
            ier = 1
            return
         end if

         select case (istat)
          case(-2)
            ! user stop
            write(me%iunit, '(A)') ' user stopped in function.'
            ier = 4
            exit
          case(-1)
            ! try another one until we get a valid evaluation
            cycle
          case default
            ! continue
         end select

         exit ! finished

      end do

   contains

      function get_xp() result(xp)
         !! get a perturbed `x` value
         real(wp),dimension(me%n) :: xp     !! the perturbed `x` value
         real(wp)                 :: lower  !! lower bound to use for random interval
         real(wp)                 :: upper  !! upper bound to use for random interval
         integer                  :: i      !! counter

         if (first_try) then
            if (me%use_initial_guess) then
               ! use the initial guess
               ! [note if this evauation fails, the subsequent ones
               !  are perturbed from this one]
               xp = x
               first_try = .false.
            else
               ! a random point in the bounds:
               ! [if it fails, a new random one is tried next time]
               do i = 1, me%n
                  xp(i) = me%perturb_variable(i, x(i), me%distribution_mode(i), &
                                              me%lb(i), me%ub(i))
               end do
            end if
         else
            ! perturb all of them using per-variable distributions:
            do i = 1, me%n
               lower = max( me%lb(i), x(i) - vm(i) )
               upper = min( me%ub(i), x(i) + vm(i) )
               xp(i) = me%perturb_variable(i, x(i), me%distribution_mode(i), &
                                           lower, upper)
            end do
         end if
      end function get_xp

   end subroutine perturb_and_evaluate