使用Fortran 90在Fortran中使用梯形的6维积分

时间:2012-05-23 17:13:58

标签: fortran fortran90 integral

我需要以有效的方式在Fortran 90中使用Trapezoid计算六维积分。以下是我需要做的一个例子:

The integral expression

其中F是要在x1到x6上积分的数值(例如非分析)函数,变量。我最初编写了一个一维子程序:

  SUBROUTINE trapzd(f,mass,x,nstep,deltam) 
      INTEGER nstep,i
      DOUBLE PRECISION mass(nstep+1),f(nstep+1),x,deltam
      x=0.d0
      do i=1,nstep
          x=x+deltam*(f(i)+f(i+1))/2.d0
      end do
  return
  END

这似乎与一个维度很好,但是,我不知道如何将其扩展到六个维度。我可以重复使用这六次,每次维度一次,还是我应该写一个新的子程序?

如果您使用其他语言(如Python,MATLAB或Java)拥有完全编码(无库/ API使用)版本,我会很高兴看一看并获得一些想法

P.S。这不是学校的功课。我是生物医学博士生,这是我干细胞活动建模研究的一部分。我没有深刻的编码和数学背景。

提前谢谢。

4 个答案:

答案 0 :(得分:3)

您可以查看GNU科学图书馆(GSL)的蒙特卡洛集成章节。哪个都是库,因为它是开源的,你可以学习的源代码。

答案 1 :(得分:1)

查看numerical recipes for C的第4.6节。

  1. 第一步是使用对称性和分析依赖性来减少问题。
  2. 第二步是将解决方案链接起来:

    f2(x2,x3,..,x6) = Integrate(f(x,x2,x3..,x6),x,1,x1end)
    f3(x3,x4,..,x6) = Integrate(f2(x,x3,..,x6),x,1,x2end)
    f4(x4,..,x6) = ...
    
    f6(x6) = Integrate(I4(x,x6),x,1,x5end)        
    result = Integrate(f6(x),x,1,x6end)
    

答案 2 :(得分:0)

直接评估多个积分在计算上具有挑战性。使用蒙特卡罗可能更好,也许使用重要性抽样。然而,对于方法的验证,蛮力直接整合有时是有意义的。

我使用的集成例程是" QuadMo"由Luke Mo撰写,大约1970年。我把它递归并放在一个模块中。需要QuadMo细化网格才能获得所需的集成精度。这是一个使用QuadMo进行n维积分的程序。

使用G95编译,使用以0.5为中心的高斯的程序验证,在nDim的所有维度中使用SD 0.1,最多6个。它会在几秒钟内运行。

  nDim ans       expected       nlvl
     1 0.249     0.251      2
     2 6.185E-02 6.283E-02  2  2
     3 1.538E-02 1.575E-02  2  2  2
     4 3.826E-03 3.948E-03  2  2  2  2
     5 9.514E-04 9.896E-04  2  2  2  2  2
     6 2.366E-04 2.481E-04  2  2  2  2  2  2

以下是代码:

!=======================================================================
      module QuadMo_MOD
      implicit none
      integer::QuadMo_MinLvl=6,QuadMo_MaxLvl=24
      integer,dimension(:),allocatable::QuadMo_nlvlk
      real*8::QuadMo_Tol=1d-5
      real*8,save,dimension(:),allocatable::thet
      integer,save::nDim

      abstract interface
        function QuadMoFunct_interface(thet,k)
           real*8::QuadMoFunct_interface
           real*8,intent(in)::thet
           integer,intent(in),optional::k
        end function
      end interface

      abstract interface
        function MultIntFunc_interface(thet)
           real*8::MultIntFunc_interface
           real*8,dimension(:),intent(in)::thet
        end function
      end interface

      procedure(MultIntFunc_interface),pointer :: stored_func => null()

      contains

