将fortran程序转换为从R调用的子程序

时间:2016-01-05 19:42:56

标签: r fortran

我之前发布了一个关于尝试从统计文件中重现样本大小的问题here的问题。作者向我提供了本文中使用的代码,该代码是Fortran程序。我想使用我工作团队使用的R包调用此代码。为此,我需要将此Fortran程序转换为子例程。这是我对Fortran的第一次体验,我似乎以某种方式破坏了程序并得到了错误的数字,当我更改输入时这些数字没有改变。我还应该注意,我必须对作者提供的原始代码进行一些小的更改,以使程序正常工作。

"原作"编译后生成正确结果然后运行的程序:

    PROGRAM Exact_Mid_P_binomial_sample_size
          real(8) probA,probB,part1,part2,part3,part4
          real(8) totprA,totprB,factt, resp
!       character  resp
1       format ('Enter proportion       ',$)
2       format ('Enter error limit      ',$)
3       format ('Enter confidence level ',$)
4       format ('Calculated sample size is   ',i6)
5       format ('Exact mid-P with ',f7.5,' 2-tail probability')
6       format ('Sorry, unable to mathmatically solve this problem.')
7       format ('Reported sample size is not accuarate.')
8       format ('Enter q to quit  ',$)
9       format ('Actual limits for distribution  ',f5.3,' - ',f5.3)
        print *, 'Exact sampleroportions'
        print *, 'Using Mid-P methods'
        print *, 'Geoff Fosgate DVM PhD'
        print *, 'College of Veterinary Medicine'
        print *, 'Texas A&M University'
        print *
10      print *
        print 1
          read *, prop1
        print 2
          read *,range
        print 3
          read *,conlev
        print *
!           Convert proportions less than 0.5 for algorithm
        if (prop1 .lt. 0.5) then
            prop = 1 - prop1
            nprop = 1
              else
                prop = prop1
                nprop = 0
              end if
        slimit = max ((prop - range) , 0.0001)
        supper = min ((prop + range) , 0.9999)
!           Probabilities cannot be calculated for p=0 and p=1
        alpha = (1 - conlev)
        if (alpha .gt. 1.0) go to 10
        if (alpha .lt. 0.0) go to 10
        if (prop .gt. 1.0) go to 10
        if (prop .lt. 0.0) go to 10
        numbr = (1 / (1 - prop)) - 1
!           Define and initialize variables
!             Note names of variables based on Fortran 77 rules
!             Starting sample size is based on estimated proportion
!             Resulting sample size must be large enough to obtain this proportion
100     numbr = numbr + 1
        numx = (numbr * prop) + 0.001
!             This is the number of binomial "successes" resulting in the proportion
        if (numx .eq. numbr) go to 100
        if (numx .lt. 1) go to 100
        totprA = slimit**numbr
        totprB = supper**numbr
          do 130 loop1 = numx, (numbr - 1)
!               Must initialize variables within loop
            factt = 1.0
            probA = 0.0
            probB = 0.0
            part1 = 0.0
            part2 = 0.0
            part3 = 0.0
            part4 = 0.0
!                Start loop to calculate factorial component of binomial probability
!                Note that complete factorial calculations not necessary due to cancellations
            do 110 loop2 = (loop1 + 1) , numbr
               factt = factt * (loop2) / (numbr - (loop2 - 1))
