見出し画像

数値計算・虎の穴#3 数値微分(2)~超実数を使った実装

 この”数値計算・虎の穴”シリーズの記事を書いていたことをすっかり忘れていました。『数値微分(1)』を書いてから、ずいぶんと時間が経過してしまいました。もう少し短い間隔で記事を書くはずだったのですが・・・。今回はおちゃらけた部分がない超真面目な記事ですので、覚悟して下さい。

 数値計算で使う微分は、簡単な数式なら元の式の微分から直接求めることができますが、複雑な式の場合は数値微分を使って計算します。数値微分といっても難しいことは無く、微分の定義式から極限の部分を省いたいわゆる差分を使います。

 しかし、この差分で近似する方法も、うまく行く場合とそうでない場合があります。単調増加や単調減少などの大きな変化のない関数なら比較的うまく行きますが、増加と減少を繰り返すような振動を伴うような関数では、差分を行なう微小量の数値によっては、大きな誤差が生じます。

 大きな誤差が生じない数値微分の方法には何種類かありますが、ここでは”超実数(Hyperreal numbers)”を使った実装法を紹介します。超実数というのは、通常の実数に微小部分を付加した拡張実数です。虚数計算の実数版を考えれば何となくわかると思います。ただし、虚数では虚数単位の二乗が-1となるのですが、超実数の微小単位の二乗は0となるのが大きな特徴です。

 Fortranプログラムでは、超実数同志の計算をモジュール化し、微分演算を演算子”//”に多重定義しました。超実数モジュールでは、通常の四則演算に加え、標準の三角関数や対数関数、指数関数を実装しています。また、Fortran標準ではありませんが、ベッセル関数(J0とJ1)と誤差関数(erf)も実装しています。

 実際のHyperrealモジュールはかなり長いのでここでは載せませんが、下記のサンプルプログラムを読めば、中身が想像できると思います。サンプルプログラムでは、実際の使用法を中心にテストをしています。test1からtest7までが1変数の微分で、test8とtest9が2変数の偏微分のテストプログラムになっています。テストプログラムでは、数値微分と実際の微分の比較を行なっていますが、数値微分は真値と一致します。

program main
 call test1
 call test2
 call test3
 call test4
 call test5
 call test6
 call test7
 call test8
 call test9
end program main
!--------------------------------------------
!             Test No.1
!
!  f(x)     = (c*x+d)/(a*x+b)
!  df(x)/dx = (b*c-a*d)/(a*x+b)**2
!--------------------------------------------
subroutine test1
 use HyperReal
 implicit none
 type(Hreal) :: x, y
 real(dpr) :: xx, z, diff1, diff2
 integer :: i
 write(6,'(/,a)') ' Test No.1 '
 write(6,'(/,a)') ' No.         f(x)                  df/dx.Cal               df/dx.Theo '
 do i=1,10
   z=dble(i)
   x=independent(1,z)
   y=(3.0*x+4.0)/(x+2.0)
! numerical solution
   diff1 = y // x                  
! analytical solution
   xx=real(x)
   diff2 = 2.0/(xx+2.0)**2.0
   write(6,'(i4,3e24.16)') i,real(y),diff1,diff2
 enddo
end subroutine test1
!--------------------------------------------
!             Test No.2
!
!  f(x)     = x+sqrt(x*x+1.0)
!  df(x)/dx = (x+sqrt(x*x+1.0))/sqrt(x*x+1.0)
!--------------------------------------------
subroutine test2
 use HyperReal
 implicit none
 type(Hreal) :: x, y
 real(dpr) :: xx, z, diff1, diff2
 integer :: i
 write(6,'(/,a)') ' Test No.2 '
 write(6,'(/,a)') ' No.         f(x)                  df/dx.Cal               df/dx.Theo '
 do i=1,10
   z=dble(i)
   x=independent(1,z)
   y=x+sqrt(x**2.0+1.0)
! numerical solution
   diff1 = y // x                  
! analytical solution
   xx=real(x)
   diff2 = (xx+sqrt(xx**2.0+1.0))/sqrt(xx**2.0+1.0)
   write(6,'(i4,3e24.16)') i,real(y),diff1,diff2
 enddo
