|
| 1 | +!******************************************************************************* |
| 2 | +!> author: Jacob Williams |
| 3 | +! date: October 1, 2022 |
| 4 | +! |
| 5 | +! Test2 for the numerical differentiation module. |
| 6 | +! Makes a plot of the errors using different methods and step sizes. |
| 7 | + |
| 8 | + program test2 |
| 9 | + |
| 10 | + use iso_fortran_env |
| 11 | + use numerical_differentiation_module |
| 12 | + use numdiff_kinds_module, only: wp |
| 13 | + use pyplot_module |
| 14 | + |
| 15 | + implicit none |
| 16 | + |
| 17 | + integer,parameter :: n = 1 !! number of variables |
| 18 | + integer,parameter :: m = 1 !! number of functions |
| 19 | + real(wp),dimension(n),parameter :: x = 1.0_wp !! point at which to compute the derivative |
| 20 | + real(wp),dimension(n),parameter :: xlow = -100000.0_wp !! bounds not really needed |
| 21 | + real(wp),dimension(n),parameter :: xhigh = 100000.0_wp !! |
| 22 | + integer,parameter :: perturb_mode = 1 !! absolute step |
| 23 | + integer,parameter :: cache_size = 0 !! `0` indicates not to use cache |
| 24 | + integer,parameter :: sparsity_mode = 1 !! assume dense |
| 25 | + integer,dimension(*),parameter :: methods = [1,3,10,21,36,500,600,700,800] |
| 26 | + !! array of method IDs: |
| 27 | + !! [1,3,10,21,36,500,600,700,800] |
| 28 | + !! forward + all the central diff ones |
| 29 | + integer,parameter :: exp_star = -ceiling(log10(epsilon(1.0_wp))) !! exponent to start with |
| 30 | + integer,parameter :: exp_stop = -2 !! exponent to end with |
| 31 | + integer,parameter :: exp_step = -1 !! ecponent step |
| 32 | + integer,parameter :: exp_scale = 5 !! number of substeps from one to the next |
| 33 | + |
| 34 | + type(numdiff_type) :: my_prob !! main class to compute the derivatives |
| 35 | + integer :: i !! method counter |
| 36 | + integer :: j !! counter |
| 37 | + real(wp),dimension(:),allocatable :: jac !! jacobian |
| 38 | + integer :: func_evals !! function evaluation counter |
| 39 | + character(len=:),allocatable :: error_msg !! error message string |
| 40 | + real(wp),dimension(n) :: dpert !! perturbation step size |
| 41 | + integer :: ipert !! perturbation step size counter |
| 42 | + real(wp) :: error !! diff from true derivative |
| 43 | + integer :: num_dperts !! number of dperts to test |
| 44 | + integer :: num_methods !! number of methods to test |
| 45 | + real(wp),dimension(:),allocatable :: results_dpert !! results array - dpert |
| 46 | + real(wp),dimension(:),allocatable :: results_errors !! results array - errors |
| 47 | + type(pyplot) :: plt !! for plotting the results |
| 48 | + character(len=:),allocatable :: formula !! finite diff forumla for the plot legend |
| 49 | + integer :: idx !! index in results arrays |
| 50 | + character(len=:),allocatable :: real_kind_str !! real kind for the plot title |
| 51 | + real(wp),dimension(3) :: color !! line color array |
| 52 | + real(wp),dimension(2) :: ylim !! plot y limit array |
| 53 | + |
| 54 | + ! size the arrays: |
| 55 | + num_methods = size(methods) |
| 56 | + num_dperts = size([(i, i = exp_star*exp_scale, exp_stop*exp_scale, exp_step)]) |
| 57 | + allocate(results_errors(num_dperts)); results_errors = -huge(1.0_wp) |
| 58 | + |
| 59 | + ! for plot title: |
| 60 | + select case(wp) |
| 61 | + case(REAL32); real_kind_str = '[Single Precision]' |
| 62 | + case(REAL64); real_kind_str = '[Double Precision]' |
| 63 | + case(REAL128); real_kind_str = '[Quad Precision]' |
| 64 | + case default; error stop 'Invalid real kind' |
| 65 | + end select |
| 66 | + |
| 67 | + ! initialize the plot: |
| 68 | + call plt%initialize(grid = .true.,& |
| 69 | + figsize = [20,10],& |
| 70 | + axes_labelsize = 30, & |
| 71 | + xtick_labelsize = 30, & |
| 72 | + ytick_labelsize = 30, & |
| 73 | + font_size = 30, & |
| 74 | + xlabel = 'Finite Difference Perturbation Step Size $h$',& |
| 75 | + ylabel = 'Finite Difference Derivative Error',& |
| 76 | + title = 'Derivative of $x + \sin(x)$ at $x=1$ '//real_kind_str,& |
| 77 | + legend = .true., & |
| 78 | + legend_fontsize = 10,& |
| 79 | + usetex = .true.) |
| 80 | + |
| 81 | + ! try different finite diff methods |
| 82 | + do j=1,size(methods) |
| 83 | + |
| 84 | + idx = 0 |
| 85 | + i = methods(j) ! method id |
| 86 | + call get_finite_diff_formula(i,formula) |
| 87 | + |
| 88 | + ! cycle through perturbation step sizes: |
| 89 | + do ipert = exp_star*exp_scale, exp_stop*exp_scale, exp_step |
| 90 | + |
| 91 | + idx = idx + 1 ! index for arrays |
| 92 | + dpert = 10.0_wp ** (-ipert/real(exp_scale,wp)) ! compute perturbation step size |
| 93 | + if (j==1) then |
| 94 | + if (.not. allocated(results_dpert)) allocate(results_dpert(0)) |
| 95 | + results_dpert = [results_dpert, dpert(1)] ! save dpert |
| 96 | + end if |
| 97 | + |
| 98 | + func_evals = 0 |
| 99 | + call my_prob%destroy() |
| 100 | + call my_prob%initialize(n,m,xlow,xhigh,perturb_mode,dpert,& |
| 101 | + problem_func=my_func,& |
| 102 | + sparsity_mode=sparsity_mode,& |
| 103 | + jacobian_method=i,& |
| 104 | + partition_sparsity_pattern=.false.,& |
| 105 | + cache_size=cache_size) |
| 106 | + if (my_prob%failed()) then |
| 107 | + call my_prob%get_error_status(error_msg=error_msg) |
| 108 | + error stop error_msg |
| 109 | + end if |
| 110 | + |
| 111 | + call my_prob%compute_jacobian(x,jac) |
| 112 | + if (my_prob%failed()) then |
| 113 | + call my_prob%get_error_status(error_msg=error_msg) |
| 114 | + error stop error_msg |
| 115 | + end if |
| 116 | + error = abs(deriv(x(1)) - jac(1)) |
| 117 | + !write(output_unit,'(I5,1X,*(F27.16))') i , dpert, error |
| 118 | + if (error<=epsilon(1.0_wp)) error = 0.0_wp |
| 119 | + results_errors(idx) = error ! save result |
| 120 | + |
| 121 | + end do |
| 122 | + |
| 123 | + ! line color for the plot: |
| 124 | + select case (j) |
| 125 | + case(1); color = [1.0_wp, 0.0_wp, 0.0_wp] ! red |
| 126 | + case(2); color = [0.0_wp, 1.0_wp, 0.0_wp] ! green |
| 127 | + case default |
| 128 | + ! blue gradient for the others |
| 129 | + color = [real(j-2,wp)/num_methods, & |
| 130 | + real(j-2,wp)/num_methods, & |
| 131 | + 0.9_wp] |
| 132 | + end select |
| 133 | + ylim = [10.0_wp**(ceiling(log10(epsilon(1.0_wp)))), 1.0_wp] |
| 134 | + |
| 135 | + ! plot for this method: |
| 136 | + call plt%add_plot(results_dpert,results_errors,& |
| 137 | + xscale='log', yscale='log',& |
| 138 | + label=formula,linestyle='.-',markersize=5,linewidth=2, & |
| 139 | + color = color,& |
| 140 | + xlim = ylim,& |
| 141 | + ylim = ylim) |
| 142 | + |
| 143 | + end do |
| 144 | + |
| 145 | + ! save plot: |
| 146 | + call plt%savefig('results '//real_kind_str//'.pdf', pyfile='results.py') |
| 147 | + |
| 148 | +contains |
| 149 | + |
| 150 | + function func(x) |
| 151 | + !! Problem function |
| 152 | + real(wp), intent(in) :: x |
| 153 | + real(wp) :: func |
| 154 | + func = x + sin(x) |
| 155 | + end function func |
| 156 | + function deriv(x) |
| 157 | + !! Problem function true derivative |
| 158 | + real(wp), intent(in) :: x |
| 159 | + real(wp) :: deriv |
| 160 | + deriv = 1.0_wp + cos(x) |
| 161 | + end function deriv |
| 162 | + |
| 163 | + subroutine my_func(me,x,f,funcs_to_compute) |
| 164 | + !! Problem function interface for numdiff |
| 165 | + class(numdiff_type),intent(inout) :: me |
| 166 | + real(wp),dimension(:),intent(in) :: x |
| 167 | + real(wp),dimension(:),intent(out) :: f |
| 168 | + integer,dimension(:),intent(in) :: funcs_to_compute |
| 169 | + if (any(funcs_to_compute==1)) f(1) = func(x(1)) |
| 170 | + func_evals = func_evals + 1 |
| 171 | + end subroutine my_func |
| 172 | + |
| 173 | +!******************************************************************************* |
| 174 | + end program test2 |
| 175 | +!******************************************************************************* |
0 commit comments