110         continue
!                Calculate probability for this particular number of successes
!                Total probability is a running total
!                Note that real variables must have high precision and be comprised
!                of multiple bytes because factorial component can be very large
!                and exponentiated component can be very small
!                Program will fail if any component is recognized as zero or infinity
            part1 = slimit**loop1
              part2 = (1.0-slimit)**(numbr-loop1)
            part3 = supper**loop1
              part4 = (1.0-supper)**(numbr-loop1)
            if (part1 .eq. 0.0) part1 = 1.0D-307
            if (part2 .eq. 0.0) part2 = 1.0D-307
            if (part3 .eq. 0.0) part3 = 1.0D-307
            if (part4 .eq. 0.0) part4 = 1.0D-307
            if (factt .gt. 1.0D308) factt = 1.0D308
            probA = part1 * part2 * factt
            probB = part3 * part4 * factt
            if (loop1 .eq. numx)  then
                totprA = totprA + (0.5 * probA)
                totprB = totprB + (0.5 * probB)
                else
                    totprA = totprA + probA
                    totprB = totprB + probB
                end if
            if (probA .eq. 0.0) then 
                    print 6
                    print 7
                    print *
                    go to 150
                end if
            if (probB .eq. 0.0) then
                    print 6
                    print 7
                    print *
                    go to 150
                end if
130       continue
140     if ((totprA + (1 - totprB)) .gt. alpha) go to 100
!             go to beginning and increase sample size by 1 if have not
!             reached specified level of confidence
150         if (nprop .eq. 1) then
                print 4,numbr
                print 9, (1-supper),(1-slimit)
            else
                print 4,numbr
                print 9, slimit,supper
            end if
            if (totprA+(1-totprB) .lt. alpha) print 5,(totprA+(1-totprB))
            print *
            print 8
            result = resp
!           print *
!       if (resp .ne. 'q') go to 10
      print *
      print *
999   end

上面的程序变成了一个子程序:

       subroutine midpss(prop1, range, conlev, numbr)

       integer numbr, nprop
       real(8) prop1, range, conlev, prop, slimit, supper
       real(8) probA,probB,part1,part2,part3,part4,factt
       real(8) totprA,totprB, resp
c         character  resp

c           Convert proportions less than 0.5 for algorithm
           if (prop1 .lt. 0.5) then
              prop = 1 - prop1
              nprop = 1
             else
                prop = prop1
                nprop = 0
             end if
         slimit = max ((prop - range) , 0.0001)
         supper = min ((prop + range) , 0.9999)

           numbr = (1 / (1 - prop)) - 1
c           Define and initialize variables
c             Note names of variables based on Fortran 77 rules
c             Starting sample size is based on estimated proportion
c             Resulting sample size must be large enough to obtain this proportion
100      numbr = numbr + 1
           numx = (numbr * prop) + 0.001
c             This is the number of binomial "successes" resulting in the proportion
           if (numx .eq. numbr) go to 100
           if (numx .lt. 1) go to 100
           totprA = slimit**numbr
           totprB = supper**numbr
           do 130 loop1 = numx, (numbr - 1)
c               Must initialize variables within loop
           factt = 1.0
           probA = 0.0
             probB = 0.0
           part1 = 0.0
           part2 = 0.0
             part3 = 0.0
             part4 = 0.0
c                Start loop to calculate factorial component of binomial probability
c                Note that complete factorial calculations not necessary due to cancellations
         do 110 loop2 = (loop1 + 1) , numbr
         factt = factt * (loop2) / (numbr - (loop2 - 1))
110    continue
c                Calculate probability for this particular number of successes
c                Total probability is a running total
c                Note that real variables must have high precision and be comprised
c                of multiple bytes because factorial component can be very large
c                and exponentiated component can be very small
c                Program will fail if any component is recognized as zero or infinity
           part1 = slimit**loop1
       part2 = (1.0-slimit)**(numbr-loop1)
           part3 = supper**loop1
       part4 = (1.0-supper)**(numbr-loop1)
             if (part1 .eq. 0.0) part1 = 1.0D-307
             if (part2 .eq. 0.0) part2 = 1.0D-307
             if (part3 .eq. 0.0) part3 = 1.0D-307
             if (part4 .eq. 0.0) part4 = 1.0D-307
             if (factt .gt. 1.0D308) factt = 1.0D308
         probA = part1 * part2 * factt
             probB = part3 * part4 * factt
           if (loop1 .eq. numx)  then
                totprA = totprA + (0.5 * probA)
                totprB = totprB + (0.5 * probB)
             else
              totprA = totprA + probA
              totprB = totprB + probB
             end if

