Skip to content

tests: establish systematic tolerance strategy for numerical operations #32

Description

@ultimatile

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:

  1. Operation category — exact operations (copy, reshape) vs. numerically sensitive decompositions (SVD, eig)
  2. Problem size — larger matrices accumulate more floating-point error
  3. BLAS/LAPACK implementation — different backends (OpenBLAS, MKL, cuSOLVER, etc.) use different algorithms with different numerical properties
  4. 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

  • Should we run a reference benchmark across multiple BLAS implementations (OpenBLAS, MKL, Apple Accelerate) to empirically determine reasonable multiplier ranges?
  • Should tolerance be problem-size-dependent (tighter for small test matrices, looser for large)?
  • How to handle the case where a backend's algorithm is fundamentally different (e.g., randomized SVD vs. divide-and-conquer)?
  • Is the current eps * 100 for 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions