blob: 6cd64efa6503deaa774158804f3340c5cdcb997e [file] [view] [edit]
# AGENTS.md
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.
## Scope and precedence
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.
Read this file for every task. Then read every row below that matches the work; do not load unrelated guides by
default.
| Work area | Additional guidance |
|---|---|
| Any new or rewritten code | [`.agents/conventions.md`](.agents/conventions.md) |
| Tests and CMake test targets | [`.agents/testing.md`](.agents/testing.md) |
| Numerical kernels, decompositions, solvers, accuracy | [`.agents/numerics.md`](.agents/numerics.md) |
| Sparse matrices, sparse solvers, external sparse backends | [`.agents/sparse.md`](.agents/sparse.md) |
| Performance changes and benchmarks | [`.agents/benchmarking.md`](.agents/benchmarking.md) |
| Packet math, CUDA, HIP, SYCL, `contrib/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) |
| Doxygen blocks, `doc/` pages, snippets, and examples | [`.agents/docs.md`](.agents/docs.md) |
| Changes under `ci/`, `.gitlab-ci.yml`, or the test-selection and cache scripts | [`.agents/ci-internals.md`](.agents/ci-internals.md) |
| Writing or updating a merge request description | [`.agents/merge-requests.md`](.agents/merge-requests.md) |
| Answering merge request review comments | [`.agents/review-response.md`](.agents/review-response.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) |
## Non-negotiable rules
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; a `Co-Authored-By` trailer naming the model that
actually produced the change is accurate attribution, not an invented one, and is permitted.
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 `contrib/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. This protects code you are not otherwise changing; it does not license writing
new code in a superseded form. Write new declarations in the form
[`.agents/conventions.md`](.agents/conventions.md) records.
6. **Gate a fast path on the property it needs.** State the exact precondition a new specialization, capability flag,
or enable condition depends on and test for that, not for an adjacent capability, an overload's existence, or a
property the built-in types merely happen to share. New opt-in traits default to the conservative answer;
user-specializable extension points must stay correct while unannotated.
7. **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. Confirm the new test
fails at the parent commit when practical; otherwise demonstrate that it reaches the changed path by construction.
See [`.agents/testing.md`](.agents/testing.md).
8. **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.
## Standard workflow
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` (`--hidden` reaches `.agents/`).
Before writing a helper, check `numext`, `NumTraits`, `MathFunctions.h`, `Meta.h`, `XprHelper.h`, and the
`test/*_helpers.h` headers for an existing one; if it exists but lacks needed hardening, fix it there rather than
adding a local copy.
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 the task's changed lines with `git clang-format --binary clang-format-17 --force <base-sha> -- <files>`.
Inspect the selected files' diffs first to exclude unrelated changes; `--force` permits unstaged edits. Untracked
files are absent from the diff, so format task-created files with `clang-format-17 -i <files>`. Whole-file formatting
of existing files and `scripts/format.sh` also rewrite pre-existing lines that are not clang-format-17 clean, so use
them only when that churn is intended. See [`.agents/ci.md`](.agents/ci.md) for the matching check.
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.
8. When review comments arrive, follow [`.agents/review-response.md`](.agents/review-response.md).
## Repository essentials
Eigen is a header-only expression-template library. Consumers include module headers under `Eigen/` or
`contrib/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.
The supported implementation is under `Eigen/src/`; tests are under `test/`. Modules with looser API-stability
guarantees are under `contrib/Eigen/`, with tests under `contrib/test/`. Legacy `unsupported/Eigen/...` include paths
remain valid: one-line forwarding shims under `unsupported/Eigen/` point at the `contrib/` headers and are installed
alongside them. "Contrib" 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.
The `lapack/*.f` files are vendored netlib LAPACK reference sources and are read-only here: do not edit them ad hoc,
and flag a merge request that changes one unless it is an explicit refresh from a named netlib release, in which case
check the diff against that release. Tree-wide clang-format and SPDX-tagging commits are listed in
`.git-blame-ignore-revs`; pass that file to `git blame` with `--ignore-revs-file` to see the history beneath them.
Every new source file needs accurate REUSE metadata; [`.agents/conventions.md`](.agents/conventions.md) records the
required header form and the `REUSE.toml` rules for files that cannot carry an inline tag.
## Essential Eigen hazards
### Expressions, lifetimes, and aliasing
Eigen expressions are lazy and frequently retain references. Consumption can occur through assignment, construction,
coefficient access, reductions, or `.eval()`.
- `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.
- Prefer block and view expressions when a uniform operation or existing Eigen method applies to a submatrix; for
example, scale a 2-by-2 block or call its `determinant()` instead of spelling out its coefficients. Preserve known
extents with fixed-size accessors such as `block<Rows, Cols>(i, j)`; in dependent template code, write
`m.template block<Rows, Cols>(i, j)`. Use runtime extents only when they are genuinely dynamic, and use individual
coefficient access when entries require different operations. Blocks remain lazy, non-owning views, so the lifetime
and overlap rules above still apply.
- The two arms of `?:` must have a common C++ type; distinct Eigen expression types often do not. Use `if`/`else` when
necessary.
- Declare dynamically sized matrix and vector workspaces outside the loop that fills them: a plain object named inside
the loop body allocates on every iteration, as does every subexpression that materializes a temporary into it.
### Scalar, index, and storage genericity
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.
An algebraic property that holds for the built-in types — commutativity, exactness, tie behavior of `min`/`max` — is
not a property of every `Scalar`; establish it per scalar category and leave custom scalars on the conservative path.
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.
### Public APIs and diagnostics
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.
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. Reviewers here read mathematics
and code faster than English: where a formula, a recurrence, an error bound, or two lines of pseudo-code state the
point more precisely than a paragraph, write that instead. The same preference applies to merge request descriptions
and review comments; [`.agents/merge-requests.md`](.agents/merge-requests.md) records the KaTeX syntax GitLab renders.
## 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 --no-tests=error
```
For a split test such as `foo_3`, build that exact target and match it exactly with CTest, keeping `--no-tests=error`:
a filter that matches nothing otherwise exits 0. The generated `buildtests.sh` and `check.sh` wrappers accept
source/test-name regexes and are useful for building all matching parts.
Use `buildtests`, `BuildOfficial`, `BuildContrib`, `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.
## Completion checklist
Before declaring the task complete:
- 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 lines and task-created source files pass the clang-format-17 checks in `.agents/ci.md`;
`git diff --check` is clean.
- Documentation describing the changed behavior — the Doxygen block above a changed declaration, the module `README`,
and nearby comments naming a value or precondition the change moved — is updated with it.
- 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.
Commit subjects normally use `Category: Short description`, for example
`Core: Fix alias handling in product assignment`.