skainet
0.33.0indexedOpen-source deep learning framework simplifies creation of modern AI applications, adhering to GitFlow for branching and Semantic Versioning for release management.
Open-source deep learning framework simplifies creation of modern AI applications, adhering to GitFlow for branching and Semantic Versioning for release management.
Surfaced from shared tags and platforms — no rankings paid for.
Click the diagram for the full architecture reference, or read the short ARCHITECTURE.md.
SKaiNET is a Kotlin Multiplatform AI framework. New here? Choose the path that matches what you want to try first.
Working in Java? SKaiNET ships first-class Java support — see the Java getting-started guide.
[!NOTE] Looking for LLM inference? Llama, Qwen, Gemma, Apertus, BERT embeddings and GGUF chat models live in SKaiNET-transformers — this repository is the engine underneath it (tensors, NN DSL, compiler, CPU/native backends, GGUF/SafeTensors IO). Depend on the
sk.ainet.transformersartifacts, pinned together by the transformers BOM.
Use the version shown in this README as the source of truth for first-run snippets. If another page shows a different version, please open an issue or PR.
Add the core dependencies (Gradle Kotlin DSL):
dependencies {
// Recommended: import the umbrella BOM and drop versions on the engine modules.
implementation(platform("sk.ainet:skainet-bom:0.40.1"))
implementation("sk.ainet.core:skainet-lang-core")
implementation("sk.ainet.core:skainet-backend-cpu")
}
val model = nn {
input(28 * 28)
dense(out = 128)
relu()
dense(out = 10)
}
val a = tensor(shape(2, 2)) { float(1f, 2f, 3f, 4f) }
val b = tensor(shape(2, 2)) { float(5f, 6f, 7f, 8f) }
val c = a matMul b
val d = c.relu()
// Recommended: streaming reader — memory-efficient, supports quantized types
val source = JvmRandomAccessSource.open("model.gguf")
StreamingGGUFReader.open(source).use { reader ->
println("Tensors: ${reader.tensorCount}")
// Load specific tensor on demand (no whole-file loading)
val bytes = reader.loadTensor("token_embd.weight")
// Or get a TensorStorage descriptor with encoding/placement metadata
val storage = reader.loadTensorStorage("token_embd.weight")
}
More examples: SKaiNET-examples | SKaiNET-notebook
SKaiNET is a modular ecosystem. While this repository contains the core engine, specialized high-level libraries are maintained in standalone repositories:
| Project | Description |
|---|---|
| SKaiNET-transformers | Pre-built transformer architectures and layers |
| SKaiNET-examples | Sample projects and integration demos |
| Goal |
|---|
Small fixes can go straight through the normal contribution flow described in CONTRIBUTING.md and GITFLOW.adoc.
Use a SKEEP when a change affects public APIs, DSL syntax, tensor semantics,
compiler/runtime integration, storage behavior, compatibility policy, or other
decisions that need a durable design record. SKEEP files live under
docs/modules/skeep/pages/ and use three-digit numbering, starting with
001.
SKaiNET ships an official Phoronix-Test-Suite-compatible benchmark
program for the compute engine. See the
methodology and replay docs,
the release manifest, and the
CI workflow. Smoke runs fire
on every PR via ubuntu-latest; full publishable runs fire on a
self-hosted Linux x86 runner on release.
Quick local replay:
./gradlew :skainet-backends:benchmarks:jvm-cpu-publish:shadowJar
./scripts/run_engine_smoke.sh
SKaiNET is built around one path: a model is defined once in the Kotlin DSL, then either compiled or executed eagerly — without rewriting it.
StableHLO/MLIR is therefore one code-generation backend among siblings — the IREE/native path next to the C99/Arduino and Minerva MCU paths — not a separate pipeline.
flowchart LR
DSL["Model — Kotlin DSL"] --> Graph["Tape / DAG (ComputeGraph)"]
Graph --> Eager["Eager backend (JVM, …)"]
Graph -->|code generation| HLO["StableHLO / MLIR"]
Graph -->|code generation| C99["Arduino / C99"]
Graph -->|code generation| Minerva["Minerva"]
HLO --> Native["IREE → native / edge / accelerator"]
C99 --> MCU["Microcontroller"]
Minerva --> SecMCU["Secure-MCU bundle"]
The same DSL model feeds every path: eager execution for development and JVM deployment, and the code-generation backends — StableHLO/MLIR (→ IREE), Arduino/C99, and Minerva — as sibling alternatives for native, edge, and secure-MCU targets.
SKaiNET now includes a Minerva export backend for secure MCU deployment. It is a sibling to StableHLO and Arduino/C99 export: it starts from a supported ComputeGraph, lowers static MLPs to a Minerva compiler input, invokes libminerva when configured, and packages generated weights, host fixtures, firmware skeletons, and a fingerprinted manifest.json.
Start here:
Runnable examples:
./gradlew :skainet-compile:skainet-compile-minerva:runMinervaSecureMcuExamples
./gradlew :skainet-compile:skainet-compile-minerva:runMinervaSecureMcuExamples \
-Pminerva.example=sensor-classifier
safe-lowbit, balanced, experimental-max. See for integration guide.nn { input(); dense(); relu(); dense() }dag { } for ResNet, YOLO-style architecturesfile://, https://, , and val raw = JvmDataSourceResolver().rawDataset {
from("hf://datasets/org/repo@main/train.jsonl")
format(DataFormat.JSON_LINES)
cachePolicy(CachePolicy.Use)
}
val withoutLabel = dataPipeline<RawDataset>()
.stage(
dataTransformer(
name = "drop-label",
outputSchema = { schema -> DataSchema(schema.columns - "label") }
) { dataset ->
val columns = dataset.schema.columns - "label"
dataset.copy(
schema = DataSchema(columns),
rows = dataset.rows.map { row ->
RawDataRow(row.values.filterKeys { key -> key in columns })
}
)
}
)
.execute(raw)
HloGeneratortranspose() was silently wrong, not crashing. ops.matmul(x, ops.transpose(W)) on a packed-quantized weight (Q4_0/Q5_0/Q5_1/Q8_0/Q4_K/Q5_K/Q6_K) with more than one quant block per row produced silently incorrect output — sometimes all-zero — across the scalar, Panama-vector, native (FFM/JNI) kernel tiers, with no exception raised. now performs a real block-grid byte permutation instead of a shape-only relabel; a misaligned packed tensor now throws instead of silently truncating. Closes . for anyone calling on packed-quantized weights.FloatArray buffer, benefiting every non-JVM target — Android, Kotlin/Native, JS/Wasm. DirectCpuExecutionContext.ops is also cached instead of rebuilt per access.See CHANGELOG.md for details and the full release history.
We love contributions! Whether it's a new operator, documentation, or a bug fix:
Browse the full codebase documentation on DeepWiki.
transpose() block-grid correctness fix, all three kernel tiers (#968, #969)DEQUANTIZE_TO_FP32 over-allocation fix (#782), native Q5_0/Q5_1 packed matmul kernels (#708), Apple arm64 runtime FEAT_DotProd dispatch (#958), Apple iOS/macOS Kotlin/Native kernel targets (#959)createRandomAccessSource streaming loads (#922), cinterop klib archive embedding (#942), Q4_0 NEON kernel (#939), GGUF loader fail-fast (#919), tensor-storage correctness fixes (#927, #928, #929, #930, #931), AAR release publishing (#947)Lstm layer (#824), Dropout masking (#867), LR schedules (#866), optional/open (#870, #875), SDPA scale fix (#880), autograd fixes (#877), DAG spec (#878), tokenizer + fixes (#879), Android native IO targets (#836, #842, #845)MIT — see LICENCE.
| Goal | Start here | Time |
|---|
| Run tensor operations | Quickstart (below) | 2–5 min |
| Build and train a neural net | Hello Neural Net (below) | 5 min |
| Run a local GGUF model | SKaiNET Transformers starter | 5 min after model setup |
| Export a secure MCU bundle | Minerva getting started | 10 min without firmware flashing |
| Start here |
|---|
| Examples and sample projects | SKaiNET-examples |
| Interactive notebooks | SKaiNET-notebook |
| Eager backends & kernels (what runs where) | Backends & kernels mindmap |
| Design proposals and long-lived API decisions | SKEEP proposals |
nn { } / dag { }).ComputeGraph.ComputeGraph through one of several
sibling code-generation backends, each emitting code for a different target
from the same graph:
HloGenerator) → IREE-compilable, for native / edge /
accelerator targets and the wider MLIR ecosystem.TurboQuantUsagehf+https://hf://....jsonl, .ndjson)transpose()ops.transpose()DEQUANTIZE_TO_FP32 no longer over-allocates. A 1.1B Q4_K_M GGUF transiently needed >12 GB heap against a ~4.4 GB dense-FP32 floor. Three compounding allocation sources in the loader and K-quant kernels are fixed, bringing peak live allocation to ~1.05x of the dense FP32 size.NATIVE_OPTIMIZED.skainet-backend-native-cpu now publishes iosArm64, iosSimulatorArm64, and macosArm64 Kotlin/Native targets with embedded kernel archives; a single Apple arm64 archive dispatches FEAT_DotProd at runtime, so one build serves A12 through M-series.skainet-backend-jni-cpu module: the hand-tuned ARM matmul kernels reach Android through a JNI bridge (ART has no java.lang.foreign, so the FFM provider can never run there). Two .so tiers are built from the same sources and selected at load time from /proc/cpuinfo — a baseline armv8-a build that runs on every 64-bit core, and an armv8.2-a+dotprod build for the vdotq_s32 Q4_K/Q6_K paths — so a single artifact is safe from Cortex-A53 up. Measured on a Pixel 8a: ~24 tok/s SmolLM2-135M Q8_0 decode versus ~3.8 scalar (6.4x), clearing the on-device usability bar. The provider auto-registers via ServiceLoader; an app just adds the AAR.createRandomAccessSource returned null on Android, forcing every model load through a full-file heap read that exhausted the ART heap on real devices. It now streams via positional FileChannel reads across skainet-io-gguf / -safetensors / -onnx.skainet-backend-native-cpu (-linuxx64 / -linuxarm64, and the path future Apple targets will use) link with no manual setup. A NEON body was also added for the Q4_0 matmul kernel.FileBacked/Aliased transfer path; and a rank-safe default copyToFloatArray.Dim vocabulary makes "dynamic extent" explicit instead of an overloaded -1, and the StableHLO emitter renders it as an MLIR ?. One compiled vmfb now serves every autoregressive decode step with a growing cache, instead of one fixed cache length. Verified end-to-end: the full FunctionGemma with_past decode graph and the Moonshine v2 decoder (dynamic self and cross caches) self-compile from the DSL to a CPU vmfb — graphs that could not be compiled before. Static graphs are emitted byte-for-byte unchanged.KEEP_NATIVE, two bytes per element at rest instead of widening to FP32, and reach format-specific matmul kernels still packed. Narrow floats are a storage width only: kernels widen to f32 lanes and accumulate in f32.VoidTensorOps propagates shapes through a ShapeOnlyTensorData that allocates no backing buffer, so a dynamic extent flows through a whole decode trace instead of throwing on a negative-size allocation.Lstm layer — single-layer, batch-first LSTM built from existing primitives only, with torch.nn.LSTM-compatible gate order and a caller-owned LstmState + step() API.Dropout, mutable optimizer lr plus linearWarmupCosineDecay, bias-less and open Linear.scaledDotProductAttention at its default scale multiplied every score by zero on the CPU backend; it now resolves to 1/sqrt(headDim) as documented.CrossEntropyLoss no longer detaches the tape, and softmax/logSoftmax/variance backward now work for rank ≥ 3.LinearargMaxgather