Работа с матрицами (интеграция Rust и R) пакет {extendr}

Возьмем следующую функцию 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);
}

Обновлено: Возврат объекта R

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;
}

Все еще то же сообщение об ошибке.

Это аналогичная проблема Как я могу вызвать функцию ржавчины из R, которая возвращает Vec<Vec<f64>>? По сути, R не понимает na::DMatrix<f64>, поэтому вы можете либо реализовать признак для этого объекта, чтобы преобразовать его во что-то, что понимает R, либо просто вернуть объект другого типа, например extendr_api::wrapper::matrix::RMatrix.

SamR 03.06.2024 17:03

Внимательно изучив сообщение об ошибке, можно сказать, что другая половина заключается в том, что вам необходимо реализовать from_robj для параметров, которые вы передаете функции, или иным образом передать ей различные типы аргументов, которые уже имеют это, например векторы.

SamR 03.06.2024 19:33

@SamR Как мне реализовать from_robj для моих параметров?

Alberson Miranda 03.06.2024 19:43

Я думаю, что проще всего было бы заставить функцию принимать аргументы типа, который может обрабатываться. Возможно, сигнатура функции вроде: extendr. Я никогда не использовал foo(x: &Vec<f64>, y: &Vec<f64>, x_nrow: &u32, x_ncol: &u32), но выглядит как, тогда вы можете сделать что-то вроде nalgebra и y_nalg = DVector::from(y).

SamR 03.06.2024 20:24

Тогда из R вы могли бы просто вызвать foo(c(1,2,3), c(1,2,3), 3L, 3L) или что-то в этом роде.

SamR 03.06.2024 20:25
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
5
85
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Благодаря комментариям @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.

Другие вопросы по теме