Take the following R function:
'# @param x A nxn matrix
'# @param y A 1xn matrix (vector)
foo = function(x, y) {
return(x %*% diag(1 / y)
}
What it does is to transform each element of the 1xn y vector into 1/y and diagonalize it into a nxn matrix with 1/y in it's diagonal. Then the matrix product x %*% diag(1/y) produces a nxn matrix consisting of each element from the jth column of x matrix divided by the jth element of y. Pretty standard.
Since R may be slow working with big matrices (e.g., 5000x5000), I'd like to take a shot with Rust. After setting up the project structure with {rextendr}, lib.rs looks like:
use extendr_api::prelude::*;
use nalgebra as na;
/// Calculates ratio x/y.
/// @param x A nxn matrix.
/// @param y A 1xn vector.
/// @return A nxn matrix.
/// @export
#[extendr]
fn foo(
x: na::DMatrix<f64>,
y: na::DVector<f64>,
) -> na::DMatrix<f64> {
let inv_y = y.map(|y| 1.0 / y);
let m = x * na::Matrix::from_diagonal(&inv_y);
m
}
// Macro to generate exports.
// This ensures exported functions are registered with R.
// See corresponding C code in `entrypoint.c`.
extendr_module! {
mod package_name;
fn foo;
}
foo does compile with no errors in Rust. But trying to integrate with R, I get the error no function or associated item named 'from_robj' found for struct 'Matrix' in the current scope. I guess what is missing is constructing a {extendr} wrapper for Matrix type.
How to proceed from here?
Reproducible example of foo in Rust:
use nalgebra as na;
fn foo(
x: na::DMatrix<f64>,
y: na::DVector<f64>,
) -> na::DMatrix<f64> {
let inv_y = y.map(|y| 1.0 / y);
let m = x * na::Matrix::from_diagonal(&inv_y);
m
}
fn main() {
let x = na::DMatrix::from_row_slice(3, 3, &[100.0, 150.0, 200.0, 200.0, 100.0, 300.0, 400.0, 100.0, 50.0]);
let y = na::DVector::from_row_slice(&[1000.0, 2000.0, 4000.0]);
let m = foo(x, y);
println!("{:?}", m);
}
use extendr_api::prelude::*;
use nalgebra as na;
/// Calculates ratio x/y.
/// @param x A nxn matrix.
/// @param y A 1xn vector.
/// @return A nxn matrix.
/// @export
#[extendr]
fn foo(
x: na::DMatrix<f64>,
y: na::DVector<f64>,
// change output to RArray
) -> RArray<f64, [usize;2]> {
let inv_y = y.map(|y| 1.0 / y);
// add .clone for I'll use x again later
let m = x.clone() * na::Matrix::from_diagonal(&inv_y);
// Convert m to R matrix
let m_r = RArray::new_matrix(x.nrows() as usize, y.len() as usize, |r, c| m[(r, c)]);
m_r
}
// Macro to generate exports.
// This ensures exported functions are registered with R.
// See corresponding C code in `entrypoint.c`.
extendr_module! {
mod package_name;
fn foo;
}
Still same error message.
Thanks to @SamR comments I was able to understand how {rextendr} works in this scenario.
use extendr_api::prelude::*;
use nalgebra as na;
/// Calculates ratio x/y.
/// @param m A nxn matrix.
/// @param v A 1xn vector.
/// @return A nxn matrix.
/// @export
#[extendr]
fn foo(
m: Vec<f64>,
v: Vec<f64>,
) -> RArray<f64, [usize;2]> {
// matrix dimensions
let n = (m.len() as f64).sqrt() as usize;
// convert input to nalgebra types and invert vector
let m_na = na::DMatrix::from_column_slice(n, n, &m);
let v_na = na::DVector::from_fn(n, |i, _| 1.0 / v[i]);
// calculate ratio
let ratio = m_na * v_na;
// Convert m to R matrix
let ratio_r = RArray::new_matrix(n, n, |row, col| ratio[(row, col)]);
ratio_r
}
// Macro to generate exports.
// This ensures exported functions are registered with R.
// See corresponding C code in `entrypoint.c`.
extendr_module! {
mod package_name;
fn foo;
}
In the function definition
fn foo(
m: Vec<f64>,
v: Vec<f64>,
) -> RArray<f64, [usize;2]> {
In this use case, my input m is a R matrix and is read as column-major vector by Rust, so m is Vec<64> (same as v that is just a 1xn matrix).
Therefore, I have to read that input as a big n² single vector and convert to nalgebra (DMatrix or DVector) so I can compute matrix algebra (cross product in this case).
Then I have to convert back to R through RArray::new_matrix() so my foo() outputs a matrix back in R.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With