Summary
Numerical tolerance in tests is currently ad-hoc: most tests use the base eps from the fixture, while 9 linear algebra tests use eps * 100 without documented rationale. A systematic strategy is needed, but the appropriate tolerances depend on factors outside the conformance test's control (BLAS implementation, algorithmic choices, problem conditioning).
Current state
Problem
The right tolerance depends on:
- Operation category — exact operations (copy, reshape) vs. numerically sensitive decompositions (SVD, eig)
- Problem size — larger matrices accumulate more floating-point error
- BLAS/LAPACK implementation — different backends (OpenBLAS, MKL, cuSOLVER, etc.) use different algorithms with different numerical properties
- Conditioning — the test matrices' condition numbers affect result accuracy
A conformance test should be tight enough to catch bugs but loose enough to not reject correct implementations that differ only in rounding.
Proposed approach
Step 1: Categorize operations by expected error scaling
| Category |
Examples |
Expected error |
Notes |
| Exact |
copy, reshape, transpose, fill, zeros, eye |
0 |
No floating-point arithmetic |
| Single arithmetic |
scale, set_elem/get_elem |
O(eps) |
One multiply/assignment |
| Multi-element reduction |
norm, trace, linear_combine |
O(sqrt(N) * eps) |
Summation error |
| Factorization |
QR, LQ, SVD, eig, eigh |
O(N * eps * cond) |
BLAS/LAPACK dependent |
| Iterative/composite |
exp, inverse |
O(N^k * eps) |
Algorithm dependent |
Step 2: Design tolerance as a function, not a constant
Rather than hardcoding multipliers, provide a tolerance helper that takes the operation category and problem size:
enum class tol_category { exact, elementwise, reduction, factorization, iterative };
template <typename TenT>
auto tolerance(tci_test_fixture<TenT>& fix, tol_category cat, std::size_t N = 1)
-> tci::real_t<TenT>;
Step 3: Allow backend-specific override
Backends should be able to adjust tolerance multipliers if their BLAS implementation has known characteristics. This could be via:
- Specializing the fixture
- Compile-time defines (e.g.,
TCICT_TOL_FACTORIZATION_MULT=1000)
- Or a configuration mechanism TBD
Open questions
Non-goals
This issue is NOT about changing the current hardcoded values immediately. It's about designing a principled framework so that tolerance decisions are explicit, documented, and adjustable.
Related
Summary
Numerical tolerance in tests is currently ad-hoc: most tests use the base
epsfrom the fixture, while 9 linear algebra tests useeps * 100without documented rationale. A systematic strategy is needed, but the appropriate tolerances depend on factors outside the conformance test's control (BLAS implementation, algorithmic choices, problem conditioning).Current state
1e-10(see fixture: epsilon should be type-aware based on real_t precision #29 for type-awareness fix)eps * 100: QR, LQ, SVD, trunc_SVD, exp (inlinear_algebra.h)epsProblem
The right tolerance depends on:
A conformance test should be tight enough to catch bugs but loose enough to not reject correct implementations that differ only in rounding.
Proposed approach
Step 1: Categorize operations by expected error scaling
Step 2: Design tolerance as a function, not a constant
Rather than hardcoding multipliers, provide a tolerance helper that takes the operation category and problem size:
Step 3: Allow backend-specific override
Backends should be able to adjust tolerance multipliers if their BLAS implementation has known characteristics. This could be via:
TCICT_TOL_FACTORIZATION_MULT=1000)Open questions
eps * 100for factorizations too tight or too loose? Need empirical data.Non-goals
This issue is NOT about changing the current hardcoded values immediately. It's about designing a principled framework so that tolerance decisions are explicit, documented, and adjustable.
Related