--- title: "03 - Converting between R and Armadillo types" output: litedown::html_format: options: toc: true number_sections: true vignette: > %\VignetteIndexEntry{03 - Converting between R and Armadillo types} %\VignetteEngine{litedown::vignette} %\VignetteEncoding{UTF-8} editor: markdown: wrap: sentence bibliography: ["references.bib"] --- **Adapted from the official Armadillo [documentation](https://arma.sourceforge.net/docs.html).** `armadillo4r` provides a set of conversion functions, declared across `armadillo4r/wrappers/matrices.hpp`, `armadillo4r/wrappers/vectors.hpp`, `armadillo4r/wrappers/cubes.hpp`, and `armadillo4r/wrappers/fields.hpp`, and all automatically available after including ``. This vignette documents every function and shows a minimal C++ example for each group. # R to Armadillo ## Matrices R matrices are represented in `cpp4r` as `doubles_matrix<>` (numeric) or `integers_matrix<>` (integer). The following functions convert them to Armadillo dense matrices: | cpp4r input | Function | Armadillo output | Notes | |-------------|----------|-----------------|-------| | `doubles_matrix<>` | `as_Mat(x)` / `as_mat(x)` | `Mat` | `as_mat()` is a lowercase alias | | `integers_matrix<>` | `as_Mat(x)` / `as_mat(x)` | `Mat` | same alias applies | | `integers_matrix<>` | `as_imat(x)` | `imat` (`Mat`) | signed integer matrix | | `integers_matrix<>` | `as_umat(x)` | `umat` (`Mat`) | unsigned integer matrix; copies element-by-element | | `doubles_matrix<>` | `as_fmat(x)` | `fmat` (`Mat`) | narrows double to float | ```cpp #include #include using namespace arma; using namespace cpp4r; [[cpp4r::register]] doubles_matrix<> mat_demo_(const doubles_matrix<>& x, const integers_matrix<>& xi) { mat A = as_Mat(x); // double matrix imat Ai = as_imat(xi); // signed int matrix umat Au = as_umat(xi); // unsigned int matrix fmat Af = as_fmat(x); // float matrix // use A, Ai, Au, Af ... return as_doubles_matrix(A * A.t()); } ``` ```{r, eval=FALSE} x <- matrix(c(1.5, 2.5, 3.5, 4.5), nrow = 2) xi <- matrix(1:4, nrow = 2) mat_demo_(x, xi) ``` ## Vectors R vectors (`doubles` / `integers`) map to Armadillo column vectors: | cpp4r input | Function | Armadillo output | Notes | |-------------|---------------------------|-----------------------|--------------------------------------------| | `doubles` | `as_Col(x)` / `as_col(x)` | `Col` (`vec`) | `as_col()` is a lowercase alias | | `integers` | `as_Col(x)` / `as_col(x)` | `Col` | same alias applies | | `integers` | `as_uvec(x)` | `uvec` (`Col`) | copies element-by-element (no reinterpret) | ```cpp [[cpp4r::register]] doubles vec_demo_(const doubles& y, const integers& yi) { vec v = as_Col(y); // double column vector uvec vu = as_uvec(yi); // unsigned int column vector // use v, vu ... return as_doubles(v % v); // element-wise square } ``` ```{r, eval=FALSE} vec_demo_(c(1.0, 2.0, 3.0), 1:3) ``` ## Cubes R has no native quasi third-order tensors ("3D matrices") that maps directly onto Armadillo's memory layout. `armadillo4r` therefore represents a cube on the R side as a **`list` of matrices**, where every element is a matrix of the **same dimensions**. Slice `s` of the Armadillo cube corresponds to element `s + 1` of the R list (R is 1-indexed). The most commonly used cube typedefs are: | Typedef | Equivalent | Element type | |---------|-----------|--------------| | `cube` / `dcube` | `Cube` | `double` | | `fcube` | `Cube` | `float` | | `icube` | `Cube` | `long long` (signed, platform-dependent) | | `ucube` | `Cube` | `unsigned int` | | `cx_cube` / `cx_dcube` | `Cube` | `std::complex` | | cpp4r input | Function | Armadillo output | Notes | |-------------|----------|-----------------|-------| | `list` of `doubles_matrix<>` | `as_Cube(x)` / `as_cube(x)` | `Cube` (`cube`) | `as_cube()` is a lowercase alias | | `list` of `integers_matrix<>` | `as_icube(x)` | `Cube` | note: assigns to `Cube`, not `icube` (`Cube`) | Both functions throw `std::runtime_error` if the list is empty or any matrix has dimensions different from the first. ```cpp [[cpp4r::register]] doubles_matrix<> cube_col_means_(const list& slices) { cube C = as_Cube(slices); // list of doubles_matrix<> -> cube mat result(C.n_slices, C.n_cols); for (uword s = 0; s < C.n_slices; ++s) { result.row(s) = mean(C.slice(s), 0); // column means of each slice } return as_doubles_matrix(result); } ``` ```{r, eval=FALSE} slices <- list( matrix(1:6, nrow = 2), # slice 1 matrix(7:12, nrow = 2), # slice 2 matrix(13:18, nrow = 2) # slice 3 ) cube_col_means_(slices) ``` ## Fields An Armadillo `field` stores arbitrary objects (matrices, vectors, strings, …) in a matrix-like or cube-like layout. Unlike `Cube`, the elements of a field **do not need to have the same size** — each element is an independent object. > **Tip:** when all matrices share the same dimensions, prefer `Cube` over > `field` because `Cube` is more memory-efficient and faster. On the R side a field is always represented as a plain **`list`** (or `strings` for `field`). Element `i` of the Armadillo field (0-indexed) corresponds to element `i + 1` of the R list (1-indexed). | cpp4r input | Function | Armadillo output | Notes | |-------------|----------|-----------------|-------| | `list` of `doubles_matrix<>` | `as_field_mat(x)` | `field` | matrices may differ in size | | `list` of `integers_matrix<>` | `as_field_imat(x)` | `field` | matrices may differ in size | | `list` of `doubles` | `as_field_vec(x)` | `field` | vectors may differ in length | | `strings` | `as_field_str(x)` | `field` | | All four functions throw `std::runtime_error` if the input list is empty. ```cpp [[cpp4r::register]] list field_mat_demo_(const list& mats) { // Ragged list of matrices -> field field F = as_field_mat(mats); // Normalise each matrix independently for (uword i = 0; i < F.n_elem; ++i) { F(i) = normalise(F(i)); // L2-normalise columns } // field -> list of doubles_matrix<> return as_doubles_matrix_field(F); } ``` ```{r, eval=FALSE} mats <- list( matrix(1:6, nrow = 2), # 2x3 matrix matrix(1:12, nrow = 4) # 4x3 matrix (different nrow!) ) field_mat_demo_(mats) ``` # Armadillo to R ## Matrices | Armadillo input | Function | cpp4r output | |----------------|----------|-------------| | `Mat` / `mat` / `fmat` | `as_doubles_matrix(A)` | `doubles_matrix<>` | | `Mat` / `imat` / `umat` | `as_integers_matrix(A)` | `integers_matrix<>` | | `Mat` / `cx_mat` | `as_complexes_matrix(A)` | `complexes_matrix<>` | ```cpp [[cpp4r::register]] doubles_matrix<> mat_out_demo_(const doubles_matrix<>& x) { mat A = as_Mat(x); return as_doubles_matrix(A.t() * A); // mat -> doubles_matrix<> } ``` ```{r, eval=FALSE} x <- matrix(1:9, nrow = 3) * 1.0 mat_out_demo_(x) ``` ## Vectors | Armadillo input | Function | cpp4r output | |----------------|----------|-------------| | `Col` / `vec` | `as_doubles(v)` | `doubles` | | `Col` / `ivec` | `as_integers(v)` | `integers` | | `uvec` | `as_integers(v)` | `integers` | ```cpp [[cpp4r::register]] doubles vec_out_demo_(const doubles& y) { return as_doubles(normalise(as_Col(y))); // vec -> doubles } ``` ```{r, eval=FALSE} vec_out_demo_(c(3.0, 4.0)) # should give c(0.6, 0.8) ``` ## Cubes | Armadillo input | Function | cpp4r output | |----------------|----------|-------------| | `Cube` / `cube` | `as_doubles_matrix_list(C)` | `list` of `doubles_matrix<>` | | `Cube` / `icube` | `as_integers_matrix_list(C)` | `list` of `integers_matrix<>` | | `ucube` | `as_integers_matrix_list(C)` | `list` of `integers_matrix<>` | Slice `s` of the cube becomes element `s + 1` of the returned R list. ```cpp [[cpp4r::register]] list random_cube_(const int n_rows, const int n_cols, const int n_slices) { cube C(n_rows, n_cols, n_slices, fill::randn); return as_doubles_matrix_list(C); // cube -> list of doubles_matrix<> } ``` ```{r, eval=FALSE} set.seed(42) result <- random_cube_(3L, 4L, 2L) length(result) # 2 (one element per slice) dim(result[[1]]) # 3 4 ``` ## Fields | Armadillo input | Function | cpp4r output | |----------------|----------|--------------| | `field` | `as_doubles_matrix_field(F)` | `list` of `doubles_matrix<>` | | `field` | `as_integers_matrix_field(F)` | `list` of `integers_matrix<>` | | `field` | `as_doubles_field(F)` | `list` of `doubles` | | `field` | `as_strings_field(F)` | `strings` | Element `i` of the field (0-indexed) becomes element `i + 1` of the returned R list (1-indexed). Because field elements may differ in size, the returned list is potentially ragged. ```cpp [[cpp4r::register]] list build_field_(const integers& sizes) { // Build a field where each vector has a different length field F(sizes.size()); for (uword i = 0; i < F.n_elem; ++i) { F(i) = randn(sizes[static_cast(i)]); // random vector of length sizes[i] } return as_doubles_field(F); // field -> list of doubles } ``` ```{r, eval=FALSE} build_field_(c(3L, 5L, 2L)) # list of three vectors of lengths 3, 5, 2 ``` ## Generic conversion with `cpp4r::as_sexp()` For cases where the return type is determined at compile time, `cpp4r::as_sexp()` provides a single generic entry point that dispatches to the appropriate conversion based on the Armadillo type: | Armadillo input | `as_sexp()` output | |----------------|--------------------| | `vec` / `fvec` | `doubles` | | `ivec` / `uvec` | `integers` | | `mat` / `fmat` | `doubles_matrix<>` | | `imat` / `umat` | `integers_matrix<>` | | `rowvec` / `frowvec` | `doubles_matrix<>` (1-row matrix) | | `irowvec` / `urowvec` | `integers_matrix<>` (1-row matrix) | ```cpp [[cpp4r::register]] SEXP as_sexp_demo_(const doubles& y) { return cpp4r::as_sexp(arma::sort(as_Col(y))); // vec -> doubles (dispatched automatically) } ``` ```{r, eval=FALSE} as_sexp_demo_(c(3.0, 1.0, 2.0)) # returns c(1, 2, 3) ``` # References