!----------------------------------------------------------------------
      recursive function quadMoMult(funct,lower,upper,k) result(ans) 
  ! very powerful integration routine written by Luke Mo
  !  then at the Stanford Linear Accelerator Center circa 1970
  ! QuadMo_Eps is error tolerance  
  ! QuadMo_MinLvl determines initial grid of 2**(MinLvl+1) + 1 points
  !  to avoid missing a narrow peak, this may need to be increased.  
  ! QuadMo_Nlvl returns number of subinterval refinements required beyond
  ! QuadMo_MaxLvl

  ! Modified by making recursive and adding argument k
  !  for multiple integrals (GuthrieMiller@gmail.com)
      real*8::ans
      procedure(QuadMoFunct_interface) :: funct
      real*8,intent(in)::lower,upper
      integer,intent(in),optional::k
      real*8::Middle,Left,Right,eps,est,fLeft,fMiddle,fRight
     & ,fml,fmr,rombrg,coef,estl,estr,estint,area,abarea
      real*8::valint(50,2), Middlex(50), Rightx(50), fmx(50), frx(50)
     & ,fmrx(50), estrx(50), epsx(50)
      integer retrn(50),i,level
      level = 0
      QuadMo_nlvlk(k) = 0
      abarea = 0
      Left = lower
      Right = upper
      if(present(k))then
         fLeft = funct(Left,k)
         fMiddle = funct((Left+Right)/2,k)
         fRight = funct(Right,k)
      else
         fLeft = funct(Left)
         fMiddle = funct((Left+Right)/2)
         fRight = funct(Right)
      endif
      est = 0
      eps = QuadMo_Tol
  100 level = level+1
      Middle = (Left+Right)/2
      coef = Right-Left
      if(coef.ne.0) go to 150
      rombrg = est
      go to 300
  150 continue
      if(present(k))then
         fml = funct((Left+Middle)/2,k)
         fmr = funct((Middle+Right)/2,k)
      else
         fml = funct((Left+Middle)/2)
         fmr = funct((Middle+Right)/2)
      endif
      estl = (fLeft+4*fml+fMiddle)*coef
      estr = (fMiddle+4*fmr+fRight)*coef
      estint = estl+estr
      area= abs(estl)+ abs(estr)
      abarea=area+abarea- abs(est)
      if(level.ne.QuadMo_MaxLvl) go to 200
      QuadMo_nlvlk(k) = QuadMo_nlvlk(k)+1
      rombrg = estint
      go to 300
  200 if(( abs(est-estint).gt.(eps*abarea)).or.
     1(level.lt.QuadMo_MinLvl))  go to 400
      rombrg = (16*estint-est)/15
  300 level = level-1
      i = retrn(level)
      valint(level, i) = rombrg
      go to (500, 600), i
  400 retrn(level) = 1
      Middlex(level) = Middle
      Rightx(level) = Right
      fmx(level) = fMiddle
      fmrx(level) = fmr
      frx(level) = fRight
      estrx(level) = estr
      epsx(level) = eps
      eps = eps/1.4d0
      Right = Middle
      fRight = fMiddle
      fMiddle = fml
      est = estl
      go to 100
  500 retrn(level) = 2
      Left = Middlex(level)
      Right = Rightx(level)
      fLeft = fmx(level)
      fMiddle = fmrx(level)
      fRight = frx(level)
      est = estrx(level)
      eps = epsx(level)
      go to 100
  600 rombrg = valint(level,1)+valint(level,2)
      if(level.gt.1) go to 300
      ans  = rombrg /12
      end function quadMoMult
!-----------------------------------------------------------------------
      recursive function MultInt(k,func) result(ans)
      ! MultInt(nDim,func) returns multi-dimensional integral from 0 to 1
      !  in all dimensions of function func
      !  variable QuadMo_Mod: nDim needs to be set initially to number of dimensions      
      procedure(MultIntFunc_interface) :: func
      real*8::ans
      integer,intent(in)::k

      stored_func => func

      if(k.eq.nDim)then
         if(allocated(thet))deallocate(thet)
         allocate (thet(nDim))
         if(allocated(QuadMo_nlvlk))deallocate(QuadMo_nlvlk)
         allocate(QuadMo_nlvlk(nDim))
      endif

      if(k.eq.0)then
         ans=func(thet)
         return
      else
         ans=QuadMoMult(MultIntegrand,0d0,1d0,k)
      endif
      end function MultInt
