--- title: "Saving and loading Armadillo objects on C++ side" output: rmarkdown::html_vignette bibliography: "references.bib" vignette: > %\VignetteIndexEntry{Saving and loading Armadillo objects on C++ side} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` This vignette is adapted from the official Armadillo [documentation](https://arma.sourceforge.net/docs.html). # Saving and loading matrices and cubes Armadillo provides functions to save and load matrices and cubes to and from files. The following file types are supported: - `arma_binary`: Numerical data stored in machine dependent binary format, with a simple header to speed up loading. The header indicates the type and size of matrix/cube. - `arma_ascii`: Numerical data stored in human readable text format, with a simple header to speed up loading. The header indicates the type and size of matrix/cube. - `raw_binary`: Numerical data stored in machine dependent raw binary format, without a header. Matrices are loaded to have one column, while cubes are loaded to have one slice with one column. The `.reshape()` function can be used to alter the size of the loaded matrix/cube without losing data. - `raw_ascii`: Numerical data stored in raw ASCII format, without a header. The numbers are separated by whitespace. The number of columns must be the same in each row. Cubes are loaded as one slice. Data which was saved in Matlab/Octave using the `-ascii` option can be read in Armadillo, except for complex numbers. Complex numbers are stored in standard C++ notation, which is a tuple surrounded by brackets: eg. `(1.23,4.56)` indicates 1.24 + 4.56i. - `csv_ascii`: Numerical data stored in comma separated value (CSV) text format, without a header. To save/load with a header, use the `csv_name(filename, header)` specification instead. Handles complex numbers stored in the compound form of `1.24+4.56i`. Applicable to `Mat` and `SpMat`. - `coord_ascii`: Numerical data stored as a text file in coordinate list format, without a header. Only non-zero values are stored. For real matrices, each line contains information in the following format: `row column value`. For complex matrices, each line contains information in the following format: `row column real_value imag_value`. The rows and columns start at zero. Applicable to `Mat` and `SpMat`. - `pgm_binary`: Image data stored in Portable Gray Map (PGM) format. Applicable to `Mat` only. Saving int, float or double matrices is a lossy operation, as each element is copied and converted to an 8 bit representation. As such the matrix should have values in the `[0,255]` interval, otherwise the resulting image may not display correctly. - `ppm_binary`: Image data stored in Portable Pixel Map (PPM) format. Applicable to `Cube` only. Saving int, float or double matrices is a lossy operation, as each element is copied and converted to an 8 bit representation. As such the cube/field should have values in the `[0,255]` interval, otherwise the resulting image may not display correctly. - `hdf5_binary`: Numerical data stored in portable HDF5 binary format. For saving, the default dataset name within the HDF5 file is `"dataset"`. For loading, the order of operations is: (1) try loading a dataset named `"dataset"`, (2) try loading a dataset named `"value"`, (3) try loading the first available dataset. To explicitly control the dataset name, specify it via the `hdf5_name(filename, dataset)` argument. HDF5 support can be enabled by defining `ARMA_USE_HDF5` before including the Armadillo header. The following file types are supported for fields: - `arma_binary`: See above. - `ppm_binary`: See above. Usage: ```cpp .save(filename) .load(filename) .save(filename, file_type) .load(filename, file_type) .save(stream) .load(stream) .save(stream, file_type) .load(stream, file_type) .save(hdf5_name(filename, dataset)) .load(hdf5_name(filename, dataset)) .save(hdf5_name(filename, dataset, settings)) .load(hdf5_name(filename, dataset, settings)) .save(csv_name(filename, header)) .load(csv_name(filename, header)) .save(csv_name(filename, header, settings)) .load(csv_name(filename, header, settings)) ``` By providing either `hdf5_name(filename, dataset)` or `hdf5_name(filename, dataset, settings)`, the `file_type` type is assumed to be `hdf5_binary`. The `dataset` argument specifies an HDF5 dataset name (eg. `"my_dataset"`) that can include a full path (eg. `"/group_name/my_dataset"`); if a blank dataset name is specified (ie. `""`), it is assumed to be `"dataset"`. The `settings` argument is optional; it is one of the following, or a combination thereof - `hdf5_opts::trans`: Save/load the data with columns transposed to rows (and vice versa). - `hdf5_opts::append`: Instead of overwriting the file, append the specified dataset to the file; the specified dataset must not already exist in the file. - `hdf5_opts::replace`: Instead of overwriting the file, replace the specified dataset in the file. - These settings can be combined using the `+` operator (e.g., `hdf5_opts::trans + hdf5_opts::append`) By providing either `csv_name(filename, header)` or `csv_name(filename, header, settings)`, the file is assumed to have data in comma separated value (CSV) text format. The `header` argument specifies the object which stores the separate elements of the header line; it must have the type `field`. The optional `settings` argument is one of the following, or a combination thereof - `csv_opts::trans`: Save/load the data with columns transposed to rows (and vice versa). - `csv_opts::no_header`: Assume there is no header line; the header argument is not referenced. - `csv_opts::semicolon`: Use semicolon (`;`) instead of comma (`,`) as the separator character. - `csv_opts::strict`: Interpret missing values as `NaN` (not applicable to sparse matrices). - These settings can be combined using the `+` operator (e.g., `csv_opts::trans + csv_opts::no_header`) ## Caveats * For saving/loading HDF5 files, support for HDF5 must be enabled within Armadillo's configuration. The `hdf5.h` header file must be available on your system and you will need to link with the HDF5 library (eg. `-lhdf5`). HDF5 support can be enabled by defining `ARMA_USE_HDF5` before including the Armadillo header. * Enabling HD5 requires to vendor cpp11armadillo in orderto adhere to CRAN compliance. * Armadillo save and load methods are only accessible from C++ side. * Once an object is exported to R, it is more efficient to load/save using `saveRDS()` and `readRDS()`. ## Examples Save and load matrices: ```cpp [[cpp11::register]] int saveload1_(const int& n) { arma::mat A(n, n, fill::randu); // default save format is arma_binary A.save("A.bin"); // save in raw_ascii format A.save("A.txt", arma::raw_ascii); // save in CSV format without a header A.save("A.csv", arma::csv_ascii); // save in CSV format with a header arma::field header(A.n_cols); header(0) = "foo"; header(1) = "bar"; // etc A.save(arma::csv_name("A.csv", header)); // save in HDF5 format with internal dataset named as "my_data" // see the caveats // A.save(arma::hdf5_name("A.h5", "my_data")); // automatically detect format type while loading arma::mat B; B.load("A.bin"); // force loading in arma_ascii format arma::mat C; C.load("A.txt", arma::arma_ascii); // example of testing for success arma::mat D; bool ok = D.load("A.bin"); if(ok == true) { message("Matrix loaded successfully"); } else { stop("Problem with loading"); } return 0; } ``` Save and load fields: ```cpp [[cpp11::register]] int saveload2_(const int& n) { arma::field F(n); for (int i = 0; i < n; i++) { F(i) = arma::mat(n, n, fill::randu); } // default save format is arma_binary F.save("F.bin"); // save in PPM format F.save("F.ppm", arma::ppm_binary); // automatically detect format type while loading arma::field G; G.load("F.bin"); return 0; } ```