end subroutine test2
!--------------------------------------------
!             Test No.3
!
!  f(x)     = (exp(x)*sin(x))/x
!  df(x)/dx = exp(x)*(x*cos(x)+(x-1.0)*sin(x))/x**2
!--------------------------------------------
subroutine test3
 use HyperReal
 implicit none
 type(Hreal) :: x, y
 real(dpr) :: xx, z, diff1, diff2
 integer :: i
 write(6,'(/,a)') ' Test No.3 '
 write(6,'(/,a)') ' No.         f(x)                  df/dx.Cal               df/dx.Theo '
 do i=1,10
   z=dble(i)
   x=independent(1,z)
   y=exp(x)*sin(x)/x
! numerical solution
   diff1 = y // x                  
! analytical solution
   xx=real(x)
   diff2 = exp(xx)*(xx*cos(xx)+(xx-1.0)*sin(xx))/xx**2
   write(6,'(i4,3e24.16)') i,real(y),diff1,diff2
 enddo
end subroutine test3
!--------------------------------------------
!             Test No.4
!
!  f(x)     = exp(sqrt(x))
!  df(x)/dx = exp(sqrt(x))/(2.0*sqrt(x))
!--------------------------------------------
subroutine test4
 use HyperReal
 implicit none
 type(Hreal) :: x, y
 real(dpr) :: xx, z, diff1, diff2
 integer :: i
 write(6,'(/,a)') ' Test No.4 '
 write(6,'(/,a)') ' No.         f(x)                  df/dx.Cal               df/dx.Theo '
 do i=1,10
   z=dble(i)
   x=independent(1,z)
   y=exp(sqrt(x))
! numerical solution
   diff1 = y // x                  
! analytical solution
   xx=real(x)
   diff2 = exp(sqrt(xx))/(2.0*sqrt(xx))
   write(6,'(i4,3e24.16)') i,real(y),diff1,diff2
 enddo
end subroutine test4
!--------------------------------------------
!             Test No.5
!
!  f(x)     = exp(x)*log(x)
!  df(x)/dx = exp(x)*(1.0/x+log(x))
!--------------------------------------------
subroutine test5
 use HyperReal
 implicit none
 type(Hreal) :: x, y
 real(dpr) :: xx, z, diff1, diff2
 integer :: i
 write(6,'(/,a)') ' Test No.5 '
 write(6,'(/,a)') ' No.         f(x)                  df/dx.Cal               df/dx.Theo '
 do i=1,10
   z=dble(i)
   x=independent(1,z)
   y=exp(x)*log(x)
! numerical solution
   diff1 = y // x                  
! analytical solution
   xx=real(x)
   diff2 = exp(xx)*(1.0/xx+log(xx))
   write(6,'(i4,3e24.16)') i,real(y),diff1,diff2
 enddo
end subroutine test5
!--------------------------------------------
!             Test No.6
!
!  f(x)     = asin(x)/sqrt(1.0-x**2)
!  df(x)/dx = (sqrt(1.0-x**2)+x*asin(x))/(1.0-x**2)**(3/2)
!--------------------------------------------
subroutine test6
 use HyperReal
 implicit none
 type(Hreal) :: x, y
 real(dpr) :: xx, z, diff1, diff2
 integer :: i
 write(6,'(/,a)') ' Test No.6 '
 write(6,'(/,a)') ' No.         f(x)                  df/dx.Cal               df/dx.Theo '
 do i=1,10
   z=dble(i)/20.0
   x=independent(1,z)
   y=asin(x)/sqrt(1.0-x**2.0)
! numerical solution
   diff1 = y // x                  
! analytical solution
   xx=real(x)
   diff2 = (sqrt(1.0-xx**2.0)+xx*asin(xx))/(1.0-xx**2.0)**1.5
   write(6,'(i4,3e24.16)') i,real(y),diff1,diff2
 enddo
end subroutine test6
!--------------------------------------------
!             Test No.7
!
!  f(x)     = exp(atan(x))
!  df(x)/dx = exp(atan(x))/(1.0+x**2)
!--------------------------------------------
subroutine test7
 use HyperReal
 implicit none
 type(Hreal) :: x, y
 real(dpr) :: xx, z, diff1, diff2
 integer :: i
 write(6,'(/,a)') ' Test No.7 '
 write(6,'(/,a)') ' No.         f(x)                  df/dx.Cal               df/dx.Theo '
 do i=1,10
   z=dble(i)
   x=independent(1,z)
   y=exp(atan(x))
! numerical solution
   diff1 = y // x                  
