Возьмем следующую функцию R:
'# @param x A nxn matrix
'# @param y A 1xn matrix (vector)
foo = function(x, y) {
return(x %*% diag(1 / y)
}
Что он делает, так это преобразует каждый элемент вектора 1xn y
в 1/y и диагонализирует его в матрицу nxn с 1/y на диагонали. Затем матричное произведение x %*% diag(1/y)
создает матрицу nxn, состоящую из каждого элемента из j-го столбца матрицы x
, разделенного на j-й элемент y
. Довольно стандартно.
Поскольку R может медленно работать с большими матрицами (например, 5000x5000), я бы хотел попробовать использовать Rust. После настройки структуры проекта с помощью {rextendr} lib.rs
выглядит так:
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
компилируется без ошибок в Rust. Но при попытке интеграции с R получаю ошибку no function or associated item named 'from_robj' found for struct 'Matrix' in the current scope
. Я думаю, чего не хватает, так это создания оболочки {extendr} для типа Matrix.
Как действовать дальше?
Воспроизводимый пример foo
в 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;
}
Все еще то же сообщение об ошибке.
Внимательно изучив сообщение об ошибке, можно сказать, что другая половина заключается в том, что вам необходимо реализовать from_robj
для параметров, которые вы передаете функции, или иным образом передать ей различные типы аргументов, которые уже имеют это, например векторы.
@SamR Как мне реализовать from_robj
для моих параметров?
Я думаю, что проще всего было бы заставить функцию принимать аргументы типа, который может обрабатываться. Возможно, сигнатура функции вроде: extendr
. Я никогда не использовал foo(x: &Vec<f64>, y: &Vec<f64>, x_nrow: &u32, x_ncol: &u32)
, но выглядит как, тогда вы можете сделать что-то вроде nalgebra
и y_nalg = DVector::from(y)
.
Тогда из R вы могли бы просто вызвать foo(c(1,2,3), c(1,2,3), 3L, 3L)
или что-то в этом роде.
Благодаря комментариям @SamR я смог понять, как {rextendr} работает в этом сценарии.
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;
}
В определении функции
fn foo(
m: Vec<f64>,
v: Vec<f64>,
) -> RArray<f64, [usize;2]> {
В этом случае мои входные данные m
представляют собой матрицу R и считываются Rust как главный вектор-столбец, поэтому m
— это Vec<64> (то же самое, что v
, это просто матрица 1xn).
Следовательно, мне нужно прочитать эти входные данные как одиночный вектор большого размера n² и преобразовать в налалгебру (DMatrix
или DVector
), чтобы я мог вычислить матричную алгебру (в данном случае перекрестное произведение).
Затем мне нужно преобразовать обратно в R через RArray::new_matrix()
, чтобы мой foo()
вывел матрицу обратно в R.
Это аналогичная проблема Как я могу вызвать функцию ржавчины из R, которая возвращает Vec<Vec<f64>>? По сути, R не понимает
na::DMatrix<f64>
, поэтому вы можете либо реализовать признак для этого объекта, чтобы преобразовать его во что-то, что понимает R, либо просто вернуть объект другого типа, напримерextendr_api::wrapper::matrix::RMatrix
.