Dear all, I am doing SDE now. I know Julia has fancy differentialequations.jl. It has several SDE solver like SRIW1 or something like that, which uses adaptive Runge-Kutta (RK).
I also found there is a fortran version of fix step RK method for SDE, by John Burkardt,
https://people.math.sc.edu/Burkardt/f_src/stochastic_rk/stochastic_rk.html
It is based on Kasdin’s method,
- Jeremy Kasdin,
Runge-Kutta algorithm for the numerical integration of stochastic differential equations,
Journal of Guidance, Control, and Dynamics,
Volume 18, Number 1, January-February 1995, pages 114-120. - Jeremy Kasdin,
Discrete Simulation of Colored Noise and Stochastic Processes and 1/f^a Power Law Noise Generation,
Proceedings of the IEEE,
Volume 83, Number 5, 1995, pages 802-827.
Eqns is simple,
Although John’s version is for just for one SDE which means it is a scaler version, it is trivial to extend it to make the vector version to solve SDEs. For example, a 2D version (so 2 SDEs) can be like this in Fortran,
subroutine rk4_ti_step_vec_mod ( x, t, h, q, fi, gi, n, xstar )
use random
implicit none
interface !! Declare function
function fi ( x ) result (f)
use constants
real ( kind = r8 ) :: f(2)
real ( kind = r8 ) :: x(2)
end function
function gi ( x ) result (g)
use constants
real ( kind = r8 ) :: g(2)
real ( kind = r8 ) :: x(2)
end function
end interface
integer ( kind = i8 ) n
real ( kind = r8 ) h
real ( kind = r8 ) k1(n)
real ( kind = r8 ) k2(n)
real ( kind = r8 ) k3(n)
real ( kind = r8 ) k4(n)
real ( kind = r8 ) q
real ( kind = r8 ) t
real ( kind = r8 ) t1
real ( kind = r8 ) t2
real ( kind = r8 ) t3
real ( kind = r8 ) t4
real ( kind = r8 ) w1
real ( kind = r8 ) w2
real ( kind = r8 ) w3
real ( kind = r8 ) w4
real ( kind = r8 ) x(n)
real ( kind = r8 ) x1(n)
real ( kind = r8 ) x2(n)
real ( kind = r8 ) x3(n)
real ( kind = r8 ) x4(n)
real ( kind = r8 ) xstar(n)
real ( kind = r8 ), parameter :: a21 = 2.71644396264860_r8 &
,a31 = - 6.95653259006152_r8 &
,a32 = 0.78313689457981_r8 &
,a41 = 0.0_r8 &
,a42 = 0.48257353309214_r8 &
,a43 = 0.26171080165848_r8 &
,a51 = 0.47012396888046_r8 &
,a52 = 0.36597075368373_r8 &
,a53 = 0.08906615686702_r8 &
,a54 = 0.07483912056879_r8
real ( kind = r8 ), parameter, dimension(4) :: qs = [ 2.12709852335625_r8 &
,2.73245878238737_r8 &
,11.22760917474960_r8 &
,13.36199560336697_r8 ]
real ( kind = r8 ) :: qoh
real ( kind = r8 ) :: warray(n,4)
integer (kind = i8) :: i
qoh = q / h
do i =1,4
warray(:,i) = gaussian(n)*sqrt(qs(i)*qoh)
enddo
!t1 = t
x1 = x
k1 = h * ( fi ( x1 ) + gi ( x1 ) * warray(:,1) )
!t2 = t1 + a21 * h
x2 = x1 + a21 * k1
k2 = h * ( fi ( x2 ) + gi ( x2 ) * warray(:,2) )
!t3 = t1 + ( a31 + a32 )* h
x3 = x1 + a31 * k1 + a32 * k2
k3 = h * ( fi ( x3 ) + gi ( x3 ) * warray(:,3) )
!t4 = t1 + ( a41 + a42 + a43 ) * h
x4 = x1 + a41 * k1 + a42 * k2 + a43 * k3 ! the a43 * k3 seems need to be added.
k4 = h * ( fi ( x4 ) + gi ( x4 ) * warray(:,4) )
xstar = x1 + a51 * k1 + a52 * k2 + a53 * k3 + a54 * k4
return
end subroutine rk4_ti_step_vec_mod
An improved version can be like below ( mostly suggested by veryreverie from stackoverflow,
algorithm - Is there room to further optimize the stochastic_rk Fortran 90 code? - Stack Overflow),
subroutine rk4_ti_step_vec_mod1 ( x, t, h, q, fi_vec, gi_vec, n, xstar )
use random
implicit none
interface !! Declare function
function fi_vec ( x ) result (f)
use constants
real ( kind = r8 ) :: f(2)
real ( kind = r8 ) :: x(2)
end function
function gi_vec ( x ) result (g)
use constants
real ( kind = r8 ) :: g(2)
real ( kind = r8 ) :: x(2)
end function
end interface
integer(kind = i8), intent(in) :: n
real(kind=r8), intent(in) :: x(n)
real(kind = r8), intent(in) :: t
real(kind = r8), intent(in) :: h
real(kind = r8), intent(in) :: q
!real(kind = r8), external :: fi_vec
!real(kind = r8), external :: gi_vec
real(kind = r8), intent(out) :: xstar(n)
real ( kind = r8 ), parameter :: alphas(4) = [ 0.47012396888046_r8 &
,0.36597075368373_r8 &
,0.08906615686702_r8 &
,0.07483912056879_r8 ]
real(kind = r8), parameter :: as(4,4) = reshape([ &
& 0.0_r8, 0.0_r8, 0.0_r8, 0.0_r8, &
& 2.71644396264860_r8, 0.0_r8, 0.0_r8, 0.0_r8, &
& -6.95653259006152_r8, 0.78313689457981_r8, 0.0_r8, 0.0_r8, &
& 0.0_r8, 0.48257353309214_r8, 0.26171080165848_r8, 0.0_r8 ], [4,4])
real(kind = r8), parameter :: qs(4) = [ 2.12709852335625_r8, &
& 2.73245878238737_r8, &
& 11.22760917474960_r8, &
& 13.36199560336697_r8 ]
real(kind = r8) :: ks(n,4)
real(kind = r8) :: ts(n,4)
real(kind = r8) :: ws(n,4)
real(kind = r8) :: xs(n,4)
real(kind = r8) :: warray(n,4)
real(kind = r8) :: ak(n)
integer(kind = i8) :: j,i
real(kind = r8) :: qoh
qoh = q / h
xstar = x
do j=1,4
warray(:,j) = gaussian(n)*sqrt(qs(j)*qoh)
!ts(j) = t + sum(as(:j-1,j)) * h
do concurrent (i=1:n)
ak(i)=dot_product(as(:j-1,j), ks(i,:j-1))
enddo
!ak=zero
!do i=1,j-1
! ak = ak + as(i,j)*ks(:,i)
!enddo
xs(:,j) = x + ak
ks(:,j) = h * ( fi_vec(xs(:,j)) + gi_vec(xs(:,j))*warray(:,j) )
xstar = xstar + alphas(j)*ks(:,j)
enddo
return
end subroutine rk4_ti_step_vec_mod1
Now, just a simple question, has anyone used Kasdin’s method?
How is it compared with those SDEs solvers in the differentialequations.jl?
Thank you very much!
PS
I used Kasdin’s method in Fortran with very a simple model for just one SDE, f=-x, g=1. Time is in [0,1], x(0)=20. As a bench mark test, I set step size = 1/1000, and run it for 10000 times. So totally call John’s RK subroutine for 10^7 times, and it took my laptop 1.1 seconds using Fortran.
subroutine rk4_ti_step ( x, t, h, q, fi, gi, seed, xstar )
!*****************************************************************************80
!
!! RK4_TI_STEP takes one step of a stochastic Runge Kutta scheme.
!
! Discussion:
!
! The Runge-Kutta scheme is fourth-order, and suitable for time-invariant
! systems in which F and G do not depend explicitly on time.
!
! d/dx X(t,xsi) = F ( X(t,xsi) ) + G ( X(t,xsi) ) * w(t,xsi)
!
! Licensing:
!
! This code is distributed under the GNU LGPL license.
!
! Modified:
!
! 20 June 2010
!
! Author:
!
! John Burkardt
!
! Reference:
!
! Jeremy Kasdin,
! Runge-Kutta algorithm for the numerical integration of
! stochastic differential equations,
! Journal of Guidance, Control, and Dynamics,
! Volume 18, Number 1, January-February 1995, pages 114-120.
!
! Jeremy Kasdin,
! Discrete Simulation of Colored Noise and Stochastic Processes
! and 1/f^a Power Law Noise Generation,
! Proceedings of the IEEE,
! Volume 83, Number 5, 1995, pages 802-827.
!
! Parameters:
!
! Input, real ( kind = 8 ) X, the value at the current time.
!
! Input, real ( kind = 8 ) T, the current time.
!
! Input, real ( kind = 8 ) H, the time step.
!
! Input, real ( kind = 8 ) Q, the spectral density of the input white noise.
!
! Input, external real ( kind = 8 ) FI, the name of the deterministic
! right hand side function.
!
! Input, external real ( kind = 8 ) GI, the name of the stochastic
! right hand side function.
!
! Input/output, integer ( kind = 4 ) SEED, a seed for the random
! number generator.
!
! Output, real ( kind = 8 ) XSTAR, the value at time T+H.
!
implicit none
real ( kind = 8 ) a21
real ( kind = 8 ) a31
real ( kind = 8 ) a32
real ( kind = 8 ) a41
real ( kind = 8 ) a42
real ( kind = 8 ) a43
real ( kind = 8 ) a51
real ( kind = 8 ) a52
real ( kind = 8 ) a53
real ( kind = 8 ) a54
real ( kind = 8 ), external :: fi
real ( kind = 8 ), external :: gi
real ( kind = 8 ) h
real ( kind = 8 ) k1
real ( kind = 8 ) k2
real ( kind = 8 ) k3
real ( kind = 8 ) k4
real ( kind = 8 ) q
real ( kind = 8 ) q1
real ( kind = 8 ) q2
real ( kind = 8 ) q3
real ( kind = 8 ) q4
real ( kind = 8 ) r8_normal_01
integer ( kind = 4 ) seed
real ( kind = 8 ) t
real ( kind = 8 ) t1
real ( kind = 8 ) t2
real ( kind = 8 ) t3
real ( kind = 8 ) t4
real ( kind = 8 ) w1
real ( kind = 8 ) w2
real ( kind = 8 ) w3
real ( kind = 8 ) w4
real ( kind = 8 ) x
real ( kind = 8 ) x1
real ( kind = 8 ) x2
real ( kind = 8 ) x3
real ( kind = 8 ) x4
real ( kind = 8 ) xstar
a21 = 2.71644396264860D+00
a31 = - 6.95653259006152D+00
a32 = 0.78313689457981D+00
a41 = 0.0D+00
a42 = 0.48257353309214D+00
a43 = 0.26171080165848D+00
a51 = 0.47012396888046D+00
a52 = 0.36597075368373D+00
a53 = 0.08906615686702D+00
a54 = 0.07483912056879D+00
q1 = 2.12709852335625D+00
q2 = 2.73245878238737D+00
q3 = 11.22760917474960D+00
q4 = 13.36199560336697D+00
t1 = t
x1 = x
w1 = r8_normal_01 ( seed ) * sqrt ( q1 * q / h )
k1 = h * fi ( x1 ) + h * gi ( x1 ) * w1
t2 = t1 + a21 * h
x2 = x1 + a21 * k1
w2 = r8_normal_01 ( seed ) * sqrt ( q2 * q / h )
k2 = h * fi ( x2 ) + h * gi ( x2 ) * w2
t3 = t1 + a31 * h + a32 * h
x3 = x1 + a31 * k1 + a32 * k2
w3 = r8_normal_01 ( seed ) * sqrt ( q3 * q / h )
k3 = h * fi ( x3 ) + h * gi ( x3 ) * w3
t4 = t1 + a41 * h + a42 * h + a43 * h
x4 = x1 + a41 * k1 + a42 * k2
w4 = r8_normal_01 ( seed ) * sqrt ( q4 * q / h )
k4 = h * fi ( x4 ) + h * gi ( x4 ) * w4
xstar = x1 + a51 * k1 + a52 * k2 + a53 * k3 + a54 * k4
return
end