Docs: Streamline agent guidance

libeigen/eigen!2718

Co-authored-by: Rasmus Munk Larsen <rmlarsen@gmail.com>
This commit is contained in:
Rasmus Munk Larsen
2026-07-12 11:18:39 -07:00
co-authored by Rasmus Munk Larsen
parent 0bb1537ff2
commit 1da4c9e532
9 changed files with 697 additions and 356 deletions
+71
View File
@@ -0,0 +1,71 @@
# Benchmarking
Use this guidance for performance-sensitive changes and benchmark reviews. Performance claims need a benchmark that
ships in the same merge request; correctness tests still ship separately and run before timing.
## Projects and Builds
The supported and unsupported benchmark trees are separate, standalone CMake projects. They are not part of Eigen's
main test build and both require Google Benchmark:
```bash
cmake -G Ninja -S benchmarks -B build-bench -DCMAKE_BUILD_TYPE=Release
cmake --build build-bench --target <benchmark-target>
cmake -G Ninja -S unsupported/benchmarks -B build-unsupported-bench -DCMAKE_BUILD_TYPE=Release
cmake --build build-unsupported-bench --target <benchmark-target>
```
The unsupported parent project automatically adds its GPU subtree when it detects `CUDAToolkit`. That configuration
also requires a working CUDA compiler and architecture selection. On a host with only a partial toolkit installation,
configure CPU-only unsupported benchmarks with `-DCMAKE_DISABLE_FIND_PACKAGE_CUDAToolkit=TRUE` or report the GPU
configuration as unavailable.
Consult [`benchmarks/CMakeLists.txt`](../benchmarks/CMakeLists.txt) and
[`unsupported/benchmarks/CMakeLists.txt`](../unsupported/benchmarks/CMakeLists.txt) for current targets and compile
settings. CUDA benchmarks also have a standalone project and instructions in
[`unsupported/benchmarks/GPU/CMakeLists.txt`](../unsupported/benchmarks/GPU/CMakeLists.txt). The CI scripts
[`build.benchmark.sh`](../ci/scripts/build.benchmark.sh) and
[`run.benchmark.sh`](../ci/scripts/run.benchmark.sh) describe the supported-tree scheduled build and result format;
do not assume they validate `unsupported/benchmarks` changes.
## Benchmark Design
- Benchmark the user-visible operation affected by the change, with representative scalar types, sizes, shapes,
storage layouts, sparsity, and thread counts. Include transition sizes where a kernel or blocking strategy changes.
- Keep allocation, input generation, validation, and unrelated setup outside the timed region. Prevent dead-code
elimination with Google Benchmark's `DoNotOptimize` and `ClobberMemory` where appropriate.
- Validate results outside the measured loop. A faster incorrect kernel is not a useful result.
- Use enough work per iteration to dominate timer noise without hiding important small-problem behavior. Report
meaningful rates or byte/operation counters when they improve interpretation.
- Compare the change against the relevant baseline with identical compiler, optimization, ISA, dependency, and
benchmark arguments. Record the commit, hardware, compiler, flags, and command needed to reproduce the result.
## Argument Grids
Express static grids declaratively on the registration:
- `Args({a, b})` for individual points.
- `Range`, `DenseRange`, or `Ranges` for swept dimensions.
- `ArgsProduct({{...}, {...}})` for Cartesian products.
Use `Apply()` only for a genuinely computed grid that these APIs cannot express. In that exceptional case, match the
Google Benchmark version used by the project and note that the callback currently names
`benchmark::internal::Benchmark*`, an internal API. Prefer an existing local pattern and keep the grid-generation
function small and deterministic.
## Running Measurements
1. Check `uptime` and stop or finish competing builds and compute-heavy work. Run only one benchmark process at a
time; concurrent benchmarks invalidate both measurements.
2. Keep the machine, CPU affinity, power/governor policy, thermal state, compiler, flags, ISA, and dependencies as
constant as practical. Disclose anything that could not be controlled.
3. Use multiple repetitions, for example `--benchmark_repetitions=10`, and retain raw results. Compare medians plus a
dispersion measure such as MAD, IQR, or standard deviation; do not select the best run.
4. For before/after binaries, alternate separate invocations (`A, B, A, B`) to expose thermal or background-load
drift. Use the same benchmark filter and arguments for each pair.
5. Re-run suspicious or noisy cases. Treat changes smaller than the observed run-to-run variation as inconclusive,
not as wins or regressions.
Never infer a general speedup from one convenient size or one warm run. State the tested domain, include regressions
as well as improvements, and keep numerical accuracy results separate from performance measurements.
+73
View File
@@ -0,0 +1,73 @@
# Formatting And CI
Use the checked-out configuration as the source of truth. [`.gitlab-ci.yml`](../.gitlab-ci.yml) defines stages and
includes; [`ci/*.gitlab-ci.yml`](../ci) and [`ci/scripts/`](../ci/scripts) define the actual jobs. Default MR pipelines
run a limited smoke matrix; labels such as `all-tests` and `gpu-tests`, plus scheduled or manually started pipelines,
enable broader jobs. A green default MR pipeline is not proof that every supported configuration was exercised.
Build jobs publish the configured build directory as an artifact. Their paired test jobs consume that artifact and
run CTest without rebuilding. When changing either side, keep the test job's `needs`, CTest label or filter, and the
corresponding build target consistent; otherwise CTest can discover tests whose executables are absent.
## Worktree-Safe Formatting
Inspect `git status --short` before formatting and preserve unrelated changes. Eigen requires `clang-format-17`
exactly. Format only files owned by the task:
```bash
clang-format-17 -i path/to/file.cpp path/to/header.h
clang-format-17 --dry-run --Werror path/to/file.cpp path/to/header.h
git clang-format --binary clang-format-17 --diff <base-sha>
```
`.clang-format` intentionally disables include sorting and registers Eigen-specific macros and attributes. Do not
reorder includes or restyle those macros manually.
[`scripts/format.sh`](../scripts/format.sh) rewrites every matching file in the tree in parallel. Run it only when the
worktree is clean or every affected change is owned by the task. Review `git diff` afterward in either case.
## Local Checks
Run checks relevant to the changed files and report unavailable tools:
```bash
codespell --config setup.cfg path/to/changed-file
reuse lint
```
The whole-tree codespell invocation used by CI can expose pre-existing findings. Do not modify unrelated files merely
to make a local broad scan clean. In the current CI configuration, clang-format, codespell, and clang-tidy jobs are
`allow_failure`; treat their diagnostics as review findings anyway. The REUSE job is blocking.
Source-like files normally carry an inline SPDX copyright and license header using the file type's comment syntax.
Files that should not carry inline comments need coverage in [`REUSE.toml`](../REUSE.toml). To process selected new
source files with the repository helper, pass them explicitly because its default scan considers tracked files:
```bash
python3 scripts/add_spdx_headers.py --paths path/to/new-file.cpp
```
## Clang-Tidy
Use the CI driver rather than invoking clang-tidy directly on an implementation header; the driver routes such a
header through its public umbrella include.
```bash
cmake -G Ninja -S . -B .tidy-build \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DEIGEN_BUILD_TESTING=ON
ci/scripts/run-clang-tidy.sh <base-sha> .tidy-build
```
The driver examines files committed between `<base-sha>` and `HEAD`; uncommitted-only edits are not included. Eigen's
`.clang-tidy` policy is authoritative. Do not apply generic `modernize-*` or `cppcoreguidelines-*` campaigns.
## Before Review
1. Inspect `git diff` and `git diff --check`.
2. Format the exact changed source files with clang-format-17.
3. Run the focused builds and tests documented in [`testing.md`](testing.md).
4. Run applicable spelling, REUSE, and clang-tidy checks.
5. State what ran, what did not run, and why. Do not claim coverage from jobs or hardware that were unavailable.
+98
View File
@@ -0,0 +1,98 @@
# Numerical Code
Use this guidance when changing scalar math, packet math, decompositions, eigensolvers, linear solvers, matrix
functions, or numerical tests. The nearby implementation, tests, and public documentation in the checked-out tree
are the source of truth; this file defines the review standard rather than an algorithm.
## Standards and Accuracy Contracts
- Follow the applicable ISO C++ and incorporated ISO C library contracts. IEEE 754 requirements apply where the
platform and API claim IEC 60559 behavior. cppreference is a useful secondary summary, not a normative
specification.
- Distinguish exact semantic requirements from approximation quality. NaN, infinity, signed zero, domain errors,
and function-specific boundary behavior must follow the contract. For ordinary finite inputs, the C++ standard
generally does not promise correctly rounded elementary functions, so Eigen's documented or established ULP
budget is the relevant target.
- Do not use `VERIFY_IS_APPROX` as the acceptance criterion for a newly designed numerical kernel. Its defaults in
[`test/main.h`](../test/main.h) are deliberately loose test-framework tolerances, not machine-epsilon or ULP
bounds.
- Scale coverage with the change. A narrow fix needs focused regression cases and nearby coverage; a new algorithm
or shared kernel needs broad conditioning, scalar-type, and backend coverage.
## Scalar Math
Test regular inputs across the full supported domain and concentrate samples near discontinuities, roots, extrema,
range-reduction boundaries, overflow and underflow thresholds, and difficult rounding cases. Use an error metric
that matches the contract:
- Use ULP error when evaluating a floating-point approximation against a correctly rounded or high-precision
reference. Relative error is not meaningful near zero, and absolute error alone hides scale-dependent failures.
- Use MPFR for ground truth when an accuracy decision depends on the reference. Eigen's C++
[`ULP accuracy tool`](../test/ulp_accuracy/README.md) supports MPFR and standard-library references. The
[`coefficient-wise math table`](../doc/CoeffwiseMathFunctionsTable.dox) records existing accuracy expectations.
- Sollya is appropriate for polynomial or rational approximation design. Record the function, domain, precision,
error objective, tool version, and generation command or script so coefficients are reproducible; verify the
emitted implementation independently with MPFR.
Test special values explicitly: `+0`, `-0`, positive and negative infinity, quiet NaN, normal/subnormal boundaries,
the smallest subnormal, and values immediately on both sides of each domain boundary. Approximate comparisons can
treat two NaNs as matching and cannot distinguish the sign of zero. Therefore use explicit predicates:
- Check NaN with `(numext::isnan)(value)`.
- Check infinity with `(numext::isinf)(value)` and check its sign separately.
- Check zero by equality and its sign with `(numext::signbit)(value)`.
- Check finite classification when overflow or invalid results are possible.
## Decompositions and Solvers
Prefer backward-error and invariant checks over forward comparison with one reference answer. Depending on the
operation, test normalized reconstruction error, solve residual, eigenpair residual, orthogonality/unitarity, rank,
symmetry, or structure preservation. Express tolerances as named bounds derived from
`NumTraits<RealScalar>::epsilon()`, dimension, and the expected operation count; avoid unexplained decimal literals.
Forward error is condition-dependent. A well-conditioned problem may support a tight result comparison, while a
near-singular problem can have a small residual and a large forward error. Estimate or bound conditioning when a
forward comparison is necessary, and do not reject a stable answer merely because a different stable algorithm
selects different vectors, signs, phases, pivots, or bases for a clustered invariant subspace.
Exercise structures relevant to the algorithm: well-conditioned, ill-conditioned, near-singular, singular,
rank-deficient, clustered/repeated spectra, extreme scaling, and the matrix properties promised by the API. Useful
families include Hilbert, Vandermonde, Wilkinson, Toeplitz/KMS, banded, defective or near-defective, and barely
positive-definite matrices. Check error/status reporting as well as successful results.
Where LAPACK has a counterpart, require comparable backward stability, conditioning behavior, pivoting robustness,
and test-category coverage. Do not require identical internal steps, pivot order, eigenvector signs/phases, or
roundoff-level output. Higham's *Accuracy and Stability of Numerical Algorithms* and Golub and Van Loan's *Matrix
Computations* are standard references for choosing error measures and adversarial inputs.
## Packet Accuracy
- Test the scalar path, generic packet fallback, and every affected backend specialization that is available. Build
and run [`test/packetmath.cpp`](../test/packetmath.cpp) and, for special functions,
[`unsupported/test/special_packetmath.cpp`](../unsupported/test/special_packetmath.cpp). Report backends that were
not available locally.
- Compare packet results with the scalar contract for special values, but use MPFR rather than assuming the scalar
standard-library result is accurate enough to set a new finite-input ULP target.
- Cover every lane, mixed regular/special lanes, alignment and tail cases where applicable, and values around
approximation-region boundaries. A packet implementation must not let one lane's special value affect another.
- Treat a few-ULP performance tradeoff as a measured, documented finite-input decision. It does not waive NaN,
infinity, signed-zero, or domain semantics unless the API and build mode explicitly document different behavior.
## Subnormals and Flush-to-Zero
Require gradual-underflow behavior when the target and active floating-point mode support it. Some targets or build
modes have fixed or enabled flush-to-zero (FTZ) behavior, so an impossible subnormal expectation must be detected and
conditionalized rather than made flaky. Use the facilities and platform notes in
[`test/fp_control.h`](../test/fp_control.h) and nearby packet tests.
Keep an FTZ exception narrow: document the affected target and operation, preserve and restore controllable FP
state, and still verify normal values, NaN, infinity, signed zero, and scalar/packet consistency in that mode. Do not
use FTZ as a blanket reason to skip underflow tests or to hide accidental compiler flags that changed semantics.
## Provenance
Learn algorithms from published papers, standards, and textbooks, then write an original Eigen implementation. Cite
the specific reference inline by author/year and algorithm, routine, paper, or working-note identifier. If adapting
source code rather than an idea, first confirm that its license and provenance are compatible with Eigen. A citation
does not make copied expression from an incompatible or unknown source permissible, and attribution must never be
invented. Include the numerical rationale for non-obvious scaling, pivoting, stopping, and tolerance choices.
+89
View File
@@ -0,0 +1,89 @@
# SIMD and GPU Changes
Use this guide for packet math, architecture backends, device annotations, CUDA/HIP/SYCL code, Tensor device
execution, and the `unsupported/Eigen/GPU` module. The repository-root `AGENTS.md` still applies.
## Packet math
Eigen's vectorization API is the `Eigen::internal` packet layer. `packet_traits` and `unpacket_traits` describe a
packet type and its capabilities; `p*` operations such as `pload`, `pstore`, `padd`, and `pmul` provide the common
interface used by evaluators.
- Scalar fallbacks and the default traits live in `Eigen/src/Core/GenericPacketMath.h`. Shared vector
implementations live under `Eigen/src/Core/arch/Default/`; backend specializations live in the relevant sibling
directories under `Eigen/src/Core/arch/`.
- Start a new operation with a correct generic fallback when one is possible. Add specializations only for relevant
backends that support the operation; do not require an implementation in every backend merely because one backend
gains an intrinsic.
- Guard each intrinsic with the feature macro that enables it, even inside a broader backend directory. For example,
AVX2 or FMA intrinsics in `arch/AVX/` still require `EIGEN_VECTORIZE_AVX2` or `EIGEN_VECTORIZE_FMA`, plus a fallback
for narrower configurations. Consult `Eigen/src/Core/util/ConfigureVectorization.h` for the current feature macros.
- Keep capability flags (`Has*`), packet and half-packet types, alignment, masked access, casts, and cost metadata
consistent with the implementation. A capability flag must not advertise an unavailable or semantically different
operation.
- Preserve scalar-remainder behavior and unaligned paths. Packet-sized inputs alone do not cover an evaluator.
- Standard mathematical functions should match the scalar contract for special values. Measure ordinary-input error
in ULPs against an appropriate scalar or higher-precision reference; test NaN, infinities, signed zero, subnormals,
and domain boundaries explicitly where the platform exposes those IEEE-754 behaviors.
The current source tree and `test/CMakeLists.txt` are authoritative for supported backends and configuration options;
do not copy an architecture inventory into documentation.
## Device-callable code
For CUDA and HIP, `EIGEN_DEVICE_FUNC` supplies the host/device qualifiers required by functions reached from device
code. Under SYCL device compilation it supplies Eigen's required flattening and inlining attributes rather than alone
determining callability. Preserve it on coefficient accessors, evaluators, functors, small helpers, constructors, and
operators reached from device code.
- Keep device code allocation-free unless the specific backend and API deliberately provide an allocator.
- Avoid host-only standard-library calls, exceptions, RTTI assumptions, and function-local static state on device
paths.
- Define configuration macros before the first Eigen public header and keep index configuration consistent across
translation units that exchange Eigen objects.
- Include public module headers in tests and examples. Implementation headers under `Eigen/src/` and
`unsupported/Eigen/src/` are not user include points.
## Three GPU models
### Core types inside kernels
CUDA and HIP kernels can use fixed-size owning matrices, vectors, and arrays through public Eigen headers; see
`doc/UsingNVCC.dox`. Dynamic owning `Matrix` and `Array` objects require allocation and are generally unsuitable
inside kernels. Runtime dimensions are not inherently unsupported: `Map` over caller-managed device memory can use
dynamic dimensions when every operation reached by the expression is device-callable.
CUDA/HIP compilation disables host SIMD. Move substantial host-side Eigen work to a normal `.cpp` translation unit.
Use `EIGEN_NO_CUDA` or `EIGEN_NO_HIP` only when the corresponding compiler processes Eigen exclusively for host use.
If device code requires a different dense index type, define `EIGEN_DEFAULT_DENSE_INDEX_TYPE` consistently wherever
objects cross the host/device boundary.
### Tensor devices
`unsupported/Eigen/Tensor` evaluates expressions through an explicit device. `GpuDevice` handles CUDA/HIP and
`SyclDevice` handles SYCL; Tensor GPU kernels remain part of the Tensor implementation. Device-resident storage is
normally supplied through `TensorMap`, and the destination selects execution with `out.device(device) = expression`.
Consult `unsupported/Eigen/src/Tensor/README.md` and the nearby device implementation before changing memory,
synchronization, or callback semantics.
### `unsupported/Eigen/GPU`
This is a host-side NVIDIA-library wrapper selected explicitly with `Eigen::gpu` types. `gpu::DeviceMatrix` is not a
`MatrixBase` expression, and a supported expression maps to a CUDA library operation rather than Core coefficient
evaluation or packet fusion. Define `EIGEN_USE_GPU` before including `<unsupported/Eigen/GPU>`, and consult
`unsupported/Eigen/src/GPU/README.md`. Its tests under `unsupported/test/GPU/` are intentionally host-compiled `.cpp`
files.
## Validation
- Packet API or math changes: run the relevant parts of `packetmath`, the generic packet tests, and
`special_packetmath`; exercise every locally available affected backend.
- Core device-callability changes: build and run the relevant `gpu_basic` or `gpu_example` target with the available
CUDA/HIP compiler.
- Tensor device changes: run the operation's CPU Tensor test plus the matching CUDA/HIP/SYCL test where available.
- `unsupported/Eigen/GPU` changes: run the focused target under `unsupported/test/GPU/` and any affected library
integration target enabled by the local toolkit.
- Report backends or hardware that were unavailable. Do not claim cross-backend validation from a host-only build.
Performance-sensitive packet or GPU changes require a representative benchmark under identical compiler flags,
device state, and workload conditions. Correctness tests are not performance evidence.
+102
View File
@@ -0,0 +1,102 @@
# Tensor and Thread-Pool Changes
Use this guide for `unsupported/Eigen/Tensor`, `Eigen/ThreadPool`, Core's custom GEMM thread-pool backend, and explicit
thread-pool devices. The repository-root `AGENTS.md` still applies.
## Compatibility and risk
Tensor and ThreadPool are foundational to TensorFlow and other downstream users. "Unsupported" describes Tensor's
API-stability policy, not its importance. Changes to signatures, header layout, evaluation order, allocation,
synchronization, numerical behavior, or performance can have a large downstream impact.
- Prefer additive changes and preserve public header paths. Use `<unsupported/Eigen/Tensor>` and
`<Eigen/ThreadPool>`; never expose implementation-header includes to users.
- Paths below `unsupported/Eigen/CXX11/` are backward-compatibility forwarding shims only. New code must use the
canonical `unsupported/Eigen/` headers and must not add new headers under `CXX11/`.
- Preserve `EIGEN_DEVICE_FUNC` on code reachable by CUDA, HIP, or SYCL device evaluation.
- Treat evaluator flags, layouts, scalar/packet/block paths, zero-sized tensors, aliasing, and asynchronous object
lifetimes as part of the behavior under test.
- Changes to contraction, reduction, convolution, morphing, scheduling, or the cost model are performance-sensitive.
Add or update a benchmark and compare representative shapes, layouts, thread counts, and scalar types.
- Call out intentional compatibility or performance changes prominently in the merge request.
## Keep the threading mechanisms separate
### OpenMP
OpenMP is Core's primary implicit multithreading mechanism and covers the algorithms listed in
`doc/TopicMultithreading.dox`. It is controlled through the compiler's OpenMP support, `Eigen::setNbThreads`, and the
OpenMP runtime. Do not infer that every algorithm in that list is also supported by the custom GEMM thread pool.
### `EIGEN_GEMM_THREADPOOL`
This macro selects Eigen's custom thread-pool backend for general dense matrix-matrix products only. It is mutually
exclusive with OpenMP. Define it before including Eigen, create an `Eigen::ThreadPool`, and register that pool with
`Eigen::setGemmThreadPool(&pool)` before concurrent GEMM work begins.
The registered pointer is process-global state and the pool remains caller-owned. It must outlive all GEMM using it;
do not replace it while a product is running. `Eigen::setNbThreads` controls the active thread limit, while registering
a pool resets that limit to the pool's thread count. Passing `nullptr` currently queries the registered pool; it does
not clear the registration. Treat `doc/TopicMultithreading.dox` and
`Eigen/src/Core/products/Parallelizer.h` as the current API and implementation references.
### `CoreThreadPoolDevice`
`Eigen::CoreThreadPoolDevice` is an explicit device for parallel Core coefficient-wise assignment:
```cpp
#include <Eigen/ThreadPool>
Eigen::ThreadPool pool(thread_count);
Eigen::CoreThreadPoolDevice device(pool);
destination.device(device) = expression;
```
It is distinct from implicit GEMM parallelization. Changes belong with the device/evaluator tests represented by
`test/assignment_threaded.cpp`, not only the GEMM tests.
### Tensor `ThreadPoolDevice`
Define `EIGEN_USE_THREADS` before `<unsupported/Eigen/Tensor>`, then construct a `ThreadPoolDevice` over an existing
`ThreadPoolInterface` and evaluate explicitly:
```cpp
Eigen::ThreadPool pool(pool_threads);
Eigen::ThreadPoolDevice device(&pool, execution_threads);
output.device(device) = expression;
```
The device does not own the pool. The pool, allocator, input storage, output storage, and callback state must remain
alive until synchronous evaluation returns or asynchronous completion is signaled. Tensor's executor, contraction,
reduction, and device code have `ThreadPoolDevice`-specific paths; a serial `DefaultDevice` test alone is insufficient.
See `unsupported/Eigen/src/Tensor/README.md` and `TensorDeviceThreadPool.h`.
## Scheduling changes
- Preserve the `ThreadPoolInterface` contract, including `Schedule`, `ScheduleWithHint`, `CurrentThreadId`,
cancellation behavior, and caller ownership.
- Test one-thread and multi-thread execution, work invoked from a worker, completion/wakeup behavior, and shutdown with
pending or cancelled work when those paths are affected.
- Avoid blocking a worker on work that can only run on the same exhausted pool. Make callback and barrier lifetime
rules explicit in code when they are not self-evident.
- `DenseBase::Random()` and `setRandom()` use `std::rand` and are not re-entrant. Do not call them concurrently;
pre-generate inputs or use thread-local `<random>` generators through `NullaryExpr`.
- Cost-model and grain-size changes need both small-workload overhead measurements and large-workload throughput
measurements. Check oversubscription and nested parallelism rather than assuming more threads are faster.
- Benchmark only on an otherwise idle system, one benchmark process at a time, and report repeated measurements rather
than a single timing.
## Validation
- Thread-pool internals: run the affected `threads_*` target, especially event-count, run-queue, non-blocking-pool, or
fork-join tests.
- Custom GEMM pool: run `product_threaded` and the ordinary product tests affected by the change.
- Core explicit device: build and run the assignment-threaded test represented by `test/assignment_threaded.cpp` if
it is registered in the current test configuration.
- Tensor pool/device changes: run `tensor_thread_pool`, `tensor_executor`, and the focused operation tests such as
contraction or reduction.
- Tensor behavior shared with accelerators: also follow `simd-gpu.md` and run the locally available device tests.
- Report unavailable sanitizers, GPU toolchains, platforms, and downstream TensorFlow validation explicitly.
Use `test/CMakeLists.txt`, `unsupported/test/CMakeLists.txt`, and the checked-out CMake configuration as the source of
truth for target names. Do not maintain a duplicate test or backend inventory here.
+110
View File
@@ -0,0 +1,110 @@
# Testing Eigen Changes
Use this guide when adding or changing tests. The checked-out source is authoritative:
- [`test/main.h`](../test/main.h) defines the test framework and assertion helpers.
- [`cmake/EigenTesting.cmake`](../cmake/EigenTesting.cmake) defines test registration and splitting.
- [`test/CMakeLists.txt`](../test/CMakeLists.txt) and
[`unsupported/test/CMakeLists.txt`](../unsupported/test/CMakeLists.txt) register the suites.
- [`cmake/EigenConfigureTesting.cmake`](../cmake/EigenConfigureTesting.cmake) defines aggregate build and check
targets.
## Configure And Build
Configure a dedicated build directory. Unit tests are excluded from CMake's default `all` target, although a bare
build may still build enabled auxiliary libraries.
```bash
cmake -G Ninja -S . -B build
cmake --build build --target buildtests
ctest --test-dir build --parallel --output-on-failure
```
Useful aggregate targets are `BuildOfficial`, `BuildUnsupported`, `buildsmoketests`, `buildtests_gpu`, `check`, and
`check_gpu`. Build and run one test explicitly when possible:
```bash
cmake --build build --target bdcsvd_3
ctest --test-dir build -R '^bdcsvd_3$' --output-on-failure
```
Run the generated wrappers from the build directory because they invoke the configured build tool relative to their
working directory:
```bash
cd build
./buildtests.sh <regex>
./check.sh <regex>
```
They filter registered parent names such as `bdcsvd`, not generated part names such as `bdcsvd_3`; use the explicit
target recipe for one part.
Use a separate build directory for each materially different configuration. Do not rewrite one cache and describe
the result as a second test run.
```bash
cmake -G Ninja -S . -B build-row-major -DEIGEN_DEFAULT_TO_ROW_MAJOR=ON
cmake -G Ninja -S . -B build-no-vector -DEIGEN_TEST_NO_EXPLICIT_VECTORIZATION=ON
```
Consult the top-level [`CMakeLists.txt`](../CMakeLists.txt) and nearby test CMake files for current options instead of
copying an option inventory into documentation.
## Current Test Framework
Eigen currently uses its own framework, not GoogleTest:
1. Add `test/<name>.cpp` or `unsupported/test/<name>.cpp`.
2. Include `main.h`, then the public umbrella header for tests of public behavior. A focused test of a private utility
may include its implementation header only when that matches an established nearby pattern; never present such a
path as a user include.
3. Use `VERIFY`, `VERIFY_IS_EQUAL`, `VERIFY_IS_APPROX`, and the other helpers defined in `test/main.h`.
4. End with `EIGEN_DECLARE_TEST(<name>) { ... }`.
5. Register the source with `ei_add_test(<name>)` in the matching `CMakeLists.txt`, then reconfigure.
For compile-failure coverage, use the established `failtest/` pattern. Its `_ok` target must compile and its `_ko`
target must fail with `EIGEN_SHOULD_FAIL_TO_BUILD` defined.
## Split Tests
`ei_add_test` scans the source for `CALL_SUBTEST_N`, `EIGEN_TEST_PART_N`, and `EIGEN_SUFFIXES;...` markers.
- With `EIGEN_SPLIT_LARGE_TESTS=ON`, every discovered suffix becomes an executable `<name>_<N>` compiled with
`EIGEN_TEST_PART_<N>=1`; the parent `<name>` target builds all parts.
- `EIGEN_SUFFIXES;...` supplies an explicit suffix list when ordinary source scanning cannot see macro-generated or
conditional parts.
- With splitting off, tests containing only `CALL_SUBTEST_N` or `EIGEN_SUFFIXES` fold into one `<name>` executable
compiled with `EIGEN_TEST_PART_ALL=1`.
- An explicit `EIGEN_TEST_PART_N` marker forces splitting even when the option is off. If any such marker is present,
all suffixes discovered in that source are emitted.
`ctest -R '^<name>$'` does not match split parts. Use `ctest -R '<name>'` for every part or anchor one generated name.
## Numerical Assertions
`VERIFY_IS_APPROX` is a convenient broad comparison, not a machine-epsilon guarantee. `test_precision<T>()` uses
`NumTraits<T>::dummy_precision()` generically and currently specializes float to `1e-3` and double/long double to
`1e-6`. Do not use it alone to claim ULP accuracy, backward stability, or IEEE special-value conformance.
For numerical kernels, add explicit named bounds based on epsilon, dimension, conditioning, or a backward-error
model as appropriate. Check NaN, infinity, and signed zero explicitly when their distinction matters. Follow
[`numerics.md`](numerics.md) for solver, packet, and scalar-math coverage.
Run reproducible failures directly with a fixed seed and repeat count:
```bash
EIGEN_REPEAT=10 EIGEN_SEED=1 build/test/foo_3
build/test/foo_3 r10 s1
```
## External BLAS And Shim Libraries
`EIGEN_TEST_EXTERNAL_BLAS=ON` finds a system BLAS, defines `EIGEN_USE_BLAS`, and links that BLAS into applicable
official tests. With it off, ordinary tests exercise Eigen's normal implementation; they do not transparently use
the in-tree `eigen_blas` library. `EIGEN_BUILD_BLAS` and `EIGEN_BUILD_LAPACK` separately build Eigen's ABI shim
libraries, which are also used to satisfy some optional sparse-backend links. There is currently no
`EIGEN_TEST_EXTERNAL_LAPACK` option.
Report the exact targets, CTest regexes, configurations, compiler, and seeds run. Also report relevant hardware or
optional backends that were unavailable locally.
+138 -351
View File
@@ -1,386 +1,173 @@
# AGENTS.md
Guidance for AI coding agents (Claude Code, Cursor, Copilot Workspace, Aider, OpenAI Codex, etc.) working in this repository. Human contributors should start from [`README.md`](README.md) and the project sites it links to (<https://eigen.tuxfamily.org>, <https://libeigen.gitlab.io>, and the upstream repo at <https://gitlab.com/libeigen/eigen>); this file is the agent-facing condensation.
Guidance for AI coding agents working in Eigen. Human contributors should start with
[`README.md`](README.md) and the project documentation it links to. Per-tool files such as `CLAUDE.md` should import
this file and contain only tool-specific additions.
Per-tool config files (e.g. `CLAUDE.md`) import or point to this file rather than duplicating it; put shared guidance here and only tool-specific notes there.
## Scope and precedence
## Agent guidelines (read first)
Follow the user's task, then the nearest applicable `AGENTS.md`, then repository documentation and established local
patterns. The checked-out source, tests, CMake files, and CI configuration are authoritative for current mechanics. If
this guide disagrees with the tree, follow the tree, report the discrepancy, and update the guidance when that is in
scope.
These are the cross-cutting rules that catch agents most often:
Read this file for every task. Then read every row below that matches the work; do not load unrelated guides by
default.
1. **Provenance and attribution — no plagiarism, no license laundering.** Eigen code must be original or derived from publicly published MPL-2.0-compatible material. Do **not** copy — verbatim, paraphrased, or "translated" to another syntax — from incompatibly-licensed sources (proprietary, NDA-encumbered, prior-employer internal); paraphrasing is still a derivative work. **Cite published references inline** when they inform an implementation — LAPACK / LAWN, ACM TOMS / SIAM papers, Higham, Golub & van Loan, textbook algorithms, Boost components, vendor application notes — by name (author, year, identifier; a comment or Doxygen `\note` block suffices). Same discipline for AI-suggested code: cite the source, or rewrite from a known reference and cite that, or drop it. Ideas aren't copyrightable, specific expressions are — learn from the source, write your own, credit it.
2. **Header-only contract.** Eigen ships as headers only. **Never** include anything under `Eigen/src/...` or `unsupported/Eigen/src/...` directly — `InternalHeaderCheck.h` makes that a hard compile error. User code reaches implementation only through the umbrella module headers (`Eigen/Core`, `Eigen/Dense`, `Eigen/SVD`, …). When moving or renaming files inside any `src/` subtree, delete the old file outright; only the public umbrella headers ever get back-compat forwarding shims.
3. **Preserve `EIGEN_DEVICE_FUNC`.** It is pervasive on coefficient-level methods so the same code compiles for host and CUDA / HIP / SYCL device. Dropping it silently breaks GPU builds and is rarely caught locally.
4. **Don't apply general C++ "modernize" advice.** Do not propose `modernize-*` or `cppcoreguidelines-*` clang-tidy fixes, replace `EIGEN_STRONG_INLINE` with `inline`, "fix" Eigen's macro indentation, or reorder includes. Eigen has its own conventions encoded in `.clang-format` (`SortIncludes: false`, custom `StatementMacros` / `AttributeMacros`); CI will diff against them.
5. **Format before you commit.** Style is `clang-format-17` exactly (Google base, 120 cols). Other versions diff against CI. Run `scripts/format.sh` (whole tree) or `clang-format-17 -i <file>` (one file) before pushing. Format failures are the single most common reason an MR is red.
6. **Tests are not gtest** (today). They use Eigen's own framework (`test/main.h`): `VERIFY_*` assertions, `CALL_SUBTEST_N` / `EIGEN_TEST_PART_N` for splitting, `EIGEN_DECLARE_TEST(<name>) { ... }` as the entry point. See "Adding a test" below. **In-flight migration to Google Test:** [MR 2159](https://gitlab.com/libeigen/eigen/-/merge_requests/2159) (draft) replaces `EIGEN_DECLARE_TEST` / `CALL_SUBTEST_N` with gtest `TEST` / `TYPED_TEST`, brings in gtest 1.15.2 via `FetchContent`, and bridges `VERIFY` / `VERIFY_IS_APPROX` to gtest expectations. When that lands, this rule flips — write new tests as gtest fixtures and use the bridged `VERIFY_*` macros.
7. **Tests are not in the default `all` target.** Bare `ninja` builds nothing relevant. Use the named targets (`buildtests`, `check`, `BuildOfficial`, `BuildUnsupported`, `buildsmoketests`, `buildtests_gpu`, `check_gpu`).
8. **`auto` traps.** `auto x = A + B;` captures a lazy expression holding references that can dangle. Use `.eval()` or an explicit type. Likewise be careful with `.noalias()` — it must only be used when the destination doesn't appear on the right side.
9. **Prefer Eigen expressions over scalar loops.** When the operation maps naturally and efficiently to Eigen's API, use array, matrix, or vector expressions instead of hand-written coefficient loops. This makes intent clearer and lets Eigen select vectorized and fused evaluation paths. Keep an explicit loop when it better expresses control flow, avoids unnecessary temporaries or repeated evaluation, or benchmarks faster.
10. **Stage commits explicitly.** Don't `git add -A` / `git add .` inside an Eigen working copy. Repo roots commonly accumulate untracked dotfiles and tool config (`.vscode/`, `.idea/`, `.claude/`, etc.) that must not enter commits. Add files by path or with targeted globs (e.g. `git add 'Eigen/src/Cholesky/'`).
11. **Pause before pushing or filing an MR.** External-system writes (push, MR creation, MR comments) have non-trivial blast radius. After a local commit, summarize what changed and wait for the human to say "push" or "file the MR" before doing so. The same applies to scope decisions like bundling/splitting commits.
12. **Tests and benchmarks ship with the code.** New functionality lands with its tests; performance-sensitive changes land with a benchmark. Don't defer either to a follow-up MR.
13. **Benchmarking discipline.** Benchmarks on a loaded system are useless. Before running any benchmark: check `uptime` shows a low load average, finish/cancel background builds, and **never** run two benchmark binaries in the same shell invocation (parallel or chained with `&&`) — run each in its own shell, take medians within a binary, and optionally alternate (A, B, A, B) across separate invocations to detect drift.
14. **Tensor module is foundational to TensorFlow.** `unsupported/Eigen/Tensor` and the work-stealing thread pool in `Eigen/ThreadPool` together form TensorFlow's core compute backend. "Unsupported" here means "looser API-stability guarantees" — it does **not** mean low-traffic or low-stakes. Breaking changes (signatures, header layout, semantics, or performance regressions on contraction / reduction / morphing kernels) ripple into every TensorFlow build and from there into every project that pulls TensorFlow as a dependency. Treat Tensor and ThreadPool as load-bearing: prefer additive changes, keep header paths stable, run downstream Tensor tests (`unsupported/test/tensor_*`), and call out any behavior change prominently in the MR description.
| Work area | Additional guidance |
|---|---|
| Tests and CMake test targets | [`.agents/testing.md`](.agents/testing.md) |
| Numerical kernels, decompositions, solvers, accuracy | [`.agents/numerics.md`](.agents/numerics.md) |
| Performance changes and benchmarks | [`.agents/benchmarking.md`](.agents/benchmarking.md) |
| Packet math, CUDA, HIP, SYCL, `unsupported/Eigen/GPU` | [`.agents/simd-gpu.md`](.agents/simd-gpu.md) |
| Tensor, ThreadPool, and multithreading | [`.agents/tensor-threadpool.md`](.agents/tensor-threadpool.md) |
| Formatting, lint, and GitLab CI | [`.agents/ci.md`](.agents/ci.md) |
| Expression templates or evaluator internals | [`doc/TopicLazyEvaluation.dox`](doc/TopicLazyEvaluation.dox), [`doc/NewExpressionType.dox`](doc/NewExpressionType.dox), and [`doc/ClassHierarchy.dox`](doc/ClassHierarchy.dox) |
## Repository overview
## Non-negotiable rules
Eigen is a **header-only** C++ template library for linear algebra: dense and sparse matrices, vectors, decompositions, geometry, iterative solvers. The library itself does not need to be built — consumers just `#include <Eigen/Dense>` (or other module headers). CMake is required only for tests, BLAS/LAPACK shims, demos, and docs.
1. **Preserve existing work.** Start with `git status --short`. Never discard, overwrite, reformat, or stage unrelated
user changes. Do not use destructive Git commands unless the user explicitly requests that operation. Stage named
paths, never `git add .` or `git add -A`.
2. **Keep provenance clean.** Code must be original or derived from source material whose license is compatible with
Eigen's MPL-2.0 distribution. Do not copy, paraphrase, or translate code from proprietary, NDA-covered, internal, or
incompatibly licensed sources. Published papers, standards, textbooks, and algorithm descriptions may inform an
independent implementation; cite them inline when they materially inform it. A citation does not make copied code
permissible. Never invent an attribution for AI-generated code.
3. **Respect the header-only and C++14 contracts.** Supported headers must compile as C++14 unless a guarded backend has
a documented newer requirement. User code, examples, and public-behavior tests include umbrella headers such as
`Eigen/Core` or `Eigen/SVD`, not files below `Eigen/src/` or `unsupported/Eigen/src/`. Focused tests of private
utilities may follow an established direct-include pattern, but those paths remain private even where a header is not
mechanically guarded. Definitions in public headers must have valid header linkage and avoid ODR violations.
4. **Protect compatibility.** Treat supported public names, signatures, header paths, semantics, and ABI-affecting
configuration as compatibility surfaces. Prefer additive changes and deprecation over removal. When moving private
implementation headers, update the public umbrella and remove the old private file rather than adding a private-path
forwarding shim. ABI-affecting Eigen macros must be consistent across translation units.
5. **Preserve Eigen annotations and style.** Do not drop `EIGEN_DEVICE_FUNC` from coefficient-level or device-callable
functions. Do not replace `EIGEN_STRONG_INLINE` with `inline`, reorder includes, normalize Eigen macro layout, or
apply broad `modernize-*` or `cppcoreguidelines-*` rewrites. The repository's conventions and `.clang-format` take
precedence over generic C++ advice.
6. **Ship verification with behavior.** New functionality includes focused tests. Bug fixes include a regression test
that fails without the fix when practical. Performance-sensitive changes include an appropriate benchmark. Scale
broader coverage to the affected scalar types, storage orders, backends, and public contracts.
7. **Treat external writes as deliberate actions.** Unless the user already asked for them, pause after the local commit
before pushing, opening or updating a merge request, commenting on an issue, or making another external-system write.
Upstream is GitLab: <https://gitlab.com/libeigen/eigen>. Bug reports, feature requests, and merge requests go there — the GitHub mirror is read-only. Minimum standard is **C++14** (`target_compile_features(eigen INTERFACE cxx_std_14)` in `CMakeLists.txt`); SYCL builds force C++17. The project aspires to bump the baseline to C++17 in a future release. The pace on baseline bumps is deliberately slow so that new Eigen improvements remain available to users on embedded platforms, whose toolchains are often several years behind mainline. License: MPL2 for the bulk, with a few files under other compatible licenses (`COPYING.*` at the root).
## Standard workflow
## Quality bar
1. Inspect `git status --short`, the current branch, and the diff. Separate pre-existing work from the requested change.
2. As applicable, read the public header, implementation, nearby tests, registration in `CMakeLists.txt`, and relevant
task guides before deciding on an implementation. Search with `rg` or `rg --files`.
3. Keep the patch within the owning module and established patterns. Avoid opportunistic refactors and generated or
metadata churn.
4. Add or update applicable tests and benchmarks in the same patch. Test public behavior through its umbrella header so
missing exports are caught; follow nearby patterns for focused private-internal tests.
5. Format only files changed by the task with `clang-format-17 -i <files>`. `scripts/format.sh` rewrites matching files
across the tree; use it only when the worktree is clean and a whole-tree pass is intentional.
6. Build and run the narrowest relevant test first, then widen validation according to the change's risk. Use separate
build directories for materially different CMake configurations.
7. Review `git diff --check`, `git diff`, and `git status --short`. Report the exact validation run and any unavailable
compiler, ISA, GPU, dependency, or downstream coverage.
Eigen aspires to state-of-the-art results on two axes — **performance** and **numerical accuracy / IEEE-754 conformance** — and treats them as separate goals that are sometimes in tension.
## Repository essentials
**Performance.** Eigen Core emphasizes single-core throughput via two levers: SIMD through per-architecture packet backends (see "SIMD / packet math layer"), and memory-hierarchy use through blocked algorithms — cache-aware blocking and panel/kernel decompositions in `Eigen/src/Core/products/`, tile-based traversal in the Tensor module. **Eigen Core is mostly optimized for single-core throughput**; multi-core in Core is opt-in (OpenMP or `EIGEN_GEMM_THREADPOOL`) and covers a subset of operations. The **Tensor module** is the opposite — designed for multi-core via `ThreadPoolDevice`, which dispatches across worker threads of the work-stealing thread pool. See "Multi-threading" below.
Eigen is a header-only expression-template library. Consumers include module headers under `Eigen/` or
`unsupported/Eigen/`. The top-level CMake project builds tests, documentation, demos, and BLAS/LAPACK shims rather than
a core Eigen library; benchmarks use separate CMake projects. `Eigen/Dense` aggregates the dense modules, while
`Eigen/Eigen` includes `Dense` and `Sparse`. External backend support modules and `Eigen/ThreadPool` remain separate
includes. The upstream project is on GitLab; its GitHub repository is a read-only mirror.
**Numerical accuracy.** The bar is LAPACK-level for linear algebra (decompositions, solvers — backward stability, pivoting, conditioning) and C++ standard-library level for standard math functions (`exp`, `log`, `sin`, `pow`, …) on scalars. For **special values** — IEEE-754 entities (NaN, ±0, ±∞, subnormals) and function-specific edge cases (singularities, branch boundaries; e.g. `log(0)`, `pow(0, 0)`) — the bar is exact conformance to IEEE 754 / ISO C / C++ specifications, with the cppreference page for each function as the authoritative spec (e.g. [`std::pow`](https://en.cppreference.com/cpp/numeric/math/pow)). On regular inputs **a few ULPs** of error in vectorized math in exchange for SIMD throughput is the long-standing trade-off; larger deviations need explicit justification. **Special-value handling is not subject to the few-ULPs trade-off** — it must match spec exactly.
The supported implementation is under `Eigen/src/`; tests are under `test/`. Modules with looser API-stability
guarantees are under `unsupported/Eigen/`, with tests under `unsupported/test/`. "Unsupported" does not imply low
impact: Tensor is a foundational TensorFlow dependency. Public umbrella headers are the source of truth for a module's
exported internals.
When adding or modifying a numerical kernel:
- Test **numerical corner cases**, not just typical inputs: ±0, ±∞, NaN, subnormals, values at and around the function's domain boundaries (e.g. `log(0)`, `log(-x)`, `pow(0, 0)`), values near overflow / underflow, denormalized results, signed-zero preservation, and ULP behavior near hard cases (e.g. argument-reduction breakdown for `sin`/`cos` at large arguments).
- **Matrix coverage matters as much as scalar coverage.** Decomposition, solver, and matrix-function tests should exercise ill-conditioned inputs across a range of condition numbers (well-conditioned through near-singular through singular) and matrices with structure relevant to the algorithm: Hilbert, Vandermonde, Pascal, Wilkinson's W, Frank, Lehmer, KMS / Toeplitz, banded, rank-deficient, defective and near-defective (Jordan blocks), positive-definite-but-barely, etc. Standard references: **Higham's *Accuracy and Stability of Numerical Algorithms* and *Functions of Matrices*** (and MATLAB `gallery()`, largely drawn from those books) and **Golub & van Loan, *Matrix Computations***. Where the algorithm has a LAPACK counterpart, match its `TESTING/` category coverage as the bar.
- Verify behavior across all enabled packet backends — `test/packetmath.cpp` and `unsupported/test/special_packetmath.cpp` are the canonical entry points; a missing or divergent backend specialization usually shows up there first.
- Quantify accuracy regressions in ULPs against the scalar reference, not just relative error. Sollya / MPFR are the standard tools for ground-truth and polynomial generation; do the verification in C++ with MPFR rather than Python.
- Performance-sensitive changes ship with a benchmark (under `benchmarks/` or `unsupported/benchmarks/`). See "Benchmarking discipline" in the agent guidelines above.
For decompositions and solvers: the bar is matching LAPACK on conditioning, pivoting strategy, and backward stability. Don't trade numerical robustness for speed in those code paths without explicit sign-off.
## Build / test
> **In-flight test-framework migration:** [MR 2159](https://gitlab.com/libeigen/eigen/-/merge_requests/2159) migrates the test framework from Eigen's custom `EIGEN_DECLARE_TEST` / `CALL_SUBTEST_N` macros to Google Test. The "Adding a test" and "Test split" subsections below describe the **current** framework on master. Once 2159 lands, tests become gtest `TEST` / `TYPED_TEST` fixtures, per-`N` executable splitting goes away, and `VERIFY_*` survives as a bridge to gtest expectations.
Tests are intentionally **not** in the default `all` target — `ninja` (or `make`) on its own builds nothing relevant. Drive everything through the named targets:
```bash
mkdir -p build && cd build
cmake -G Ninja .. # plain config
ninja buildtests # build all unit tests
ninja buildtests_gpu # GPU-only tests
ninja BuildOfficial # only test/ (subproject "Official")
ninja BuildUnsupported # only unsupported/test/ (subproject "Unsupported")
ninja buildsmoketests # the MR smoke set
ninja check # = buildtests + ctest
ninja check_gpu # = buildtests_gpu + ctest -L gpu
# generated wrappers (after `cmake` configure; run from inside the build dir):
./buildtests.sh <regex> # build tests whose name matches
./check.sh <regex> # build + run tests whose name matches
```
### Common CMake knobs
ISA / vectorization (turn on per-ISA test compile flags — see `CMakeLists.txt`, `test/CMakeLists.txt`, and `cmake/EigenTesting.cmake` for the authoritative list):
- x86: `EIGEN_TEST_SSE2`, `EIGEN_TEST_SSE3`, `EIGEN_TEST_SSSE3`, `EIGEN_TEST_SSE4_1`, `EIGEN_TEST_SSE4_2`, `EIGEN_TEST_AVX`, `EIGEN_TEST_AVX2`, `EIGEN_TEST_AVX512`, `EIGEN_TEST_AVX512DQ`, `EIGEN_TEST_AVX512FP16`, `EIGEN_TEST_FMA`, `EIGEN_TEST_F16C`, `EIGEN_TEST_X87`, `EIGEN_TEST_32BIT`
- ARM: `EIGEN_TEST_NEON`, `EIGEN_TEST_NEON64`, `EIGEN_TEST_SME`
- PowerPC: `EIGEN_TEST_VSX`, `EIGEN_TEST_ALTIVEC`
- IBM Z (s390x): `EIGEN_TEST_Z13`, `EIGEN_TEST_Z14`
- LoongArch: `EIGEN_TEST_LSX`
- MIPS: `EIGEN_TEST_MSA`
- GPU / SYCL: `EIGEN_TEST_CUDA`, `EIGEN_TEST_CUDA_CLANG`, `EIGEN_TEST_CUDA_NVC` (NVHPC), `EIGEN_TEST_HIP`, `EIGEN_TEST_SYCL`
- Negative / behavioral: `EIGEN_TEST_NO_EXPLICIT_VECTORIZATION`, `EIGEN_TEST_NO_EXPLICIT_ALIGNMENT`, `EIGEN_TEST_NO_EXCEPTIONS`
Test-wide knobs:
- `EIGEN_TEST_MAX_SIZE=320` (default) — clamp the random matrix sizes used by tests.
- `EIGEN_SPLIT_LARGE_TESTS=ON` (default) — splits any test using `CALL_SUBTEST_N` / `EIGEN_TEST_PART_N` into per-`N` executables (`foo_1`, `foo_2`, …); see "Test split" below.
- `EIGEN_DEFAULT_TO_ROW_MAJOR=ON` — re-runs the suite with row-major default storage.
- `EIGEN_LEAVE_TEST_IN_ALL_TARGET=ON` — adds tests back to `all` (used by some CI harnesses driving ctest's automatic build path).
- `EIGEN_TEST_CUSTOM_CXX_FLAGS=…`, `EIGEN_TEST_CUSTOM_LINKER_FLAGS=…` — extra flags applied **only** to test targets (handy for working around codegen bugs).
- `EIGEN_TEST_OPENMP=ON` — link tests against OpenMP.
- `EIGEN_TEST_EXTERNAL_BLAS=ON` — exercise the `EIGEN_USE_BLAS` path against an external BLAS implementation; without it, the in-tree `eigen_blas` from `blas/` is used. (An external-LAPACK equivalent is not yet wired up — `test/CMakeLists.txt` carries a `TODO do the same for EXTERNAL_LAPACK`, so `EIGEN_TEST_EXTERNAL_LAPACK` currently has no effect.)
Auxiliary trees:
- `EIGEN_BUILD_BLAS=ON` / `EIGEN_BUILD_LAPACK=ON` (default ON only for top-level builds) — build the Eigen-backed BLAS/LAPACK shim libraries under `blas/` and `lapack/`.
- `EIGEN_BUILD_DOC=ON` — Doxygen documentation (`ninja doc`).
- `EIGEN_BUILD_DEMOS=ON` — the small demos under `demos/`.
### Running tests
```bash
ctest --parallel --output-on-failure # everything that's been built
ctest -L Official # only tests under test/
ctest -L Unsupported # only tests under unsupported/test/
ctest -L gpu # GPU-tagged tests
ctest -L smoketest # the MR smoke set
ctest -R '^cholesky' # regex over test names
ctest -R '^bdcsvd_3$' --output-on-failure -V # one specific split
```
Subproject labels come from `set_property(GLOBAL PROPERTY EIGEN_CURRENT_SUBPROJECT "Official"|"Unsupported")` in `test/CMakeLists.txt` and `unsupported/test/CMakeLists.txt`. Build-group targets (`BuildOfficial`, `BuildUnsupported`) are kept in sync with the ctest labels.
A test binary can be invoked directly to control seed / repeat:
```bash
./test/foo_3 r5 s1234 # repeat 5 times, fixed seed 1234
EIGEN_REPEAT=10 EIGEN_SEED=1 ./test/foo_3
```
### Test split (important)
A test source file containing `CALL_SUBTEST_N(...)` or `EIGEN_TEST_PART_N` macros is compiled into **N separate executables** named `<testname>_1`, `<testname>_2`, …, each built with `-DEIGEN_TEST_PART_<N>=1` (logic in `cmake/EigenTesting.cmake``ei_add_test`). The umbrella `<testname>` target builds all parts. `ctest -R ^<testname>$` does **not** match individual parts; use `ctest -R <testname>` for the regex form. Set `EIGEN_SPLIT_LARGE_TESTS=OFF` to fold them into a single binary if you need to debug across parts.
### Adding a test
1. Create `test/<name>.cpp` (or `unsupported/test/<name>.cpp`). Include `main.h`. Use the `VERIFY*` macros (listed below). For multi-part tests, structure the body as `CALL_SUBTEST_1(...) ... CALL_SUBTEST_N(...)`.
2. End the file with the entry point: `EIGEN_DECLARE_TEST(<name>) { ... }` — that macro expands to the per-binary `main()` (random seed / repeat handling, signal handlers, etc.). It is **not** gtest.
3. Register with `ei_add_test(<name>)` in the matching `CMakeLists.txt` near similar tests. The second/third args are extra compile flags / libraries (e.g. `ei_add_test(packetmath "-DEIGEN_FAST_MATH=1")`).
4. Re-run CMake configure; the new target is then in `buildtests` and ctest.
Test assertion macros (defined in `test/main.h`):
- `VERIFY(cond)` — assert condition
- `VERIFY_IS_APPROX(a, b)`, `VERIFY_IS_NOT_APPROX(a, b)` — approximate floating-point equality
- `VERIFY_IS_EQUAL(a, b)`, `VERIFY_IS_NOT_EQUAL(a, b)` — exact equality
- `VERIFY_IS_MUCH_SMALLER_THAN(a, b)`
- `VERIFY_RAISES_ASSERT(expr)` — assert that `eigen_assert` fires
**Test tolerances.** Prefer `VERIFY_IS_APPROX` / `VERIFY_IS_MUCH_SMALLER_THAN`: they compare against `test_precision<T>()`, a machine-epsilon multiple that already scales with the scalar type. When a custom or looser bound is unavoidable, make it a **named constant expressed as a multiple of `NumTraits<RealScalar>::epsilon()`** (times the problem size or a backward-error factor as appropriate) — never a bare literal like `1e-9`, which is opaque and precision-blind (it is below `eps` for `float`, so a `Scalar`-templated test would demand impossible accuracy). Check **residuals** (∝ `eps`) rather than forward errors (∝ `cond · eps`) where you can; and when the comparison's reference is itself the less-accurate side (e.g. a dense LU determinant checked against a closed form), scale the tolerance by the matrix conditioning rather than picking a fixed constant that is flaky across random draws.
`failtest/` holds compile-failure tests: each has an `_ok` and `_ko` target — `_ok` must compile, `_ko` must fail to compile (driven via `-DEIGEN_SHOULD_FAIL_TO_BUILD`).
### Benchmarks
Benchmarks under `benchmarks/` are **not** part of the main test build — `benchmarks/CMakeLists.txt` is a **standalone CMake project** (`project(EigenBenchmarks CXX)`) that depends on Google Benchmark (`find_package(benchmark REQUIRED)`) and finds Eigen as a sibling header-only include. Configure and build it separately (e.g. `cmake -G Ninja -S benchmarks -B build-bench && ninja -C build-bench`), not through the `buildtests`/`check` targets. CI builds them in the dedicated `benchmark` stage via `ci/scripts/build.benchmark.sh`. (See "Benchmarking discipline" in the agent guidelines for how to *run* them meaningfully.)
**Specify arg grids declaratively with `Args` / `Range` / `ArgsProduct`, never a hand-written `Apply()` callback.** Google Benchmark's `Apply(fn)` passes its registration object as `benchmark::internal::Benchmark*` — an *internal* type (note the `internal` namespace) that is not part of the public API and is easy to misname. The frequent mistake is writing `void MyArgs(benchmark::Benchmark* b)` or `::benchmark::Benchmark*` (no such type — the public alias is only `benchmark::internal::Benchmark`), which fails to compile and broke the whole `benchmark` CI stage. Reach for the chainable macros on the registration itself instead: `->Args({a, b})` for one point, `->Range(lo, hi)` / `->DenseRange(lo, hi, step)` for one swept dimension, and `->ArgsProduct({{...}, {...}})` for the Cartesian product of several dimensions (the declarative form of nested `for` loops calling `b->Args(...)`). These keep the grid on the registration, need no internal types, and read more clearly. Only fall back to `Apply()` for genuinely computed grids that the macros can't express, and then spell the parameter `benchmark::internal::Benchmark*`.
## Formatting and lint
Style is `clang-format-17` (Google base, 120 cols, see `.clang-format`). The version is hard-coded — newer or older clang-format will diff against CI.
```bash
scripts/format.sh # reformat the whole tree
clang-format-17 -i <file> # reformat one file
clang-format-17 --dry-run --Werror <file> # check (CI's `checkformat:clangformat`)
git clang-format --diff --commit <base-sha> # diff what CI will diff
codespell --config setup.cfg # spell-check (also a CI job)
```
`.clang-format` registers Eigen-specific macros (`EIGEN_STATIC_ASSERT`, `EIGEN_INITIALIZE_COEFFS_IF_THAT_OPTION_IS_ENABLED`, `EIGEN_INTERNAL_DENSE_STORAGE_CTOR_PLUGIN`, etc.) as `StatementMacros`, and `EIGEN_STRONG_INLINE` / `EIGEN_ALWAYS_INLINE` / `EIGEN_DEVICE_FUNC` / `EIGEN_DONT_INLINE` / `EIGEN_DEPRECATED` / `EIGEN_UNUSED` as `AttributeMacros`. Don't "fix" their indentation or strip them. `SortIncludes: false` — include order is meaningful in this codebase.
`clang-tidy` runs in the `checkformat:clangtidy` MR job. Locally: `cmake -G Ninja -S . -B .tidy-build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON && clang-tidy -p .tidy-build <files>`. Eigen has its own conventions — do not apply `modernize-*` or `cppcoreguidelines-*` checks.
**SPDX / REUSE.** Every new source file (`.h`, `.cpp`, `.cu`, `.inc`, `.cmake`, `CMakeLists.txt`, etc.) must carry inline copyright + license headers; the `checkformat:reuse` CI job (`reuse lint`) blocks otherwise. The standard Eigen header is:
Every new source file needs accurate REUSE metadata. Original Eigen code normally uses MPL-2.0; prefer the collective
form when an agent cannot truthfully attribute an individual author:
```cpp
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) <year> <Your Name> <your.email@example.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
```
An alternative SPDX-style header — `// SPDX-FileCopyrightText: The Eigen Authors` plus `// SPDX-License-Identifier: MPL-2.0`, with no individual `Copyright (C)` line — is also accepted and is in active use (e.g. files attributed to "The Eigen Authors" and some top-level umbrella headers). The `Copyright (C) <year> <name>` form above remains the dominant convention; either passes `reuse lint`.
Use the language's comment syntax. Documentation or assets that should not carry inline tags must be covered precisely
in `REUSE.toml`; do not add a broad annotation that hides unrelated files. Compatible adapted material may require a
different license expression and attribution, which must be preserved rather than relabeled as MPL-2.0.
Top-level docs (`*.md`), generated files (`*.in`), and binary assets that can't carry inline headers are covered by path annotations in `REUSE.toml` — add the path if you're creating one. When adding headers to many files at once, `scripts/add_spdx_headers.py` inserts the right tag per path and is idempotent (files already carrying an `SPDX-License-Identifier` are left untouched).
## Essential Eigen hazards
## Module layout
### Expressions, lifetimes, and aliasing
> **In-flight rename:** [MR 2522](https://gitlab.com/libeigen/eigen/-/merge_requests/2522) renames the top-level `unsupported/` directory to `contrib/`. Paths below track master (still `unsupported/`). When 2522 lands, substitute `contrib/` for `unsupported/` mechanically — it doesn't affect the canonical/forwarding-shim distinction on the `unsupported/Eigen/` bullet.
Eigen expressions are lazy and frequently retain references. Consumption can occur through assignment, construction,
coefficient access, reductions, or `.eval()`.
- `Eigen/` — supported public headers. Each filename without an extension (`Eigen/Core`, `Eigen/Dense`, `Eigen/SVD`, …) is the umbrella include for one module; implementation lives in `Eigen/src/<Module>/`. **Never include anything under `Eigen/src/...` directly** — that is a hard error (each implementation header includes `InternalHeaderCheck.h` to enforce it).
- `Eigen/src/Core/arch/<Backend>/` — per-architecture SIMD backends; the authoritative directory list lives in "SIMD / packet math layer" below. `GenericPacketMath.h` defines the `internal::p*` API each backend specializes.
- `Eigen/src/Core/products/` — gemm/gemv kernels (`GeneralBlockPanelKernel.h`, triangular / self-adjoint variants, BLAS bridges in `*_BLAS.h`).
- `Eigen/src/Core/util/` — meta-programming, macros, memory, `ForwardDeclarations.h`. `Macros.h` and `ConfigureVectorization.h` set the compile-time feature flags.
- `Eigen/ThreadPool` (with `Eigen/src/ThreadPool/`) — work-stealing thread pool (`NonBlockingThreadPool`, `RunQueue`, `EventCount`, `ForkJoin`, `CoreThreadPoolDevice`). Originally developed for TensorFlow; now part of Core. It is the backend behind `EIGEN_GEMM_THREADPOOL` and the `ThreadPoolDevice` used by the Tensor module.
- `unsupported/Eigen/` — modules with looser API-stability guarantees, but **not** low-traffic. The headliner is `Tensor` (umbrella `unsupported/Eigen/Tensor`, sources under `unsupported/Eigen/src/Tensor/`) — TensorFlow's core compute backend; treat as load-bearing (see agent guideline 13). Other modules: `TensorSymmetry`, `AutoDiff`, `Polynomials`, `MatrixFunctions`, `NNLS`, `FFT`, `GPU` (cuBLAS / cuSOLVER dispatch), `Splines`, `NumericalDiff`, etc. Tests go under `unsupported/test/`. Anything under `unsupported/Eigen/CXX11/` is **backward-compatibility forwarding shims only** — don't include those paths in new code or add new headers there. Likewise `unsupported/Eigen/IterativeSolvers`: the solvers it used to hold (GMRES, DGMRES, MINRES, IDRS, IDRSTABL, BiCGSTABL) were promoted into the supported `Eigen/IterativeLinearSolvers` module, and the old header just forwards there with a deprecation warning.
- `test/` — main test sources and `test/main.h` (the test framework: `VERIFY_*`, `CALL_SUBTEST_N`, `EIGEN_TEST_PART_N`, `EIGEN_DECLARE_TEST`).
- `failtest/` — compile-failure tests.
- `blas/`, `lapack/` — Eigen's BLAS/LAPACK shim libraries (`eigen_blas`, `eigen_lapack`), built only when `EIGEN_BUILD_BLAS` / `EIGEN_BUILD_LAPACK` are on. These get linked by sparse-solver tests (CHOLMOD, UMFPACK, KLU, SuperLU, …) when those packages are present.
- `cmake/``EigenTesting.cmake`, `EigenConfigureTesting.cmake` define the `ei_add_test` / `ei_add_failtest` machinery. Also the `Find*.cmake` modules for optional backends.
- `debug/` — GDB / LLDB pretty-printers and MSVC visualizers for Eigen types (`debug/gdb/`, `debug/lldb/`, `debug/msvc/`).
- `ci/` — GitLab CI (`*.gitlab-ci.yml` per stage) and shell drivers under `ci/scripts/`.
- `auto x = A + B;` stores a lazy expression whose references may dangle. Materialize with `(A + B).eval()` or use an
appropriate plain-object type when ownership is required.
- `.noalias()` is a promise, not a runtime check. Use it only when the destination cannot appear in the right-hand side.
`mat = mat * mat` is protected by product evaluation; `mat.noalias() = mat * mat` is wrong.
- Prefer Eigen expressions when they express the operation clearly and avoid repeated evaluation. Keep a scalar loop
when it represents control flow better, avoids an unnecessary temporary, or has measured performance benefits.
- The two arms of `?:` must have a common C++ type; distinct Eigen expression types often do not. Use `if`/`else` when
necessary.
| Module | Header | Contents |
|---|---|---|
| Core | `Eigen/Core` | Matrix, Array, basic linear algebra, Map, Block, Ref |
| LU | `Eigen/LU` | FullPivLU, PartialPivLU, inverse, determinant |
| Cholesky | `Eigen/Cholesky` | LLT, LDLT |
| QR | `Eigen/QR` | HouseholderQR, ColPivHouseholderQR, FullPivHouseholderQR |
| SVD | `Eigen/SVD` | JacobiSVD, BDCSVD |
| Eigenvalues | `Eigen/Eigenvalues` | SelfAdjointEigenSolver, EigenSolver, ComplexEigenSolver |
| Geometry | `Eigen/Geometry` | Quaternion, AngleAxis, Transform, Hyperplane, `cross()` |
| Sparse | `Eigen/Sparse` | SparseMatrix, sparse solvers (SparseLU, SparseQR, SimplicialCholesky) |
| IterativeLinearSolvers | `Eigen/IterativeLinearSolvers` | ConjugateGradient, LeastSquaresConjugateGradient, BiCGSTAB, BiCGSTABL, GMRES, DGMRES, MINRES, IDRS, IDRSTABL, LSMR (`unsupported/Eigen/IterativeSolvers` is a deprecated forwarding shim to this header) |
### Scalar, index, and storage genericity
External backend umbrella headers (`Eigen/<Pkg>Support`): `AccelerateSupport`, `CholmodSupport`, `KLUSupport`, `MetisSupport`, `PaStiXSupport`, `PardisoSupport`, `SPQRSupport`, `SuperLUSupport`, `UmfPackSupport`. Intel MKL and AMD AOCL are *not* umbrella headers — activate them by defining `EIGEN_USE_MKL_ALL` / `EIGEN_USE_AOCL_ALL` (and friends like `EIGEN_USE_BLAS`, `EIGEN_USE_LAPACKE`, `EIGEN_USE_AOCL_VML`, `EIGEN_USE_AOCL_BLAS`) before including Core or Dense; glue in `Eigen/src/Core/util/MKL_support.h` and `AOCL_Support.h`. Convenience headers: `Eigen/Dense` = Core + all dense solvers; `Eigen/Eigen` = everything supported.
Use `Eigen::Index` for dimensions and counts, but remember that its underlying type is configurable. Use `NumTraits` for
scalar properties and Eigen's `numext` helpers when custom-scalar or device support matters. Do not store sizes or loop
counts in `Scalar`, hard-code `float`/`double` without an API reason, or narrow to a vendor API's `int` without checking
the range. Test real, complex, integer, and narrow/custom scalar types according to the operation's documented domain.
## Architecture
Propagate storage-order and expression flags deliberately. `RowMajorBit`, fixed versus dynamic dimensions, alignment,
and vectorization eligibility affect evaluators and fast paths. Eigen alignment depends on configuration and
architecture; do not encode a presumed byte value. Include configuration-sensitive behavior in tests when it changes
semantics or ABI.
### Expression templates and lazy evaluation
### Public APIs and diagnostics
Eigen's central design pattern is **expression templates with lazy evaluation**. Arithmetic operations do not compute immediately — they return lightweight expression objects that store references to operands and encode the operation. Evaluation happens only on assignment:
For generic APIs, accept the least restrictive established Eigen base (`EigenBase`, `DenseBase`, `MatrixBase`,
`ArrayBase`, or a suitable `Ref`) that preserves the intended semantics. Follow nearby established patterns for writable
expression arguments; do not cast away constness from genuinely const storage. Public-header additions with non-template
definitions or objects deserve a multiple-translation-unit link test when an ODR regression is plausible.
```cpp
// v + w returns CwiseBinaryOp<scalar_sum_op, VectorXf, VectorXf>
// No computation until assigned — then fused into a single vectorized loop
VectorXf u = v + w;
The supported C++14 configurations cannot rely on C++17 over-aligned value passing. Pass fixed-size vectorizable Eigen
objects by reference rather than by value; see [`doc/PassingByValue.dox`](doc/PassingByValue.dox).
Use `eigen_assert` for runtime preconditions that belong to Eigen's public debug behavior and `eigen_internal_assert`
for internal invariants gated by `EIGEN_INTERNAL_DEBUGGING`. Use the local compile-time assertion style that gives the
clearest diagnostic. Comments should explain non-obvious mathematics, invariants, compatibility constraints, or
provenance rather than narrating the code. Keep comments concise and proportional to the code's complexity. Avoid
tutorial-style prose, section-by-section narration, and comments that restate identifiers or control flow. Longer
comments are justified only when that rationale cannot be expressed clearly in code.
## Quick build and test
By default, tests are not part of the `all` target, although that target may build configured auxiliary libraries. A
typical focused workflow is:
```bash
cmake -G Ninja -S . -B build
cmake --build build --target <test-name>
ctest --test-dir build -R '^<test-name>$' --output-on-failure
```
This eliminates temporaries and lets the compiler fuse operations into single-pass, SIMD-vectorized loops. Key expression types:
- `CwiseUnaryOp`, `CwiseBinaryOp`, `CwiseTernaryOp` — element-wise operations
- `Product` — matrix product (special eager rules, see below)
- `CwiseNullaryOp` — procedural matrices (Zero, Identity, Random, custom functors)
- `Block`, `Transpose`, `Diagonal`, `Reshaped`, `IndexedView`, `Map`, `Ref` — view-style expressions
- `Inverse`, `Solve` — solver-result expressions
For a split test such as `foo_3`, build that exact target and match it exactly with CTest. The generated
`buildtests.sh` and `check.sh` wrappers accept source/test-name regexes and are useful for building all matching parts.
Use `buildtests`, `BuildOfficial`, `BuildUnsupported`, `buildsmoketests`, or `check` only when the requested validation
warrants that scope. See [`.agents/testing.md`](.agents/testing.md) for the current test framework, split rules,
configuration variants, and failure-test workflow.
**When Eigen evaluates eagerly into temporaries:**
1. **Matrix products on assignment**`mat = mat * mat` auto-creates a temporary to prevent aliasing. Use `.noalias()` to suppress when safe: `m1.noalias() += m2 * m3;`
2. **Nested products** — in `mat1 = mat2 * mat3 + mat4 * mat5`, each product evaluates to a temporary before combining.
3. **Cost-based** — sub-expressions are cached when recomputation would be more expensive than storage (e.g. `mat1 = mat2 * (mat3 + mat4)` evaluates the sum once).
## Completion checklist
Use `.eval()` to force evaluation; `.noalias()` to override automatic temporary creation.
Before declaring the task complete:
### Evaluator system
- The diff contains only intentional changes and preserves pre-existing work.
- New public implementation is reachable through the intended umbrella header.
- New files have correct REUSE metadata and no generated or local-tool files are staged.
- Changed source files pass `clang-format-17`; `git diff --check` is clean.
- Focused regression tests pass, with broader tests or benchmarks run when the risk warrants them.
- Numerical, aliasing, scalar, storage-order, device, threading, and ABI implications have been considered where
relevant.
- The final report names validation performed, residual risk, and anything that could not be tested locally.
The expression-template engine is implemented through `evaluator<>` traits (in `Eigen/src/Core/CoreEvaluators.h` and `Eigen/src/Core/ProductEvaluators.h`). Assignment goes through `Eigen/src/Core/AssignEvaluator.h` and `Assign.h`. New expression types must specialize `evaluator<>` (and often `assign_op` / `nested_eval`). Operations are lazy by default — work happens at assignment time inside `AssignEvaluator`, which picks between scalar / vectorized / linear / inner / outer traversal strategies based on `Flags`.
### Class hierarchy (CRTP)
Eigen avoids virtual functions. Polymorphism is compile-time via CRTP — each class inherits from a base templated on itself (e.g. `Matrix` inherits `MatrixBase<Matrix>`).
```
EigenBase — root for anything evaluable to a matrix
├── DenseCoeffsBase — coefficient accessors
│ └── DenseBase — shared dense ops (block, reshape, visitors)
│ ├── MatrixBase — linear algebra ops (all dense matrix/vector expressions)
│ │ └── PlainObjectBase — manages storage and resizing
│ │ ├── Matrix — concrete dense matrix (linear algebra semantics)
│ │ └── Array — concrete dense array (coefficient-wise semantics)
│ └── ArrayBase — coefficient-wise ops (all array expressions)
└── SparseMatrixBase — sparse matrix expressions
└── SparseCompressedBase — compressed sparse storage (CSC/CSR)
└── SparseMatrix, SparseVector
```
Every expression type (Block, Transpose, Map, CwiseBinaryOp, Product, …) inherits from `MatrixBase<Derived>` or `ArrayBase<Derived>` without owning storage. `PlainObjectBase``DenseStorage` manages actual memory for `Matrix` and `Array`.
**Matrix vs Array**: `Matrix` types live in MatrixBase-world (linear algebra semantics: `*` is matrix multiply). `Array` types live in ArrayBase-world (coefficient-wise semantics: `*` is element-wise multiply). Convert with `.array()` and `.matrix()`.
### Writing functions that accept Eigen types
Because each expression has a unique type, functions should accept base-class references to avoid forcing evaluation:
```cpp
// Good: accepts any dense matrix expression, no temporaries
template <typename Derived>
void foo(const Eigen::MatrixBase<Derived>& x);
// Also good: non-templated, uses Ref to avoid copies when layouts match
void bar(const Eigen::Ref<const Eigen::MatrixXf>& x);
// Hierarchy of genericity:
// EigenBase > DenseBase > MatrixBase/ArrayBase > concrete types
```
For writable parameters, take `const MatrixBase<Derived>&` and `const_cast` internally — that is the standard Eigen pattern. The reason is that callers commonly want to pass *expression* arguments like `m.row(i)` or `m.block(...)` as out-params; those are temporaries whose const-ness is a language artifact, not a semantic restriction. The const-ref-plus-`const_cast` idiom lets you accept them without forcing the user to materialize a named lvalue.
### SIMD / packet math layer (`src/Core/arch/`)
Vectorization is abstracted through a "packet" layer. Each scalar type maps to a platform-specific SIMD vector type via `internal::packet_traits<Scalar>::type`. Architecture backends provide specializations of packet operations (`padd`, `pmul`, `pload`, `pstore`, `pblend`, …):
- `GenericPacketMath.h` — generic scalar fallback API
- `arch/Default/` — shared helpers (`GenericPacketMathFunctions.h`, `BFloat16.h`, `Half.h`)
- x86: `arch/SSE/`, `arch/AVX/`, `arch/AVX512/`
- ARM: `arch/NEON/`, `arch/SVE/`, `arch/SME/` (SME is a GEMM micro-kernel backend driven by the runtime streaming vector length, not packet math; test knob `EIGEN_TEST_SME`)
- RISC-V: `arch/RVV10/` (scalable vector, multiple LMUL)
- PowerPC: `arch/AltiVec/` (includes VSX and MMA support — no separate `VSX/` directory)
- IBM Z: `arch/ZVector/`
- Other: `arch/MSA/` (MIPS), `arch/LSX/` (Loongson), `arch/HVX/` (Qualcomm Hexagon)
- GPU: `arch/GPU/` (CUDA), `arch/HIP/`, `arch/SYCL/`
- `arch/clang/` — generic clang vector-extension backend
Packets are selected at compile time; the assignment loop splits into an aligned vectorized path plus a scalar remainder. New packet-math intrinsics get added in **every** backend that supports the type. `arch/Default/` holds generic SIMD implementations shared across backends; scalar fallbacks live in `Eigen/src/Core/GenericPacketMath.h` and `Eigen/src/Core/MathFunctions.h`. `test/packetmath.cpp` (and `unsupported/test/special_packetmath.cpp`) exercises them across all enabled backends — failures there often indicate a missing or divergent specialization.
**Guard intrinsics by ISA feature macro.** Inside a backend directory, an intrinsic is only available when its ISA is enabled — `arch/AVX/` is compiled when `EIGEN_VECTORIZE_AVX` is set, but AVX2 / FMA / AVX512* intrinsics within those files must each be guarded by their own `EIGEN_VECTORIZE_*` macro (`#ifdef EIGEN_VECTORIZE_AVX2`, `EIGEN_VECTORIZE_FMA`, `EIGEN_VECTORIZE_AVX512DQ`, etc.), with a fallback for the un-guarded path. Full list in `Eigen/src/Core/util/ConfigureVectorization.h`. Same discipline applies elsewhere (`EIGEN_VECTORIZE_NEON_FP16`, `EIGEN_VECTORIZE_VSX`, …). Missing guards typically compile fine locally and break CI on narrower ISA targets.
### CUDA / HIP / SYCL
Eigen has **two independent GPU stories**, and conflating them causes confusion:
1. **In-kernel use of Eigen types.** When Eigen headers are included from `.cu` / HIP / SYCL files, most functions are automatically annotated `__device__ __host__` via `EIGEN_DEVICE_FUNC` (unified under `EIGEN_GPUCC` for both CUDA and HIP). Only fixed-size types work in kernels. Host SIMD is disabled in `.cu` files — move expensive host-side Eigen code to `.cpp`. Define `EIGEN_NO_CUDA` or `EIGEN_NO_HIP` to suppress device annotations for the respective backend. On 64-bit systems, set `EIGEN_DEFAULT_DENSE_INDEX_TYPE` to `int` for device compatibility.
2. **Host-side dispatch to NVIDIA libraries** (`unsupported/Eigen/GPU`). Plain `.cpp` files orchestrating cuBLAS / cuSOLVER / cuFFT / cuSPARSE / cuDSS calls on device-resident `gpu::DeviceMatrix<Scalar>`. Public API in `Eigen::gpu`, internals in `Eigen::gpu::internal`; solvers (`gpu::LLT`, `gpu::LU`, `gpu::QR`, `gpu::SVD`, `gpu::SelfAdjointEigenSolver`) wrap cuSOLVER. **Not** an expression-template system — every supported expression maps to a single library call, and `DeviceMatrix` does not inherit from `MatrixBase`. Tests compile as `.cpp` (not `.cu`) so NVCC doesn't instantiate Eigen CPU packet ops for CUDA vector types. See `unsupported/Eigen/src/GPU/README.md`.
Tensor GPU kernels live under `unsupported/Eigen/src/Tensor/` (`TensorReductionGpu.h`, `TensorContractionGpu.h`, `TensorDeviceGpu.h`).
### Multi-threading
Eigen parallelizes general dense matrix-matrix products, `PartialPivLU`, row-major sparse-dense products, and some iterative solvers (CG, BiCGSTAB, LeastSquaresCG) via OpenMP or the `EIGEN_GEMM_THREADPOOL` backend (mutually exclusive with OpenMP). Enable OpenMP with `-fopenmp` (GCC) or equivalent. Control threads with `Eigen::setNbThreads(n)` or `OMP_NUM_THREADS`. Limit to physical cores — hyperthreading hurts Eigen's cache-bound kernels. `Eigen::initParallel()` is deprecated and no longer needed.
The `EIGEN_GEMM_THREADPOOL` backend is Eigen's own **work-stealing thread pool**: umbrella `Eigen/ThreadPool`, implementation under `Eigen/src/ThreadPool/`. Headline class `NonBlockingThreadPool` (work-stealing pool over `RunQueue` per-thread deques, `EventCount` for parking), with `CoreThreadPoolDevice` wiring it into Eigen's parallel-for loops and `ThreadPoolInterface` as the abstract base. Originally developed for TensorFlow and used by the Tensor module via `ThreadPoolDevice` — changes are subject to the same caution as Tensor itself.
## Key preprocessor macros
**Performance / behavior** (define before including Eigen):
- `EIGEN_DONT_VECTORIZE` — disable explicit SIMD vectorization
- `EIGEN_DONT_PARALLELIZE` — disable multi-threading
- `EIGEN_FAST_MATH` — exists, default 1 (current usage across the codebase is uneven; a future cleanup will either standardize or remove it)
- `EIGEN_NO_MALLOC` / `EIGEN_RUNTIME_NO_MALLOC` — assert on heap allocation
- `EIGEN_UNROLLING_LIMIT` — loop unrolling threshold (default: 110)
- `EIGEN_STACK_ALLOCATION_LIMIT` — max stack buffer size (default: 128 KB)
- `EIGEN_DEFAULT_TO_ROW_MAJOR` — change default storage from column-major to row-major
- `EIGEN_MAX_ALIGN_BYTES` — alignment for dynamic/static data (auto-detected: 64 for AVX-512, 32 for AVX, 16 default)
- `EIGEN_USE_BLAS` / `EIGEN_USE_LAPACKE` — delegate to external BLAS/LAPACK
- `EIGEN_USE_MKL_ALL` / `EIGEN_USE_AOCL_ALL` — delegate broadly to MKL / AOCL
**Debugging:**
- `EIGEN_NO_DEBUG` — disable runtime assertions (auto-set when `NDEBUG` is defined)
- `EIGEN_INITIALIZE_MATRICES_BY_NAN` — initialize all matrices to NaN
- `EIGEN_INITIALIZE_MATRICES_BY_ZERO` — initialize all matrices to zero
- `EIGEN_INTERNAL_DEBUGGING` — enable assertions in internal routines
**Extending Eigen:**
- `EIGEN_MATRIXBASE_PLUGIN`, `EIGEN_MATRIX_PLUGIN`, `EIGEN_ARRAYBASE_PLUGIN`, … — path to a header file `#include`d inside the class body, adding custom methods to all expressions of that base.
**Compiler annotations** (used in Eigen source code):
- `EIGEN_STRONG_INLINE` — force inline (`__forceinline` on MSVC)
- `EIGEN_ALWAYS_INLINE` — stronger than `STRONG_INLINE`
- `EIGEN_DEVICE_FUNC` — marks functions callable from CUDA/HIP/SYCL device code
- `EIGEN_DONT_INLINE` — prevent inlining
- `EIGEN_DEPRECATED` — deprecation marker
## Conventions worth knowing
(Header-only contract and `EIGEN_DEVICE_FUNC`: see agent guidelines 2 and 3.)
- **Aliasing**: aliasing safety is not a uniform invariant inside Eigen. The standard product-evaluation path inserts an auto-temporary so `mat = mat * mat` is safe; many other paths rely on the user being correct (`.noalias()` is a promise from the caller, not a check). Optimized fast paths that bypass the general assignment machinery have historically introduced aliasing bugs — when writing or modifying one, think explicitly about whether the LHS can alias the RHS, and prefer falling back to the general path when in doubt. User-facing rules are in "Common pitfalls" below.
- **Storage order**: most expressions are templated on `int Options` carrying `RowMajor` / `ColMajor`. When writing new evaluators, propagate `Flags & RowMajorBit` correctly — it is the source of many subtle bugs.
- **`Eigen::internal` namespace**: all internal implementation lives there. Public-facing types and functions stay in `Eigen::` (or specific module namespaces).
- **Default storage order**: column-major.
- **Index type**: `Eigen::Index` (alias for `std::ptrdiff_t`).
- **Naming**: classes PascalCase; methods camelCase; macros / constants `EIGEN_UPPER_CASE`.
- **Forward declarations**: `Eigen/src/Core/util/ForwardDeclarations.h` is the canonical entry point for "where is type X declared?". `internal::traits<T>` carries compile-time information (scalar type, dimensions, flags) without forward-declaration issues.
- **Assertions**: use `eigen_assert(cond)` (defined in `Eigen/src/Core/util/Macros.h`), not raw `assert()` / `static_assert()` for runtime preconditions in library code. The test harness redefines `eigen_assert` so `VERIFY_RAISES_ASSERT(expr)` can verify failures, and it honors `EIGEN_NO_DEBUG` / `NDEBUG`. For internal-only invariants, `eigen_internal_assert(cond)` is gated on `EIGEN_INTERNAL_DEBUGGING`. For compile-time conditions, `EIGEN_STATIC_ASSERT(cond, MSG_TOKEN)` is preferred over plain `static_assert` because it integrates with Eigen's diagnostic-token machinery.
### Comments
Keep comments concise. Coding agents in particular should avoid lengthy commentary and comments that narrate what self-evident code does. Prefer comments that explain the mathematics, invariants, or reasoning behind an algorithm, especially where these are not obvious from the implementation. Use Doxygen's `\f$ ... \f$` for inline formulas or `\f[ ... \f]` for displayed formulas.
## Common pitfalls
- **Aliasing**: `mat = mat * mat` is safe (auto-temporary), but `mat.noalias() = mat * mat` is **wrong**. Only use `.noalias()` when the destination doesn't appear on the right side.
- **`auto` with expressions**: `auto x = A + B;` captures a lazy expression holding references — the references may dangle. Use `auto x = (A + B).eval();` or an explicit type.
- **Missing headers**: some methods require additional includes (e.g. `cross()` needs `Eigen/Geometry`).
- **Ternary operator**: `cond ? exprA : exprB` can fail with expression types because the two branches have different types. Use `if/else`.
- **`template` keyword**: in dependent contexts, write `x.template triangularView<Upper>()` — without `template`, `<` is parsed as less-than.
- **Pass-by-value alignment**: pre-C++17, passing fixed-size vectorizable Eigen objects by value can crash due to alignment. Pass by const reference. With C++17 and modern compilers (GCC ≥ 7, Clang ≥ 5, MSVC ≥ 19.12), over-aligned allocation handles this automatically.
- **`Random()` not thread-safe**: `DenseBase::Random()` and `setRandom()` use `std::rand` internally and are not re-entrant. Use C++11 `<random>` generators via `NullaryExpr` for multi-threaded code.
## CI (GitLab)
Pipeline stages: `checkformat``build``test``benchmark``deploy`. Configuration in `.gitlab-ci.yml` and `ci/*.gitlab-ci.yml`; shell drivers under `ci/scripts/`.
`build` jobs produce a `.build/` artifact (test binaries) consumed by the matching `test` job — the test job only runs `ctest`, it does **not** rebuild. A test job that runs ctest without restricting via `-L` or `-R` will report `Could not find executable` for everything outside the build job's target. Test-job and build-job names must stay paired (see `needs:` in `ci/test.linux.gitlab-ci.yml`).
Test jobs filter via `EIGEN_CI_CTEST_LABEL` (consumed in `ci/scripts/test.linux.script.sh` as `ctest -L $LABEL`). The `:official` and `:unsupported` job-name suffixes are convention only — actual filtering is through that variable. The top-level `.gitlab-ci.yml` `variables:` block declares these with empty/global defaults (`EIGEN_CI_BUILDDIR=.build`, `EIGEN_CI_BUILD_TARGET=""`, `EIGEN_CI_CTEST_LABEL=""`); the meaningful values are set per-job in `ci/*.gitlab-ci.yml` (e.g. `EIGEN_CI_BUILD_TARGET=buildtests`/`BuildOfficial`/`buildtests_gpu` in `ci/build.linux.gitlab-ci.yml`, `EIGEN_CI_CTEST_LABEL=Official` in the matching test jobs).
MR pipelines build / run only a smoke subset; scheduled (nightly) pipelines exercise the full matrix. Format failures (`scripts/format.sh` diff) are the single most common reason an MR is red — run it before pushing.
### Commit message convention
`Category: Short description` (e.g. `GPU: Fix special-function test coverage`, `TriangularView: alias-aware fallback for structured-diagonal product fast path`).
Commit subjects normally use `Category: Short description`, for example
`Core: Fix alias handling in product assignment`.
+15 -5
View File
@@ -2,7 +2,9 @@
Eigen is written and maintained by volunteers. Contributions — code, documentation, bug triage, tests on uncommon platforms, design feedback — are all welcome.
This document is the human-facing on-ramp. For the deeper "how this codebase actually works" reference (architecture, expression templates, SIMD layer, GPU stories, pitfalls), see [`AGENTS.md`](AGENTS.md); it's written for AI coding agents but is also the most up-to-date single-file overview of the repo.
This document is the human-facing on-ramp. [`AGENTS.md`](AGENTS.md) is the AI-facing repository contract and routes
readers to focused guides for testing, numerical work, performance, SIMD/GPU, Tensor/ThreadPool, and CI. Those guides
are also useful to human contributors working in the corresponding areas.
## Where Eigen lives
@@ -10,7 +12,8 @@ This document is the human-facing on-ramp. For the deeper "how this codebase act
- **Issue tracker:** <https://gitlab.com/libeigen/eigen/-/issues>
- **CI pipelines:** <https://gitlab.com/libeigen/eigen/-/pipelines>
- **API documentation (nightly):** <https://libeigen.gitlab.io/eigen/docs-nightly>
- **Project site:** <https://libeigen.gitlab.io> (note: some pages predate current practice — when in doubt, prefer `AGENTS.md` and this file)
- **Project site:** <https://libeigen.gitlab.io> (note: some pages predate current practice — when in doubt, prefer
this file and the guide map in `AGENTS.md`)
- **Chat:** [Discord](https://discord.com/channels/777904510169382942/777904791136370758) — the primary support and discussion channel.
## Ways to contribute
@@ -59,7 +62,9 @@ ctest --test-dir build --parallel --output-on-failure
Other useful targets: `BuildOfficial` (just `test/`), `BuildUnsupported` (just `unsupported/test/`), `buildtests_gpu`, `check_gpu`. Once configured, the build dir also exposes `./buildtests.sh <regex>` and `./check.sh <regex>` for narrowing by test-name regex.
Full reference for CMake options (per-ISA test flags, GPU/SYCL toggles, external BLAS/LAPACK, etc.), the test-split mechanism, and the `VERIFY_*` macro family lives in [`AGENTS.md`](AGENTS.md) § "Build / test".
For focused build recipes, test registration, split-test mechanics, and assertion guidance, see
[`.agents/testing.md`](.agents/testing.md). Packet and accelerator validation is covered by
[`.agents/simd-gpu.md`](.agents/simd-gpu.md). The checked-out CMake files remain authoritative for current options.
> **In-flight migration:** [MR 2159](https://gitlab.com/libeigen/eigen/-/merge_requests/2159) is migrating the test framework from Eigen's own `EIGEN_DECLARE_TEST` / `CALL_SUBTEST_N` macros to Google Test. Until it lands, write new tests with the existing framework (`test/main.h`, `VERIFY_*`, `EIGEN_DECLARE_TEST`, `ei_add_test` in the matching `CMakeLists.txt`).
>
@@ -75,7 +80,8 @@ Full reference for CMake options (per-ISA test flags, GPU/SYCL toggles, external
## Coding standards
Eigen has a few hard rules that catch contributors most often. The full discussion is in [`AGENTS.md`](AGENTS.md) § "Agent guidelines (read first)"; the short version:
Eigen has a few hard rules that catch contributors most often. The repository-wide rules are in
[`AGENTS.md`](AGENTS.md#non-negotiable-rules), which also routes to task-specific guidance. The short version:
1. **Header-only contract.** Never `#include` anything under `Eigen/src/...` or `unsupported/Eigen/src/...` — `InternalHeaderCheck.h` makes that a hard compile error. User code reaches implementation only through the umbrella headers (`Eigen/Core`, `Eigen/Dense`, `Eigen/SVD`, …).
2. **Preserve `EIGEN_DEVICE_FUNC`** on coefficient-level methods. Dropping it silently breaks CUDA / HIP / SYCL builds and rarely shows up in local testing.
@@ -180,7 +186,11 @@ Some "unsupported" modules carry stability guarantees beyond what the name sugge
## Further reading
- [`AGENTS.md`](AGENTS.md) — deep dive on architecture, expression templates, evaluators, the SIMD packet layer, CUDA / HIP / SYCL, multi-threading, common pitfalls, and CI structure.
- [`AGENTS.md`](AGENTS.md) — repository-wide agent contract, standard workflow, essential Eigen hazards, and the map
to task-specific guidance.
- Task guides: [testing](.agents/testing.md), [numerical code](.agents/numerics.md),
[benchmarking](.agents/benchmarking.md), [SIMD/GPU](.agents/simd-gpu.md),
[Tensor/ThreadPool](.agents/tensor-threadpool.md), and [formatting/CI](.agents/ci.md).
- [`README.md`](README.md) — high-level project description and pointers to the websites.
- [`CHANGELOG.md`](CHANGELOG.md) — release-by-release notes.
- API reference (nightly): <https://libeigen.gitlab.io/eigen/docs-nightly>.
+1
View File
@@ -11,6 +11,7 @@ version = 1
[[annotations]]
path = [
".agents/*.md",
"AGENTS.md",
"CHANGELOG.md",
"INSTALL",