Contributing Guide¶
Thank you for your interest in contributing to Yet Another SPDNet! This guide will help you get started.
Development Setup¶
1. Fork and Clone¶
git clone https://github.com/YOUR_USERNAME/YetAnotherSPDNet.git
cd YetAnotherSPDNet
2. Install Development Dependencies¶
pip install -e ".[all]"
3. Install Pre-commit Hooks¶
pre-commit install
This will automatically run code formatting and linting checks before each commit.
Development Workflow¶
Branch Strategy¶
We use a simple branch-based workflow:
main: Production-ready code (protected, requires CI to pass)feature/feature-name: New featuresbugfix/issue-description: Bug fixeshotfix/critical-issue: Critical fixes for production
Creating a New Branch¶
# For new features
git checkout -b feature/my-awesome-feature
# For bug fixes
git checkout -b bugfix/fix-issue-123
# For hotfixes
git checkout -b hotfix/critical-bug
Making Changes¶
1. Code Style¶
We use ruff for linting and formatting (replaces black, isort, flake8):
# Format code
ruff format .
# Check for issues
ruff check .
# Auto-fix issues
ruff check --fix .
Configuration is in pyproject.toml under [tool.ruff].
2. Type Checking¶
We use mypy for static type checking:
mypy src/
Add type hints to all new functions:
def my_function(x: torch.Tensor, n: int = 10) -> torch.Tensor:
"""Function with type hints."""
...
3. Writing Tests¶
All new features must include tests. We use pytest:
# Run all tests
pytest
# Run with coverage
pytest --cov=yetanotherspdnet
# Run specific test file (coverage is enforced by default, skip it here)
pytest tests/functions/test_spd_linalg.py --no-cov
# Run specific test class
pytest tests/nn/test_base.py::TestBiMap --no-cov
Test Structure¶
Tests use pytest fixtures for device, dtype and a seeded generator
(see the top of each test module). Operations that exist on both gradient
paths must be tested on both (torch.autograd.gradcheck in float64, plus
agreement between the autograd and manual gradients):
import pytest
import torch
from torch.testing import assert_close
from yetanotherspdnet.functions.spd_linalg import LogmSPD, logm_SPD
from yetanotherspdnet.random.spd import random_SPD
@pytest.mark.parametrize("n_features, n_matrices", [(10, 5)])
def test_logm_gradients_agree(n_features, n_matrices, device, dtype, generator):
X = random_SPD(
n_features, n_matrices, device=device, dtype=dtype, generator=generator
)
X_auto = X.clone().requires_grad_()
X_manual = X.clone().requires_grad_()
logm_SPD(X_auto)[0].sum().backward()
LogmSPD.apply(X_manual).sum().backward()
assert_close(X_auto.grad, X_manual.grad)
4. Writing Documentation¶
We use Sphinx with MyST (Markdown support):
Docstring Format¶
Use NumPy style docstrings. Classes get a class docstring (description and an
Attributes section for parameters and buffers). Constructor arguments are
documented in the __init__ docstring. The API reference shows both.
def my_function(X: torch.Tensor, eps: float = 1e-3) -> torch.Tensor:
"""Short one-line description.
Longer description with more details about what the function does,
including mathematical background if relevant.
Parameters
----------
X : torch.Tensor
Input SPD matrices of shape (..., n, n)
eps : float, optional
Regularization parameter, by default 1e-3
Returns
-------
torch.Tensor
Output tensor of shape (..., n, n)
Raises
------
ValueError
If X is not positive definite
Examples
--------
>>> X = random_SPD(50, 10, dtype=torch.float64)
>>> result = my_function(X, eps=1e-4)
"""
...
Building Documentation¶
pip install -e ".[docs]"
cd docs
make html SPHINXOPTS="-W --keep-going" # warnings are errors
xdg-open _build/html/index.html
The API reference pages (docs/reference/*.md) are written by hand: each
opens with the context of its objects and a summary table, then documents
them with autoclass / automodule (inside {eval-rst} blocks). A new
public class or function must be added to the page of its module (a module
documented with automodule picks it up automatically). The
dualpath-table directive (docs/_ext/dualpath.py) pairs each snake_case
function with its CamelCase manual-backward Function by name.
Pull Request Process¶
1. Commit Your Changes¶
Use conventional commit messages:
git commit -m "feat: add new SPD mean computation method"
git commit -m "fix: correct gradient computation in LogEig"
git commit -m "docs: update installation instructions"
git commit -m "test: add tests for BiMap layer"
Commit types:
feat: New featurefix: Bug fixdocs: Documentation changestest: Test additions or changesrefactor: Code refactoringperf: Performance improvementschore: Maintenance tasks
2. Push to Your Fork¶
git push origin feature/my-awesome-feature
3. Create Pull Request¶
Go to GitHub and create a pull request from your fork to the main repository.
Pull Request Checklist:
[ ] Code follows style guidelines (ruff passes)
[ ] All tests pass (
pytest)[ ] New tests added for new features
[ ] Coverage not decreased (CI enforces ≥ 40%; aim for ≥ 80% on new code)
[ ] Documentation updated (docstrings and/or markdown files)
[ ] Type hints added for new functions
[ ] Commit messages follow conventional commits
[ ] No merge conflicts with main branch
4. Code Review¶
A maintainer will review your PR and may request changes. Address feedback by:
# Make changes
git add .
git commit -m "fix: address review comments"
git push origin feature/my-awesome-feature
5. Merge¶
Once approved and CI passes, a maintainer will merge your PR into main.
CI/CD Pipeline¶
All pull requests must pass CI checks:
Tests: all tests must pass on Python 3.11 and 3.12
Coverage: the suite fails below 40% (
--cov-fail-underinpyproject.toml)Linting:
ruff check .andruff format --check .must pass, with the ruff version pinned inpyproject.toml(devextra), in the CI workflow and in.pre-commit-config.yaml. Bump all three together.Type checking:
mypy src/is not run in CI (known pre-existing errors)
Only merges to main require all CI checks to pass. Development branches can be pushed without restrictions for fast iteration.
Testing Requirements¶
Coverage¶
pytest measures coverage by default and fails below 40%. New code should
come with tests covering both gradient paths.
Markers¶
slow and integration markers are declared in pyproject.toml; deselect
with pytest -m "not slow".
Getting Help¶
GitHub Issues: Report bugs or request features
GitHub Discussions: Ask questions or discuss ideas
Email: Contact maintainers directly
Code of Conduct¶
Be respectful and inclusive
Provide constructive feedback
Focus on what is best for the project
Show empathy towards other contributors
Thank you for contributing to Yet Another SPDNet!