! analytical solution
   xx=real(x)
   diff2 = exp(atan(xx))/(1.0+xx**2.0)
   write(6,'(i4,3e24.16)') i,real(y),diff1,diff2
!    write(6,'(e24.16)') (diff1-diff2)/
 enddo
end subroutine test7
!--------------------------------------------
!             Test No.8
!
!  f(x,y)     = (3*x*x+2*x+1)*(4*y**3+3*y*y+2*y+1)
!  Df(x,y)/Dx = (6*x+2)*(4*y**3+3*y*y+2*y+1)
!  Df(x,y)/Dy = (3*x*x+2*x+1)*(12*y**2+6*y+2)
!--------------------------------------------
subroutine test8
 use HyperReal
 implicit none
 type(Hreal) :: x, y, z
 real(dpr) :: xx, yy, w, pdiff1, pdiff2
 integer :: i
 write(6,'(/,a)') ' Test No.8 '
 write(6,'(/,a)') ' No.         f(x,y)                Df/Dx.Cal               Df/Dx.Theo '
 do i=1,10
   w=dble(i)/10.0
   x=independent(1,w)
   y=independent(2,w)
   z=(3.0*x*x+2.0*x+1.0)*(4.0*y**3.0+3.0*y*y+2.0*y+1.0)
! numerical solution
   pdiff1 = z // x  ! Df(x,y)/Dx                
! analytical solution
   xx=real(x)
   yy=real(y)
   pdiff2 = (6.0*xx+2.0)*(4.0*yy**3.0+3.0*yy*yy+2.0*yy+1.0)
   write(6,'(i4,3e24.16)') i,real(y),pdiff1,pdiff2
 enddo
 write(6,'(/,a)') ' No.         f(x,y)                Df/Dy.Cal               Df/Dy.Theo '
 do i=1,10
   w=dble(i)/10.0
   x=independent(1,w)
   y=independent(2,w)
   z=(3.0*x*x+2.0*x+1.0)*(4.0*y**3.0+3.0*y*y+2.0*y+1.0)
! numerical solution
   pdiff1 = z // y  ! Df(x,y)/Dy                 
! analytical solution
   xx=real(x)
   yy=real(y)
   pdiff2 = (3.0*xx*xx+2.0*xx+1.0)*(12.0*yy**2.0+6.0*yy+2.0)
   write(6,'(i4,3e24.16)') i,real(y),pdiff1,pdiff2
 enddo
end subroutine test8
!--------------------------------------------
!             Test No.9
!
!  f(x,y)     = exp(sin(x*y))
!  Df(x,y)/Dx = exp(sin(x*y))*y*cos(x*y)
!  Df(x,y)/Dy = exp(sin(x*y))*x*cos(x*y)
!--------------------------------------------
subroutine test9
 use HyperReal
 implicit none
 type(Hreal) :: x, y, z
 real(dpr) :: xx, yy, w, pdiff1, pdiff2
 integer :: i
 write(6,'(/,a)') ' Test No.9 '
 write(6,'(/,a)') ' No.         f(x,y)                Df/Dx.Cal               Df/Dx.Theo '
 do i=1,10
   w=dble(i)/10.0
   x=independent(1,w)
   y=independent(2,w)
   z=exp(sin(x*y))
! numerical solution
   pdiff1 = z // x  ! Df(x,y)/Dx                
! analytical solution
   xx=st(x)  ! standard part of HyperReal
   yy=st(y)  ! standard part of HyperReal
   pdiff2 = exp(sin(xx*yy))*yy*cos(xx*yy)
   write(6,'(i4,3e24.16)') i,real(y),pdiff1,pdiff2
 enddo
 write(6,'(/,a)') ' No.         f(x,y)                Df/Dy.Cal               Df/Dy.Theo '
 do i=11,20
   w=dble(i)/10.0
   x=independent(1,w)
   y=independent(2,w)
   z=exp(sin(x*y))
! numerical solution
   pdiff1 = z // y  ! Df(x,y)/Dy                 
! analytical solution
   xx=real(x)  ! function overload = st(x)
   yy=real(y)  ! function overload = st(y)
   pdiff2 = exp(sin(xx*yy))*xx*cos(xx*yy)
   write(6,'(i4,3e24.16)') i,real(y),pdiff1,pdiff2
 enddo
end subroutine test9

この記事が気に入ったらサポートをしてみませんか?