Привет, Хабр!
Линейная алгебра сейчас применяется практические везде. В связс с этим сегодня рассмотрим одну из библиотек для 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-разложение разделяет матрицу на нижнюю треугольную матрицу и верхнюю треугольную матрицу .
Пример кода:
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-разложение разлагает матрицу на ортогональную матрицу и верхнюю треугольную матрицу .
Пример кода:
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-разложение представляет матрицу в виде произведения трех матриц: , диагональная матрица с сингулярными значениями и .
Пример разложения:
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<f32>, b: &Vector3<f32>) -> Vector3<f32> {
a + b
}
Для компиляции есть команда:
wasm-pack build --target web
Nalgebra также может использоваться с библиотеками для GPU вычислений, такими как wgpu и ash.
Библиотека nalgebra — мощный и гибкий инструмент для работы с линейной алгеброй в Rust.
Что делать, если у вас неопытная команда, а надо делать сложную задачу? Можно написать заявление об увольнении, а потом с ужасом рассказывать в интернете про прошлые места работы. А можно попробовать выстроить процессы так, чтобы получить требуемый результат. На открытом уроке мы не будем обсуждать, какая из этих двух стратегий правильная, но поговорим о том, как математика может помочь выстроить архитектуру приложения, которая позволит решить эту трудную задачу.
Урок пройдёт в рамках курса «Математика для программистов» 26 июля. Записаться можно по ссылке.