Thank you for your interest in contributing to SPD Learn! This guide will help you get started with contributing to the project.
- Getting Started
- Development Workflow
- Types of Contributions
- Adding New Features
- Documentation
- Code Organization
- Testing Guidelines
- Community
-
Fork and clone the repository:
git clone https://github.com/spdlearn/spd_learn.git cd spd_learn -
Create a virtual environment:
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
-
Install in development mode with all dependencies:
pip install -e ".[all]" -
Install pre-commit hooks (required for consistent formatting):
pip install pre-commit pre-commit install pre-commit run --all-files
Create a new branch for your feature or bug fix:
git checkout -b feature/your-feature-name
# or
git checkout -b fix/your-bug-fixRun the test suite to ensure your changes don't break existing functionality:
pytestRun tests with coverage:
pytest --cov=spd_learn --cov-report=htmlWe use ruff for linting and black for formatting. Run before committing:
ruff check spd_learn/
black spd_learn/If you find a bug, please open an issue on GitHub with:
- A clear, descriptive title
- Steps to reproduce the bug
- Expected behavior vs. actual behavior
- Your environment (Python version, PyTorch version, OS)
- Minimal code example that reproduces the issue
We welcome feature requests! Please open an issue describing:
- The problem you're trying to solve
- Your proposed solution
- Any alternatives you've considered
- Create an issue first for significant changes
- Write tests for new functionality
- Update documentation if needed
- Follow the code style guidelines
- Keep PRs focused - one feature/fix per PR
Before submitting a PR, ensure:
- All tests pass (
pytest) - Code is formatted (
black,ruff) - Documentation is updated
- Commit messages are clear and descriptive
-
pre-commit run --all-filespasses
To add a new neural network layer:
- Create the layer in the appropriate file under
spd_learn/modules/ - Add comprehensive docstrings following NumPy format
- Export in
spd_learn/modules/__init__.py - Add to
docs/source/api.rst - Write unit tests in
tests/
Example layer structure:
import torch
import torch.nn as nn
class MyNewLayer(nn.Module):
"""Short description of the layer.
Longer description explaining what the layer does,
its mathematical formulation, and when to use it.
Parameters
----------
in_features : int
Input dimension.
out_features : int
Output dimension.
References
----------
.. [1] Author, A. (Year). Paper Title. Journal.
Examples
--------
>>> layer = MyNewLayer(64, 32)
>>> x = torch.randn(16, 64, 64)
>>> output = layer(x)
>>> output.shape
torch.Size([16, 32, 32])
"""
def __init__(self, in_features: int, out_features: int):
super().__init__()
self.in_features = in_features
self.out_features = out_features
# Initialize parameters...
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""Forward pass.
Parameters
----------
x : torch.Tensor
Input tensor of shape ``(batch, in_features, in_features)``.
Returns
-------
torch.Tensor
Output tensor of shape ``(batch, out_features, out_features)``.
"""
# Implementation...
return xTo add a new model architecture:
- Create the model in
spd_learn/models/ - Include a docstring with:
- Architecture description
- Figure reference (if available)
- All parameters documented
- Original paper reference
- Export in
spd_learn/models/__init__.py - Add to
docs/source/api.rst - Consider adding an example script
For low-level operations:
- Add to
spd_learn/functional/ - Implement both forward and backward passes if using custom autograd
- Export in
spd_learn/functional/__init__.py - Document in
docs/source/api.rst
Build the documentation locally:
cd docs
make htmlView the built documentation:
open build/html/index.html # macOS
xdg-open build/html/index.html # Linux- Use NumPy-style docstrings for all public functions and classes
- Include examples in docstrings where helpful
- Add references to papers when implementing published methods
- Update the User Guide for conceptual changes
spd_learn/
├── __init__.py # Package initialization
├── version.py # Version string
├── functional/ # Low-level operations
│ ├── __init__.py
│ ├── functional.py # Matrix operations
│ ├── covariance.py # Covariance estimators
│ ├── manifolds.py # Manifold operations
│ └── ...
├── modules/ # Neural network layers
│ ├── __init__.py
│ ├── bilinear.py # BiMap layers
│ ├── modeig.py # LogEig, ReEig, ExpEig
│ ├── batchnorm.py # SPD batch normalization
│ └── ...
└── models/ # Pre-built architectures
├── __init__.py
├── spdnet.py
├── tensorcsp.py
└── ...
- Modules:
CamelCase(e.g.,BiMap,SPDBatchNormMeanVar) - Functions:
snake_case(e.g.,matrix_log,sample_covariance) - Private methods: prefix with
_(e.g.,_compute_mean)
Tests are organized in tests/ mirroring the package structure:
tests/
├── test_functional.py
├── test_modules.py
└── test_models.py
- Test both forward and backward passes
- Test edge cases (empty batches, single samples)
- Test numerical stability with extreme values
- Use parametrized tests for multiple configurations
Example test:
import pytest
import torch
from spd_learn.modules import BiMap
@pytest.mark.parametrize(
"in_features,out_features",
[
(64, 32),
(32, 16),
(16, 8),
],
)
def test_bimap_output_shape(in_features, out_features):
layer = BiMap(in_features, out_features)
x = torch.randn(8, in_features, in_features)
x = x @ x.transpose(-1, -2) # Make SPD
y = layer(x)
assert y.shape == (8, out_features, out_features)
def test_bimap_preserves_spd():
layer = BiMap(32, 16)
x = torch.randn(8, 32, 32)
x = x @ x.transpose(-1, -2) + torch.eye(32) # SPD
y = layer(x)
# Check positive definiteness via eigenvalues
eigvals = torch.linalg.eigvalsh(y)
assert (eigvals > 0).all()- GitHub Issues — Bug reports and feature requests
- Pull Requests — Code contributions
- Discussions — Questions and ideas
Thank you for contributing to SPD Learn!