!-----------------------------------------------------------------------
      recursive function MultIntegrand(thetARG,k) result(ans)
      real*8::ans
      real*8,intent(in)::thetARG
      integer,optional,intent(in)::k

      if(present(k))then
         thet(k)=thetARG
      else
         write(*,*)'MultIntegrand: not expected, k not present!'
         stop
      endif
      ans=MultInt(k-1,stored_func)
      end function MultIntegrand
!-----------------------------------------------------------------------
      end module QuadMo_MOD
!=======================================================================
      module test_MOD
      use QuadMo_MOD
      implicit none

      contains

!----------------------------------------------------------------------- 
      real*8 function func(thet) ! multidimensional function
      ! this is the function defined in nDim dimensions
      !  in this case a Gaussian centered at 0.5 with SD 0.1
      real*8,intent(in),dimension(:)::thet
      func=exp(-sum(((thet-5d-1)/1d-1)
     & *((thet-5d-1)/1d-1))/2)
      end function func
!----------------------------------------------------------------------- 
      end module test_MOD 
!=======================================================================
      ! test program to evaluate multiple integrals 
      use test_MOD
      implicit none 
      real*8::ans 

      ! these values are set for speed, not accuracy
      QuadMo_MinLvl=2
      QuadMo_MaxLvl=3
      QuadMo_Tol=1d-1

      write(*,*)'     nDim ans       expected       nlvl'
      do nDim=1,6
         ! expected answer is (0.1 sqrt(2pi))**nDim
         ans=MultInt(nDim,func)
         write(*,'(i10,2(1pg10.3),999(i3))')nDim,ans,(0.250663)**nDim
     &    ,QuadMo_nlvlk
      enddo
      end
!-----------------------------------------------------------------------

答案 3 :(得分:0)

double MultInt(int k);
double MultIntegrand(double thetARG, int k);
double quadMoMult(double(*funct)(double, int), double lower, double upper, int k);
double funkn(double *thet);

int QuadMo_MinLvl = 2;
int  QuadMo_MaxLvl = 3;
double QuadMo_Tol = 0.1;
int *QuadMo_nlvlk;
double *thet;
int nDim;


//double MultInt(int k, double(*func)(double *))
double MultInt(int k)
{

//MultInt(nDim, func) returns multi - dimensional integral from 0 to 1
//in all dimensions of function func
    double ans;

    if (k == 0) 
    {
        ans = funkn(thet);
    }
    else
    {
        ans = quadMoMult(MultIntegrand, 0.0, 1.0, k); //limits hardcoded here
    }
    return ans;
}


double MultIntegrand(double thetARG, int k)
{
    double ans;

    if (k > 0)
        thet[k] = thetARG;
    else
        printf("\n***MultIntegrand: not expected, k not present!***\n");

        //Recursive call
    //ans = MultInt(k - 1, func);
    ans = MultInt(k - 1);
    return ans;
}


