R中调用FORTRAN的两种方式

Fortran是最古老的计算机语言之一,是专为数值计算设计的,在过去几十年中积累了大量代码。很多情况下,人们无需用R或Python重新实现fortran中已有的算法, 而是直接调用这些Fortran代码即可。相对其他编程语言,Fortran比较简单,而且很多编译器经过多次优化,代码的执行效率非常高,一般会超过C语言。虽然Fortran已经60岁了,但在涉及高性能计算的很多领域,如天文、物理、气象、统计等学科中仍发挥着重要作用。

Fortran语言的标准包括F77,F90,F95,F2003, F2008等。每一次版本调整, 一般是为语言增加部分新的特性。最常用的Fortran版本为F77和F90。Fortran77版本编写的代码,扩展名为.f, 以固定格式书写;而Fortran90的源代码文件,扩展名一般为f90,以相对自由的格式书写。

本文简要介绍在R中如何调用Fortran77和Fortran90代码,最后介绍在inline程序包中编译和调用fortran代码。

在R中调用Fortran代码的基本流程是:

  1. 先将Fortran用gfortran编译为shared library。 在Windows下,要将fortran源代码编译为动态链接库 dll, 在Linux和Mac下编译为so文件。编译过程可通过cmd或者terminal调用gfortran(或者其他fortran编译器)实现,也可以直接在R中用system()函数调用gfortran编译。
  2. 在R中,用dyn.load()函数加载编译好的bishared library。
  3. 在R中,使用.Fortran()函数调用shared library中的代码,结果将以List的格式返回。

目标:

计算n的阶乘 n!=∏nk=1k∀n≥1 源代码用Fortran写成。

所需软件

  1. R(https://cran.r-project.org/)
  2. Windows下,请安装Rtools,Rtools中包含gfortran编译器,安装时请将Rtools添加到启动 路径,参考 (https://github.com/helixcn/programing_in_r_cn/blob/master/beamer/00_software_installation.pdf )

方法1,编译代码后加载和调用Fortran代码

将Fortran代码编译为share library后,再用R的.Fortran函数调用。

编译和调用Fortran77代码

Fortran 77源代码

1
2
3
4
5
6
7
8
9
C   Content of file factorial.f
C FINDING THE FACTORIAL IN FORTRAN 77
SUBROUTINE FACTO1(N,ANSWER)
INTEGER N, ANSWER, I
ANSWER = 1
DO 100 I = 2,N
ANSWER = ANSWER * I
100 CONTINUE
END

Mac和Linux下的R脚本

1
2
3
4
setwd("/Users/jinlong/Desktop/fortran_example/fortran77/")
system("R CMD SHLIB factorial.f")
dyn.load("factorial.so")
.Fortran("facto1",n=as.integer(5),answer=as.integer(1))

Windows下的R脚本

1
2
3
4
setwd("C:\Users\jlzhang\Desktop\fortran77")
system("Rcmd SHLIB factorial.f")
dyn.load("factorial.dll")
.Fortran("facto1",n=as.integer(5),answer=as.integer(1))

编译和调用Fortran90代码

Fortran90源代码

1
2
3
4
5
6
7
8
9
10
! Content of file factorial.f90
! Finding the factorial in Fortran 90
subroutine facto2(n,answer)
implicit none
integer n, answer, i
answer = 1
do i = 2,n
answer = answer * i
end do
end subroutine facto2

Linux下的R代码

1
2
3
4
setwd("/Users/jinlong/Desktop/fortran_example/fortran90/")
system("R CMD SHLIB factorial.f90")
dyn.load("factorial.so")
.Fortran("facto2",n=as.integer(5),answer=as.integer(1))

Windows下的R代码

1
2
3
4
setwd("C:\Users\jlzhang\Desktop\fortran90")
system("Rcmd SHLIB factorial.f90")
dyn.load("factorial.dll")
.Fortran("facto2", n=as.integer(5), answer=as.integer(1))

方法2,使用inline程序包

1
2
3
4
5
6
7
8
9
10
library(inline)
fcode <- "
integer::i
do i = 2, n(1)
res(1) = res(1) * i
end do
"
fcodefun <- cfunction(signature(n="integer", res = "integer"),
fcode, convention=".Fortran")
fcodefun(n = as.integer(5), res = as.integer(1))

若要将Fortran编写到程序包中, 则需要将fortran源代码放入src文件夹中, 用.Fortran()直接调用fortran中的subroutine。R程序包的源代码中是不允许有已经编译好的可执行程序的, 因为这会造成安全风险以及可移植性的问题。注意,.Fortran()中的参数数据类型,必须与fortran代码中的类型一致,一般用 as.integer, as.double, as.character等转换成fortran代码能识别的数据类型。

labdsv程序包(https://cran.r-project.org/web/packages/labdsv/index.html )中包含了f77,可供参考。

参考资料