Skigen
High-performance machine learning for modern C++ and Eigen.
Skigen is a header-only C++ template library for machine learning. It brings the scikit-learn API — fit(), transform(), predict() — to the Eigen ecosystem.
Overview
- Skigen is versatile.
- Preprocessing, linear models, decomposition, clustering, trees, neighbors, pipelines, and metrics — covering the core scikit-learn surface with a consistent API.
- All estimators are templatized on
Scalar(default:double). Switch tofloatfor 2× SIMD throughput on the same hardware.
- Skigen is fast.
- Eigen's expression templates eliminate temporaries and enable lazy evaluation.
- Explicit SIMD vectorization via Eigen for SSE, AVX, AVX-512, and ARM NEON.
- Compile-time polymorphism via CRTP — zero vtable overhead, zero runtime dispatch.
- No interpreter, no garbage collector, no GIL.
- Skigen is reliable.
- Bit-level parity with scikit-learn for identical inputs and default parameters, verified via cross-language parity tests.
- CI-tested on GCC, Clang, and MSVC across Linux, macOS, and Windows.
- Skigen is elegant.
- Header-only — drop
Skigen/next toEigen/and#include. No compiled libraries, no linker flags. - Include patterns mirror Eigen:
#include <Skigen/Dense>feels like#include <Eigen/Dense>. - The same API you know from scikit-learn, native to C++.
- Header-only — drop
Design
| Principle | Implementation |
|---|---|
| Eigen-native | Headers, namespaces, and include patterns mirror Eigen conventions. |
| Header-only | No compiled libraries, no link-time dependencies. |
| Templatized | Scalar template parameter — double default, float for dense SIMD. |
| Zero-copy | Inputs use Eigen::Ref<const MatrixType> — sub-blocks and memory-mapped data without copying. |
| CRTP | Static polymorphism via the Curiously Recurring Template Pattern. |
| Bit-level parity | Results match scikit-learn for identical inputs and default parameters. |
Quick Example
#include <Eigen/Dense>
#include <Skigen/Preprocessing>
#include <iostream>
int main() {
Eigen::MatrixXd X(4, 2);
X << 1, 10,
2, 20,
3, 30,
4, 40;
Skigen::StandardScaler scaler;
Eigen::MatrixXd Z = scaler.fit_transform(X);
std::cout << "Standardized:\n" << Z << "\n";
return 0;
}