commit
23ac85e896
|
@ -69,6 +69,12 @@ jobs:
|
||||||
- name: test nalgebra-sparse (slow tests)
|
- name: test nalgebra-sparse (slow tests)
|
||||||
# Unfortunately, the "slow-tests" take so much time that we need to run them with --release
|
# Unfortunately, the "slow-tests" take so much time that we need to run them with --release
|
||||||
run: PROPTEST_CASES=10000 cargo test --release --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,slow-tests slow
|
run: PROPTEST_CASES=10000 cargo test --release --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,slow-tests slow
|
||||||
|
test-nalgebra-macros:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: test nalgebra-macros
|
||||||
|
run: cargo test -p nalgebra-macros
|
||||||
build-wasm:
|
build-wasm:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# env:
|
# env:
|
||||||
|
|
|
@ -22,7 +22,7 @@ name = "nalgebra"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [ "std" ]
|
default = [ "std", "macros" ]
|
||||||
std = [ "matrixmultiply", "simba/std" ]
|
std = [ "matrixmultiply", "simba/std" ]
|
||||||
sparse = [ ]
|
sparse = [ ]
|
||||||
debug = [ "approx/num-complex", "rand" ]
|
debug = [ "approx/num-complex", "rand" ]
|
||||||
|
@ -32,6 +32,7 @@ compare = [ "matrixcompare-core" ]
|
||||||
libm = [ "simba/libm" ]
|
libm = [ "simba/libm" ]
|
||||||
libm-force = [ "simba/libm_force" ]
|
libm-force = [ "simba/libm_force" ]
|
||||||
no_unsound_assume_init = [ ]
|
no_unsound_assume_init = [ ]
|
||||||
|
macros = [ "nalgebra-macros" ]
|
||||||
|
|
||||||
# Conversion
|
# Conversion
|
||||||
convert-mint = [ "mint" ]
|
convert-mint = [ "mint" ]
|
||||||
|
@ -60,6 +61,7 @@ proptest-support = [ "proptest" ]
|
||||||
slow-tests = []
|
slow-tests = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
nalgebra-macros = { version = "0.1", path = "nalgebra-macros", optional = true }
|
||||||
typenum = "1.12"
|
typenum = "1.12"
|
||||||
rand-package = { package = "rand", version = "0.8", optional = true, default-features = false }
|
rand-package = { package = "rand", version = "0.8", optional = true, default-features = false }
|
||||||
num-traits = { version = "0.2", default-features = false }
|
num-traits = { version = "0.2", default-features = false }
|
||||||
|
@ -92,7 +94,7 @@ matrixcompare = "0.3.0"
|
||||||
itertools = "0.10"
|
itertools = "0.10"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [ "nalgebra-lapack", "nalgebra-glm", "nalgebra-sparse" ]
|
members = [ "nalgebra-lapack", "nalgebra-glm", "nalgebra-sparse", "nalgebra-macros" ]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
|
@ -109,4 +111,4 @@ lto = true
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
# Enable certain features when building docs for docs.rs
|
# Enable certain features when building docs for docs.rs
|
||||||
features = [ "proptest-support", "compare" ]
|
features = [ "proptest-support", "compare", "macros" ]
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
[package]
|
||||||
|
name = "nalgebra-macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = [ "Andreas Longva", "Sébastien Crozet <developer@crozet.re>" ]
|
||||||
|
edition = "2018"
|
||||||
|
description = "Procedural macros for nalgebra"
|
||||||
|
documentation = "https://www.nalgebra.org/docs"
|
||||||
|
homepage = "https://nalgebra.org"
|
||||||
|
repository = "https://github.com/dimforge/nalgebra"
|
||||||
|
readme = "../README.md"
|
||||||
|
categories = [ "science", "mathematics" ]
|
||||||
|
keywords = [ "linear", "algebra", "matrix", "vector", "math" ]
|
||||||
|
license = "Apache-2.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
syn = { version="1.0", features = ["full"] }
|
||||||
|
quote = "1.0"
|
||||||
|
proc-macro2 = "1.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
nalgebra = { version = "0.26.1", path = ".." }
|
||||||
|
trybuild = "1.0.42"
|
|
@ -0,0 +1,282 @@
|
||||||
|
//! Macros for `nalgebra`.
|
||||||
|
//!
|
||||||
|
//! This crate is not intended for direct consumption. Instead, the macros are re-exported by
|
||||||
|
//! `nalgebra` if the `macros` feature is enabled (enabled by default).
|
||||||
|
|
||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
|
use syn::parse::{Error, Parse, ParseStream, Result};
|
||||||
|
use syn::punctuated::Punctuated;
|
||||||
|
use syn::Expr;
|
||||||
|
use syn::{parse_macro_input, Token};
|
||||||
|
|
||||||
|
use proc_macro2::{Delimiter, Spacing, TokenStream as TokenStream2, TokenTree};
|
||||||
|
use proc_macro2::{Group, Punct};
|
||||||
|
|
||||||
|
struct Matrix {
|
||||||
|
// Represent the matrix as a row-major vector of vectors of expressions
|
||||||
|
rows: Vec<Vec<Expr>>,
|
||||||
|
ncols: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Matrix {
|
||||||
|
fn nrows(&self) -> usize {
|
||||||
|
self.rows.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ncols(&self) -> usize {
|
||||||
|
self.ncols
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces a stream of tokens representing this matrix as a column-major nested array.
|
||||||
|
fn to_col_major_nested_array_tokens(&self) -> TokenStream2 {
|
||||||
|
let mut result = TokenStream2::new();
|
||||||
|
for j in 0..self.ncols() {
|
||||||
|
let mut col = TokenStream2::new();
|
||||||
|
let col_iter = (0..self.nrows()).map(move |i| &self.rows[i][j]);
|
||||||
|
col.append_separated(col_iter, Punct::new(',', Spacing::Alone));
|
||||||
|
result.append(Group::new(Delimiter::Bracket, col));
|
||||||
|
result.append(Punct::new(',', Spacing::Alone));
|
||||||
|
}
|
||||||
|
TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, result)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces a stream of tokens representing this matrix as a column-major flat array
|
||||||
|
/// (suitable for representing e.g. a `DMatrix`).
|
||||||
|
fn to_col_major_flat_array_tokens(&self) -> TokenStream2 {
|
||||||
|
let mut data = TokenStream2::new();
|
||||||
|
for j in 0..self.ncols() {
|
||||||
|
for i in 0..self.nrows() {
|
||||||
|
self.rows[i][j].to_tokens(&mut data);
|
||||||
|
data.append(Punct::new(',', Spacing::Alone));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, data)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MatrixRowSyntax = Punctuated<Expr, Token![,]>;
|
||||||
|
|
||||||
|
impl Parse for Matrix {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let mut rows = Vec::new();
|
||||||
|
let mut ncols = None;
|
||||||
|
|
||||||
|
while !input.is_empty() {
|
||||||
|
let row_span = input.span();
|
||||||
|
let row = MatrixRowSyntax::parse_separated_nonempty(input)?;
|
||||||
|
|
||||||
|
if let Some(ncols) = ncols {
|
||||||
|
if row.len() != ncols {
|
||||||
|
let row_idx = rows.len();
|
||||||
|
let error_msg = format!(
|
||||||
|
"Unexpected number of entries in row {}. Expected {}, found {} entries.",
|
||||||
|
row_idx,
|
||||||
|
ncols,
|
||||||
|
row.len()
|
||||||
|
);
|
||||||
|
return Err(Error::new(row_span, error_msg));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ncols = Some(row.len());
|
||||||
|
}
|
||||||
|
rows.push(row.into_iter().collect());
|
||||||
|
|
||||||
|
// We've just read a row, so if there are more tokens, there must be a semi-colon,
|
||||||
|
// otherwise the input is malformed
|
||||||
|
if !input.is_empty() {
|
||||||
|
input.parse::<Token![;]>()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
rows,
|
||||||
|
ncols: ncols.unwrap_or(0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a fixed-size matrix directly from data.
|
||||||
|
///
|
||||||
|
/// **Note: Requires the `macro` feature to be enabled (enabled by default)**.
|
||||||
|
///
|
||||||
|
/// This macro facilitates easy construction of matrices when the entries of the matrix are known
|
||||||
|
/// (either as constants or expressions). This macro produces an instance of `SMatrix`. This means
|
||||||
|
/// that the data of the matrix is stored on the stack, and its dimensions are fixed at
|
||||||
|
/// compile-time. If you want to construct a dynamic matrix, use [`dmatrix!`] instead.
|
||||||
|
///
|
||||||
|
/// `matrix!` is intended to be both the simplest and most efficient way to construct (small)
|
||||||
|
/// matrices, and can also be used in *const fn* contexts.
|
||||||
|
///
|
||||||
|
/// The syntax is MATLAB-like. Column elements are separated by a comma (`,`), and a semi-colon
|
||||||
|
/// (`;`) designates that a new row begins.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use nalgebra::matrix;
|
||||||
|
///
|
||||||
|
/// // Produces a Matrix3<_> == SMatrix<_, 3, 3>
|
||||||
|
/// let a = matrix![1, 2, 3;
|
||||||
|
/// 4, 5, 6;
|
||||||
|
/// 7, 8, 9];
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// You can construct matrices with arbitrary expressions for its elements:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use nalgebra::{matrix, Matrix2};
|
||||||
|
/// let theta = 0.45f64;
|
||||||
|
///
|
||||||
|
/// let r = matrix![theta.cos(), - theta.sin();
|
||||||
|
/// theta.sin(), theta.cos()];
|
||||||
|
/// ```
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn matrix(stream: TokenStream) -> TokenStream {
|
||||||
|
let matrix = parse_macro_input!(stream as Matrix);
|
||||||
|
|
||||||
|
let row_dim = matrix.nrows();
|
||||||
|
let col_dim = matrix.ncols();
|
||||||
|
|
||||||
|
let array_tokens = matrix.to_col_major_nested_array_tokens();
|
||||||
|
|
||||||
|
// TODO: Use quote_spanned instead??
|
||||||
|
let output = quote! {
|
||||||
|
nalgebra::SMatrix::<_, #row_dim, #col_dim>
|
||||||
|
::from_array_storage(nalgebra::ArrayStorage(#array_tokens))
|
||||||
|
};
|
||||||
|
|
||||||
|
proc_macro::TokenStream::from(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a dynamic matrix directly from data.
|
||||||
|
///
|
||||||
|
/// **Note: Requires the `macro` feature to be enabled (enabled by default)**.
|
||||||
|
///
|
||||||
|
/// The syntax is exactly the same as for [`matrix!`], but instead of producing instances of
|
||||||
|
/// `SMatrix`, it produces instances of `DMatrix`. At the moment it is not usable
|
||||||
|
/// in `const fn` contexts.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use nalgebra::dmatrix;
|
||||||
|
///
|
||||||
|
/// // Produces a DMatrix<_>
|
||||||
|
/// let a = dmatrix![1, 2, 3;
|
||||||
|
/// 4, 5, 6;
|
||||||
|
/// 7, 8, 9];
|
||||||
|
/// ```
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn dmatrix(stream: TokenStream) -> TokenStream {
|
||||||
|
let matrix = parse_macro_input!(stream as Matrix);
|
||||||
|
|
||||||
|
let row_dim = matrix.nrows();
|
||||||
|
let col_dim = matrix.ncols();
|
||||||
|
|
||||||
|
let array_tokens = matrix.to_col_major_flat_array_tokens();
|
||||||
|
|
||||||
|
// TODO: Use quote_spanned instead??
|
||||||
|
let output = quote! {
|
||||||
|
nalgebra::DMatrix::<_>
|
||||||
|
::from_vec_storage(nalgebra::VecStorage::new(
|
||||||
|
nalgebra::Dynamic::new(#row_dim),
|
||||||
|
nalgebra::Dynamic::new(#col_dim),
|
||||||
|
vec!#array_tokens))
|
||||||
|
};
|
||||||
|
|
||||||
|
proc_macro::TokenStream::from(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Vector {
|
||||||
|
elements: Vec<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vector {
|
||||||
|
fn to_array_tokens(&self) -> TokenStream2 {
|
||||||
|
let mut data = TokenStream2::new();
|
||||||
|
data.append_separated(&self.elements, Punct::new(',', Spacing::Alone));
|
||||||
|
TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, data)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.elements.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Vector {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
// The syntax of a vector is just the syntax of a single matrix row
|
||||||
|
if input.is_empty() {
|
||||||
|
Ok(Self {
|
||||||
|
elements: Vec::new(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let elements = MatrixRowSyntax::parse_separated_nonempty(input)?
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
Ok(Self { elements })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a fixed-size column vector directly from data.
|
||||||
|
///
|
||||||
|
/// **Note: Requires the `macro` feature to be enabled (enabled by default)**.
|
||||||
|
///
|
||||||
|
/// Similarly to [`matrix!`], this macro facilitates easy construction of fixed-size vectors.
|
||||||
|
/// However, whereas the [`matrix!`] macro expects each row to be separated by a semi-colon,
|
||||||
|
/// the syntax of this macro is instead similar to `vec!`, in that the elements of the vector
|
||||||
|
/// are simply listed consecutively.
|
||||||
|
///
|
||||||
|
/// `vector!` is intended to be the most readable and performant way of constructing small,
|
||||||
|
/// fixed-size vectors, and it is usable in `const fn` contexts.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use nalgebra::vector;
|
||||||
|
///
|
||||||
|
/// // Produces a Vector3<_> == SVector<_, 3>
|
||||||
|
/// let v = vector![1, 2, 3];
|
||||||
|
/// ```
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn vector(stream: TokenStream) -> TokenStream {
|
||||||
|
let vector = parse_macro_input!(stream as Vector);
|
||||||
|
let len = vector.len();
|
||||||
|
let array_tokens = vector.to_array_tokens();
|
||||||
|
let output = quote! {
|
||||||
|
nalgebra::SVector::<_, #len>
|
||||||
|
::from_array_storage(nalgebra::ArrayStorage([#array_tokens]))
|
||||||
|
};
|
||||||
|
proc_macro::TokenStream::from(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a dynamic column vector directly from data.
|
||||||
|
///
|
||||||
|
/// **Note: Requires the `macro` feature to be enabled (enabled by default)**.
|
||||||
|
///
|
||||||
|
/// The syntax is exactly the same as for [`vector!`], but instead of producing instances of
|
||||||
|
/// `SVector`, it produces instances of `DVector`. At the moment it is not usable
|
||||||
|
/// in `const fn` contexts.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use nalgebra::dvector;
|
||||||
|
///
|
||||||
|
/// // Produces a DVector<_>
|
||||||
|
/// let v = dvector![1, 2, 3];
|
||||||
|
/// ```
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn dvector(stream: TokenStream) -> TokenStream {
|
||||||
|
let vector = parse_macro_input!(stream as Vector);
|
||||||
|
let len = vector.len();
|
||||||
|
let array_tokens = vector.to_array_tokens();
|
||||||
|
let output = quote! {
|
||||||
|
nalgebra::DVector::<_>
|
||||||
|
::from_vec_storage(nalgebra::VecStorage::new(
|
||||||
|
nalgebra::Dynamic::new(#len),
|
||||||
|
nalgebra::Const::<1>,
|
||||||
|
vec!#array_tokens))
|
||||||
|
};
|
||||||
|
proc_macro::TokenStream::from(output)
|
||||||
|
}
|
|
@ -0,0 +1,258 @@
|
||||||
|
use nalgebra::{
|
||||||
|
DMatrix, DVector, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2, Matrix2x1, Matrix2x3, Matrix2x4,
|
||||||
|
Matrix3, Matrix3x1, Matrix3x2, Matrix3x4, Matrix4, Matrix4x1, Matrix4x2, Matrix4x3, SMatrix,
|
||||||
|
SVector, Vector1, Vector2, Vector3, Vector4, Vector5, Vector6,
|
||||||
|
};
|
||||||
|
use nalgebra_macros::{dmatrix, dvector, matrix, vector};
|
||||||
|
|
||||||
|
fn check_statically_same_type<T>(_: &T, _: &T) {}
|
||||||
|
|
||||||
|
/// Wrapper for `assert_eq` that also asserts that the types are the same
|
||||||
|
macro_rules! assert_eq_and_type {
|
||||||
|
($left:expr, $right:expr $(,)?) => {
|
||||||
|
check_statically_same_type(&$left, &$right);
|
||||||
|
assert_eq!($left, $right);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip rustfmt because it just makes the test bloated without making it more readable
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[test]
|
||||||
|
fn matrix_small_dims_exhaustive() {
|
||||||
|
// 0x0
|
||||||
|
assert_eq_and_type!(matrix![], SMatrix::<i32, 0, 0>::zeros());
|
||||||
|
|
||||||
|
// 1xN
|
||||||
|
assert_eq_and_type!(matrix![1], SMatrix::<i32, 1, 1>::new(1));
|
||||||
|
assert_eq_and_type!(matrix![1, 2], Matrix1x2::new(1, 2));
|
||||||
|
assert_eq_and_type!(matrix![1, 2, 3], Matrix1x3::new(1, 2, 3));
|
||||||
|
assert_eq_and_type!(matrix![1, 2, 3, 4], Matrix1x4::new(1, 2, 3, 4));
|
||||||
|
|
||||||
|
// 2xN
|
||||||
|
assert_eq_and_type!(matrix![1; 2], Matrix2x1::new(1, 2));
|
||||||
|
assert_eq_and_type!(matrix![1, 2; 3, 4], Matrix2::new(1, 2, 3, 4));
|
||||||
|
assert_eq_and_type!(matrix![1, 2, 3; 4, 5, 6], Matrix2x3::new(1, 2, 3, 4, 5, 6));
|
||||||
|
assert_eq_and_type!(matrix![1, 2, 3, 4; 5, 6, 7, 8], Matrix2x4::new(1, 2, 3, 4, 5, 6, 7, 8));
|
||||||
|
|
||||||
|
// 3xN
|
||||||
|
assert_eq_and_type!(matrix![1; 2; 3], Matrix3x1::new(1, 2, 3));
|
||||||
|
assert_eq_and_type!(matrix![1, 2; 3, 4; 5, 6], Matrix3x2::new(1, 2, 3, 4, 5, 6));
|
||||||
|
assert_eq_and_type!(matrix![1, 2, 3; 4, 5, 6; 7, 8, 9], Matrix3::new(1, 2, 3, 4, 5, 6, 7, 8, 9));
|
||||||
|
assert_eq_and_type!(matrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12],
|
||||||
|
Matrix3x4::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12));
|
||||||
|
|
||||||
|
// 4xN
|
||||||
|
assert_eq_and_type!(matrix![1; 2; 3; 4], Matrix4x1::new(1, 2, 3, 4));
|
||||||
|
assert_eq_and_type!(matrix![1, 2; 3, 4; 5, 6; 7, 8], Matrix4x2::new(1, 2, 3, 4, 5, 6, 7, 8));
|
||||||
|
assert_eq_and_type!(matrix![1, 2, 3; 4, 5, 6; 7, 8, 9; 10, 11, 12],
|
||||||
|
Matrix4x3::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12));
|
||||||
|
assert_eq_and_type!(matrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12; 13, 14, 15, 16],
|
||||||
|
Matrix4::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matrix_const_fn() {
|
||||||
|
// Ensure that matrix! can be used in const contexts
|
||||||
|
const _: SMatrix<i32, 0, 0> = matrix![];
|
||||||
|
const _: SMatrix<i32, 1, 2> = matrix![1, 2];
|
||||||
|
const _: SMatrix<i32, 2, 3> = matrix![1, 2, 3; 4, 5, 6];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip rustfmt because it just makes the test bloated without making it more readable
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[test]
|
||||||
|
fn dmatrix_small_dims_exhaustive() {
|
||||||
|
// 0x0
|
||||||
|
assert_eq_and_type!(dmatrix![], DMatrix::<i32>::zeros(0, 0));
|
||||||
|
|
||||||
|
// 1xN
|
||||||
|
assert_eq_and_type!(dmatrix![1], DMatrix::from_row_slice(1, 1, &[1]));
|
||||||
|
assert_eq_and_type!(dmatrix![1, 2], DMatrix::from_row_slice(1, 2, &[1, 2]));
|
||||||
|
assert_eq_and_type!(dmatrix![1, 2, 3], DMatrix::from_row_slice(1, 3, &[1, 2, 3]));
|
||||||
|
assert_eq_and_type!(dmatrix![1, 2, 3, 4], DMatrix::from_row_slice(1, 4, &[1, 2, 3, 4]));
|
||||||
|
|
||||||
|
// 2xN
|
||||||
|
assert_eq_and_type!(dmatrix![1; 2], DMatrix::from_row_slice(2, 1, &[1, 2]));
|
||||||
|
assert_eq_and_type!(dmatrix![1, 2; 3, 4], DMatrix::from_row_slice(2, 2, &[1, 2, 3, 4]));
|
||||||
|
assert_eq_and_type!(dmatrix![1, 2, 3; 4, 5, 6], DMatrix::from_row_slice(2, 3, &[1, 2, 3, 4, 5, 6]));
|
||||||
|
assert_eq_and_type!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8], DMatrix::from_row_slice(2, 4, &[1, 2, 3, 4, 5, 6, 7, 8]));
|
||||||
|
|
||||||
|
// 3xN
|
||||||
|
assert_eq_and_type!(dmatrix![1; 2; 3], DMatrix::from_row_slice(3, 1, &[1, 2, 3]));
|
||||||
|
assert_eq_and_type!(dmatrix![1, 2; 3, 4; 5, 6], DMatrix::from_row_slice(3, 2, &[1, 2, 3, 4, 5, 6]));
|
||||||
|
assert_eq_and_type!(dmatrix![1, 2, 3; 4, 5, 6; 7, 8, 9], DMatrix::from_row_slice(3, 3, &[1, 2, 3, 4, 5, 6, 7, 8, 9]));
|
||||||
|
assert_eq_and_type!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12],
|
||||||
|
DMatrix::from_row_slice(3, 4, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]));
|
||||||
|
|
||||||
|
// 4xN
|
||||||
|
assert_eq_and_type!(dmatrix![1; 2; 3; 4], DMatrix::from_row_slice(4, 1, &[1, 2, 3, 4]));
|
||||||
|
assert_eq_and_type!(dmatrix![1, 2; 3, 4; 5, 6; 7, 8], DMatrix::from_row_slice(4, 2, &[1, 2, 3, 4, 5, 6, 7, 8]));
|
||||||
|
assert_eq_and_type!(dmatrix![1, 2, 3; 4, 5, 6; 7, 8, 9; 10, 11, 12],
|
||||||
|
DMatrix::from_row_slice(4, 3, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]));
|
||||||
|
assert_eq_and_type!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12; 13, 14, 15, 16],
|
||||||
|
DMatrix::from_row_slice(4, 4, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip rustfmt because it just makes the test bloated without making it more readable
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[test]
|
||||||
|
fn vector_small_dims_exhaustive() {
|
||||||
|
assert_eq_and_type!(vector![], SVector::<i32, 0>::zeros());
|
||||||
|
assert_eq_and_type!(vector![1], Vector1::<i32>::new(1));
|
||||||
|
assert_eq_and_type!(vector![1, 2], Vector2::new(1, 2));
|
||||||
|
assert_eq_and_type!(vector![1, 2, 3], Vector3::new(1, 2, 3));
|
||||||
|
assert_eq_and_type!(vector![1, 2, 3, 4], Vector4::new(1, 2, 3, 4));
|
||||||
|
assert_eq_and_type!(vector![1, 2, 3, 4, 5], Vector5::new(1, 2, 3, 4, 5));
|
||||||
|
assert_eq_and_type!(vector![1, 2, 3, 4, 5, 6], Vector6::new(1, 2, 3, 4, 5, 6));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vector_const_fn() {
|
||||||
|
// Ensure that vector! can be used in const contexts
|
||||||
|
const _: SVector<i32, 0> = vector![];
|
||||||
|
const _: Vector1<i32> = vector![1];
|
||||||
|
const _: Vector2<i32> = vector![1, 2];
|
||||||
|
const _: Vector6<i32> = vector![1, 2, 3, 4, 5, 6];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip rustfmt because it just makes the test bloated without making it more readable
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[test]
|
||||||
|
fn dvector_small_dims_exhaustive() {
|
||||||
|
assert_eq_and_type!(dvector![], DVector::<i32>::zeros(0));
|
||||||
|
assert_eq_and_type!(dvector![1], DVector::from_column_slice(&[1]));
|
||||||
|
assert_eq_and_type!(dvector![1, 2], DVector::from_column_slice(&[1, 2]));
|
||||||
|
assert_eq_and_type!(dvector![1, 2, 3], DVector::from_column_slice(&[1, 2, 3]));
|
||||||
|
assert_eq_and_type!(dvector![1, 2, 3, 4], DVector::from_column_slice(&[1, 2, 3, 4]));
|
||||||
|
assert_eq_and_type!(dvector![1, 2, 3, 4, 5], DVector::from_column_slice(&[1, 2, 3, 4, 5]));
|
||||||
|
assert_eq_and_type!(dvector![1, 2, 3, 4, 5, 6], DVector::from_column_slice(&[1, 2, 3, 4, 5, 6]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matrix_trybuild_tests() {
|
||||||
|
let t = trybuild::TestCases::new();
|
||||||
|
|
||||||
|
// Verify error message when we give a matrix with mismatched dimensions
|
||||||
|
t.compile_fail("tests/trybuild/matrix_mismatched_dimensions.rs");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dmatrix_trybuild_tests() {
|
||||||
|
let t = trybuild::TestCases::new();
|
||||||
|
|
||||||
|
// Verify error message when we give a matrix with mismatched dimensions
|
||||||
|
t.compile_fail("tests/trybuild/dmatrix_mismatched_dimensions.rs");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matrix_builtin_types() {
|
||||||
|
// Check that matrix! compiles for all built-in types
|
||||||
|
const _: SMatrix<i8, 2, 2> = matrix![0, 1; 2, 3];
|
||||||
|
const _: SMatrix<i16, 2, 2> = matrix![0, 1; 2, 3];
|
||||||
|
const _: SMatrix<i32, 2, 2> = matrix![0, 1; 2, 3];
|
||||||
|
const _: SMatrix<i64, 2, 2> = matrix![0, 1; 2, 3];
|
||||||
|
const _: SMatrix<isize, 2, 2> = matrix![0, 1; 2, 3];
|
||||||
|
const _: SMatrix<u8, 2, 2> = matrix![0, 1; 2, 3];
|
||||||
|
const _: SMatrix<u16, 2, 2> = matrix![0, 1; 2, 3];
|
||||||
|
const _: SMatrix<u32, 2, 2> = matrix![0, 1; 2, 3];
|
||||||
|
const _: SMatrix<u64, 2, 2> = matrix![0, 1; 2, 3];
|
||||||
|
const _: SMatrix<usize, 2, 2> = matrix![0, 1; 2, 3];
|
||||||
|
const _: SMatrix<f32, 2, 2> = matrix![0.0, 1.0; 2.0, 3.0];
|
||||||
|
const _: SMatrix<f64, 2, 2> = matrix![0.0, 1.0; 2.0, 3.0];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vector_builtin_types() {
|
||||||
|
// Check that vector! compiles for all built-in types
|
||||||
|
const _: SVector<i8, 4> = vector![0, 1, 2, 3];
|
||||||
|
const _: SVector<i16, 4> = vector![0, 1, 2, 3];
|
||||||
|
const _: SVector<i32, 4> = vector![0, 1, 2, 3];
|
||||||
|
const _: SVector<i64, 4> = vector![0, 1, 2, 3];
|
||||||
|
const _: SVector<isize, 4> = vector![0, 1, 2, 3];
|
||||||
|
const _: SVector<u8, 4> = vector![0, 1, 2, 3];
|
||||||
|
const _: SVector<u16, 4> = vector![0, 1, 2, 3];
|
||||||
|
const _: SVector<u32, 4> = vector![0, 1, 2, 3];
|
||||||
|
const _: SVector<u64, 4> = vector![0, 1, 2, 3];
|
||||||
|
const _: SVector<usize, 4> = vector![0, 1, 2, 3];
|
||||||
|
const _: SVector<f32, 4> = vector![0.0, 1.0, 2.0, 3.0];
|
||||||
|
const _: SVector<f64, 4> = vector![0.0, 1.0, 2.0, 3.0];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dmatrix_builtin_types() {
|
||||||
|
// Check that dmatrix! compiles for all built-in types
|
||||||
|
let _: DMatrix<i8> = dmatrix![0, 1; 2, 3];
|
||||||
|
let _: DMatrix<i16> = dmatrix![0, 1; 2, 3];
|
||||||
|
let _: DMatrix<i32> = dmatrix![0, 1; 2, 3];
|
||||||
|
let _: DMatrix<i64> = dmatrix![0, 1; 2, 3];
|
||||||
|
let _: DMatrix<isize> = dmatrix![0, 1; 2, 3];
|
||||||
|
let _: DMatrix<u8> = dmatrix![0, 1; 2, 3];
|
||||||
|
let _: DMatrix<u16> = dmatrix![0, 1; 2, 3];
|
||||||
|
let _: DMatrix<u32> = dmatrix![0, 1; 2, 3];
|
||||||
|
let _: DMatrix<u64> = dmatrix![0, 1; 2, 3];
|
||||||
|
let _: DMatrix<usize> = dmatrix![0, 1; 2, 3];
|
||||||
|
let _: DMatrix<f32> = dmatrix![0.0, 1.0; 2.0, 3.0];
|
||||||
|
let _: DMatrix<f64> = dmatrix![0.0, 1.0; 2.0, 3.0];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dvector_builtin_types() {
|
||||||
|
// Check that dvector! compiles for all built-in types
|
||||||
|
let _: DVector<i8> = dvector![0, 1, 2, 3];
|
||||||
|
let _: DVector<i16> = dvector![0, 1, 2, 3];
|
||||||
|
let _: DVector<i32> = dvector![0, 1, 2, 3];
|
||||||
|
let _: DVector<i64> = dvector![0, 1, 2, 3];
|
||||||
|
let _: DVector<isize> = dvector![0, 1, 2, 3];
|
||||||
|
let _: DVector<u8> = dvector![0, 1, 2, 3];
|
||||||
|
let _: DVector<u16> = dvector![0, 1, 2, 3];
|
||||||
|
let _: DVector<u32> = dvector![0, 1, 2, 3];
|
||||||
|
let _: DVector<u64> = dvector![0, 1, 2, 3];
|
||||||
|
let _: DVector<usize> = dvector![0, 1, 2, 3];
|
||||||
|
let _: DVector<f32> = dvector![0.0, 1.0, 2.0, 3.0];
|
||||||
|
let _: DVector<f64> = dvector![0.0, 1.0, 2.0, 3.0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Black box function that's just used for testing macros with function call expressions.
|
||||||
|
fn f<T>(x: T) -> T {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[test]
|
||||||
|
fn matrix_arbitrary_expressions() {
|
||||||
|
// Test that matrix! supports arbitrary expressions for its elements
|
||||||
|
let a = matrix![1 + 2 , 2 * 3;
|
||||||
|
4 * f(5 + 6), 7 - 8 * 9];
|
||||||
|
let a_expected = Matrix2::new(1 + 2 , 2 * 3,
|
||||||
|
4 * f(5 + 6), 7 - 8 * 9);
|
||||||
|
assert_eq_and_type!(a, a_expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[test]
|
||||||
|
fn dmatrix_arbitrary_expressions() {
|
||||||
|
// Test that dmatrix! supports arbitrary expressions for its elements
|
||||||
|
let a = dmatrix![1 + 2 , 2 * 3;
|
||||||
|
4 * f(5 + 6), 7 - 8 * 9];
|
||||||
|
let a_expected = DMatrix::from_row_slice(2, 2, &[1 + 2 , 2 * 3,
|
||||||
|
4 * f(5 + 6), 7 - 8 * 9]);
|
||||||
|
assert_eq_and_type!(a, a_expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[test]
|
||||||
|
fn vector_arbitrary_expressions() {
|
||||||
|
// Test that vector! supports arbitrary expressions for its elements
|
||||||
|
let a = vector![1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9];
|
||||||
|
let a_expected = Vector4::new(1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9);
|
||||||
|
assert_eq_and_type!(a, a_expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[test]
|
||||||
|
fn dvector_arbitrary_expressions() {
|
||||||
|
// Test that dvector! supports arbitrary expressions for its elements
|
||||||
|
let a = dvector![1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9];
|
||||||
|
let a_expected = DVector::from_column_slice(&[1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9]);
|
||||||
|
assert_eq_and_type!(a, a_expected);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
use nalgebra_macros::dmatrix;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
dmatrix![1, 2, 3;
|
||||||
|
4, 5];
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
error: Unexpected number of entries in row 1. Expected 3, found 2 entries.
|
||||||
|
--> $DIR/dmatrix_mismatched_dimensions.rs:5:13
|
||||||
|
|
|
||||||
|
5 | 4, 5];
|
||||||
|
| ^
|
|
@ -0,0 +1,6 @@
|
||||||
|
use nalgebra_macros::matrix;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
matrix![1, 2, 3;
|
||||||
|
4, 5];
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
error: Unexpected number of entries in row 1. Expected 3, found 2 entries.
|
||||||
|
--> $DIR/matrix_mismatched_dimensions.rs:5:13
|
||||||
|
|
|
||||||
|
5 | 4, 5];
|
||||||
|
| ^
|
|
@ -29,7 +29,10 @@ use crate::base::storage::{
|
||||||
ContiguousStorage, ContiguousStorageMut, Owned, SameShapeStorage, Storage, StorageMut,
|
ContiguousStorage, ContiguousStorageMut, Owned, SameShapeStorage, Storage, StorageMut,
|
||||||
};
|
};
|
||||||
use crate::base::{Const, DefaultAllocator, OMatrix, OVector, Scalar, Unit};
|
use crate::base::{Const, DefaultAllocator, OMatrix, OVector, Scalar, Unit};
|
||||||
use crate::SimdComplexField;
|
use crate::{ArrayStorage, SMatrix, SimdComplexField};
|
||||||
|
|
||||||
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||||
|
use crate::{DMatrix, DVector, Dynamic, VecStorage};
|
||||||
|
|
||||||
/// A square matrix.
|
/// A square matrix.
|
||||||
pub type SquareMatrix<T, D, S> = Matrix<T, D, D, S>;
|
pub type SquareMatrix<T, D, S> = Matrix<T, D, D, S>;
|
||||||
|
@ -317,6 +320,49 @@ impl<T, R, C, S> Matrix<T, R, C, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, const R: usize, const C: usize> SMatrix<T, R, C> {
|
||||||
|
/// Creates a new statically-allocated matrix from the given [ArrayStorage].
|
||||||
|
///
|
||||||
|
/// This method exists primarily as a workaround for the fact that `from_data` can not
|
||||||
|
/// work in `const fn` contexts.
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn from_array_storage(storage: ArrayStorage<T, R, C>) -> Self {
|
||||||
|
// This is sound because the row and column types are exactly the same as that of the
|
||||||
|
// storage, so there can be no mismatch
|
||||||
|
unsafe { Self::from_data_statically_unchecked(storage) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Consider removing/deprecating `from_vec_storage` once we are able to make
|
||||||
|
// `from_data` const fn compatible
|
||||||
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||||
|
impl<T> DMatrix<T> {
|
||||||
|
/// Creates a new heap-allocated matrix from the given [VecStorage].
|
||||||
|
///
|
||||||
|
/// This method exists primarily as a workaround for the fact that `from_data` can not
|
||||||
|
/// work in `const fn` contexts.
|
||||||
|
pub const fn from_vec_storage(storage: VecStorage<T, Dynamic, Dynamic>) -> Self {
|
||||||
|
// This is sound because the dimensions of the matrix and the storage are guaranteed
|
||||||
|
// to be the same
|
||||||
|
unsafe { Self::from_data_statically_unchecked(storage) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Consider removing/deprecating `from_vec_storage` once we are able to make
|
||||||
|
// `from_data` const fn compatible
|
||||||
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||||
|
impl<T> DVector<T> {
|
||||||
|
/// Creates a new heap-allocated matrix from the given [VecStorage].
|
||||||
|
///
|
||||||
|
/// This method exists primarily as a workaround for the fact that `from_data` can not
|
||||||
|
/// work in `const fn` contexts.
|
||||||
|
pub const fn from_vec_storage(storage: VecStorage<T, Dynamic, U1>) -> Self {
|
||||||
|
// This is sound because the dimensions of the matrix and the storage are guaranteed
|
||||||
|
// to be the same
|
||||||
|
unsafe { Self::from_data_statically_unchecked(storage) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
|
||||||
/// Creates a new matrix with the given data.
|
/// Creates a new matrix with the given data.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -136,6 +136,9 @@ pub use crate::sparse::*;
|
||||||
)]
|
)]
|
||||||
pub use base as core;
|
pub use base as core;
|
||||||
|
|
||||||
|
#[cfg(feature = "macros")]
|
||||||
|
pub use nalgebra_macros::{dmatrix, dvector, matrix, vector};
|
||||||
|
|
||||||
use simba::scalar::SupersetOf;
|
use simba::scalar::SupersetOf;
|
||||||
use std::cmp::{self, Ordering, PartialOrd};
|
use std::cmp::{self, Ordering, PartialOrd};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
use nalgebra::{dmatrix, dvector, matrix, vector};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sanity_test() {
|
||||||
|
// The macros are already tested in `nalgebra-macros`. Here we just test that they compile fine.
|
||||||
|
|
||||||
|
let _ = matrix![1, 2, 3; 4, 5, 6];
|
||||||
|
let _ = dmatrix![1, 2, 3; 4, 5, 6];
|
||||||
|
let _ = vector![1, 2, 3, 4, 5, 6];
|
||||||
|
let _ = dvector![1, 2, 3, 4, 5, 6];
|
||||||
|
}
|
|
@ -16,3 +16,6 @@ mod matrixcompare;
|
||||||
|
|
||||||
#[cfg(feature = "arbitrary")]
|
#[cfg(feature = "arbitrary")]
|
||||||
pub mod helper;
|
pub mod helper;
|
||||||
|
|
||||||
|
#[cfg(feature = "macros")]
|
||||||
|
mod macros;
|
||||||
|
|
11
tests/lib.rs
11
tests/lib.rs
|
@ -1,7 +1,12 @@
|
||||||
#[cfg(not(all(feature = "debug", feature = "compare", feature = "rand")))]
|
#[cfg(not(all(
|
||||||
|
feature = "debug",
|
||||||
|
feature = "compare",
|
||||||
|
feature = "rand",
|
||||||
|
feature = "macros"
|
||||||
|
)))]
|
||||||
compile_error!(
|
compile_error!(
|
||||||
"Please enable the `debug`, `compare`, and `rand` features in order to compile and run the tests.
|
"Please enable the `debug`, `compare`, `rand` and `macros` features in order to compile and run the tests.
|
||||||
Example: `cargo test --features debug,compare,rand`"
|
Example: `cargo test --features debug,compare,rand,macros`"
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "abomonation-serialize")]
|
#[cfg(feature = "abomonation-serialize")]
|
||||||
|
|
Loading…
Reference in New Issue