2

Since Armadillo (afaik) doesn't have a triangular solver, I'd like to use the LAPACK triangular solver available in dtrtrs. I have looked at the following two (first, second) SO threads and pieced something together, but it isn't working.

I have created a fresh package using RStudio while also enabling RcppArmadillo. I have a header file header.h:

#include <RcppArmadillo.h>

#ifdef ARMA_USE_LAPACK
#if !defined(ARMA_BLAS_CAPITALS)
#define arma_dtrtrs dtrtrs
#else
#define arma_dtrtrs DTRTRS
#endif
#endif

extern "C" {
  void arma_fortran(arma_dtrtrs)(char* UPLO, char* TRANS, char* DIAG, int* N, int* NRHS,
                    double* A, int* LDA, double* B, int* LDB, int* INFO);
}

int trtrs(char uplo, char trans, char diag, int n, int nrhs, double* A, int lda, double* B, int ldb);

static int trisolve(const arma::mat &in_A, const arma::mat &in_b, arma::mat &out_x);

which essentially is the answer to the first linked question, with also a wrapper function and the main function. The meat of the functions go in trisolve.cpp and is as follows:

#include "header.h"

int trtrs(char uplo, char trans, char diag, int n, int nrhs, double* A, int lda, double* B, int ldb) {
  int info = 0;
  wrapper_dtrtrs_(&uplo, &trans, &diag, &n, &nrhs, A, &lda, B, &ldb, &info);
  return info;
}


static int trisolve(const arma::mat &in_A, const arma::mat &in_b, arma::mat &out_x) {
  size_t  rows = in_A.n_rows;
  size_t  cols = in_A.n_cols;

  double *A = new double[rows*cols];
  double *b = new double[in_b.size()];

  //Lapack has column-major order
  for(size_t col=0, D1_idx=0; col<cols; ++col)
  {
    for(size_t row = 0; row<rows; ++row)
    {
      // Lapack uses column major format
      A[D1_idx++] = in_A(row, col);
    }
    b[col] = in_b(col);
  }

  for(size_t row = 0; row<rows; ++row)
  {
    b[row] = in_b(row);
  }

  int info = trtrs('U', 'N', 'N', cols, 1, A, rows, b, rows);

  for(size_t col=0; col<cols; col++) {
    out_x(col)=b[col];
  }

  delete[] A;
  delete[] b;

  return 0;
}


// [[Rcpp::export]]

arma::mat RtoRcpp(arma::mat A, arma::mat b) {
  arma::uword n = A.n_rows;
  arma::mat x = arma::mat(n, 1, arma::fill::zeros);

  int info = trisolve(A, b, x);
  return x;
}

There are (at least) two problems for me:

  1. When trying to compile, I get: conflicting types for 'dtrtrs_' from the header file. However, I don't see what is wrong with the inputs (and this is literally copied from the second linked thread).
  2. Not unsurprisingly, wrapper_dtrtrts_ is not correct. But from what I can tell from Armadillo's compiler_setup.hpp, arma_fortran should create a function called wrapper_dtrtrs_ for me. What is the name I should use here in the main cpp file?
hejseb
  • 2,064
  • 3
  • 18
  • 28
  • 2
    The conflicting types warning is caused by Armadillo already using `dtrtrts`, c.f. https://gitlab.com/conradsnicta/armadillo-code/blob/9.200.x/include/armadillo_bits/wrapper_lapack.hpp#L908. So it seems that `solve()` will already use it given suitable input. – Ralf Stubner Oct 20 '18 at 22:39
  • 2
    @RalfStubner might be on to something. The [Armadillo documentation](http://arma.sourceforge.net/docs.html#solve) says " If A is known to be a triangular matrix, the solution can be computed faster by explicitly indicating that A is triangular through trimatu() or trimatl(); indicating a triangular matrix also implies that solve_opts::fast is enabled", and gives an example of doing so under `solve()` – duckmayr Oct 20 '18 at 23:27
  • @RalfStubner Thanks for clearing that up. But from what I’ve read online, I was under the impression that Armadillo doesn’t use backsubstitution (but that trimatu is still helpful). See the two links in comments to the answer below. – hejseb Oct 21 '18 at 06:48
  • @duckmayr See comments above and to the answer below. – hejseb Oct 21 '18 at 06:49

2 Answers2

3

Armadillo already uses dtrtrs for solving tridiagonal problems. Some code references:

So if we can trigger this debug statement, we can be sure the dtrtrs is indeed used:

#define ARMA_EXTRA_DEBUG
// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>

// [[Rcpp::export]]
void testTrisolve() {
  arma::mat A = arma::randu<arma::mat>(5,5);
  arma::mat B = arma::randu<arma::mat>(5,5);

  arma::mat X1 = arma::solve(A, B);
  arma::mat X3 = arma::solve(arma::trimatu(A), B);
}

/*** R
testTrisolve()
*/

This produces a lot of debug messages, among them:

lapack::gesvx()
[...]
lapack::trtrs()

So we clearly see that dtrtrs is used in the tridiagonal case.

As for your original questions:

  1. The conflicting types error is a consequence of Aramdillo already using dtrtrs, but with slightly different signature (A is const).
  2. The C-level name for the Fortran function depends on the values of ARMA_BLAS_UNDERSCORE and ARMA_USE_WRAPPER. I am not sure if that is always the case, but for me the former is defined and the latter not (c.f. config.hpp), leading to dtrtrs_ as name.

Indeed, if I add a const where Armadillo uses it and call the function as dtrtrs_, your code compiles without errors or warnings (with the exception of an unused variable ...):

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

extern "C" {
  void arma_fortran(dtrtrs)(char* UPLO, char* TRANS, char* DIAG, int* N, int* NRHS,
                    const double* A, int* LDA, double* B, int* LDB, int* INFO);
}

int trtrs(char uplo, char trans, char diag, int n, int nrhs, double* A, int lda, double* B, int ldb) {
  int info = 0;
  dtrtrs_(&uplo, &trans, &diag, &n, &nrhs, A, &lda, B, &ldb, &info);
  return info;
}

[...]
Ralf Stubner
  • 26,263
  • 3
  • 40
  • 75
  • BTW @hejseb, `dtrtrs` has been used by Armadillo for a really long time. The usage is just tricky, c.f. http://lists.r-forge.r-project.org/pipermail/rcpp-devel/2014-June/007783.html – Ralf Stubner Oct 22 '18 at 14:53
2

Armadillo already has a triangular solver. Code adapted from the documentation:

mat A(5,5, fill::randu);
// ... make A triangular here ...

mat B(5,5, fill::randu);

// tell solve() to treat A as upper triangular
// and automatically enable fast mode
mat X = solve(trimatu(A), B);

Based on the documentation, it looks like the Armadillo solver can automatically detect band matrices and symmetric positive definite matrices.

hbrerkere
  • 1,561
  • 8
  • 12
  • That is very interesting. What you’re doing is what I am currently doing, but after having read about this on the famous internet I was under the impression that it did something else and did not use the triangular solver in LAPACK. I’ll see if I can dig up the source. – hejseb Oct 21 '18 at 06:11
  • Here are two links to where this is mentioned: http://lists.r-forge.r-project.org/pipermail/rcpp-devel/2014-June/007780.html and http://lists.r-forge.r-project.org/pipermail/rcpp-devel/2014-February/007147.html Both posts are fairly old, however, so things could of course have changed. – hejseb Oct 21 '18 at 06:23
  • The two links indeed have outdated content. – hbrerkere Nov 07 '18 at 06:21