I was wondering if there is a good way to test two Eigen matrices for approximate equality using Google Test, or Google Mock.
Take the following test-case as a simplified example: I am multiplying two complex valued matrices A
, and B
, and expect a certain result C_expect
. I calculate the numerical result C_actual = A * B
, using Eigen. Now, I want to compare C_expect
, and C_actual
. Right now, the corresponding code looks like this:
#include <complex>
#include <Eigen/Dense>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
typedef std::complex<double> Complex;
typedef Eigen::Matrix2cd Matrix;
TEST(Eigen, MatrixMultiplication) {
Matrix A, B, C_expect, C_actual;
A << Complex(1, 1), Complex(2, 3),
Complex(3, 2), Complex(4, 4);
B << Complex(4, 4), Complex(3, 2),
Complex(2, 3), Complex(1, 1);
C_expect << Complex(-5, 20), Complex(0, 10),
Complex(0, 40), Complex(5, 20);
C_actual = A * B;
// !!! These are the lines that bother me.
for (int j = 0; j < C_actual.cols(); ++j) {
for (int i = 0; i < C_actual.rows(); ++i) {
EXPECT_NEAR(C_expect(i, j).real(), C_actual(i, j).real(), 1e-7)
<< "Re(" << i << "," << j << ")";
EXPECT_NEAR(C_expect(i, j).imag(), C_actual(i, j).imag(), 1e-7)
<< "Im(" << i << "," << j << ")";
}
}
}
What's wrong with this? Well, I have to manually iterate through all indices of the matrix, and then compare the real-part and imaginary-part individually. I would much prefer something along the lines of Google Mock's ElementsAreArray
matcher. E.g.
EXPECT_THAT(C_actual, ElementsAreArray(C_expect));
// or
EXPECT_THAT(C_actual, Pointwise(MyComplexNear(1e-7), C_expect));
Unfortunately, the built-in capabilities of Google Mock only seem to work on 1-dimensional C-style, or STL-type containers. Furthermore, I need an approximate comparison for the complex values of my matrix.
My question: Do you know if (and how) it is possible to teach Google Mock to iterate over multiple dimensions, and compare complex floating point numbers to approximate equality?
Please note, that I cannot just handle the data-pointers as C-style arrays, because the storage layout might differ between C_expect
, and C_actual
. Also, in reality, the matrices are larger than just 2x2 matrices. I.e. some sort of loop is definitely necessary.
Why not use the isApprox
or isMuchSmallerThan
member functions of Eigen Matrix types?
The documentation of these above functions are available here
So for most cases ASSERT_TRUE(C_actual.isApprox(C_expect));
is what you need. You can also provide a precision parameter as the second arguement to isApprox.
A simplified solution would be to compare the norm of the difference with some epsilon, i.e.
(C_expect - C_actual).norm() < 1e-6
In a vector space || X - Y || == 0 if and only if X == Y, and the norm is always non-negative (real). This way, you won't have to manually do the loop and compare element-wise (of course the norm will perform more calculations in the background than simple element-wise comparisons)
PS: the Matrix::norm()
implemented in Eigen is the Frobenius norm, which is computationally very fast to evaluate, see http://mathworld.wolfram.com/FrobeniusNorm.html
EXPECT_PRED2
from GoogleTest can be used for this.
Under C++11 using a lambda works fine but looks unseemly:
ASSERT_PRED2([](const MatrixXf &lhs, const MatrixXf &rhs) {
return lhs.isApprox(rhs, 1e-4);
},
C_expect, C_actual);
If that fails, you get a print-out of the input arguments.
Instead of using a lambda, a normal predicate function can be defined like this:
bool MatrixEquality(const MatrixXf &lhs, const MatrixXf &rhs) {
return lhs.isApprox(rhs, 1e-4);
}
TEST(Eigen, MatrixMultiplication) {
...
ASSERT_PRED2(MatrixEquality, C_expected, C_actual);
}
The later version also works on pre-C++11.
来源:https://stackoverflow.com/questions/25094989/compare-eigen-matrices-in-google-test-or-google-mock