3

我最近发现了 TMB 的奇妙之处,并且我正在开发一个包,该包理想地包含 TMB c++ 模板,用于计算相当昂贵的模型。

我假设有以下可能性:

  • 在安装包时自动编译 TMB 源代码

但我在 TMB 文档中找不到任何明确的指导方针。到目前为止,我的替代方法是编写在第一次调用使用未编译类的函数时编译 TMB 代码的函数……但我觉得有更好的方法来做到这一点。

有没有人成功地将 TMB 功能包含在另一个包中,并且可以指出相关文档或示例的方向?

4

2 回答 2

5

通过更多的搜索,我终于在这个线程中找到了我的答案。我想我错过了它,因为它详细说明的解决方案已移至名为development的 wiki 页面,其中内容专门针对希望为 TMB 开发做出贡献的用户,而我只想分发包含 TMB 的代码。

总而言之,该线程建议我采用这样的一些更改(myPkg应该是您的包的名称):

源/

  • 将您的.cpp模板放在mypkg/src. 当你构建你的包时,这将由 R 自动编译。

描述

将这些行添加到您的描述文件中,以便 R 拥有编译模型模板所需的所有工具。

Depends: TMB, RcppEigen
LinkingTo: TMB, RcppEigen

R/roxygentags.r

现在我们需要将我们的 TMB 模板添加到命名空间文件中。我们可以通过 roxygen 轻松地做到这一点,方法是制作一个像这样的虚拟文件:

#' Roxygen commands
#'
#' @useDynLib myPkg
#'
dummy <- function(){
  return(NULL)
}

虚拟函数只是将标签@useDynLib myPkg放在我的源代码中某个我不会弄乱它的地方的借口。此标签将使用... 填充您的NAMESPACEuseDynLib(myPkg),据我所知,这会在为您加载包时加载共享库。

调用包中的函数:

最后,在调用 时MakeADFun,设置DLL="myPkg"通过此设置,您可以将单个TMB 模型编译到您的包中。这是因为在你的文件夹中编译的内容./src/会根据你的包名自动重命名,因此你不能创建唯一命名的模型。

编辑:分发多个 DLL 的解决方案

经过更多搜索(与上面引用的相同线程)...我意识到官方 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,替换sodll和 并使用相关的编译器标志(用于调试)。有关?TMB::compile用于调试的编译器标志的信息,请参阅。

R/roxygentags.r

这与上面略有不同:

#' 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 文件的好建议!

于 2018-02-05T20:12:50.910 回答
2

如果您想使用来自 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 的注意事项

每次构建时, Rstudio 都会调用Rcpp::compileAttributes(或类似的东西)。因此,您不能使用它。解决此问题的一种方法是创建与此处类似的自定义构建脚本。它本质上是R CMD INSTALL在删除RcppExports.cppRcpp::compileAttributes. 我也喜欢通过调用来运行测试,devtools::test()但如果你愿意,你可以删除它。

于 2020-09-30T12:59:31.843 回答