From e97692255b863a8b47c037c5b855e00045d4dffa Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Sun, 11 Apr 2021 08:01:30 +0200 Subject: [PATCH 01/25] Initial hacked together prototype without syn --- Cargo.toml | 2 +- nalgebra-macros/Cargo.toml | 16 ++++++ nalgebra-macros/src/lib.rs | 99 ++++++++++++++++++++++++++++++++++ nalgebra-macros/tests/tests.rs | 6 +++ 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 nalgebra-macros/Cargo.toml create mode 100644 nalgebra-macros/src/lib.rs create mode 100644 nalgebra-macros/tests/tests.rs diff --git a/Cargo.toml b/Cargo.toml index 45fdb17a2..ad24b8095 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,7 @@ matrixcompare = "0.2.0" itertools = "0.10" [workspace] -members = [ "nalgebra-lapack", "nalgebra-glm", "nalgebra-sparse" ] +members = [ "nalgebra-lapack", "nalgebra-glm", "nalgebra-sparse", "nalgebra-macros" ] resolver = "2" [[example]] diff --git a/nalgebra-macros/Cargo.toml b/nalgebra-macros/Cargo.toml new file mode 100644 index 000000000..74b5d1b23 --- /dev/null +++ b/nalgebra-macros/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "nalgebra-macros" +version = "0.1.0" +authors = ["Andreas Longva "] +edition = "2018" + +[lib] +proc-macro = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +syn = "1.0" + +[dev-dependencies] +nalgebra = { version = "0.25.4", path = ".." } diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs new file mode 100644 index 000000000..b4e2d4b2f --- /dev/null +++ b/nalgebra-macros/src/lib.rs @@ -0,0 +1,99 @@ +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree, Literal, Ident, Punct, Spacing, Group, Delimiter}; +use std::iter::FromIterator; + +struct MatrixEntries { + entries: Vec> +} + +impl MatrixEntries { + fn new() -> Self { + Self { + entries: Vec::new() + } + } + + fn begin_new_row(&mut self) { + self.entries.push(Vec::new()); + } + + fn push_entry(&mut self, entry: TokenTree) { + if self.entries.is_empty() { + self.entries.push(Vec::new()); + } + let mut last_row = self.entries.last_mut().unwrap(); + last_row.push(entry); + } + + fn build_stream(&self) -> TokenStream { + let num_rows = self.entries.len(); + let num_cols = self.entries.first() + .map(|first_row| first_row.len()) + .unwrap_or(0); + + // First check that dimensions are consistent + for (i, row) in self.entries.iter().enumerate() { + if row.len() != num_cols { + panic!("Unexpected number of columns in row {}: {}. Expected {}", i, row.len(), num_cols); + } + } + + let mut array_tokens = Vec::new(); + + // Collect entries in column major order + for i in 0 .. num_rows { + for j in 0 .. num_cols { + let entry = &self.entries[i][j]; + array_tokens.push(entry.clone()); + array_tokens.push(TokenTree::Punct(Punct::new(',', Spacing::Alone))); + } + } + + let row_dim = format!("U{}", num_rows); + let col_dim = format!("U{}", num_cols); + // let imports = format!("use nalgebra::\{Matrix, {}, {}\};", row_dim, col_dim); + // let constructor = format!("Matrix::<_, {}, {}>::from_slice", row_dim, col_dim); + // let array_group = Group::new(Delimiter::Bracket, TokenStream::from_iter(array_tokens.into_iter())); + + let array_stream = TokenStream::from_iter(array_tokens); + + // TODO: Build this up without parsing? + format!(r"{{ + nalgebra::MatrixMN::<_, nalgebra::{row_dim}, nalgebra::{col_dim}>::from_column_slice(&[ + {array_tokens} + ]) + }}", row_dim=row_dim, col_dim=col_dim, array_tokens=array_stream.to_string()).parse().unwrap() + + + // let mut outer_group = Group::new(Delimiter::Brace, + // + // ); + + + // TODO: Outer group + + + + + // todo!() + } +} + +#[proc_macro] +pub fn matrix(stream: TokenStream) -> TokenStream { + let mut entries = MatrixEntries::new(); + for tree in stream { + match tree { + // TokenTree::Ident(ident) => entries.push_entry(tree), + // TokenTree::Literal(literal) => entries.push_entry(tree), + TokenTree::Punct(punct) if punct == ';' => entries.begin_new_row(), + TokenTree::Punct(punct) if punct == ',' => {}, + // TokenTree::Punct(punct) => panic!("Unexpected punctuation: '{}'", punct), + // TokenTree::Group(_) => panic!("Unexpected token group"), + _ => entries.push_entry(tree) + } + } + + entries.build_stream() +} \ No newline at end of file diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs new file mode 100644 index 000000000..5763e1394 --- /dev/null +++ b/nalgebra-macros/tests/tests.rs @@ -0,0 +1,6 @@ +use nalgebra_macros::matrix; + +#[test] +fn basic_usage() { + matrix![ 1, 3; 4, 5*3]; +} \ No newline at end of file From ab95cf702067a5cb81fcd98d0aa92d348cd45818 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Sun, 11 Apr 2021 11:08:41 +0200 Subject: [PATCH 02/25] Initial impl using syn and quote --- nalgebra-macros/Cargo.toml | 5 +- nalgebra-macros/src/lib.rs | 136 +++++++++++++++------------------ nalgebra-macros/tests/tests.rs | 4 +- 3 files changed, 68 insertions(+), 77 deletions(-) diff --git a/nalgebra-macros/Cargo.toml b/nalgebra-macros/Cargo.toml index 74b5d1b23..8e98e017d 100644 --- a/nalgebra-macros/Cargo.toml +++ b/nalgebra-macros/Cargo.toml @@ -10,7 +10,10 @@ proc-macro = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -syn = "1.0" +# TODO: Determine minimal features that we need +syn = { version="1.0", features = ["full"] } +quote = "1.0" +proc-macro2 = "1.0" [dev-dependencies] nalgebra = { version = "0.25.4", path = ".." } diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs index b4e2d4b2f..10127094d 100644 --- a/nalgebra-macros/src/lib.rs +++ b/nalgebra-macros/src/lib.rs @@ -1,99 +1,85 @@ extern crate proc_macro; -use proc_macro::{TokenStream, TokenTree, Literal, Ident, Punct, Spacing, Group, Delimiter}; -use std::iter::FromIterator; - -struct MatrixEntries { - entries: Vec> +use syn::{Expr}; +use syn::parse::{Parse, ParseStream, Result, Error}; +use syn::punctuated::{Punctuated}; +use syn::{parse_macro_input, Token}; +use quote::{quote, format_ident}; +use proc_macro::TokenStream; + +struct Matrix { + // Represent the matrix as a row-major vector of vectors of expressions + rows: Vec>, + ncols: usize, } -impl MatrixEntries { - fn new() -> Self { - Self { - entries: Vec::new() - } +impl Matrix { + fn nrows(&self) -> usize { + self.rows.len() } - fn begin_new_row(&mut self) { - self.entries.push(Vec::new()); + fn ncols(&self) -> usize { + self.ncols } - fn push_entry(&mut self, entry: TokenTree) { - if self.entries.is_empty() { - self.entries.push(Vec::new()); + fn to_col_major_repr(&self) -> Vec { + let mut data = Vec::with_capacity(self.nrows() * self.ncols()); + for j in 0 .. self.ncols() { + for i in 0 .. self.nrows() { + data.push(self.rows[i][j].clone()); + } } - let mut last_row = self.entries.last_mut().unwrap(); - last_row.push(entry); + data } +} - fn build_stream(&self) -> TokenStream { - let num_rows = self.entries.len(); - let num_cols = self.entries.first() - .map(|first_row| first_row.len()) - .unwrap_or(0); +type MatrixRowSyntax = Punctuated; +type MatrixSyntax = Punctuated; - // First check that dimensions are consistent - for (i, row) in self.entries.iter().enumerate() { - if row.len() != num_cols { - panic!("Unexpected number of columns in row {}: {}. Expected {}", i, row.len(), num_cols); - } - } +impl Parse for Matrix { + fn parse(input: ParseStream) -> Result { + let span = input.span(); + // TODO: Handle empty matrix case + let ast = MatrixSyntax::parse_separated_nonempty_with(input, + |input| MatrixRowSyntax::parse_separated_nonempty(input))?; + let ncols = ast.first().map(|row| row.len()) + .unwrap_or(0); - let mut array_tokens = Vec::new(); + let mut rows = Vec::new(); - // Collect entries in column major order - for i in 0 .. num_rows { - for j in 0 .. num_cols { - let entry = &self.entries[i][j]; - array_tokens.push(entry.clone()); - array_tokens.push(TokenTree::Punct(Punct::new(',', Spacing::Alone))); + for row in ast { + if row.len() != ncols { + // TODO: Is this the correct span? + // Currently it returns the span corresponding to the first element in the macro + // invocation, but it would be nice if it returned the span of the first element + // in the first row that has an unexpected number of columns + return Err(Error::new(span, "Unexpected number of columns. TODO")) } + rows.push(row.into_iter().collect()); } - let row_dim = format!("U{}", num_rows); - let col_dim = format!("U{}", num_cols); - // let imports = format!("use nalgebra::\{Matrix, {}, {}\};", row_dim, col_dim); - // let constructor = format!("Matrix::<_, {}, {}>::from_slice", row_dim, col_dim); - // let array_group = Group::new(Delimiter::Bracket, TokenStream::from_iter(array_tokens.into_iter())); - - let array_stream = TokenStream::from_iter(array_tokens); - - // TODO: Build this up without parsing? - format!(r"{{ - nalgebra::MatrixMN::<_, nalgebra::{row_dim}, nalgebra::{col_dim}>::from_column_slice(&[ - {array_tokens} - ]) - }}", row_dim=row_dim, col_dim=col_dim, array_tokens=array_stream.to_string()).parse().unwrap() - - - // let mut outer_group = Group::new(Delimiter::Brace, - // - // ); - - - // TODO: Outer group - - - - - // todo!() + Ok(Self { + rows, + ncols + }) } } #[proc_macro] pub fn matrix(stream: TokenStream) -> TokenStream { - let mut entries = MatrixEntries::new(); - for tree in stream { - match tree { - // TokenTree::Ident(ident) => entries.push_entry(tree), - // TokenTree::Literal(literal) => entries.push_entry(tree), - TokenTree::Punct(punct) if punct == ';' => entries.begin_new_row(), - TokenTree::Punct(punct) if punct == ',' => {}, - // TokenTree::Punct(punct) => panic!("Unexpected punctuation: '{}'", punct), - // TokenTree::Group(_) => panic!("Unexpected token group"), - _ => entries.push_entry(tree) - } - } + let matrix = parse_macro_input!(stream as Matrix); + + let dim_ident = |dim| format_ident!("U{}", dim); + let row_dim = dim_ident(matrix.nrows()); + let col_dim = dim_ident(matrix.ncols()); + let entries_col_major = matrix.to_col_major_repr(); + + // TODO: Use quote_spanned instead?? + // TODO: Construct directly from array? + let output = quote! { + nalgebra::MatrixMN::<_, nalgebra::dimension::#row_dim, nalgebra::dimension::#col_dim> + ::from_column_slice(&[#(#entries_col_major),*]) + }; - entries.build_stream() + proc_macro::TokenStream::from(output) } \ No newline at end of file diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index 5763e1394..5c87ad87c 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -2,5 +2,7 @@ use nalgebra_macros::matrix; #[test] fn basic_usage() { - matrix![ 1, 3; 4, 5*3]; + matrix![ 1, 3; + 4, 5*3; + 3, 3]; } \ No newline at end of file From ed833506a9d152776f251684facf9b7d7154a39d Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Sun, 11 Apr 2021 17:29:33 +0200 Subject: [PATCH 03/25] Fix warnings, refactor code --- nalgebra-macros/src/lib.rs | 40 ++++++++++++++++++++-------------- nalgebra-macros/tests/tests.rs | 10 +++++---- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs index 10127094d..3a75b4711 100644 --- a/nalgebra-macros/src/lib.rs +++ b/nalgebra-macros/src/lib.rs @@ -34,33 +34,41 @@ impl Matrix { } type MatrixRowSyntax = Punctuated; -type MatrixSyntax = Punctuated; impl Parse for Matrix { fn parse(input: ParseStream) -> Result { - let span = input.span(); - // TODO: Handle empty matrix case - let ast = MatrixSyntax::parse_separated_nonempty_with(input, - |input| MatrixRowSyntax::parse_separated_nonempty(input))?; - let ncols = ast.first().map(|row| row.len()) - .unwrap_or(0); - 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)?; - for row in ast { - if row.len() != ncols { - // TODO: Is this the correct span? - // Currently it returns the span corresponding to the first element in the macro - // invocation, but it would be nice if it returned the span of the first element - // in the first row that has an unexpected number of columns - return Err(Error::new(span, "Unexpected number of columns. TODO")) + 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::()?; + } } Ok(Self { rows, - ncols + ncols: ncols.unwrap_or(0) }) } } diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index 5c87ad87c..ddbcc1ed5 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -1,8 +1,10 @@ use nalgebra_macros::matrix; +use nalgebra::Matrix3x2; #[test] -fn basic_usage() { - matrix![ 1, 3; - 4, 5*3; - 3, 3]; +fn matrix_basic_construction() { + let matrix: Matrix3x2<_> = matrix![ 1, 2; + 3, 4; + 5, 6 ]; + assert_eq!(matrix, Matrix3x2::new(1, 2, 3, 4, 5, 6)); } \ No newline at end of file From 1dccdb1f20b6579969f82d9c2c5ec4b300085d57 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Mon, 12 Apr 2021 08:36:38 +0200 Subject: [PATCH 04/25] Exhaustive tests for small dimensions --- nalgebra-macros/src/lib.rs | 2 +- nalgebra-macros/tests/tests.rs | 37 ++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs index 3a75b4711..16fbe6da3 100644 --- a/nalgebra-macros/src/lib.rs +++ b/nalgebra-macros/src/lib.rs @@ -85,7 +85,7 @@ pub fn matrix(stream: TokenStream) -> TokenStream { // TODO: Use quote_spanned instead?? // TODO: Construct directly from array? let output = quote! { - nalgebra::MatrixMN::<_, nalgebra::dimension::#row_dim, nalgebra::dimension::#col_dim> + nalgebra::MatrixMN::<_, nalgebra::#row_dim, nalgebra::dimension::#col_dim> ::from_column_slice(&[#(#entries_col_major),*]) }; diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index ddbcc1ed5..cd2c3623a 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -1,10 +1,35 @@ use nalgebra_macros::matrix; -use nalgebra::Matrix3x2; +use nalgebra::{MatrixMN, Matrix3x2, U0, U1, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2x1, Matrix2, Matrix2x3, Matrix2x4, Matrix3x1, Matrix3, Matrix3x4, Matrix4x1, Matrix4x2, Matrix4x3, Matrix4}; #[test] -fn matrix_basic_construction() { - let matrix: Matrix3x2<_> = matrix![ 1, 2; - 3, 4; - 5, 6 ]; - assert_eq!(matrix, Matrix3x2::new(1, 2, 3, 4, 5, 6)); +fn matrix_small_dims_exhaustive() { + // 0x0 + assert_eq!(matrix![], MatrixMN::::zeros()); + + // 1xN + assert_eq!(matrix![1], MatrixMN::::new(1)); + assert_eq!(matrix![1, 2], Matrix1x2::new(1, 2)); + assert_eq!(matrix![1, 2, 3], Matrix1x3::new(1, 2, 3)); + assert_eq!(matrix![1, 2, 3, 4], Matrix1x4::new(1, 2, 3, 4)); + + // 2xN + assert_eq!(matrix![1; 2], Matrix2x1::new(1, 2)); + assert_eq!(matrix![1, 2; 3, 4], Matrix2::new(1, 2, 3, 4)); + assert_eq!(matrix![1, 2, 3; 4, 5, 6], Matrix2x3::new(1, 2, 3, 4, 5, 6)); + assert_eq!(matrix![1, 2, 3, 4; 5, 6, 7, 8], Matrix2x4::new(1, 2, 3, 4, 5, 6, 7, 8)); + + // 3xN + assert_eq!(matrix![1; 2; 3], Matrix3x1::new(1, 2, 3)); + assert_eq!(matrix![1, 2; 3, 4; 5, 6], Matrix3x2::new(1, 2, 3, 4, 5, 6)); + assert_eq!(matrix![1, 2, 3; 4, 5, 6; 7, 8, 9], Matrix3::new(1, 2, 3, 4, 5, 6, 7, 8, 9)); + assert_eq!(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!(matrix![1; 2; 3; 4], Matrix4x1::new(1, 2, 3, 4)); + assert_eq!(matrix![1, 2; 3, 4; 5, 6; 7, 8], Matrix4x2::new(1, 2, 3, 4, 5, 6, 7, 8)); + assert_eq!(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!(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)); } \ No newline at end of file From e60136fbb18759fe7c9d8275290cabdd5f35df69 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 14 Apr 2021 17:30:52 +0200 Subject: [PATCH 05/25] Update nalgebra-macros to nalgebra 0.26 and const generics --- nalgebra-macros/Cargo.toml | 2 +- nalgebra-macros/src/lib.rs | 7 +++---- nalgebra-macros/tests/tests.rs | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/nalgebra-macros/Cargo.toml b/nalgebra-macros/Cargo.toml index 8e98e017d..1f27b572b 100644 --- a/nalgebra-macros/Cargo.toml +++ b/nalgebra-macros/Cargo.toml @@ -16,4 +16,4 @@ quote = "1.0" proc-macro2 = "1.0" [dev-dependencies] -nalgebra = { version = "0.25.4", path = ".." } +nalgebra = { version = "0.26.1", path = ".." } diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs index 16fbe6da3..154f48765 100644 --- a/nalgebra-macros/src/lib.rs +++ b/nalgebra-macros/src/lib.rs @@ -77,15 +77,14 @@ impl Parse for Matrix { pub fn matrix(stream: TokenStream) -> TokenStream { let matrix = parse_macro_input!(stream as Matrix); - let dim_ident = |dim| format_ident!("U{}", dim); - let row_dim = dim_ident(matrix.nrows()); - let col_dim = dim_ident(matrix.ncols()); + let row_dim = matrix.nrows(); + let col_dim = matrix.ncols(); let entries_col_major = matrix.to_col_major_repr(); // TODO: Use quote_spanned instead?? // TODO: Construct directly from array? let output = quote! { - nalgebra::MatrixMN::<_, nalgebra::#row_dim, nalgebra::dimension::#col_dim> + nalgebra::SMatrix::<_, #row_dim, #col_dim> ::from_column_slice(&[#(#entries_col_major),*]) }; diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index cd2c3623a..5d5a91b06 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -1,13 +1,13 @@ use nalgebra_macros::matrix; -use nalgebra::{MatrixMN, Matrix3x2, U0, U1, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2x1, Matrix2, Matrix2x3, Matrix2x4, Matrix3x1, Matrix3, Matrix3x4, Matrix4x1, Matrix4x2, Matrix4x3, Matrix4}; +use nalgebra::{SMatrix, Matrix3x2, U0, U1, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2x1, Matrix2, Matrix2x3, Matrix2x4, Matrix3x1, Matrix3, Matrix3x4, Matrix4x1, Matrix4x2, Matrix4x3, Matrix4}; #[test] fn matrix_small_dims_exhaustive() { // 0x0 - assert_eq!(matrix![], MatrixMN::::zeros()); + assert_eq!(matrix![], SMatrix::::zeros()); // 1xN - assert_eq!(matrix![1], MatrixMN::::new(1)); + assert_eq!(matrix![1], SMatrix::::new(1)); assert_eq!(matrix![1, 2], Matrix1x2::new(1, 2)); assert_eq!(matrix![1, 2, 3], Matrix1x3::new(1, 2, 3)); assert_eq!(matrix![1, 2, 3, 4], Matrix1x4::new(1, 2, 3, 4)); From ec2a5a39090ce39ef73f8a133e4a1dc11240e18b Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Sat, 17 Apr 2021 09:43:55 +0200 Subject: [PATCH 06/25] Construct ArrayStorage directly in matrix![] --- nalgebra-macros/src/lib.rs | 30 ++++++++++++++++++++---------- nalgebra-macros/tests/tests.rs | 2 +- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs index 154f48765..06f6a2cc8 100644 --- a/nalgebra-macros/src/lib.rs +++ b/nalgebra-macros/src/lib.rs @@ -4,9 +4,12 @@ use syn::{Expr}; use syn::parse::{Parse, ParseStream, Result, Error}; use syn::punctuated::{Punctuated}; use syn::{parse_macro_input, Token}; -use quote::{quote, format_ident}; +use quote::{quote, TokenStreamExt}; use proc_macro::TokenStream; +use proc_macro2::{TokenStream as TokenStream2, Delimiter, TokenTree, Spacing}; +use proc_macro2::{Group, Punct}; + struct Matrix { // Represent the matrix as a row-major vector of vectors of expressions rows: Vec>, @@ -22,14 +25,18 @@ impl Matrix { self.ncols } - fn to_col_major_repr(&self) -> Vec { - let mut data = Vec::with_capacity(self.nrows() * self.ncols()); + /// Produces a stream of tokens representing this matrix as a column-major array. + fn col_major_array_tokens(&self) -> TokenStream2 { + let mut result = TokenStream2::new(); for j in 0 .. self.ncols() { - for i in 0 .. self.nrows() { - data.push(self.rows[i][j].clone()); - } + 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)); } - data + TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Bracket, result))) } } @@ -79,13 +86,16 @@ pub fn matrix(stream: TokenStream) -> TokenStream { let row_dim = matrix.nrows(); let col_dim = matrix.ncols(); - let entries_col_major = matrix.to_col_major_repr(); + + let array_tokens = matrix.col_major_array_tokens(); // TODO: Use quote_spanned instead?? - // TODO: Construct directly from array? + // TODO: Avoid use of unsafe here let output = quote! { + unsafe { nalgebra::SMatrix::<_, #row_dim, #col_dim> - ::from_column_slice(&[#(#entries_col_major),*]) + ::from_data_statically_unchecked(nalgebra::ArrayStorage(#array_tokens)) + } }; proc_macro::TokenStream::from(output) diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index 5d5a91b06..1113a79d0 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -1,5 +1,5 @@ use nalgebra_macros::matrix; -use nalgebra::{SMatrix, Matrix3x2, U0, U1, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2x1, Matrix2, Matrix2x3, Matrix2x4, Matrix3x1, Matrix3, Matrix3x4, Matrix4x1, Matrix4x2, Matrix4x3, Matrix4}; +use nalgebra::{SMatrix, Matrix3x2, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2x1, Matrix2, Matrix2x3, Matrix2x4, Matrix3x1, Matrix3, Matrix3x4, Matrix4x1, Matrix4x2, Matrix4x3, Matrix4}; #[test] fn matrix_small_dims_exhaustive() { From 7098a4f07ec49044514e149b20e07a68b339622e Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Sat, 17 Apr 2021 09:45:53 +0200 Subject: [PATCH 07/25] Test that matrix![] can be used with const --- nalgebra-macros/tests/tests.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index 1113a79d0..0b594b318 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -32,4 +32,12 @@ fn matrix_small_dims_exhaustive() { Matrix4x3::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)); assert_eq!(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 = matrix![]; + const _: SMatrix = matrix![1, 2]; + const _: SMatrix = matrix![1, 2, 3; 4, 5, 6]; } \ No newline at end of file From 9142dc8f84fa466114c8e784f05b8f6d5f961242 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Sat, 17 Apr 2021 16:41:52 +0200 Subject: [PATCH 08/25] Implement SMatrix::from_array_storage and use it in matriX! impl This allows us to avoid injecting unsafe code into every macro invocation, which seems desirable. --- nalgebra-macros/src/lib.rs | 5 +---- src/base/array_storage.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs index 06f6a2cc8..a18162ab2 100644 --- a/nalgebra-macros/src/lib.rs +++ b/nalgebra-macros/src/lib.rs @@ -90,12 +90,9 @@ pub fn matrix(stream: TokenStream) -> TokenStream { let array_tokens = matrix.col_major_array_tokens(); // TODO: Use quote_spanned instead?? - // TODO: Avoid use of unsafe here let output = quote! { - unsafe { nalgebra::SMatrix::<_, #row_dim, #col_dim> - ::from_data_statically_unchecked(nalgebra::ArrayStorage(#array_tokens)) - } + ::from_array_storage(nalgebra::ArrayStorage(#array_tokens)) }; proc_macro::TokenStream::from(output) diff --git a/src/base/array_storage.rs b/src/base/array_storage.rs index 68da4c517..dc6b22cea 100644 --- a/src/base/array_storage.rs +++ b/src/base/array_storage.rs @@ -25,6 +25,7 @@ use crate::base::storage::{ ContiguousStorage, ContiguousStorageMut, Owned, ReshapableStorage, Storage, StorageMut, }; use crate::base::Scalar; +use crate::SMatrix; /* * @@ -299,3 +300,14 @@ where self.as_slice().iter().fold(0, |acc, e| acc + e.extent()) } } + +// TODO: Where to put this impl block? +impl SMatrix { + /// Creates a new statically-allocated matrix from the given [ArrayStorage]. + #[inline(always)] + pub const fn from_array_storage(storage: ArrayStorage) -> 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) } + } +} From d2c11ad7975732b374d1448a4667db0f830f8634 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Sat, 17 Apr 2021 17:00:37 +0200 Subject: [PATCH 09/25] Impl DMatrix/DVector::from_vec_storage These methods enable safe & const construction of DMatrix/DVector from a given VecStorage. --- src/base/vec_storage.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/base/vec_storage.rs b/src/base/vec_storage.rs index 29da127d8..15ea9237f 100644 --- a/src/base/vec_storage.rs +++ b/src/base/vec_storage.rs @@ -15,6 +15,7 @@ use crate::base::{Scalar, Vector}; #[cfg(feature = "abomonation-serialize")] use abomonation::Abomonation; +use crate::{DMatrix, DVector}; /* * @@ -410,3 +411,23 @@ impl Extend for VecStorage { self.nrows = Dynamic::new(self.data.len()); } } + +impl DMatrix +{ + /// Creates a new heap-allocated matrix from the given [VecStorage]. + pub const fn from_vec_storage(storage: VecStorage) -> 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 DVector +{ + /// Creates a new heap-allocated matrix from the given [VecStorage]. + pub const fn from_vec_storage(storage: VecStorage) -> 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) } + } +} From 5c843022c2691664ec20447f406c56c9d9776944 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Thu, 29 Apr 2021 17:06:52 +0200 Subject: [PATCH 10/25] Implement dmatrix![] macro --- nalgebra-macros/src/lib.rs | 42 ++++++++++++++++++++++++++++++---- nalgebra-macros/tests/tests.rs | 39 ++++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs index a18162ab2..c8b9b4214 100644 --- a/nalgebra-macros/src/lib.rs +++ b/nalgebra-macros/src/lib.rs @@ -4,7 +4,7 @@ use syn::{Expr}; use syn::parse::{Parse, ParseStream, Result, Error}; use syn::punctuated::{Punctuated}; use syn::{parse_macro_input, Token}; -use quote::{quote, TokenStreamExt}; +use quote::{quote, TokenStreamExt, ToTokens}; use proc_macro::TokenStream; use proc_macro2::{TokenStream as TokenStream2, Delimiter, TokenTree, Spacing}; @@ -25,8 +25,8 @@ impl Matrix { self.ncols } - /// Produces a stream of tokens representing this matrix as a column-major array. - fn col_major_array_tokens(&self) -> TokenStream2 { + /// 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(); @@ -38,6 +38,19 @@ impl Matrix { } 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; @@ -87,7 +100,7 @@ pub fn matrix(stream: TokenStream) -> TokenStream { let row_dim = matrix.nrows(); let col_dim = matrix.ncols(); - let array_tokens = matrix.col_major_array_tokens(); + let array_tokens = matrix.to_col_major_nested_array_tokens(); // TODO: Use quote_spanned instead?? let output = quote! { @@ -95,5 +108,26 @@ pub fn matrix(stream: TokenStream) -> TokenStream { ::from_array_storage(nalgebra::ArrayStorage(#array_tokens)) }; + proc_macro::TokenStream::from(output) +} + +#[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) } \ No newline at end of file diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index 0b594b318..8610d07ea 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -1,5 +1,5 @@ -use nalgebra_macros::matrix; -use nalgebra::{SMatrix, Matrix3x2, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2x1, Matrix2, Matrix2x3, Matrix2x4, Matrix3x1, Matrix3, Matrix3x4, Matrix4x1, Matrix4x2, Matrix4x3, Matrix4}; +use nalgebra_macros::{dmatrix, matrix}; +use nalgebra::{DMatrix, SMatrix, Matrix3x2, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2x1, Matrix2, Matrix2x3, Matrix2x4, Matrix3x1, Matrix3, Matrix3x4, Matrix4x1, Matrix4x2, Matrix4x3, Matrix4}; #[test] fn matrix_small_dims_exhaustive() { @@ -40,4 +40,37 @@ fn matrix_const_fn() { const _: SMatrix = matrix![]; const _: SMatrix = matrix![1, 2]; const _: SMatrix = matrix![1, 2, 3; 4, 5, 6]; -} \ No newline at end of file +} + +#[test] +fn dmatrix_small_dims_exhaustive() { + // 0x0 + assert_eq!(dmatrix![], DMatrix::::zeros(0, 0)); + + // // 1xN + assert_eq!(dmatrix![1], SMatrix::::new(1)); + assert_eq!(dmatrix![1, 2], Matrix1x2::new(1, 2)); + assert_eq!(dmatrix![1, 2, 3], Matrix1x3::new(1, 2, 3)); + assert_eq!(dmatrix![1, 2, 3, 4], Matrix1x4::new(1, 2, 3, 4)); + + // 2xN + assert_eq!(dmatrix![1; 2], Matrix2x1::new(1, 2)); + assert_eq!(dmatrix![1, 2; 3, 4], Matrix2::new(1, 2, 3, 4)); + assert_eq!(dmatrix![1, 2, 3; 4, 5, 6], Matrix2x3::new(1, 2, 3, 4, 5, 6)); + assert_eq!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8], Matrix2x4::new(1, 2, 3, 4, 5, 6, 7, 8)); + + // 3xN + assert_eq!(dmatrix![1; 2; 3], Matrix3x1::new(1, 2, 3)); + assert_eq!(dmatrix![1, 2; 3, 4; 5, 6], Matrix3x2::new(1, 2, 3, 4, 5, 6)); + assert_eq!(dmatrix![1, 2, 3; 4, 5, 6; 7, 8, 9], Matrix3::new(1, 2, 3, 4, 5, 6, 7, 8, 9)); + assert_eq!(dmatrix![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!(dmatrix![1; 2; 3; 4], Matrix4x1::new(1, 2, 3, 4)); + assert_eq!(dmatrix![1, 2; 3, 4; 5, 6; 7, 8], Matrix4x2::new(1, 2, 3, 4, 5, 6, 7, 8)); + assert_eq!(dmatrix![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!(dmatrix![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)); +} From d56db1a079b9aa4a1d550062a250959ab225de3b Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Fri, 30 Apr 2021 10:48:42 +0200 Subject: [PATCH 11/25] Assert type in matrix/dmatrix tests --- nalgebra-macros/tests/tests.rs | 86 +++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index 8610d07ea..f87028f59 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -1,36 +1,46 @@ use nalgebra_macros::{dmatrix, matrix}; use nalgebra::{DMatrix, SMatrix, Matrix3x2, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2x1, Matrix2, Matrix2x3, Matrix2x4, Matrix3x1, Matrix3, Matrix3x4, Matrix4x1, Matrix4x2, Matrix4x3, Matrix4}; +fn same_type(_: &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 $(,)?) => { + same_type(&$left, &$right); + assert_eq!($left, $right); + }; +} + #[test] fn matrix_small_dims_exhaustive() { // 0x0 - assert_eq!(matrix![], SMatrix::::zeros()); + assert_eq_and_type!(matrix![], SMatrix::::zeros()); // 1xN - assert_eq!(matrix![1], SMatrix::::new(1)); - assert_eq!(matrix![1, 2], Matrix1x2::new(1, 2)); - assert_eq!(matrix![1, 2, 3], Matrix1x3::new(1, 2, 3)); - assert_eq!(matrix![1, 2, 3, 4], Matrix1x4::new(1, 2, 3, 4)); + assert_eq_and_type!(matrix![1], SMatrix::::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!(matrix![1; 2], Matrix2x1::new(1, 2)); - assert_eq!(matrix![1, 2; 3, 4], Matrix2::new(1, 2, 3, 4)); - assert_eq!(matrix![1, 2, 3; 4, 5, 6], Matrix2x3::new(1, 2, 3, 4, 5, 6)); - assert_eq!(matrix![1, 2, 3, 4; 5, 6, 7, 8], Matrix2x4::new(1, 2, 3, 4, 5, 6, 7, 8)); + 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!(matrix![1; 2; 3], Matrix3x1::new(1, 2, 3)); - assert_eq!(matrix![1, 2; 3, 4; 5, 6], Matrix3x2::new(1, 2, 3, 4, 5, 6)); - assert_eq!(matrix![1, 2, 3; 4, 5, 6; 7, 8, 9], Matrix3::new(1, 2, 3, 4, 5, 6, 7, 8, 9)); - assert_eq!(matrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12], + 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!(matrix![1; 2; 3; 4], Matrix4x1::new(1, 2, 3, 4)); - assert_eq!(matrix![1, 2; 3, 4; 5, 6; 7, 8], Matrix4x2::new(1, 2, 3, 4, 5, 6, 7, 8)); - assert_eq!(matrix![1, 2, 3; 4, 5, 6; 7, 8, 9; 10, 11, 12], + 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!(matrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12; 13, 14, 15, 16], + 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)); } @@ -45,32 +55,32 @@ fn matrix_const_fn() { #[test] fn dmatrix_small_dims_exhaustive() { // 0x0 - assert_eq!(dmatrix![], DMatrix::::zeros(0, 0)); + assert_eq_and_type!(dmatrix![], DMatrix::::zeros(0, 0)); - // // 1xN - assert_eq!(dmatrix![1], SMatrix::::new(1)); - assert_eq!(dmatrix![1, 2], Matrix1x2::new(1, 2)); - assert_eq!(dmatrix![1, 2, 3], Matrix1x3::new(1, 2, 3)); - assert_eq!(dmatrix![1, 2, 3, 4], Matrix1x4::new(1, 2, 3, 4)); + // 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!(dmatrix![1; 2], Matrix2x1::new(1, 2)); - assert_eq!(dmatrix![1, 2; 3, 4], Matrix2::new(1, 2, 3, 4)); - assert_eq!(dmatrix![1, 2, 3; 4, 5, 6], Matrix2x3::new(1, 2, 3, 4, 5, 6)); - assert_eq!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8], Matrix2x4::new(1, 2, 3, 4, 5, 6, 7, 8)); + 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!(dmatrix![1; 2; 3], Matrix3x1::new(1, 2, 3)); - assert_eq!(dmatrix![1, 2; 3, 4; 5, 6], Matrix3x2::new(1, 2, 3, 4, 5, 6)); - assert_eq!(dmatrix![1, 2, 3; 4, 5, 6; 7, 8, 9], Matrix3::new(1, 2, 3, 4, 5, 6, 7, 8, 9)); - assert_eq!(dmatrix![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)); + 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!(dmatrix![1; 2; 3; 4], Matrix4x1::new(1, 2, 3, 4)); - assert_eq!(dmatrix![1, 2; 3, 4; 5, 6; 7, 8], Matrix4x2::new(1, 2, 3, 4, 5, 6, 7, 8)); - assert_eq!(dmatrix![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!(dmatrix![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)); + 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])); } From 07d41e457b78778e77fcbd93831ee248c780e3ff Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Fri, 30 Apr 2021 14:09:36 +0200 Subject: [PATCH 12/25] vector! and dvector! macros --- nalgebra-macros/src/lib.rs | 59 ++++++++++++++++++++++++++++++++++ nalgebra-macros/tests/tests.rs | 39 +++++++++++++++++++--- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs index c8b9b4214..fd29b361d 100644 --- a/nalgebra-macros/src/lib.rs +++ b/nalgebra-macros/src/lib.rs @@ -129,5 +129,64 @@ pub fn dmatrix(stream: TokenStream) -> TokenStream { vec!#array_tokens)) }; + proc_macro::TokenStream::from(output) +} + +struct Vector { + elements: Vec, +} + +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 { + // 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 + }) + } + } +} + +#[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) +} + +#[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) } \ No newline at end of file diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index f87028f59..c97ae103c 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -1,12 +1,12 @@ -use nalgebra_macros::{dmatrix, matrix}; -use nalgebra::{DMatrix, SMatrix, Matrix3x2, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2x1, Matrix2, Matrix2x3, Matrix2x4, Matrix3x1, Matrix3, Matrix3x4, Matrix4x1, Matrix4x2, Matrix4x3, Matrix4}; +use nalgebra_macros::{dmatrix, dvector, matrix, vector}; +use nalgebra::{DMatrix, DVector, SMatrix, Matrix3x2, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2x1, Matrix2, Matrix2x3, Matrix2x4, Matrix3x1, Matrix3, Matrix3x4, Matrix4x1, Matrix4x2, Matrix4x3, Matrix4, Vector1, Vector2, Vector3, Vector4, Vector5, SVector, Vector6}; -fn same_type(_: &T, _: &T) {} +fn check_statically_same_type(_: &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 $(,)?) => { - same_type(&$left, &$right); + check_statically_same_type(&$left, &$right); assert_eq!($left, $right); }; } @@ -84,3 +84,34 @@ fn dmatrix_small_dims_exhaustive() { 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])); } + +#[test] +fn vector_small_dims_exhaustive() { + assert_eq_and_type!(vector![], SVector::::zeros()); + assert_eq_and_type!(vector![1], Vector1::::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 = vector![]; + const _: Vector1 = vector![1]; + const _: Vector2 = vector![1, 2]; + const _: Vector6 = vector![1, 2, 3, 4, 5, 6]; +} + +#[test] +fn dvector_small_dims_exhaustive() { + assert_eq_and_type!(dvector![], DVector::::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])); +} \ No newline at end of file From da077497a2be5f455f4905ed0ef323604a091aca Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Fri, 30 Apr 2021 14:53:49 +0200 Subject: [PATCH 13/25] Add trybuild tests to test error message reported when matrix dims mismatch --- nalgebra-macros/Cargo.toml | 1 + nalgebra-macros/tests/tests.rs | 16 ++++++++++++++++ .../trybuild/dmatrix_mismatched_dimensions.rs | 6 ++++++ .../dmatrix_mismatched_dimensions.stderr | 5 +++++ .../trybuild/matrix_mismatched_dimensions.rs | 6 ++++++ .../trybuild/matrix_mismatched_dimensions.stderr | 5 +++++ 6 files changed, 39 insertions(+) create mode 100644 nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.rs create mode 100644 nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.stderr create mode 100644 nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.rs create mode 100644 nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.stderr diff --git a/nalgebra-macros/Cargo.toml b/nalgebra-macros/Cargo.toml index 1f27b572b..5ebef960e 100644 --- a/nalgebra-macros/Cargo.toml +++ b/nalgebra-macros/Cargo.toml @@ -17,3 +17,4 @@ proc-macro2 = "1.0" [dev-dependencies] nalgebra = { version = "0.26.1", path = ".." } +trybuild = "1.0.42" diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index c97ae103c..c9d64d910 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -114,4 +114,20 @@ fn dvector_small_dims_exhaustive() { 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"); } \ No newline at end of file diff --git a/nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.rs b/nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.rs new file mode 100644 index 000000000..786b58498 --- /dev/null +++ b/nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.rs @@ -0,0 +1,6 @@ +use nalgebra_macros::dmatrix; + +fn main() { + dmatrix![1, 2, 3; + 4, 5]; +} \ No newline at end of file diff --git a/nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.stderr b/nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.stderr new file mode 100644 index 000000000..eaedc650e --- /dev/null +++ b/nalgebra-macros/tests/trybuild/dmatrix_mismatched_dimensions.stderr @@ -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]; + | ^ diff --git a/nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.rs b/nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.rs new file mode 100644 index 000000000..c5eb87b7a --- /dev/null +++ b/nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.rs @@ -0,0 +1,6 @@ +use nalgebra_macros::matrix; + +fn main() { + matrix![1, 2, 3; + 4, 5]; +} \ No newline at end of file diff --git a/nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.stderr b/nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.stderr new file mode 100644 index 000000000..c83e8d0cb --- /dev/null +++ b/nalgebra-macros/tests/trybuild/matrix_mismatched_dimensions.stderr @@ -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]; + | ^ From b96c75549d2e52b2db92a535b009c1e7973e30de Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Fri, 30 Apr 2021 16:46:29 +0200 Subject: [PATCH 14/25] Document macros --- nalgebra-macros/src/lib.rs | 77 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs index fd29b361d..133ea41a0 100644 --- a/nalgebra-macros/src/lib.rs +++ b/nalgebra-macros/src/lib.rs @@ -93,6 +93,39 @@ impl Parse for Matrix { } } +/// Construct a fixed-size matrix directly from data. +/// +/// 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); @@ -111,6 +144,20 @@ pub fn matrix(stream: TokenStream) -> TokenStream { proc_macro::TokenStream::from(output) } +/// Construct a dynamic matrix directly from data. +/// +/// 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); @@ -164,6 +211,24 @@ impl Parse for Vector { } } +/// Construct a fixed-size column vector directly from data. +/// +/// 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); @@ -176,6 +241,18 @@ pub fn vector(stream: TokenStream) -> TokenStream { proc_macro::TokenStream::from(output) } +/// Construct a dynamic column vector directly from data. +/// +/// 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); From eeab4db69a54c17aa0f43fb9c941e51bf86f5321 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Fri, 30 Apr 2021 16:47:09 +0200 Subject: [PATCH 15/25] Add nalgebra/macros feature and re-export matrix macros from nalgebra --- Cargo.toml | 4 +++- src/lib.rs | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ad24b8095..1caec25fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ name = "nalgebra" path = "src/lib.rs" [features] -default = [ "std" ] +default = [ "std", "macros" ] std = [ "matrixmultiply", "simba/std" ] sparse = [ ] debug = [ "approx/num-complex", "rand" ] @@ -32,6 +32,7 @@ compare = [ "matrixcompare-core" ] libm = [ "simba/libm" ] libm-force = [ "simba/libm_force" ] no_unsound_assume_init = [ ] +macros = [ "nalgebra-macros" ] # Conversion convert-mint = [ "mint" ] @@ -60,6 +61,7 @@ proptest-support = [ "proptest" ] slow-tests = [] [dependencies] +nalgebra-macros = { version = "0.1", path = "nalgebra-macros", optional = true } typenum = "1.12" rand-package = { package = "rand", version = "0.8", optional = true, default-features = false } num-traits = { version = "0.2", default-features = false } diff --git a/src/lib.rs b/src/lib.rs index bc6d17187..ea05e17d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,6 +136,9 @@ pub use crate::sparse::*; )] pub use base as core; +#[cfg(feature = "macros")] +pub use nalgebra_macros::{dmatrix, dvector, matrix, vector}; + use simba::scalar::SupersetOf; use std::cmp::{self, Ordering, PartialOrd}; From 0bde07f95c3aa178a22466c18d69fc11439b1d81 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Fri, 30 Apr 2021 17:06:59 +0200 Subject: [PATCH 16/25] Document that feature needs to be enabled, and require macros feature for tests --- Cargo.toml | 2 +- nalgebra-macros/src/lib.rs | 13 +++++++++++++ tests/lib.rs | 8 +++++--- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1caec25fa..71f723845 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,4 +111,4 @@ lto = true [package.metadata.docs.rs] # Enable certain features when building docs for docs.rs -features = [ "proptest-support", "compare" ] +features = [ "proptest-support", "compare", "macros" ] diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs index 133ea41a0..c00a538f1 100644 --- a/nalgebra-macros/src/lib.rs +++ b/nalgebra-macros/src/lib.rs @@ -1,3 +1,8 @@ +//! 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 syn::{Expr}; @@ -95,6 +100,8 @@ impl Parse for Matrix { /// 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 @@ -146,6 +153,8 @@ pub fn matrix(stream: TokenStream) -> TokenStream { /// 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. @@ -213,6 +222,8 @@ impl Parse for Vector { /// 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 @@ -243,6 +254,8 @@ pub fn vector(stream: TokenStream) -> TokenStream { /// 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. diff --git a/tests/lib.rs b/tests/lib.rs index 8ee85f075..b1da01c3b 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,7 +1,7 @@ -#[cfg(not(all(feature = "debug", feature = "compare", feature = "rand")))] +#[cfg(not(all(feature = "debug", feature = "compare", feature = "rand", feature = "macros")))] compile_error!( - "Please enable the `debug`, `compare`, and `rand` features in order to compile and run the tests. - Example: `cargo test --features debug,compare,rand`" + "Please enable the `debug`, `compare`, `rand` and `macros` features in order to compile and run the tests. + Example: `cargo test --features debug,compare,rand,macros`" ); #[cfg(feature = "abomonation-serialize")] @@ -25,6 +25,8 @@ mod linalg; #[cfg(feature = "proptest-support")] mod proptest; +mod macros; + //#[cfg(all(feature = "debug", feature = "compare", feature = "rand"))] //#[cfg(feature = "sparse")] //mod sparse; From 041b8c4b2c6eb1caada5d673adb4a8cd505920e6 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Fri, 30 Apr 2021 17:07:21 +0200 Subject: [PATCH 17/25] Add macro sanity tests to macros --- tests/core/macros.rs | 11 +++++++++++ tests/core/mod.rs | 3 +++ tests/lib.rs | 2 -- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 tests/core/macros.rs diff --git a/tests/core/macros.rs b/tests/core/macros.rs new file mode 100644 index 000000000..cdc4de039 --- /dev/null +++ b/tests/core/macros.rs @@ -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]; +} \ No newline at end of file diff --git a/tests/core/mod.rs b/tests/core/mod.rs index 4f9bc745e..aa0052642 100644 --- a/tests/core/mod.rs +++ b/tests/core/mod.rs @@ -16,3 +16,6 @@ mod matrixcompare; #[cfg(feature = "arbitrary")] pub mod helper; + +#[cfg(feature = "macros")] +mod macros; \ No newline at end of file diff --git a/tests/lib.rs b/tests/lib.rs index b1da01c3b..d08f48071 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -25,8 +25,6 @@ mod linalg; #[cfg(feature = "proptest-support")] mod proptest; -mod macros; - //#[cfg(all(feature = "debug", feature = "compare", feature = "rand"))] //#[cfg(feature = "sparse")] //mod sparse; From 08a77dd05e02cb497d33cc96a182ad00fef30a96 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Fri, 30 Apr 2021 17:16:18 +0200 Subject: [PATCH 18/25] Test nalgebra-macros on CI --- .github/workflows/nalgebra-ci-build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/nalgebra-ci-build.yml b/.github/workflows/nalgebra-ci-build.yml index d302c908a..fd3ec2734 100644 --- a/.github/workflows/nalgebra-ci-build.yml +++ b/.github/workflows/nalgebra-ci-build.yml @@ -69,6 +69,12 @@ jobs: - name: test nalgebra-sparse (slow tests) # 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 + test-nalgebra-macros: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: test nalgebra-macros + run: cargo test -p nalgebra-macros build-wasm: runs-on: ubuntu-latest # env: From 8552fc8385f1b4da955b8d5f7b9a1777d0c596bb Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Mon, 3 May 2021 13:50:48 +0200 Subject: [PATCH 19/25] Cargo fmt --- nalgebra-macros/src/lib.rs | 38 +++++++++++++++++----------------- nalgebra-macros/tests/tests.rs | 16 ++++++++++++-- src/base/vec_storage.rs | 8 +++---- tests/core/macros.rs | 2 +- tests/core/mod.rs | 2 +- tests/lib.rs | 7 ++++++- 6 files changed, 44 insertions(+), 29 deletions(-) diff --git a/nalgebra-macros/src/lib.rs b/nalgebra-macros/src/lib.rs index c00a538f1..5b1dd6778 100644 --- a/nalgebra-macros/src/lib.rs +++ b/nalgebra-macros/src/lib.rs @@ -5,14 +5,14 @@ extern crate proc_macro; -use syn::{Expr}; -use syn::parse::{Parse, ParseStream, Result, Error}; -use syn::punctuated::{Punctuated}; -use syn::{parse_macro_input, Token}; -use quote::{quote, TokenStreamExt, ToTokens}; 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::{TokenStream as TokenStream2, Delimiter, TokenTree, Spacing}; +use proc_macro2::{Delimiter, Spacing, TokenStream as TokenStream2, TokenTree}; use proc_macro2::{Group, Punct}; struct Matrix { @@ -33,10 +33,9 @@ impl Matrix { /// 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() { + for j in 0..self.ncols() { let mut col = TokenStream2::new(); - let col_iter = (0 .. self.nrows()) - .map(move |i| &self.rows[i][j]); + 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)); @@ -48,8 +47,8 @@ impl Matrix { /// (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() { + 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)); } @@ -76,7 +75,8 @@ impl Parse for Matrix { "Unexpected number of entries in row {}. Expected {}, found {} entries.", row_idx, ncols, - row.len()); + row.len() + ); return Err(Error::new(row_span, error_msg)); } } else { @@ -93,7 +93,7 @@ impl Parse for Matrix { Ok(Self { rows, - ncols: ncols.unwrap_or(0) + ncols: ncols.unwrap_or(0), }) } } @@ -209,13 +209,13 @@ impl Parse for Vector { // The syntax of a vector is just the syntax of a single matrix row if input.is_empty() { Ok(Self { - elements: Vec::new() + elements: Vec::new(), }) } else { - let elements = MatrixRowSyntax::parse_separated_nonempty(input)?.into_iter().collect(); - Ok(Self { - elements - }) + let elements = MatrixRowSyntax::parse_separated_nonempty(input)? + .into_iter() + .collect(); + Ok(Self { elements }) } } } @@ -279,4 +279,4 @@ pub fn dvector(stream: TokenStream) -> TokenStream { vec!#array_tokens)) }; proc_macro::TokenStream::from(output) -} \ No newline at end of file +} diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index c9d64d910..88fbb9ae1 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -1,5 +1,9 @@ +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}; -use nalgebra::{DMatrix, DVector, SMatrix, Matrix3x2, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2x1, Matrix2, Matrix2x3, Matrix2x4, Matrix3x1, Matrix3, Matrix3x4, Matrix4x1, Matrix4x2, Matrix4x3, Matrix4, Vector1, Vector2, Vector3, Vector4, Vector5, SVector, Vector6}; fn check_statically_same_type(_: &T, _: &T) {} @@ -11,6 +15,8 @@ macro_rules! assert_eq_and_type { }; } +// Skip rustfmt because it just makes the test bloated without making it more readable +#[rustfmt::skip] #[test] fn matrix_small_dims_exhaustive() { // 0x0 @@ -52,6 +58,8 @@ fn matrix_const_fn() { const _: SMatrix = 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 @@ -85,6 +93,8 @@ fn dmatrix_small_dims_exhaustive() { 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::::zeros()); @@ -105,6 +115,8 @@ fn vector_const_fn() { const _: Vector6 = 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::::zeros(0)); @@ -130,4 +142,4 @@ fn dmatrix_trybuild_tests() { // Verify error message when we give a matrix with mismatched dimensions t.compile_fail("tests/trybuild/dmatrix_mismatched_dimensions.rs"); -} \ No newline at end of file +} diff --git a/src/base/vec_storage.rs b/src/base/vec_storage.rs index 15ea9237f..f8d287698 100644 --- a/src/base/vec_storage.rs +++ b/src/base/vec_storage.rs @@ -13,9 +13,9 @@ use crate::base::storage::{ }; use crate::base::{Scalar, Vector}; +use crate::{DMatrix, DVector}; #[cfg(feature = "abomonation-serialize")] use abomonation::Abomonation; -use crate::{DMatrix, DVector}; /* * @@ -412,8 +412,7 @@ impl Extend for VecStorage { } } -impl DMatrix -{ +impl DMatrix { /// Creates a new heap-allocated matrix from the given [VecStorage]. pub const fn from_vec_storage(storage: VecStorage) -> Self { // This is sound because the dimensions of the matrix and the storage are guaranteed @@ -422,8 +421,7 @@ impl DMatrix } } -impl DVector -{ +impl DVector { /// Creates a new heap-allocated matrix from the given [VecStorage]. pub const fn from_vec_storage(storage: VecStorage) -> Self { // This is sound because the dimensions of the matrix and the storage are guaranteed diff --git a/tests/core/macros.rs b/tests/core/macros.rs index cdc4de039..eaa134ffe 100644 --- a/tests/core/macros.rs +++ b/tests/core/macros.rs @@ -8,4 +8,4 @@ fn sanity_test() { let _ = dmatrix![1, 2, 3; 4, 5, 6]; let _ = vector![1, 2, 3, 4, 5, 6]; let _ = dvector![1, 2, 3, 4, 5, 6]; -} \ No newline at end of file +} diff --git a/tests/core/mod.rs b/tests/core/mod.rs index aa0052642..c03461dc4 100644 --- a/tests/core/mod.rs +++ b/tests/core/mod.rs @@ -18,4 +18,4 @@ mod matrixcompare; pub mod helper; #[cfg(feature = "macros")] -mod macros; \ No newline at end of file +mod macros; diff --git a/tests/lib.rs b/tests/lib.rs index d08f48071..20d38d7a5 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,4 +1,9 @@ -#[cfg(not(all(feature = "debug", feature = "compare", feature = "rand", feature = "macros")))] +#[cfg(not(all( + feature = "debug", + feature = "compare", + feature = "rand", + feature = "macros" +)))] compile_error!( "Please enable the `debug`, `compare`, `rand` and `macros` features in order to compile and run the tests. Example: `cargo test --features debug,compare,rand,macros`" From f42ecf0ebb8d14a55155a5443058f782a3d70a77 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 5 May 2021 08:51:16 +0200 Subject: [PATCH 20/25] Improve nalgebra-macros/Cargo.toml metadata --- nalgebra-macros/Cargo.toml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/nalgebra-macros/Cargo.toml b/nalgebra-macros/Cargo.toml index 5ebef960e..d95784e06 100644 --- a/nalgebra-macros/Cargo.toml +++ b/nalgebra-macros/Cargo.toml @@ -1,16 +1,21 @@ [package] name = "nalgebra-macros" version = "0.1.0" -authors = ["Andreas Longva "] +authors = [ "Andreas Longva", "Sébastien Crozet " ] 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 -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] -# TODO: Determine minimal features that we need syn = { version="1.0", features = ["full"] } quote = "1.0" proc-macro2 = "1.0" From 57541aa2533e2331505ad474b6d16ab07a7d543d Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 5 May 2021 09:42:31 +0200 Subject: [PATCH 21/25] Add tests to ensure macros compile for all built-in types --- nalgebra-macros/tests/tests.rs | 68 ++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index 88fbb9ae1..206e5e668 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -143,3 +143,71 @@ fn dmatrix_trybuild_tests() { // 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 = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0, 1; 2, 3]; + const _: SMatrix = matrix![0.0, 1.0; 2.0, 3.0]; + const _: SMatrix = 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 = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0, 1, 2, 3]; + const _: SVector = vector![0.0, 1.0, 2.0, 3.0]; + const _: SVector = 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 = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0, 1; 2, 3]; + let _: DMatrix = dmatrix![0.0, 1.0; 2.0, 3.0]; + let _: DMatrix = 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 = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0, 1, 2, 3]; + let _: DVector = dvector![0.0, 1.0, 2.0, 3.0]; + let _: DVector = dvector![0.0, 1.0, 2.0, 3.0]; +} \ No newline at end of file From 6026a0543349ee39c48847b165045df39116cc90 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 5 May 2021 10:03:18 +0200 Subject: [PATCH 22/25] Test that matrix macros work with arbitrary expressions --- nalgebra-macros/tests/tests.rs | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index 206e5e668..22daaf8df 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -210,4 +210,49 @@ fn dvector_builtin_types() { let _: DVector = dvector![0, 1, 2, 3]; let _: DVector = dvector![0.0, 1.0, 2.0, 3.0]; let _: DVector = 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(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); } \ No newline at end of file From 39b275fc924b49d0f3d6507753532aae7ea48ca9 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 5 May 2021 10:04:47 +0200 Subject: [PATCH 23/25] Formatting --- nalgebra-macros/tests/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nalgebra-macros/tests/tests.rs b/nalgebra-macros/tests/tests.rs index 22daaf8df..339dd048b 100644 --- a/nalgebra-macros/tests/tests.rs +++ b/nalgebra-macros/tests/tests.rs @@ -255,4 +255,4 @@ fn dvector_arbitrary_expressions() { 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); -} \ No newline at end of file +} From 3a3bc55f667b7a928eea0608e650ac4c6c3f4ffc Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Fri, 7 May 2021 09:00:29 +0200 Subject: [PATCH 24/25] Move from_{}_storage impl blocks to matrix.rs --- src/base/array_storage.rs | 12 ----------- src/base/matrix.rs | 43 ++++++++++++++++++++++++++++++++++++++- src/base/vec_storage.rs | 19 ----------------- 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/src/base/array_storage.rs b/src/base/array_storage.rs index dc6b22cea..68da4c517 100644 --- a/src/base/array_storage.rs +++ b/src/base/array_storage.rs @@ -25,7 +25,6 @@ use crate::base::storage::{ ContiguousStorage, ContiguousStorageMut, Owned, ReshapableStorage, Storage, StorageMut, }; use crate::base::Scalar; -use crate::SMatrix; /* * @@ -300,14 +299,3 @@ where self.as_slice().iter().fold(0, |acc, e| acc + e.extent()) } } - -// TODO: Where to put this impl block? -impl SMatrix { - /// Creates a new statically-allocated matrix from the given [ArrayStorage]. - #[inline(always)] - pub const fn from_array_storage(storage: ArrayStorage) -> 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) } - } -} diff --git a/src/base/matrix.rs b/src/base/matrix.rs index 3b4bc50c7..f9b391efc 100644 --- a/src/base/matrix.rs +++ b/src/base/matrix.rs @@ -29,7 +29,7 @@ use crate::base::storage::{ ContiguousStorage, ContiguousStorageMut, Owned, SameShapeStorage, Storage, StorageMut, }; use crate::base::{Const, DefaultAllocator, OMatrix, OVector, Scalar, Unit}; -use crate::SimdComplexField; +use crate::{ArrayStorage, DMatrix, DVector, Dynamic, SMatrix, SimdComplexField, VecStorage}; /// A square matrix. pub type SquareMatrix = Matrix; @@ -317,6 +317,47 @@ impl Matrix { } } +impl SMatrix { + /// 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) -> 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 +impl DMatrix { + /// 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) -> 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 +impl DVector { + /// 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) -> 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> Matrix { /// Creates a new matrix with the given data. #[inline(always)] diff --git a/src/base/vec_storage.rs b/src/base/vec_storage.rs index f8d287698..29da127d8 100644 --- a/src/base/vec_storage.rs +++ b/src/base/vec_storage.rs @@ -13,7 +13,6 @@ use crate::base::storage::{ }; use crate::base::{Scalar, Vector}; -use crate::{DMatrix, DVector}; #[cfg(feature = "abomonation-serialize")] use abomonation::Abomonation; @@ -411,21 +410,3 @@ impl Extend for VecStorage { self.nrows = Dynamic::new(self.data.len()); } } - -impl DMatrix { - /// Creates a new heap-allocated matrix from the given [VecStorage]. - pub const fn from_vec_storage(storage: VecStorage) -> 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 DVector { - /// Creates a new heap-allocated matrix from the given [VecStorage]. - pub const fn from_vec_storage(storage: VecStorage) -> 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) } - } -} From 922393b04f64d34bb4ff1ad4e5d3171697a706d2 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Fri, 7 May 2021 09:09:10 +0200 Subject: [PATCH 25/25] Enable from_{}_storage only when std/alloc available --- src/base/matrix.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/base/matrix.rs b/src/base/matrix.rs index f9b391efc..3cde8a4f6 100644 --- a/src/base/matrix.rs +++ b/src/base/matrix.rs @@ -29,7 +29,10 @@ use crate::base::storage::{ ContiguousStorage, ContiguousStorageMut, Owned, SameShapeStorage, Storage, StorageMut, }; use crate::base::{Const, DefaultAllocator, OMatrix, OVector, Scalar, Unit}; -use crate::{ArrayStorage, DMatrix, DVector, Dynamic, SMatrix, SimdComplexField, VecStorage}; +use crate::{ArrayStorage, SMatrix, SimdComplexField}; + +#[cfg(any(feature = "std", feature = "alloc"))] +use crate::{DMatrix, DVector, Dynamic, VecStorage}; /// A square matrix. pub type SquareMatrix = Matrix; @@ -332,6 +335,7 @@ impl SMatrix { // 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 DMatrix { /// Creates a new heap-allocated matrix from the given [VecStorage]. /// @@ -346,6 +350,7 @@ impl DMatrix { // 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 DVector { /// Creates a new heap-allocated matrix from the given [VecStorage]. ///