Рекомендации по включению кода TMB C++ в пакет R
Недавно я обнаружил чудеса TMB, и я работаю над пакетом, который в идеале включал бы в себя шаблоны TMB C++ для довольно дорогих в вычислительном отношении моделей.
Я предполагаю, что есть возможность:
- Автоматическая компиляция исходного кода TMB при установке пакета
но я не могу найти четких указаний в документации TMB по этому поводу. На данный момент моя альтернатива - писать функции, которые компилируют код TMB при первом вызове функции, которая использует не скомпилированный класс... но у меня есть ощущение, что есть более хорошие способы сделать это.
Кто-нибудь успешно включил функции TMB в другой пакет и может указать мне направление соответствующей документации или примеров?
1 ответ
С немного большим поиском я наконец нашел свой ответ в этой теме. Я думаю, что я пропустил это, потому что детали его решений были перенесены на вики-страницу под названием разработка, где контент специально предназначен для пользователей, желающих внести свой вклад в разработку TMB, тогда как я просто хочу распространять код, который работает с TMB.
Подводя итог, можно сказать, что поток предлагает некоторые изменения, которые я принял следующим образом (myPkg должно быть названием вашего пакета):
SRC /
- Поместите свой
.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
где-то в моем исходном коде, где я не буду связываться с ним. Этот тег заполнит ваше NAMESPACE useDynLib(myPkg)
... и как я понимаю, это загружает общие библиотеки при загрузке пакета для вас.
Вызов функции в вашем пакете:
Наконец при звонке MakeADFun
, задавать DLL="myPkg"
, С помощью этой настройки вы можете скомпилировать одну модель TMB в ваш пакет. Это потому, что контент, скомпилированный в вашем ./src/
папка будет автоматически переименована в соответствии с именем вашего пакета, поэтому вы не можете создавать модели с уникальными именами.
РЕДАКТИРОВАТЬ: Решение для распределения нескольких DLL
После некоторого дополнительного поиска (тот же поток, на который ссылаются выше)... Я понял, что решение, описанное в официальной вики (и подробно описанное выше), имеет отношение только к распространению одной DLL (то есть одной модели TMB).
Если вы хотите распространять несколько моделей TMB в пакете, вам придется использовать свой собственный make-файл. Я дал более подробное описание в своем блоге, поэтому я лишь кратко опишу здесь шаги относительно того, как они отличаются от предыдущих шагов, которые я описал.
SRC /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
Для окон замените so
, с dll
и используйте соответствующие флаги компилятора (для отладки). Увидеть ?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!
Если вам нужен доступ к библиотеке CppAD с дополнительным кодом из TMB (что довольно существенно!), Вы можете использовать
WITH_LIBTMB
макропеременная как я в этом заголовке здесь. Это позволит вам иметь несколько файлов.cpp, которые вы можете компилировать отдельно. Важно отметить, что вам нужно только один раз скомпилировать код из заголовка TMB, используя такой файл, который
#include
s заголовок 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.cpp
файл, созданный
Rcpp::compileAttributes
. Мне также нравится запускать тесты, вызывая
devtools::test()
но вы можете удалить это, если хотите.