我最近发现了 TMB 的奇妙之处,并且我正在开发一个包,该包理想地包含 TMB c++ 模板,用于计算相当昂贵的模型。
我假设有以下可能性:
- 在安装包时自动编译 TMB 源代码
但我在 TMB 文档中找不到任何明确的指导方针。到目前为止,我的替代方法是编写在第一次调用使用未编译类的函数时编译 TMB 代码的函数……但我觉得有更好的方法来做到这一点。
有没有人成功地将 TMB 功能包含在另一个包中,并且可以指出相关文档或示例的方向?
通过更多的搜索,我终于在这个线程中找到了我的答案。我想我错过了它,因为它详细说明的解决方案已移至名为development的 wiki 页面,其中内容专门针对希望为 TMB 开发做出贡献的用户,而我只想分发包含 TMB 的代码。
总而言之,该线程建议我采用这样的一些更改(myPkg应该是您的包的名称):
.cpp
模板放在mypkg/src
. 当你构建你的包时,这将由 R 自动编译。将这些行添加到您的描述文件中,以便 R 拥有编译模型模板所需的所有工具。
Depends: TMB, RcppEigen
LinkingTo: TMB, RcppEigen
现在我们需要将我们的 TMB 模板添加到命名空间文件中。我们可以通过 roxygen 轻松地做到这一点,方法是制作一个像这样的虚拟文件:
#' Roxygen commands
#'
#' @useDynLib myPkg
#'
dummy <- function(){
return(NULL)
}
虚拟函数只是将标签@useDynLib myPkg
放在我的源代码中某个我不会弄乱它的地方的借口。此标签将使用... 填充您的NAMESPACEuseDynLib(myPkg)
,据我所知,这会在为您加载包时加载共享库。
最后,在调用 时MakeADFun
,设置DLL="myPkg"
。通过此设置,您可以将单个TMB 模型编译到您的包中。这是因为在你的文件夹中编译的内容./src/
会根据你的包名自动重命名,因此你不能创建唯一命名的模型。
经过更多搜索(与上面引用的相同线程)...我意识到官方 wiki 中描述的解决方案(以及上面详细说明)仅与分发单个 dll(即单个 TMB 模型)有关。
如果你想在一个包中分发多个 TMB 模型,你必须使用你自己的 makefile。我已经在我的博客中给出了更详细的描述,因此我将在这里仅简要描述这些步骤,说明它们与我之前描述的步骤有何不同。
您必须定义自己的Makefile
(或Makefile.win
为 Windows 用户)并将其放到您的src/
目录中。这是一个对我有用的例子:
all: template1.so template2.so
# Comment here preserves the prior tab
template1.so: template1.cpp
Rscript --vanilla -e "TMB::compile('template1.cpp','-O0 -g')"
template2.so: template2.cpp
Rscript --vanilla -e "TMB::compile('template2.cpp','-O0 -g')"
clean:
rm -rf *o
对于 Windows,替换so
、dll
和 并使用相关的编译器标志(用于调试)。有关?TMB::compile
用于调试的编译器标志的信息,请参阅。
这与上面略有不同:
#' Roxygen commands
#'
#' This is a dummy function who's purpose is to hold the useDynLib roxygen tag.
#' This tag will populate the namespace with compiled c++ functions upon package install.
#'
#' @useDynLib template1
#' @useDynLib template2
#'
dummy <- function(){
return(NULL)
}
最后,上述更改将编译多个唯一命名的 TMB 模板并将它们加载到命名空间中。要在您的包中调用这些模型,下面是一个示例:
obj <- MakeADFun(data = data,
parameters = params,
DLL="template1",
inner.control = list(maxit = 10000),
silent=F)
当我尝试在 Windows 机器上编译它时遇到问题......结果证明与未正确清理 src 文件夹有关,并且我有旧的 linux 编译文件卡在那里。如果您有编译问题,值得从以前的构建中手动清除src/
目录中的残留文件......或者也许有人可以提供一些关于编写更好的 make 文件的好建议!
如果您想使用来自 TMB 的附加代码访问 CppAD 库(这非常重要!),那么您可以WITH_LIBTMB
像我在此标题中所做的那样使用宏变量。这将允许您拥有多个可以单独编译的 .cpp 文件。重要的是,您只需要使用这样的文件从 TMB 头编译代码一次,该文件#include
是 TMB.hpp 头而不定义WITH_LIBTMB
.
这大大减少了编译时间,因为您可以自己编译每个 .cpp,而无需在 TMB.hpp 中声明的所有代码。此外,如果您像我在链接中所做的那样取消定义并定义一些宏,您也可以将代码与 Rcpp 一起使用。
您还可以拥有一个可供TMB::MakeADFun
. 它需要一些手动工作,但也可以在使用 Rcpp 的同时通过使用Rcpp::compileAttributes
并将创建的名为 RcppExports.cpp 的文件更改为命名为 init.cpp 来完成,然后将这些附加行包含在CallEntries
数组和R_init_survTMB
函数中:
每次构建时, Rstudio 都会调用Rcpp::compileAttributes
(或类似的东西)。因此,您不能使用它。解决此问题的一种方法是创建与此处类似的自定义构建脚本。它本质上是R CMD INSTALL
在删除RcppExports.cpp
由Rcpp::compileAttributes
. 我也喜欢通过调用来运行测试,devtools::test()
但如果你愿意,你可以删除它。