use crate::core::RealScalar;
use nalgebra::{DMatrix, DVector, LU};
#[cfg(feature = "backend-ndarray")]
use ndarray::{Array1, Array2};
#[cfg(feature = "backend-ndarray")]
use ndarray_linalg::{Determinant as NdDeterminant, Eigh, Inverse, Lapack, Solve, UPLO};
use serde::{Serialize, Serializer};
use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::ops::{Add, Div, Mul, Neg, Sub};
pub trait LinearAlgebra<T: RealScalar>: Clone + Debug + Send + Sync + 'static {
type VectorStorage: Clone + Debug + PartialEq + Send + Sync + 'static;
type MatrixStorage: Clone + Debug + PartialEq + Send + Sync + 'static;
fn vector_zeros(len: usize) -> Self::VectorStorage;
fn vector_from_vec(values: Vec<T>) -> Self::VectorStorage;
fn vector_len(value: &Self::VectorStorage) -> usize;
fn vector_get(value: &Self::VectorStorage, index: usize) -> T;
fn vector_set(value: &mut Self::VectorStorage, index: usize, entry: T);
fn vector_to_vec(value: &Self::VectorStorage) -> Vec<T>;
fn vector_as_slice(value: &Self::VectorStorage) -> Option<&[T]> {
let _ = value;
None
}
fn vec_add(lhs: &Self::VectorStorage, rhs: &Self::VectorStorage) -> Self::VectorStorage;
fn vec_sub(lhs: &Self::VectorStorage, rhs: &Self::VectorStorage) -> Self::VectorStorage;
fn vec_neg(value: &Self::VectorStorage) -> Self::VectorStorage;
fn vec_scale(value: &Self::VectorStorage, alpha: T) -> Self::VectorStorage;
fn vec_add_scaled(
lhs: &Self::VectorStorage,
rhs: &Self::VectorStorage,
alpha: T,
) -> Self::VectorStorage {
Self::vec_add(lhs, &Self::vec_scale(rhs, alpha))
}
fn vec_dot(lhs: &Self::VectorStorage, rhs: &Self::VectorStorage) -> T;
fn vec_norm(value: &Self::VectorStorage) -> T;
fn vec_all_finite(value: &Self::VectorStorage) -> bool;
fn matrix_zeros(rows: usize, cols: usize) -> Self::MatrixStorage;
fn matrix_identity(n: usize) -> Self::MatrixStorage;
fn matrix_from_vec(rows: usize, cols: usize, values: Vec<T>) -> Self::MatrixStorage;
fn matrix_rows(value: &Self::MatrixStorage) -> usize;
fn matrix_cols(value: &Self::MatrixStorage) -> usize;
fn matrix_get(value: &Self::MatrixStorage, row: usize, col: usize) -> T;
fn matrix_set(value: &mut Self::MatrixStorage, row: usize, col: usize, entry: T);
fn mat_add(lhs: &Self::MatrixStorage, rhs: &Self::MatrixStorage) -> Self::MatrixStorage;
fn mat_sub(lhs: &Self::MatrixStorage, rhs: &Self::MatrixStorage) -> Self::MatrixStorage;
fn mat_scale(value: &Self::MatrixStorage, alpha: T) -> Self::MatrixStorage;
fn mat_transpose(value: &Self::MatrixStorage) -> Self::MatrixStorage;
fn mat_mul(lhs: &Self::MatrixStorage, rhs: &Self::MatrixStorage) -> Self::MatrixStorage;
fn mat_vec(matrix: &Self::MatrixStorage, vector: &Self::VectorStorage) -> Self::VectorStorage;
}
pub trait LinearSolve<T: RealScalar>: LinearAlgebra<T> {
fn lu_solve(
matrix: &Self::MatrixStorage,
rhs: &Self::VectorStorage,
) -> Option<Self::VectorStorage>;
fn lu_solve_matrix(
matrix: &Self::MatrixStorage,
rhs: &Self::MatrixStorage,
) -> Option<Self::MatrixStorage>;
fn lu_inverse(matrix: &Self::MatrixStorage) -> Option<Self::MatrixStorage>;
}
pub trait PseudoInverse<T: RealScalar>: LinearAlgebra<T> {
fn pseudo_inverse(matrix: &Self::MatrixStorage, epsilon: T) -> Option<Self::MatrixStorage>;
}
pub trait Determinant<T: RealScalar>: LinearAlgebra<T> {
fn determinant(matrix: &Self::MatrixStorage) -> Option<T>;
}
pub trait SymmetricEigen<T: RealScalar>: LinearAlgebra<T> {
fn symmetric_eigen(
matrix: &Self::MatrixStorage,
) -> Option<(Self::VectorStorage, Self::MatrixStorage)>;
}
#[repr(transparent)]
pub struct Scalar<T: RealScalar>(pub T);
impl<T: RealScalar> Clone for Scalar<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: RealScalar> Copy for Scalar<T> {}
impl<T: RealScalar> Debug for Scalar<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
impl<T: RealScalar> PartialEq for Scalar<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: RealScalar> Add for Scalar<T> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl<T: RealScalar> Sub for Scalar<T> {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl<T: RealScalar> Mul for Scalar<T> {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self(self.0 * rhs.0)
}
}
impl<T: RealScalar> Div for Scalar<T> {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self(self.0 / rhs.0)
}
}
impl<T: RealScalar> Neg for Scalar<T> {
type Output = Self;
fn neg(self) -> Self::Output {
Self(-self.0)
}
}
pub struct Vector<T: RealScalar = f64, B: LinearAlgebra<T> = NalgebraProvider> {
storage: B::VectorStorage,
_marker: PhantomData<(T, B)>,
}
impl<T, B> Serialize for Vector<T, B>
where
T: RealScalar,
B: LinearAlgebra<T>,
B::VectorStorage: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.storage.serialize(serializer)
}
}
impl<T, B> From<Vec<T>> for Vector<T, B>
where
T: RealScalar,
B: LinearAlgebra<T>,
{
fn from(values: Vec<T>) -> Self {
Self::from_vec(values)
}
}
impl<T, B> From<&[T]> for Vector<T, B>
where
T: RealScalar,
B: LinearAlgebra<T>,
{
fn from(values: &[T]) -> Self {
Self::from_vec(values.to_vec())
}
}
impl<T, B, const N: usize> From<[T; N]> for Vector<T, B>
where
T: RealScalar,
B: LinearAlgebra<T>,
{
fn from(values: [T; N]) -> Self {
Self::from_vec(Vec::from(values))
}
}
impl<T, B, const N: usize> From<&[T; N]> for Vector<T, B>
where
T: RealScalar,
B: LinearAlgebra<T>,
{
fn from(values: &[T; N]) -> Self {
Self::from_vec(values.to_vec())
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Vector<T, B> {
#[inline]
pub const fn from_storage(storage: B::VectorStorage) -> Self {
Self {
storage,
_marker: PhantomData,
}
}
#[inline]
pub fn into_storage(self) -> B::VectorStorage {
self.storage
}
#[inline]
pub const fn as_storage(&self) -> &B::VectorStorage {
&self.storage
}
#[inline]
pub const fn as_storage_mut(&mut self) -> &mut B::VectorStorage {
&mut self.storage
}
#[inline]
pub fn zeros(len: usize) -> Self {
Self::from_storage(B::vector_zeros(len))
}
#[inline]
pub fn from_vec(values: Vec<T>) -> Self {
Self::from_storage(B::vector_from_vec(values))
}
#[inline]
pub fn len(&self) -> usize {
B::vector_len(&self.storage)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn get(&self, index: usize) -> T {
B::vector_get(&self.storage, index)
}
#[inline]
pub fn set(&mut self, index: usize, entry: T) {
B::vector_set(&mut self.storage, index, entry);
}
#[inline]
pub fn to_vec(&self) -> Vec<T> {
B::vector_to_vec(&self.storage)
}
#[inline]
pub fn as_slice(&self) -> Option<&[T]> {
B::vector_as_slice(&self.storage)
}
#[inline]
pub fn dot(&self, rhs: &Self) -> T {
B::vec_dot(&self.storage, &rhs.storage)
}
#[inline]
pub fn norm(&self) -> T {
B::vec_norm(&self.storage)
}
#[inline]
pub fn scale(&self, alpha: T) -> Self {
Self::from_storage(B::vec_scale(&self.storage, alpha))
}
#[inline]
pub fn add(&self, rhs: &Self) -> Self {
self + rhs
}
#[inline]
pub fn sub(&self, rhs: &Self) -> Self {
self - rhs
}
#[inline]
pub fn neg(&self) -> Self {
-self
}
#[inline]
pub fn add_scaled(&self, rhs: &Self, alpha: T) -> Self {
Self::from_storage(B::vec_add_scaled(&self.storage, &rhs.storage, alpha))
}
#[inline]
pub fn all_finite(&self) -> bool {
B::vec_all_finite(&self.storage)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Clone for Vector<T, B> {
fn clone(&self) -> Self {
Self::from_storage(self.storage.clone())
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Debug for Vector<T, B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.storage.fmt(f)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> PartialEq for Vector<T, B> {
fn eq(&self, other: &Self) -> bool {
self.storage == other.storage
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Add<&Vector<T, B>> for &Vector<T, B> {
type Output = Vector<T, B>;
fn add(self, rhs: &Vector<T, B>) -> Self::Output {
Vector::from_storage(B::vec_add(&self.storage, &rhs.storage))
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Add<&Self> for Vector<T, B> {
type Output = Self;
fn add(self, rhs: &Self) -> Self::Output {
&self + rhs
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Add<Vector<T, B>> for &Vector<T, B> {
type Output = Vector<T, B>;
fn add(self, rhs: Vector<T, B>) -> Self::Output {
self + &rhs
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Add for Vector<T, B> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
&self + &rhs
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Sub<&Vector<T, B>> for &Vector<T, B> {
type Output = Vector<T, B>;
fn sub(self, rhs: &Vector<T, B>) -> Self::Output {
Vector::from_storage(B::vec_sub(&self.storage, &rhs.storage))
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Sub<&Self> for Vector<T, B> {
type Output = Self;
fn sub(self, rhs: &Self) -> Self::Output {
&self - rhs
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Sub<Vector<T, B>> for &Vector<T, B> {
type Output = Vector<T, B>;
fn sub(self, rhs: Vector<T, B>) -> Self::Output {
self - &rhs
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Sub for Vector<T, B> {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
&self - &rhs
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Neg for &Vector<T, B> {
type Output = Vector<T, B>;
fn neg(self) -> Self::Output {
Vector::from_storage(B::vec_neg(&self.storage))
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Neg for Vector<T, B> {
type Output = Self;
fn neg(self) -> Self::Output {
-&self
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Mul<T> for &Vector<T, B> {
type Output = Vector<T, B>;
fn mul(self, rhs: T) -> Self::Output {
self.scale(rhs)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Mul<T> for Vector<T, B> {
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
self.scale(rhs)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Div<T> for &Vector<T, B> {
type Output = Vector<T, B>;
fn div(self, rhs: T) -> Self::Output {
self.scale(T::one() / rhs)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Div<T> for Vector<T, B> {
type Output = Self;
fn div(self, rhs: T) -> Self::Output {
self.scale(T::one() / rhs)
}
}
pub struct Matrix<T: RealScalar = f64, B: LinearAlgebra<T> = NalgebraProvider> {
storage: B::MatrixStorage,
_marker: PhantomData<(T, B)>,
}
impl<T, B> Serialize for Matrix<T, B>
where
T: RealScalar,
B: LinearAlgebra<T>,
B::MatrixStorage: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.storage.serialize(serializer)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Matrix<T, B> {
pub const fn from_storage(storage: B::MatrixStorage) -> Self {
Self {
storage,
_marker: PhantomData,
}
}
pub fn into_storage(self) -> B::MatrixStorage {
self.storage
}
pub const fn as_storage(&self) -> &B::MatrixStorage {
&self.storage
}
pub const fn as_storage_mut(&mut self) -> &mut B::MatrixStorage {
&mut self.storage
}
pub fn zeros(rows: usize, cols: usize) -> Self {
Self::from_storage(B::matrix_zeros(rows, cols))
}
pub fn identity(n: usize) -> Self {
Self::from_storage(B::matrix_identity(n))
}
pub fn from_vec(rows: usize, cols: usize, values: Vec<T>) -> Self {
Self::from_storage(B::matrix_from_vec(rows, cols, values))
}
pub fn rows(&self) -> usize {
B::matrix_rows(&self.storage)
}
pub fn cols(&self) -> usize {
B::matrix_cols(&self.storage)
}
pub fn get(&self, row: usize, col: usize) -> T {
B::matrix_get(&self.storage, row, col)
}
pub fn set(&mut self, row: usize, col: usize, entry: T) {
B::matrix_set(&mut self.storage, row, col, entry);
}
pub fn scale(&self, alpha: T) -> Self {
Self::from_storage(B::mat_scale(&self.storage, alpha))
}
pub fn transpose(&self) -> Self {
Self::from_storage(B::mat_transpose(&self.storage))
}
pub fn mul_vec(&self, rhs: &Vector<T, B>) -> Vector<T, B> {
Vector::from_storage(B::mat_vec(&self.storage, &rhs.storage))
}
pub fn mul_mat(&self, rhs: &Self) -> Self {
Self::from_storage(B::mat_mul(&self.storage, &rhs.storage))
}
}
impl<T, B> Matrix<T, B>
where
T: RealScalar,
B: LinearSolve<T>,
{
pub fn lu_solve(&self, rhs: &Vector<T, B>) -> Option<Vector<T, B>> {
B::lu_solve(&self.storage, &rhs.storage).map(Vector::from_storage)
}
pub fn lu_solve_matrix(&self, rhs: &Self) -> Option<Self> {
B::lu_solve_matrix(&self.storage, &rhs.storage).map(Self::from_storage)
}
pub fn lu_inverse(&self) -> Option<Self> {
B::lu_inverse(&self.storage).map(Self::from_storage)
}
}
impl<T, B> Matrix<T, B>
where
T: RealScalar,
B: SymmetricEigen<T>,
{
pub fn symmetric_eigen(&self) -> Option<(Vector<T, B>, Self)> {
B::symmetric_eigen(&self.storage)
.map(|(values, vectors)| (Vector::from_storage(values), Self::from_storage(vectors)))
}
}
impl<T, B> Matrix<T, B>
where
T: RealScalar,
B: PseudoInverse<T>,
{
pub fn pseudo_inverse(&self, epsilon: T) -> Option<Self> {
B::pseudo_inverse(&self.storage, epsilon).map(Self::from_storage)
}
}
impl<T, B> Matrix<T, B>
where
T: RealScalar,
B: Determinant<T>,
{
pub fn determinant(&self) -> Option<T> {
B::determinant(&self.storage)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Clone for Matrix<T, B> {
fn clone(&self) -> Self {
Self::from_storage(self.storage.clone())
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Debug for Matrix<T, B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.storage.fmt(f)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> PartialEq for Matrix<T, B> {
fn eq(&self, other: &Self) -> bool {
self.storage == other.storage
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Add<&Matrix<T, B>> for &Matrix<T, B> {
type Output = Matrix<T, B>;
fn add(self, rhs: &Matrix<T, B>) -> Self::Output {
Matrix::from_storage(B::mat_add(&self.storage, &rhs.storage))
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Add for Matrix<T, B> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
&self + &rhs
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Sub<&Matrix<T, B>> for &Matrix<T, B> {
type Output = Matrix<T, B>;
fn sub(self, rhs: &Matrix<T, B>) -> Self::Output {
Matrix::from_storage(B::mat_sub(&self.storage, &rhs.storage))
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Sub for Matrix<T, B> {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
&self - &rhs
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Mul<T> for &Matrix<T, B> {
type Output = Matrix<T, B>;
fn mul(self, rhs: T) -> Self::Output {
self.scale(rhs)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Mul<T> for Matrix<T, B> {
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
self.scale(rhs)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Div<T> for &Matrix<T, B> {
type Output = Matrix<T, B>;
fn div(self, rhs: T) -> Self::Output {
self.scale(T::one() / rhs)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Div<T> for Matrix<T, B> {
type Output = Self;
fn div(self, rhs: T) -> Self::Output {
self.scale(T::one() / rhs)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Mul<&Vector<T, B>> for &Matrix<T, B> {
type Output = Vector<T, B>;
fn mul(self, rhs: &Vector<T, B>) -> Self::Output {
self.mul_vec(rhs)
}
}
impl<T: RealScalar, B: LinearAlgebra<T>> Mul<&Matrix<T, B>> for &Matrix<T, B> {
type Output = Matrix<T, B>;
fn mul(self, rhs: &Matrix<T, B>) -> Self::Output {
self.mul_mat(rhs)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct NalgebraProvider;
impl<T: RealScalar + nalgebra::RealField> LinearAlgebra<T> for NalgebraProvider {
type VectorStorage = DVector<T>;
type MatrixStorage = DMatrix<T>;
#[inline]
fn vector_zeros(len: usize) -> Self::VectorStorage {
DVector::from_element(len, <T as RealScalar>::zero())
}
#[inline]
fn vector_from_vec(values: Vec<T>) -> Self::VectorStorage {
DVector::from_vec(values)
}
#[inline]
fn vector_len(value: &Self::VectorStorage) -> usize {
value.len()
}
#[inline]
fn vector_get(value: &Self::VectorStorage, index: usize) -> T {
value[index]
}
#[inline]
fn vector_set(value: &mut Self::VectorStorage, index: usize, entry: T) {
value[index] = entry;
}
#[inline]
fn vector_to_vec(value: &Self::VectorStorage) -> Vec<T> {
value.iter().copied().collect()
}
#[inline]
fn vector_as_slice(value: &Self::VectorStorage) -> Option<&[T]> {
Some(value.as_slice())
}
#[inline]
fn vec_add(lhs: &Self::VectorStorage, rhs: &Self::VectorStorage) -> Self::VectorStorage {
lhs + rhs
}
#[inline]
fn vec_sub(lhs: &Self::VectorStorage, rhs: &Self::VectorStorage) -> Self::VectorStorage {
lhs - rhs
}
#[inline]
fn vec_neg(value: &Self::VectorStorage) -> Self::VectorStorage {
-value
}
#[inline]
fn vec_scale(value: &Self::VectorStorage, alpha: T) -> Self::VectorStorage {
value * alpha
}
#[inline]
fn vec_add_scaled(
lhs: &Self::VectorStorage,
rhs: &Self::VectorStorage,
alpha: T,
) -> Self::VectorStorage {
let mut result = lhs.clone();
result.axpy(alpha, rhs, <T as RealScalar>::one());
result
}
#[inline]
fn vec_dot(lhs: &Self::VectorStorage, rhs: &Self::VectorStorage) -> T {
lhs.dot(rhs)
}
#[inline]
fn vec_norm(value: &Self::VectorStorage) -> T {
value.norm()
}
#[inline]
fn vec_all_finite(value: &Self::VectorStorage) -> bool {
value.iter().all(|entry| entry.is_finite())
}
fn matrix_zeros(rows: usize, cols: usize) -> Self::MatrixStorage {
DMatrix::from_element(rows, cols, <T as RealScalar>::zero())
}
fn matrix_identity(n: usize) -> Self::MatrixStorage {
DMatrix::identity(n, n)
}
fn matrix_from_vec(rows: usize, cols: usize, values: Vec<T>) -> Self::MatrixStorage {
DMatrix::from_row_slice(rows, cols, &values)
}
fn matrix_rows(value: &Self::MatrixStorage) -> usize {
value.nrows()
}
fn matrix_cols(value: &Self::MatrixStorage) -> usize {
value.ncols()
}
fn matrix_get(value: &Self::MatrixStorage, row: usize, col: usize) -> T {
value[(row, col)]
}
fn matrix_set(value: &mut Self::MatrixStorage, row: usize, col: usize, entry: T) {
value[(row, col)] = entry;
}
fn mat_add(lhs: &Self::MatrixStorage, rhs: &Self::MatrixStorage) -> Self::MatrixStorage {
lhs + rhs
}
fn mat_sub(lhs: &Self::MatrixStorage, rhs: &Self::MatrixStorage) -> Self::MatrixStorage {
lhs - rhs
}
fn mat_scale(value: &Self::MatrixStorage, alpha: T) -> Self::MatrixStorage {
value * alpha
}
fn mat_transpose(value: &Self::MatrixStorage) -> Self::MatrixStorage {
value.transpose()
}
fn mat_mul(lhs: &Self::MatrixStorage, rhs: &Self::MatrixStorage) -> Self::MatrixStorage {
lhs * rhs
}
fn mat_vec(matrix: &Self::MatrixStorage, vector: &Self::VectorStorage) -> Self::VectorStorage {
matrix * vector
}
}
impl<T: RealScalar + nalgebra::RealField> LinearSolve<T> for NalgebraProvider {
fn lu_solve(
matrix: &Self::MatrixStorage,
rhs: &Self::VectorStorage,
) -> Option<Self::VectorStorage> {
LU::new(matrix.clone()).solve(rhs)
}
fn lu_solve_matrix(
matrix: &Self::MatrixStorage,
rhs: &Self::MatrixStorage,
) -> Option<Self::MatrixStorage> {
LU::new(matrix.clone()).solve(rhs)
}
fn lu_inverse(matrix: &Self::MatrixStorage) -> Option<Self::MatrixStorage> {
LU::new(matrix.clone()).try_inverse()
}
}
impl<T: RealScalar + nalgebra::RealField> PseudoInverse<T> for NalgebraProvider {
fn pseudo_inverse(matrix: &Self::MatrixStorage, epsilon: T) -> Option<Self::MatrixStorage> {
matrix.clone().pseudo_inverse(epsilon).ok()
}
}
impl<T: RealScalar + nalgebra::RealField> Determinant<T> for NalgebraProvider {
fn determinant(matrix: &Self::MatrixStorage) -> Option<T> {
matrix.is_square().then(|| matrix.clone().determinant())
}
}
impl<T: RealScalar + nalgebra::RealField> SymmetricEigen<T> for NalgebraProvider {
fn symmetric_eigen(
matrix: &Self::MatrixStorage,
) -> Option<(Self::VectorStorage, Self::MatrixStorage)> {
if !matrix.is_square() {
return None;
}
let decomposition = nalgebra::linalg::SymmetricEigen::new(matrix.clone());
Some((decomposition.eigenvalues, decomposition.eigenvectors))
}
}
#[cfg(feature = "backend-ndarray")]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct NdArrayProvider;
#[cfg(feature = "backend-ndarray")]
impl<T> LinearAlgebra<T> for NdArrayProvider
where
T: RealScalar + Lapack<Real = T> + ndarray::ScalarOperand,
{
type VectorStorage = Array1<T>;
type MatrixStorage = Array2<T>;
fn vector_zeros(len: usize) -> Self::VectorStorage {
Array1::zeros(len)
}
fn vector_from_vec(values: Vec<T>) -> Self::VectorStorage {
Array1::from_vec(values)
}
fn vector_len(value: &Self::VectorStorage) -> usize {
value.len()
}
fn vector_get(value: &Self::VectorStorage, index: usize) -> T {
value[index]
}
fn vector_set(value: &mut Self::VectorStorage, index: usize, entry: T) {
value[index] = entry;
}
fn vector_to_vec(value: &Self::VectorStorage) -> Vec<T> {
value.iter().copied().collect()
}
#[inline]
fn vector_as_slice(value: &Self::VectorStorage) -> Option<&[T]> {
value.as_slice()
}
fn vec_add(lhs: &Self::VectorStorage, rhs: &Self::VectorStorage) -> Self::VectorStorage {
lhs + rhs
}
fn vec_sub(lhs: &Self::VectorStorage, rhs: &Self::VectorStorage) -> Self::VectorStorage {
lhs - rhs
}
fn vec_neg(value: &Self::VectorStorage) -> Self::VectorStorage {
value.mapv(|entry| -entry)
}
fn vec_scale(value: &Self::VectorStorage, alpha: T) -> Self::VectorStorage {
value.mapv(|entry| entry * alpha)
}
fn vec_dot(lhs: &Self::VectorStorage, rhs: &Self::VectorStorage) -> T {
lhs.dot(rhs)
}
fn vec_norm(value: &Self::VectorStorage) -> T {
RealScalar::sqrt(value.dot(value))
}
fn vec_all_finite(value: &Self::VectorStorage) -> bool {
value.iter().all(|entry| entry.is_finite())
}
fn matrix_zeros(rows: usize, cols: usize) -> Self::MatrixStorage {
Array2::zeros((rows, cols))
}
fn matrix_identity(n: usize) -> Self::MatrixStorage {
Array2::eye(n)
}
fn matrix_from_vec(rows: usize, cols: usize, values: Vec<T>) -> Self::MatrixStorage {
assert_eq!(
values.len(),
rows.saturating_mul(cols),
"matrix dimensions must match the number of row-major values"
);
let mut matrix = Array2::zeros((rows, cols));
for (entry, value) in matrix.iter_mut().zip(values) {
*entry = value;
}
matrix
}
fn matrix_rows(value: &Self::MatrixStorage) -> usize {
value.nrows()
}
fn matrix_cols(value: &Self::MatrixStorage) -> usize {
value.ncols()
}
fn matrix_get(value: &Self::MatrixStorage, row: usize, col: usize) -> T {
value[(row, col)]
}
fn matrix_set(value: &mut Self::MatrixStorage, row: usize, col: usize, entry: T) {
value[(row, col)] = entry;
}
fn mat_add(lhs: &Self::MatrixStorage, rhs: &Self::MatrixStorage) -> Self::MatrixStorage {
lhs + rhs
}
fn mat_sub(lhs: &Self::MatrixStorage, rhs: &Self::MatrixStorage) -> Self::MatrixStorage {
lhs - rhs
}
fn mat_scale(value: &Self::MatrixStorage, alpha: T) -> Self::MatrixStorage {
value.mapv(|entry| entry * alpha)
}
fn mat_transpose(value: &Self::MatrixStorage) -> Self::MatrixStorage {
value.t().to_owned()
}
fn mat_mul(lhs: &Self::MatrixStorage, rhs: &Self::MatrixStorage) -> Self::MatrixStorage {
lhs.dot(rhs)
}
fn mat_vec(matrix: &Self::MatrixStorage, vector: &Self::VectorStorage) -> Self::VectorStorage {
matrix.dot(vector)
}
}
#[cfg(feature = "backend-ndarray")]
impl<T> LinearSolve<T> for NdArrayProvider
where
T: RealScalar + Lapack<Real = T> + ndarray::ScalarOperand,
{
fn lu_solve(
matrix: &Self::MatrixStorage,
rhs: &Self::VectorStorage,
) -> Option<Self::VectorStorage> {
matrix.solve(rhs).ok()
}
fn lu_solve_matrix(
matrix: &Self::MatrixStorage,
rhs: &Self::MatrixStorage,
) -> Option<Self::MatrixStorage> {
let mut solved = Self::matrix_zeros(matrix.ncols(), rhs.ncols());
for col in 0..rhs.ncols() {
let rhs_col = rhs.column(col).to_owned();
let x_col = matrix.solve(&rhs_col).ok()?;
solved.column_mut(col).assign(&x_col);
}
Some(solved)
}
fn lu_inverse(matrix: &Self::MatrixStorage) -> Option<Self::MatrixStorage> {
matrix.inv().ok()
}
}
#[cfg(feature = "backend-ndarray")]
impl<T> PseudoInverse<T> for NdArrayProvider
where
T: RealScalar + Lapack<Real = T> + ndarray::ScalarOperand,
{
fn pseudo_inverse(_matrix: &Self::MatrixStorage, _epsilon: T) -> Option<Self::MatrixStorage> {
None
}
}
#[cfg(feature = "backend-ndarray")]
impl<T> Determinant<T> for NdArrayProvider
where
T: RealScalar + Lapack<Real = T> + ndarray::ScalarOperand,
{
fn determinant(matrix: &Self::MatrixStorage) -> Option<T> {
matrix.det().ok()
}
}
#[cfg(feature = "backend-ndarray")]
impl<T> SymmetricEigen<T> for NdArrayProvider
where
T: RealScalar + Lapack<Real = T> + ndarray::ScalarOperand,
{
fn symmetric_eigen(
matrix: &Self::MatrixStorage,
) -> Option<(Self::VectorStorage, Self::MatrixStorage)> {
matrix.clone().eigh(UPLO::Lower).ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
fn vector_contract<B>()
where
B: LinearAlgebra<f64>,
{
let a = Vector::<f64, B>::from_vec(vec![1.0, 2.0, 3.0]);
let b = Vector::<f64, B>::from_vec(vec![4.0, 5.0, 6.0]);
assert_eq!(a.len(), 3);
assert_eq!(a.dot(&b), 32.0);
assert_relative_eq!(a.norm(), 14.0_f64.sqrt());
assert_eq!((&a * 2.0).to_vec(), vec![2.0, 4.0, 6.0]);
assert_eq!((&a + &b).to_vec(), vec![5.0, 7.0, 9.0]);
assert_eq!((&b - &a).to_vec(), vec![3.0, 3.0, 3.0]);
assert_eq!((-&a).to_vec(), vec![-1.0, -2.0, -3.0]);
}
fn matrix_contract<B>()
where
B: LinearAlgebra<f64> + LinearSolve<f64> + Determinant<f64>,
{
let mut matrix = Matrix::<f64, B>::identity(2);
matrix.set(0, 1, 2.0);
matrix.set(1, 0, 3.0);
let x = Vector::<f64, B>::from_vec(vec![5.0, 7.0]);
assert_eq!(matrix.mul_vec(&x).to_vec(), vec![19.0, 22.0]);
assert_eq!(matrix.transpose().get(0, 1), 3.0);
let a = Matrix::<f64, B>::from_vec(2, 2, vec![3.0, 2.0, 1.0, 2.0]);
let rhs = Vector::<f64, B>::from_vec(vec![5.0, 5.0]);
let Some(solved) = a.lu_solve(&rhs) else {
panic!("matrix should solve");
};
assert_relative_eq!(solved.get(0), 0.0);
assert_relative_eq!(solved.get(1), 2.5);
let Some(inverse) = a.lu_solve_matrix(&Matrix::identity(2)) else {
panic!("matrix RHS should solve");
};
assert_relative_eq!(inverse.get(0, 0), 0.5);
assert_relative_eq!(inverse.get(0, 1), -0.5);
assert_relative_eq!(inverse.get(1, 0), -0.25);
assert_relative_eq!(inverse.get(1, 1), 0.75);
let Some(inverse) = a.lu_inverse() else {
panic!("matrix should invert");
};
assert_relative_eq!(inverse.get(0, 0), 0.5);
assert_relative_eq!(inverse.get(0, 1), -0.5);
assert_relative_eq!(inverse.get(1, 0), -0.25);
assert_relative_eq!(inverse.get(1, 1), 0.75);
let Some(determinant) = a.determinant() else {
panic!("matrix should have determinant");
};
assert_relative_eq!(determinant, 4.0);
}
fn nalgebra_pseudo_inverse_contract() {
let singular = Matrix::<f64, NalgebraProvider>::from_vec(2, 2, vec![1.0, 2.0, 2.0, 4.0]);
let Some(pseudo_inverse) = singular.pseudo_inverse(f64::EPSILON.cbrt()) else {
panic!("pseudoinverse");
};
assert_eq!(pseudo_inverse.rows(), 2);
assert_eq!(pseudo_inverse.cols(), 2);
}
#[test]
fn nalgebra_provider_satisfies_contracts() {
vector_contract::<NalgebraProvider>();
matrix_contract::<NalgebraProvider>();
nalgebra_pseudo_inverse_contract();
}
#[test]
fn vector_converts_from_owned_and_borrowed_collections() {
let owned: Vector = vec![1.0, 2.0, 3.0].into();
let array: Vector = [4.0, 5.0, 6.0].into();
let borrowed_slice: Vector = (&[7.0, 8.0][..]).into();
let borrowed_array: Vector = (&[9.0, 10.0]).into();
assert_eq!(owned.to_vec(), vec![1.0, 2.0, 3.0]);
assert_eq!(array.to_vec(), vec![4.0, 5.0, 6.0]);
assert_eq!(borrowed_slice.to_vec(), vec![7.0, 8.0]);
assert_eq!(borrowed_array.to_vec(), vec![9.0, 10.0]);
}
#[cfg(feature = "backend-ndarray")]
#[test]
fn ndarray_provider_satisfies_contracts() {
vector_contract::<NdArrayProvider>();
matrix_contract::<NdArrayProvider>();
}
}