Macro assert_matrix_eq
macro_rules! assert_matrix_eq { ($($args:tt)*) => { ... }; }
Expand description
Compare matrices for exact or approximate equality.
The assert_matrix_eq!
simplifies the comparison of two matrices by
providing the following features:
- Verifies that the dimensions of the matrices match.
- Offers both exact and approximate comparison of individual elements.
- Multiple types of comparators available, depending on the needs of the user.
- Built-in error reporting makes it easy to determine which elements of the two matrices that do not compare equal.
§Usage
Given two matrices x
and y
, the default invocation performs an exact elementwise
comparison of the two matrices.
// Performs elementwise exact comparison
assert_matrix_eq!(x, y);
An exact comparison is often not desirable. In particular, with floating point types,
rounding errors or other sources of inaccuracies tend to complicate the matter.
For this purpose, assert_matrix_eq!
provides several comparators.
// Available comparators:
assert_matrix_eq!(x, y, comp = exact);
assert_matrix_eq!(x, y, comp = float);
assert_matrix_eq!(x, y, comp = abs, tol = 1e-12);
assert_matrix_eq!(x, y, comp = ulp, tol = 8);
Note: The comp
argument must be specified after x
and y
, and cannot come
after comparator-specific options. This is a deliberate design decision,
with the rationale that assertions should look as uniform as possible for
the sake of readability.
§The exact
comparator
This comparator simply uses the default ==
operator to compare each pair of elements.
The default comparator delegates the comparison to the exact
comparator.
§The float
comparator
The float
comparator is designed to be a conservative default for comparing floating-point numbers.
It is inspired by the AlmostEqualUlpsAndAbs
comparison function proposed in the excellent blog post
Comparing Floating Point Numbers, 2012 Edition
by Bruce Dawson.
If you expect the two matrices to be almost exactly the same, but you want to leave some room for (very small) rounding errors, then this comparator should be your default choice.
The comparison criterion can be summarized as follows:
- If
assert_matrix_eq!(x, y, comp = abs, tol = max_eps)
holds formax_eps
close to the machine epsilon for the floating point type, then the comparison is successful. - Otherwise, returns the result of
assert_matrix_eq!(x, y, comp = ulp, tol = max_ulp)
, wheremax_ulp
is a small positive integer constant.
The max_eps
and max_ulp
parameters can be tweaked to your preference with the syntax:
assert_matrix_eq!(x, y, comp = float, eps = max_eps, ulp = max_ulp);
These additional parameters can be specified in any order after the choice of comparator, and do not both need to be present.
§The abs
comparator
Compares the absolute difference between individual elements against the specified tolerance. Specifically, for every pair of elements x and y picked from the same row and column in X and Y respectively, the criterion is defined by
| x - y | <= tol.
In addition to floating point numbers, the comparator can also be used for integral numbers,
both signed and unsigned. In order to avoid unsigned underflow, the difference is always
computed by subtracting the smaller number from the larger number.
Note that the type of tol
is required to be the same as that of the scalar field.
§The ulp
comparator
Elementwise comparison of floating point numbers based on their ULP difference. Once again, this is inspired by the proposals in the aforementioned blog post by Bruce Dawson, but it handles some cases explicitly as to provide better error reporting.
Note that the ULP difference of two floating point numbers is not defined in the following cases:
- The two numbers have different signs. The only exception here is +0 and -0, which are considered an exact match.
- One of the numbers is NaN.
ULP-based comparison is typically used when two numbers are expected to be very, very close to each other. However, it is typically not very useful very close to zero, which is discussed in the linked blog post above. The error in many mathematical functions can often be bounded by a certain number of ULP, and so this comparator is particularly useful if this number is known.
Note that the scalar type of the matrix must implement the Ulp trait in order
to be used with this comparator. By default, f32
and f64
implementations are provided.
§Error reporting
One of the main motivations for the assert_matrix_eq!
macro is the ability to give
useful error messages which help pinpoint the problems. For example, consider the example
fn main() {
let a = mock_matrix![1.00, 2.00;
3.00, 4.00];
let b = mock_matrix![1.01, 2.00;
3.40, 4.00];
assert_matrix_eq!(a, b, comp = abs, tol = 1e-8);
}
which yields the output
Matrices X and Y have 2 mismatched element pairs.
The mismatched elements are listed below, in the format
(row, col): x = X[[row, col]], y = Y[[row, col]].
(0, 0): x = 1, y = 1.01. Absolute error: 0.010000000000000009.
(1, 0): x = 3, y = 3.4. Absolute error: 0.3999999999999999.
Comparison criterion: absolute difference, |x - y| <= 0.00000001.
§Trait bounds on elements
Each comparator has specific requirements on which traits the elements need to implement. To discover which traits are required for each comparator, we refer the reader to implementors of ElementwiseComparator, which provides the underlying comparison for the various macro invocations.
§Examples
let ref a = mock_matrix![1, 2;
3, 4i64];
let ref b = mock_matrix![1, 3;
3, 4i64];
let ref x = mock_matrix![1.000, 2.000,
3.000, 4.000f64];
let ref y = mock_matrix![0.999, 2.001,
2.998, 4.000f64];
// comp = abs is also applicable to integers
assert_matrix_eq!(a, b, comp = abs, tol = 1);
assert_matrix_eq!(x, y, comp = abs, tol = 0.01);