130      continue
140      if ((totprA + (1 - totprB)) .gt. alpha) go to 100


       return
       end

要编译原始程序,我在命令行中运行它:

gfortran midpSS_original.f

要编译子例程,我在命令行中运行它:

R CMD SHLIB midpSS_subroutine.f

然后从R控制台运行:

> dyn.load("midpSS_subroutine.dll")
> is.loaded("midpss")
[1] TRUE
> .Fortran("midpss", prop1=as.numeric(0.9), range=as.numeric(0.1), conlev=as.numeric(0.90), numbr=as.integer(0)) # numbr should be 29
$prop1
[1] 0.9

$range
[1] 0.1

$conlev
[1] 0.9

$numbr
[1] 2091

> .Fortran("midpss", prop1=as.numeric(0.9), range=as.numeric(0.1), conlev=as.numeric(0.95), numbr=as.integer(0)) # numbr should be 47
$prop1
[1] 0.9

$range
[1] 0.1

$conlev
[1] 0.95

$numbr
[1] 2091

在第一次调用子程序时,numbr应该是29.在第二次调用中,numbr应该是47.在编译"原始"时,结果是正确的。 fortran程序并运行相同的参数。我无法弄清楚这里发生了什么。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

以下块检查输入数据:

    if (alpha .gt. 1.0) go to 10
if (alpha .lt. 0.0) go to 10
if (prop .gt. 1.0) go to 10
if (prop .lt. 0.0) go to 10

此块使代码进入输入位置以再次读取输入数据。您可能会得到错误的结果,因为子程序中的输入数据可能超出范围。请先检查一下!

另一个缺失的块是

if (probA .eq. 0.0) then 
  print 6
  print 7
  print *
 go to 150
end if

if (probB .eq. 0.0) then
 print 6
 print 7
 print *
 go to 150
end if

和标签150。你确定省略这两个块没有改变原始代码的准确性和功能吗?

