Библиотека nalgebra в Rust

Привет, Хабр!

Линейная алгебра сейчас применяется практические везде. В связс с этим сегодня рассмотрим одну из библиотек для Rust — nalgebra.

Основная цель nalgebra — предоставить инструмент для работы с линейной алгеброй.

Основные возможности

Векторы

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

Пример статического вектора:

use nalgebra::Vector3;

fn main() {
    let v = Vector3::new(1.0, 2.0, 3.0);
    println!("Vector: {:?}", v);
}

Пример динамического вектора:

use nalgebra::DVector;

fn main() {
    let v = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]);
    println!("Dynamic Vector: {:?}", v);
}

Матрицы

Матрицы также могут быть статическими и динамическими. Статические матрицы имеют размеры, известные на этапе компиляции, тогда как размеры динамических матриц могут изменяться во время выполнения.

Пример статической матрицы:

use nalgebra::Matrix3;

fn main() {
    let m = Matrix3::new(1.0, 2.0, 3.0,
                         4.0, 5.0, 6.0,
                         7.0, 8.0, 9.0);
    println!("Matrix:\n{}", m);
}

Пример динамической матрицы:

use nalgebra::DMatrix;

fn main() {
    let m = DMatrix::from_row_slice(3, 3, &[
        1.0, 2.0, 3.0,
        4.0, 5.0, 6.0,
        7.0, 8.0, 9.0
    ]);
    println!("Dynamic Matrix:\n{}", m);
}

Операции с векторами и матрицами включают сложение, умножение, транспонирование, инверсию и другие стандартные линейные операции.

Точки и трансформации

Точки

Точки в nalgebra представляют собой координаты в пространстве. Их используют для обозначения положений объектов, в отличие от векторов, которые обозначают направления и расстояния.

Пример:

use nalgebra::{Point2, Point3};

fn main() {
    let p2 = Point2::new(1.0, 2.0);
    let p3 = Point3::new(1.0, 2.0, 3.0);
    
    println!("Point2: {:?}", p2);
    println!("Point3: {:?}", p3);
}

Трансформации

Трансформации включают в себя различные операции, такие как переводы, вращения и масштабирования.

Пример:

use nalgebra::{Translation3, Rotation3, Isometry3, Vector3};

fn main() {
    let translation = Translation3::new(1.0, 2.0, 3.0);
    let rotation = Rotation3::from_axis_angle(&Vector3::z_axis(), 0.785398163); // 45 degrees in radians

    let isometry = Isometry3::new(translation.vector, rotation.scaled_axis());
    
    println!("Isometry:\n{}", isometry);
}

Специфические структуры данных

Кватернионы

Кватернионы используются для представления и вычисления 3D вращений. Иногда это некий мастхев для анимаций и графических приложений из-за своей компактности и отсутствия эффекта »гимбаллок».

Пример:

use nalgebra::{Quaternion, UnitQuaternion};

fn main() {
    let axis = Vector3::y_axis();
    let angle = 1.57; // 90 degrees in radians

    let quaternion = UnitQuaternion::from_axis_angle(&axis, angle);
    
    println!("Quaternion: {:?}", quaternion);
}

Изометрии

Изометрия сочетает в себе перевод и вращение. Эта структура нужна для представления позиций и ориентаций объектов в пространстве.

Пример:

use nalgebra::{Isometry3, Vector3};

fn main() {
    let translation = Vector3::new(1.0, 2.0, 3.0);
    let rotation = Vector3::new(0.0, 1.0, 0.0); // Rotate around Y axis

    let iso = Isometry3::new(translation, rotation);
    
    println!("Isometry:\n{}", iso);
}

Гомогенные матрицы

Гомогенные матрицы используются для представления общих линейных преобразований: переводы, вращения, масштабирования и сдвиги.

Пример:

use nalgebra::Matrix4;

fn main() {
    let mut m = Matrix4::identity();
    m[(0, 3)] = 1.0; // Translation along X axis
    m[(1, 1)] = 0.5; // Scaling along Y axis
    
    println!("Homogeneous Matrix:\n{}", m);
}

Прочие возможности

Факторизации матриц

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

LU-разложение

LU-разложение разделяет матрицу на нижнюю треугольную матрицу L и верхнюю треугольную матрицу U.

Пример кода:

use nalgebra::DMatrix;

