mirror of
https://gitlab.com/libeigen/eigen.git
synced 2026-04-10 11:34:33 +08:00
Add boundary test coverage: stableNorm, LinSpaced, complex GEMV, triangular solve
libeigen/eigen!2291 Co-authored-by: Rasmus Munk Larsen <rmlarsen@gmail.com>
This commit is contained in:
131
test/nullary.cpp
131
test/nullary.cpp
@@ -293,6 +293,130 @@ void nullary_internal_logic() {
|
||||
}
|
||||
}
|
||||
|
||||
// Test LinSpaced at vectorization boundary sizes.
|
||||
// The packetOp in linspaced_op_impl uses mask/select logic to handle
|
||||
// the last partial packet (when vector size is not a multiple of PacketSize).
|
||||
// This exercises those boundaries with element-by-element verification.
|
||||
template <typename Scalar>
|
||||
void linspaced_boundary() {
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
const Index PS = internal::packet_traits<Scalar>::size;
|
||||
const Index sizes[] = {1, 2, 3, PS - 1, PS, PS + 1, 2 * PS - 1, 2 * PS, 2 * PS + 1, 4 * PS, 4 * PS + 1};
|
||||
typedef Matrix<Scalar, Dynamic, 1> Vec;
|
||||
|
||||
for (int si = 0; si < 11; ++si) {
|
||||
Index n = sizes[si];
|
||||
if (n <= 0) continue;
|
||||
|
||||
Scalar low(1), high(100);
|
||||
Vec v = Vec::LinSpaced(n, low, high);
|
||||
|
||||
// With n==1, LinSpaced returns [high] by design.
|
||||
if (n == 1) {
|
||||
VERIFY_IS_EQUAL(v(0), high);
|
||||
} else {
|
||||
VERIFY_IS_EQUAL(v(0), low);
|
||||
VERIFY_IS_EQUAL(v(n - 1), high);
|
||||
|
||||
// Verify monotonicity.
|
||||
for (Index k = 1; k < n; ++k) {
|
||||
VERIFY(numext::real(v(k)) >= numext::real(v(k - 1)));
|
||||
}
|
||||
|
||||
// Verify against scalar reference computation.
|
||||
for (Index k = 0; k < n; ++k) {
|
||||
Scalar ref = Scalar(low + (high - low) * RealScalar(k) / RealScalar(n - 1));
|
||||
VERIFY_IS_APPROX(v(k), ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test the "flip" path: when |high| < |low|, the implementation uses
|
||||
// a reversed computation for better precision. Verify at packet boundaries.
|
||||
for (int si = 0; si < 11; ++si) {
|
||||
Index n = sizes[si];
|
||||
if (n <= 0 || n == 1) continue; // skip n=1, flip irrelevant for single element
|
||||
|
||||
Scalar low(1000), high(1); // |high| < |low| triggers flip
|
||||
Vec v = Vec::LinSpaced(n, low, high);
|
||||
|
||||
VERIFY_IS_EQUAL(v(0), low);
|
||||
VERIFY_IS_EQUAL(v(n - 1), high);
|
||||
|
||||
// Verify monotonicity (decreasing).
|
||||
for (Index k = 1; k < n; ++k) {
|
||||
VERIFY(numext::real(v(k)) <= numext::real(v(k - 1)));
|
||||
}
|
||||
|
||||
// Verify against scalar reference.
|
||||
for (Index k = 0; k < n; ++k) {
|
||||
Scalar ref = Scalar(low + (high - low) * RealScalar(k) / RealScalar(n - 1));
|
||||
VERIFY_IS_APPROX(v(k), ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test integer LinSpaced divisor path.
|
||||
// When (abs(high - low) + 1) < num_steps, the integer LinSpaced uses
|
||||
// a divisor-based formula instead of multiplication. This path is
|
||||
// barely covered by existing tests which use random ranges.
|
||||
template <int>
|
||||
void linspaced_integer_divisor() {
|
||||
typedef Matrix<int, Dynamic, 1> VecI;
|
||||
|
||||
// Case: num_steps much larger than range → triggers divisor path.
|
||||
// LinSpaced(12, 0, 5): 12 steps over range [0,5], so range+1=6, 6 < 12.
|
||||
{
|
||||
VecI v = VecI::LinSpaced(12, 0, 5);
|
||||
VERIFY_IS_EQUAL(v(0), 0);
|
||||
// All values must be in [0, 5].
|
||||
for (Index k = 0; k < 12; ++k) {
|
||||
VERIFY(v(k) >= 0 && v(k) <= 5);
|
||||
}
|
||||
// Must be non-decreasing.
|
||||
for (Index k = 1; k < 12; ++k) {
|
||||
VERIFY(v(k) >= v(k - 1));
|
||||
}
|
||||
// Each integer 0-5 should appear at least once.
|
||||
for (int val = 0; val <= 5; ++val) {
|
||||
bool found = false;
|
||||
for (Index k = 0; k < 12; ++k) {
|
||||
if (v(k) == val) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
VERIFY(found);
|
||||
}
|
||||
}
|
||||
|
||||
// Case: range exactly divides steps → each value should appear equally.
|
||||
// LinSpaced(20, 0, 3): range+1=4, 20%4==0, so each of 0,1,2,3 appears 5 times.
|
||||
{
|
||||
VecI v = VecI::LinSpaced(20, 0, 3);
|
||||
VERIFY_IS_EQUAL(v(0), 0);
|
||||
for (Index k = 0; k < 20; ++k) {
|
||||
VERIFY(v(k) >= 0 && v(k) <= 3);
|
||||
}
|
||||
for (Index k = 1; k < 20; ++k) {
|
||||
VERIFY(v(k) >= v(k - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse: LinSpaced(12, 5, 0) should be reverse of LinSpaced(12, 0, 5).
|
||||
{
|
||||
VecI fwd = VecI::LinSpaced(12, 0, 5);
|
||||
VecI rev = VecI::LinSpaced(12, 5, 0);
|
||||
VERIFY_IS_APPROX(fwd, rev.reverse());
|
||||
}
|
||||
|
||||
// Single step: always returns high.
|
||||
{
|
||||
VecI v = VecI::LinSpaced(1, 3, 7);
|
||||
VERIFY_IS_EQUAL(v(0), 7);
|
||||
}
|
||||
}
|
||||
|
||||
EIGEN_DECLARE_TEST(nullary) {
|
||||
CALL_SUBTEST_1(testMatrixType(Matrix2d()));
|
||||
CALL_SUBTEST_2(testMatrixType(MatrixXcf(internal::random<int>(1, 300), internal::random<int>(1, 300))));
|
||||
@@ -318,4 +442,11 @@ EIGEN_DECLARE_TEST(nullary) {
|
||||
CALL_SUBTEST_6(bug1630<0>());
|
||||
CALL_SUBTEST_9(nullary_overflow<0>());
|
||||
CALL_SUBTEST_10(nullary_internal_logic<0>());
|
||||
|
||||
// LinSpaced at vectorization boundaries (deterministic, outside g_repeat).
|
||||
CALL_SUBTEST_11(linspaced_boundary<float>());
|
||||
CALL_SUBTEST_11(linspaced_boundary<double>());
|
||||
|
||||
// Integer LinSpaced divisor path tests.
|
||||
CALL_SUBTEST_12(linspaced_integer_divisor<0>());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user