Ошибка связывания пакета R с Rcpp: «неопределенный символ: LAPACKE_dgels»

#c #r #linker-errors #rcpp #lapack

#c #r #компоновщик-ошибки #rcpp #lapack

Вопрос:

Я создаю пакет R ‘lapacker’, чтобы предоставить интерфейс C для внутренней библиотеки LAPACK, предоставляемой и используемой R (только с двойной точностью и двойным комплексом), используя заголовочный файл R API «R_ext /Lapack.h». Исходный код:https://github.com/ypan1988/lapacker /

И структура проекта:

 /lapacker
  /inst
    /include
      /lapacke.h
      /someother header files
  /R
    /zzz.R
  /src
    /lapacke_dgetrf.c
    /lapacke_dgetrf_work.c
    /loads of other utility functions provided by LAPACKE 
    /rcpp_hello.cpp
  DESCRIPTION
  NAMESPACE
  

Внутри проекта я попробовал тестовую функцию в rcpp_hello.cpp файл (Обратите внимание, что этот пример взят из https://www.netlib.org/lapack/lapacke.html#_calling_code_dgels_code ):

 //'@export
// [[Rcpp::export]]
void example_lapacke_dgels()
{
  double a[5][3] = {{1,1,1},{2,3,4},{3,5,2},{4,2,5},{5,4,3}};
  double b[5][2] = {{-10,-3},{12,14},{14,12},{16,16},{18,16}};
  lapack_int info,m,n,lda,ldb,nrhs;
  int i,j;

  m = 5;
  n = 3;
  nrhs = 2;
  lda = 3;
  ldb = 2;

  info = LAPACKE_dgels(LAPACK_ROW_MAJOR,'N',m,n,nrhs,*a,lda,*b,ldb);

  for(i=0;i<n;i  )
  {
    for(j=0;j<nrhs;j  )
    {
      printf("%lf ",b[i][j]);
    }
    printf("n");
  }
}
  

Весь пакет может правильно скомпилироваться без ошибок, и в R он дает правильный ответ (можно найти обозначающий символ LAPACKE_dgels):

 > example_lapacke_dgels()
2.000000 1.000000 
1.000000 1.000000 
1.000000 2.000000 
  

Однако, когда я создаю отдельный файл C , скажем demo3.cpp с точно такой же функцией,

 #include <Rcpp.h>

#include <lapacke.h>
// [[Rcpp::depends(lapacker)]]

// [[Rcpp::export]]
void lapacke_dgels_test()
{
  double a[5][3] = {{1,1,1},{2,3,4},{3,5,2},{4,2,5},{5,4,3}};
  double b[5][2] = {{-10,-3},{12,14},{14,12},{16,16},{18,16}};
  lapack_int info,m,n,lda,ldb,nrhs;
  int i,j;

  m = 5;
  n = 3;
  nrhs = 2;
  lda = 3;
  ldb = 2;

  info = LAPACKE_dgels(LAPACK_ROW_MAJOR,'N',m,n,nrhs,*a,lda,*b,ldb);

  for(i=0;i<n;i  )
  {
    for(j=0;j<nrhs;j  )
    {
      printf("%lf ",b[i][j]);
    }
    printf("n");
  }
}
  

он больше не компилируется должным образом (на самом деле я пробовал как в macOS, так и в Ubuntu, та же проблема со связыванием) и выдает сообщения об ошибках связывания (не удается найти символ LAPACKE_dgels):

 > Rcpp::sourceCpp("~/Desktop/demo3.cpp", showOutput = TRUE)
/usr/lib/R/bin/R CMD SHLIB -o 'sourceCpp_6.so'  'demo3.cpp'  
g    -I/usr/share/R/include -DNDEBUG   -I"/home/yipan/R/x86_64-pc-linux-gnu-library/3.4/Rcpp/include" -I"/home/yipan/R/x86_64-pc-linux-gnu-library/3.4/lapacker/include" -I"/home/yipan/Desktop"    -fpic  -g -O2 -fdebug-prefix-map=/build/r-base-AitvI6/r-base-3.4.4=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -g  -c demo3.cpp -o demo3.o
g   -shared -L/usr/lib/R/lib -Wl,-Bsymbolic-functions -Wl,-z,relro -o sourceCpp_6.so demo3.o -L/usr/lib/R/lib -lR
Error in dyn.load("/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so") : 
  unable to load shared object '/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so':
  /tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so: undefined symbol: LAPACKE_dgels
  

Я также проверил lapacker.итак, в /R / x86_64-pc-linux-gnu-library/3.4/lapacker /libs и найдено:

 000000000000c6b0 g    DF .text  00000000000001bf  Base        LAPACKE_dgels
  

Я что-то пропустил, чтобы получить demo3.cpp скомпилировать правильно? Большое спасибо за ваше терпение и время!

Ответ №1:

Здесь вы сталкиваетесь со сложной проблемой. Символ, который вы пытаетесь устранить, LAPACKE_dgels является частью lapacker.so сборки во время установки пакета. Проблема в том, что библиотеки для пакетов R не предназначены для связывания. Вместо этого они динамически загружаются R во время выполнения. В принципе, я вижу четыре возможности:

  1. Преобразуйте lapacke в библиотеку только для заголовков и установите ее в inst/include (c.f. RcppArmadillo ).
  2. Ссылка с системной установкой lapacke (легко в Linux …)
  3. Зарегистрируйте все функции в R и используйте методы, предоставляемые R, для ссылки на них (например, WRE и nloptr ).
  4. Скомпилируйте библиотеку, предназначенную для связывания, и установите ее вместе с пакетом R. Для этого вам все равно понадобится плагин, поскольку вам нужно добавить -L<path/to/lib> -l<libname> .... в PKG_LIBS .

Я уверен, что в CRAN есть примеры, использующие метод 4, но прямо сейчас ни один не приходит на ум. Однако в качестве «кодового ката» я преобразовал свой недавний тестовый пакет для использования этой структуры, c.f.https://github.com/rstub/levmaR/tree/static .


(Оригинальный неполный ответ.)

В src/Makevars у вас есть

 PKG_LIBS = $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)
  