fn main() {
    let matrix = DMatrix::from_row_slice(3, 3, &[2.0, 1.0, 1.0,
                                                 4.0, -6.0, 0.0,
                                                 -2.0, 7.0, 2.0]);
    let lu = matrix.lu();
    let l = lu.l();
    let u = lu.u();
    println!("L:\n{}", l);
    println!("U:\n{}", u);
}

QR-разложение

QR-разложение разлагает матрицу на ортогональную матрицу Qи верхнюю треугольную матрицу R.

Пример кода:

use nalgebra::DMatrix;

fn main() {
    let matrix = DMatrix::from_row_slice(3, 3, &[12.0, -51.0, 4.0,
                                                 6.0, 167.0, -68.0,
                                                -4.0, 24.0, -41.0]);
    let qr = matrix.qr();
    let q = qr.q();
    let r = qr.r();
    println!("Q:\n{}", q);
    println!("R:\n{}", r);
}

SVD-разложение

SVD-разложение представляет матрицу в виде произведения трех матриц: U, Σ диагональная матрица с сингулярными значениями и V^*.

Пример разложения:

use nalgebra::DMatrix;

fn main() {
    let matrix = DMatrix::from_row_slice(3, 2, &[1.0, 0.0,
                                                 0.0, 1.0,
                                                 1.0, 1.0]);
    let svd = matrix.svd(true, true);
    let u = svd.u.unwrap();
    let s = svd.singular_values;
    let v_t = svd.v_t.unwrap();
    println!("U:\n{}", u);
    println!("Σ:\n{}", s);
    println!("V^T:\n{}", v_t);
}

Специальные виды матриц

Разреженные матрицы

Разреженные матрицы содержат преимущественно нулевые элементы, что частенько позволяет сэкономить память. В nalgebra поддерживаются различные форматы разреженных матриц, такие как compressed sparse column и compressed sparse row.

Пример создания разреженной матрицы:

use nalgebra_sparse::csr::CsrMatrix;

fn main() {
    let num_rows = 4;
    let num_cols = 4;
    let row_offsets = vec![0, 2, 4, 7, 8];
    let col_indices = vec![0, 1, 1, 2, 0, 2, 3, 3];
    let values = vec![10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0];
    let csr = CsrMatrix::try_from_csr_data(num_rows, num_cols, row_offsets, col_indices, values).unwrap();
    println!("CSR Matrix:\n{}", csr);
}

Ортографические и перспективные проекции

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

Пример ортографической проекции:

use nalgebra::Orthographic3;

fn main() {
    let orthographic = Orthographic3::new(-1.0, 1.0, -1.0, 1.0, 0.1, 100.0);
    println!("Orthographic Projection:\n{}", orthographic.as_matrix());
}

Пример перспективной проекции:

use nalgebra::Perspective3;

fn main() {
    let perspective = Perspective3::new(16.0 / 9.0, 3.14 / 4.0, 0.1, 100.0);
    println!("Perspective Projection:\n{}", perspective.as_matrix());
}

Вычисления на GPU и встраиваемые системы

Nalgebra поддерживает компиляцию для WebAssembly и различные встраиваемые системы.

Пример компиляции для WebAssembly:

# В Cargo.toml добавьте поддержку wasm
[dependencies]
nalgebra = "0.27"
wasm-bindgen = "0.2"

# В main.rs
use nalgebra::Vector3;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add_vectors(a: &Vector3, b: &Vector3) -> Vector3 {
    a + b
}

Для компиляции есть команда:

wasm-pack build --target web

Nalgebra также может использоваться с библиотеками для GPU вычислений, такими как wgpu и ash.

Библиотека nalgebra — мощный и гибкий инструмент для работы с линейной алгеброй в Rust.

•‎ nalgebra на GitHub

Что делать, если у вас неопытная команда, а надо делать сложную задачу? Можно написать заявление об увольнении, а потом с ужасом рассказывать в интернете про прошлые места работы. А можно попробовать выстроить процессы так, чтобы получить требуемый результат. На открытом уроке мы не будем обсуждать, какая из этих двух стратегий правильная, но поговорим о том, как математика может помочь выстроить архитектуру приложения, которая позволит решить эту трудную задачу.

Урок пройдёт в рамках курса «Математика для программистов» 26 июля. Записаться можно по ссылке.

© Habrahabr.ru