将程序转换为子程序非常简单。主要步骤如下:

  1. 将语法program转换为subroutine
  2. 将语法end program转换为end subroutine
  3. 在子程序名称后加上一个括号'()'
  4. 将程序的所有输入和输出放在括号'()'
  5. implicit noneprogram

    之后撰写subroutine也是一种很好的做法

    我将程序转换为我自己的子程序。请注意,子例程末尾的result = resp可能是错误的,因为据我所知,命令'result'是针对函数而不是子例程。

    在子程序中,您必须指定输出。由于我不知道哪个变量是输出,我没有在子例程名称后面的括号'()'中包含该变量。请写在那里。 我的子程序如下:

    !    PROGRAM Exact_Mid_P_binomial_sample_size
        subroutine midpss (prop1, range, conlev, output)
        implicit none
              real(8) probA,probB,part1,part2,part3,part4
              real(8) totprA,totprB,factt, resp
              integer numbr, nprop,loop1,loop2
              real(8) prop1,prop,range,conlev,slimit,supper,alpha,numx,output
    
    !       character  resp
    !1       format ('Enter proportion       ',$)
    !2       format ('Enter error limit      ',$)
    !3       format ('Enter confidence level ',$)
    4       format ('Calculated sample size is   ',i6)
    5       format ('Exact mid-P with ',f7.5,' 2-tail probability')
    6       format ('Sorry, unable to mathmatically solve this problem.')
    7       format ('Reported sample size is not accuarate.')
    8       format ('Enter q to quit  ',$)
    9       format ('Actual limits for distribution  ',f5.3,' - ',f5.3)
            print *, 'Exact sampleroportions'
            print *, 'Using Mid-P methods'
            print *, 'Geoff Fosgate DVM PhD'
            print *, 'College of Veterinary Medicine'
            print *, 'Texas A&M University'
            print *
    !10      print *
    !        print 1
    !          read *, prop1
    !        print 2
    !         read *, range
    !       print 3
    !         read *, conlev
    !       print *
    !           Convert proportions less than 0.5 for algorithm
            if (prop1 .lt. 0.5) then
                prop = 1 - prop1
                nprop = 1
                  else
                    prop = prop1
                    nprop = 0
                  end if
            slimit = max ((prop - range) , 0.0001)
            supper = min ((prop + range) , 0.9999)
    !           Probabilities cannot be calculated for p=0 and p=1
            alpha = (1 - conlev)
    !        if (alpha .gt. 1.0) go to 10
    !        if (alpha .lt. 0.0) go to 10
    !        if (prop .gt. 1.0) go to 10
    !        if (prop .lt. 0.0) go to 10
            numbr = (1 / (1 - prop)) - 1
    !           Define and initialize variables
    !             Note names of variables based on Fortran 77 rules
    !             Starting sample size is based on estimated proportion
    !             Resulting sample size must be large enough to obtain this proportion
    100     numbr = numbr + 1
            numx = (numbr * prop) + 0.001
    !             This is the number of binomial "successes" resulting in the proportion
            if (numx .eq. numbr) go to 100
            if (numx .lt. 1) go to 100
            totprA = slimit**numbr
            totprB = supper**numbr
              do 130 loop1 = numx, (numbr - 1)
    !               Must initialize variables within loop
                factt = 1.0
                probA = 0.0
                probB = 0.0
                part1 = 0.0
                part2 = 0.0
                part3 = 0.0
                part4 = 0.0
    !                Start loop to calculate factorial component of binomial probability
    !                Note that complete factorial calculations not necessary due to cancellations
                do 110 loop2 = (loop1 + 1) , numbr
                   factt = factt * (loop2) / (numbr - (loop2 - 1))
    110         continue
    !                Calculate probability for this particular number of successes
    !                Total probability is a running total
    !                Note that real variables must have high precision and be comprised
    !                of multiple bytes because factorial component can be very large
    !                and exponentiated component can be very small
    !                Program will fail if any component is recognized as zero or infinity
                part1 = slimit**loop1
                  part2 = (1.0-slimit)**(numbr-loop1)
                part3 = supper**loop1
                  part4 = (1.0-supper)**(numbr-loop1)
                if (part1 .eq. 0.0) part1 = 1.0D-307
                if (part2 .eq. 0.0) part2 = 1.0D-307
                if (part3 .eq. 0.0) part3 = 1.0D-307
                if (part4 .eq. 0.0) part4 = 1.0D-307
                if (factt .gt. 1.0D308) factt = 1.0D308
                probA = part1 * part2 * factt
                probB = part3 * part4 * factt
                if (loop1 .eq. numx)  then
                    totprA = totprA + (0.5 * probA)
                    totprB = totprB + (0.5 * probB)
                    else
                        totprA = totprA + probA
                        totprB = totprB + probB
                    end if
                if (probA .eq. 0.0) then 
                        print 6
                        print 7
                        print *
                        go to 150
                    end if
                if (probB .eq. 0.0) then
                        print 6
                        print 7
                        print *
                        go to 150
                    end if
    130       continue
    140     if ((totprA + (1 - totprB)) .gt. alpha) go to 100
    !             go to beginning and increase sample size by 1 if have not
    !             reached specified level of confidence
    150         if (nprop .eq. 1) then
                    print 4,numbr
                    print 9, (1-supper),(1-slimit)
                else
                    print 4,numbr
                    print 9, slimit,supper
                end if
                if (totprA+(1-totprB) .lt. alpha) print 5,(totprA+(1-totprB))
                print *
                print 8
                !result = resp
    !           print *
    !       if (resp .ne. 'q') go to 10
    !      output=***     !write the output variable instead of ***
          print *
          print *
    999   end subroutine midpss