Вам нужна аналогичная настройка при компиляции файла cpp через атрибуты Rcpp. Лучший способ добиться этого — использовать плагин Rcpp, c.f. Решение RcppArmadillo отRcppArmadillo (корректировки не проверены!):

 inlineCxxPlugin <- function(...) {
    plugin <-
        Rcpp::Rcpp.plugin.maker(
                  include.before = "#include <lapacke.h>",
                  libs           = "$(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)",
                  package        = "lapacker"
              )
    settings <- plugin()
    settings$env$PKG_CPPFLAGS <- "-I../inst/include"
    settings
}
  

Кстати, почему вы хотите напрямую взаимодействовать с LAPACK, когда RcppArmadillo уже это делает?

Комментарии:

1. Спасибо, я попробую это. Но у меня такое чувство, что это может сгенерировать ту же ошибку, поскольку я сделал точно то же самое для другого pkg ‘cblasr’ (как внутренний, так и отдельный файл C компилируется правильно). Я знаю о armadillo и на самом деле использую Rcpp и RcppArmadillo в нескольких проектах. Я внедряю матричную библиотеку для практики C , и, к сожалению, это основной порядок строк. Если я хочу использовать BLAS и LAPACK внутри, лучший выбор — использовать cblas.h и lapacke. h, которые не предоставлены R. Поместите ‘LinkingTo: cblasr, lapacker’ в ‘DESCRIPTION’ матрицы R pkg, и я получу ту же ошибку ссылки.

2. @ypan1988 Вы правы, мое первоначальное решение было бы неполным. Смотрите обновление. Кстати, я удивлен, что это сработало для cblasr .

3. Еще раз спасибо! Мне удалось устранить проблему, и лучший способ — использовать вариант 1 — изменить библиотеку на библиотеку только head. Вначале я немного запутался с «LinkingTo», и это в основном добавляет дополнительный каталог для файлов заголовков.