double quadMoMult(double(*funct)(double, int), double lower, double upper, int k)
{
    //Integration routine written by Luke Mo
    //Stanford Linear Accelerator Center circa 1970
    //QuadMo_Eps is error tolerance
    //QuadMo_MinLvl determines initial grid of 2 * *(MinLvl + 1) + 1 points
    //to avoid missing a narrow peak, this may need to be increased.
    //QuadMo_Nlvl returns number of subinterval refinements required beyond
    //QuadMo_MaxLvl

    //Modified by making recursive and adding argument k
    //for multiple integrals(GuthrieMiller@gmail.com)

    double ans;
    double Middle, Left, Right, eps, est, fLeft, fMiddle, fRight;
    double fml, fmr, rombrg, coef, estl, estr, estint, area, abarea;
    double valint[51][3], Middlex[51], Rightx[51], fmx[51], frx[51];  //Jack up arrays
    double fmrx[51], estrx[51], epsx[51];
    int retrn[51];
    int i, level;

    level = 0;
    QuadMo_nlvlk[k] = 0;
    abarea = 0.0;
    Left = lower;
    Right = upper;
    if (k > 0)
    {
        fLeft = funct(Left, k);
        fMiddle = funct((Left + Right) / 2, k);
        fRight = funct(Right, k);
    }
    else
    {
        fLeft = funct(Left,0);
        fMiddle = funct((Left + Right) / 2,0);
        fRight = funct(Right,0);
    }
    est = 0.0;
    eps = QuadMo_Tol;
l100:
    level = level + 1;
    Middle = (Left + Right) / 2;
    coef = Right - Left;
    if (coef != 0.0)
        goto l150;
    rombrg = est;
    goto l300;
l150:
    if (k > 0)
    {
        fml = funct((Left + Middle) / 2.0, k);
        fmr = funct((Middle + Right) / 2.0, k);
    }
    else
    {
        fml = funct((Left + Middle) / 2.0, 0);
        fmr = funct((Middle + Right) / 2.0, 0);
    }
    estl = (fLeft + 4 * fml + fMiddle)*coef;
    estr = (fMiddle + 4 * fmr + fRight)*coef;
    estint = estl + estr;
    area = abs(estl) + abs(estr);
    abarea = area + abarea - abs(est);
    if (level != QuadMo_MaxLvl)
        goto l200;
    QuadMo_nlvlk[k] = QuadMo_nlvlk[k] + 1;
    rombrg = estint;
    goto l300;
l200:
    if ((abs(est - estint) > (eps*abarea)) || (level < QuadMo_MinLvl))
        goto l400;
    rombrg = (16 * estint - est) / 15;
l300:
    level = level - 1;
    i = retrn[level];
    valint[level][i] = rombrg;
    if (i == 1)
        goto l500;
    if (i == 2)
        goto l600;
l400:
    retrn[level] = 1;
    Middlex[level] = Middle;
    Rightx[level] = Right;
    fmx[level] = fMiddle;
    fmrx[level] = fmr;
    frx[level] = fRight;
    estrx[level] = estr;
    epsx[level] = eps;
    eps = eps / 1.4;
    Right = Middle;
    fRight = fMiddle;
    fMiddle = fml;
    est = estl;
    goto l100;
l500:
    retrn[level] = 2;
    Left = Middlex[level];
    Right = Rightx[level];
    fLeft = fmx[level];
    fMiddle = fmrx[level];
    fRight = frx[level];
    est = estrx[level];
    eps = epsx[level];
    goto l100;
l600:
    rombrg = valint[level][1] + valint[level][2];
    if (level > 1)
        goto l300;
    ans = rombrg / 12.0;
    return ans;
}


double funkn(double *thet)
{
    //in this case a Gaussian centered at 0.5 with SD 0.1
    double *sm;
    double sum;
    sm = new double[nDim];
    sum = 0.0;

    for (int i = 1; i <= nDim; i++) 
    {
        sm[i] = (thet[i] - 0.5) / 0.1;
        sm[i] *= sm[i];
        sum = sum + sm[i];
    }
    return exp(-sum / 2.0);
}


int main() {

    double ans;

    printf("\nnDim ans       expected       nlvl\n");
    for (nDim = 1; nDim <= 6; nDim++)
    {
        //expected answer is(0.1 sqrt(2pi))**nDim
        QuadMo_nlvlk = new int[nDim + 1];  //array for x values
        thet = new double[nDim + 1];  //array for x values
        ans = MultInt(nDim);
        printf("\n %d %f %f ", nDim, ans, pow((0.250663),nDim));
        for (int j=1; j<=nDim;  j++)
            printf("   %d ", QuadMo_nlvlk[nDim]);
        printf("\n");
    }

    return 0;

}

全局声明相关参数

int QuadMo_MinLvl = 2;
int  QuadMo_MaxLvl = 3;
double QuadMo_Tol = 0.1;
int *QuadMo_nlvlk;
double *thet;
int nDim;

此编码比模糊的过时的fortran编码清晰得多,可以对积分限制和参数进行一些调整!

有更好的算法可以与自适应技术配合使用,并且可以处理表面上的奇异点等。