Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add macro for concatenating matrices #1080

Closed
wants to merge 13 commits into from
405 changes: 402 additions & 3 deletions nalgebra-macros/src/lib.rs

Large diffs are not rendered by default.

124 changes: 123 additions & 1 deletion nalgebra-macros/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use nalgebra::{
Point1, Point2, Point3, Point4, Point5, Point6, SMatrix, SVector, Vector1, Vector2, Vector3,
Vector4, Vector5, Vector6,
};
use nalgebra_macros::{dmatrix, dvector, matrix, point, vector};
use nalgebra_macros::{dmatrix, dvector, matrix, point, stack, vector};

fn check_statically_same_type<T>(_: &T, _: &T) {}

Expand Down Expand Up @@ -305,3 +305,125 @@ fn dvector_arbitrary_expressions() {
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);
}

#[test]
fn stack_simple() {
let m = stack![
Matrix2::<usize>::identity(), 0;
0, &Matrix2::identity();
];

assert_eq_and_type!(m, Matrix4::identity());
}

#[test]
fn stack_diag() {
let m = stack![
0, matrix![1, 2; 3, 4;];
matrix![5, 6; 7, 8;], 0;
];

let res = matrix![
0, 0, 1, 2;
0, 0, 3, 4;
5, 6, 0, 0;
7, 8, 0, 0;
];

assert_eq_and_type!(m, res);
}

#[test]
fn stack_dynamic() {
let m = stack![
matrix![ 1, 2; 3, 4; ], 0;
0, dmatrix![7, 8, 9; 10, 11, 12; ];
];

let res = dmatrix![
1, 2, 0, 0, 0;
3, 4, 0, 0, 0;
0, 0, 7, 8, 9;
0, 0, 10, 11, 12;
];

assert_eq_and_type!(m, res);
}

#[test]
fn stack_nested() {
let m = stack![
stack![ matrix![1, 2; 3, 4;]; matrix![5, 6;]],
stack![ matrix![7;9;10;], matrix![11; 12; 13;] ];
];

let res = matrix![
1, 2, 7, 11;
3, 4, 9, 12;
5, 6, 10, 13;
];

assert_eq_and_type!(m, res);
}

#[test]
fn stack_single() {
let a = matrix![1, 2; 3, 4];
let b = stack![a];

assert_eq_and_type!(a, b);
}

#[test]
fn stack_single_row() {
let a = matrix![1, 2; 3, 4];
let m = stack![a, a];

let res = matrix![
1, 2, 1, 2;
3, 4, 3, 4;
];

assert_eq_and_type!(m, res);
}

#[test]
fn stack_single_col() {
let a = matrix![1, 2; 3, 4];
let m = stack![a; a];

let res = matrix![
1, 2;
3, 4;
1, 2;
3, 4;
];

assert_eq_and_type!(m, res);
}

#[test]
fn stack_expr() {
let a = matrix![1, 2; 3, 4];
let b = matrix![5, 6; 7, 8];
let m = stack![a + b; b - a];

let res = matrix![
6, 8;
10, 12;
4, 4;
4, 4;
];

assert_eq_and_type!(m, res);
}

#[test]
fn stack_trybuild_tests() {
let t = trybuild::TestCases::new();

// Verify error message when try to conactenate no matrices
t.compile_fail("tests/trybuild/stack_empty.rs");
t.compile_fail("tests/trybuild/stack_empty_row.rs");
t.compile_fail("tests/trybuild/stack_empty_col.rs");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be a good idea to add to this file tests to ensure that the macro doesn't explode if it's passed a matrix with zero rows or zero columns, e.g.

stack![
  a, 0;
  0, b;
];

Should yield a result equal to a if b is a dynamic matrix with 0 rows and 0 columns.

Actually, that's something that might be worth considering -- what should the behaviour of this macro be if passed a DMatrix with zero in one dimension and nonzero in another dimension, and then asked to use 0? E.g., the above, but the matrix b has 2 rows and 0 columns. If a were [1, 2; 3, 4] then I think the most reasonable result would be

[
  1, 2;
  3, 4;
  0, 0;
  0, 0;
]

as that's the most predictable result of having that row and column count specified.

This also raises a question of whether a dynamic matrix with 3 rows and 0 columns should be considered "compatible" with a stack row that contains matrices with 5 rows. In theory it could be, although again raising an error might be the most predictable behaviour, as it lets us "take the dynamic matrix at its word" that it contains precisely 3 rows, even if it's got no entries.

5 changes: 5 additions & 0 deletions nalgebra-macros/tests/trybuild/stack_empty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use nalgebra_macros::stack;

fn main() {
stack![];
}
7 changes: 7 additions & 0 deletions nalgebra-macros/tests/trybuild/stack_empty.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: `stack` macro cannot be used without any arguments
--> tests/trybuild/stack_empty.rs:4:5
|
4 | stack![];
| ^^^^^^^^
|
= note: this error originates in the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info)
6 changes: 6 additions & 0 deletions nalgebra-macros/tests/trybuild/stack_empty_col.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use nalgebra_macros::{matrix, stack};

fn main() {
let m = matrix![1, 2; 3, 4];
stack![0, m];
}
7 changes: 7 additions & 0 deletions nalgebra-macros/tests/trybuild/stack_empty_col.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: At least one element in each column must be an expression of type `Matrix`
--> tests/trybuild/stack_empty_col.rs:5:5
|
5 | stack![0, m];
| ^^^^^^^^^^^^
|
= note: this error originates in the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info)
6 changes: 6 additions & 0 deletions nalgebra-macros/tests/trybuild/stack_empty_row.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use nalgebra_macros::{matrix, stack};

fn main() {
let m = matrix![1, 2; 3, 4];
stack![0; m];
}
7 changes: 7 additions & 0 deletions nalgebra-macros/tests/trybuild/stack_empty_row.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: At least one element in each row must be an expression of type `Matrix`
--> tests/trybuild/stack_empty_row.rs:5:5
|
5 | stack![0; m];
| ^^^^^^^^^^^^
|
= note: this error originates in the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info)
10 changes: 10 additions & 0 deletions src/base/constraint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ pub trait DimEq<D1: Dim, D2: Dim> {
/// This is either equal to `D1` or `D2`, always choosing the one (if any) which is a type-level
/// constant.
type Representative: Dim;

/// This constructs a value of type `Representative` with the
/// correct value
fn representative(d1: D1, d2: D2) -> Option<Self::Representative> {
if d1.value() != d2.value() {
None
} else {
Some(Self::Representative::from_usize(d1.value()))
}
}
}

impl<D: Dim> DimEq<D, D> for ShapeConstraint {
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ pub use crate::sparse::*;
pub use base as core;

#[cfg(feature = "macros")]
pub use nalgebra_macros::{dmatrix, dvector, matrix, point, vector};
pub use nalgebra_macros::{dmatrix, dvector, matrix, point, stack, vector};

use simba::scalar::SupersetOf;
use std::cmp::{self, Ordering, PartialOrd};
Expand Down