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.
| 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 |
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.transformers artifacts,
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.63.0"))
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 | Start here |
|---|---|
| Examples and sample projects | SKaiNET-examples |
| Interactive notebooks | SKaiNET-notebook |
| Eager backends & kernels (what runs where) | Backends & kernels map |
| Design proposals and long-lived API decisions | SKEEP proposals |
| Memory & storage architecture (storage, views, scopes, planning) | The memory model · Packed weight layout |
First time here? Three links are all you need:
- 🚀 Getting started as a contributor — the two workflows in one page, and how to claim a task.
- 🟣 Open good first issues — filter further by what you know:
skill:android,skill:numerics(no Kotlin),skill:docs, or by size:size:xs. - 🏷️ Issue taxonomy — what every label and
[Lane N · skill]title prefix means.
Small fixes can go straight through the normal contribution flow described in CONTRIBUTING.md and GITFLOW.adoc.
Non-trivial work goes through one of two processes:
- DARC (Document / Assess / Research / Code) for one feature — a new operator, metric, layer, format reader, or kernel strategy. A feature is one parent issue plus skill-labelled sub-issues ("lanes"). See the DARC workflow.
- SKEEP (SKaiNET Evolution and Enhancement Process) for one architectural
decision — public APIs, DSL syntax, tensor semantics, compiler/runtime
integration, storage behavior, compatibility policy. SKEEP files live under
docs/modules/skeep/pages/with three-digit numbering. See the SKEEP index.
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.shSKaiNET is built around one path: a model is defined once in the Kotlin DSL, then either compiled or executed eagerly — without rewriting it.
- Define the model with the DSL (
nn { }/dag { }). - Capture it as a tape (traced execution) or a DAG (explicit graph) — a
ComputeGraph. - Run it one of two ways:
- Compile — lower the captured
ComputeGraphthrough one of several sibling code-generation backends, each emitting code for a different target from the same graph:- StableHLO / MLIR (
HloGenerator) → IREE-compilable, for native / edge / accelerator targets and the wider MLIR ecosystem. - Arduino / C99 → standalone, statically-allocated C for microcontrollers.
- Minerva → a secure-MCU bundle (weights + firmware skeleton + fingerprinted manifest).
- StableHLO / MLIR (
- Eager — execute directly on an available backend. On the JVM this is the primary, go-to path.
- Compile — lower the captured
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:
- Minerva getting started — run the maintained tiny MLP dry sample, then the real libminerva runtime profile.
- Minerva export how-to — configure compiler paths, keys, calibration, CMake/CTest host verification, and troubleshooting.
- How Minerva secure MCU export fits — understand why Minerva is not an Arduino replacement and when to choose StableHLO instead.
Runnable examples:
./gradlew :skainet-compile:skainet-compile-minerva:runMinervaSecureMcuExamples
./gradlew :skainet-compile:skainet-compile-minerva:runMinervaSecureMcuExamples \
-Pminerva.example=sensor-classifier- Targets: JVM, macOS (Native), JS, WASM (Browser + WasmWasi)
- Single codebase shared across all platforms via Kotlin Multiplatform
- ComputeGraphExecutor: Optimized engine with fusion passes and trace-to-DAG bridging.
- SDPA & Gather: High-performance Scaled Dot-Product Attention and indexing operations.
- TurboQuant: Runtime KV-cache compression (~8x at 4-bit) for long-context LLM inference. Presets:
safe-lowbit,balanced,experimental-max. SeeTurboQuantUsagefor integration guide.
- Sequential:
nn { input(); dense(); relu(); dense() } - DAG / Graph: arbitrary wiring with
dag { }for ResNet-style skip connections and multi-scale detector heads - Layers: Dense, Conv1d/2d/3d, MaxPool, AvgPool, BatchNorm, Dropout, LeakyReLU, ELU
- KAN (Kolmogorov–Arnold Networks) layer (experimental)
- Autograd engine with reverse-mode gradients, SGD and Adam/AdamW optimizers
- Built-in loaders: MNIST, Fashion-MNIST, CIFAR-10, Iris
- URI-backed data sources:
file://,https://,hf+https://, andhf://... - Dataset operations: deterministic shuffle/split, stratified split, filter/map/transform views, batch flows, and epoch flows
- Raw dataset parsers: CSV, TSV, JSON arrays/objects, JSON Lines (
.jsonl,.ndjson) - Type-safe transform DSLs: image/tensor transforms plus suspendable raw data pipelines
- Formats: GGUF, ONNX, SafeTensors, JSON, Image (JPEG, PNG)
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)- Start with the data sources getting started guide
- Export trained models to standalone, optimized C99 with static memory allocation
- Ready-to-use Arduino library output
- Export supported static MLP graphs to Minerva project bundles for secure MCU inference
- Emits compiler NPZ input, libminerva weights, a fingerprinted manifest, host harness, firmware example, and host verification results
- Start with the Minerva getting started guide
- Lower Kotlin DSL to MLIR StableHLO dialect
- Optimization passes: constant folding, operation fusion, dead code elimination
- Valid IREE-compilable output with streaming API and public
HloGenerator
- Use StableHLO when you want portable MLIR/IREE-compatible graphs for native, accelerator, or ecosystem compiler flows.
- Use Arduino / C99 export / Minerva export when you want standalone generated C with static memory allocation or external secure runtime.
Block-quantized weights reach an NPU through StableHLO, and the engine gains mixed precision, explicit re-encoding,
convTranspose2d and a working ONNX import path.
- Q8_0 / Q4_0 weights to an NPU —
PackedConstantHandling.KEEP_PACKEDkeeps a packed weight in the traced graph,BlockQuantMatmulFusionPassfolds everyx · Wᵀover one, and the StableHLO export emits the dequantize-then-batch_matmulchain withi8/i4codes andbf16scales inline. Torq 2.3.0 (--iree-input-type=stablehlo-torq) compiles it to itsmixed_quant_matmulkernel: the whole FunctionGemma 270M decode step runs on an SL2610 NPU in one dispatch at 162 ms per step, with the same next token as the f32 reference. Weights from the GGUF loader (off-heap) export the same as heap ones. - StableHLO and TOSA passes — the
STABLE_HLOphase of a target optimizer rewrites the emitted module, andDivideByBroadcastPass,RankTwoWeightMatmulPassandTosaFinishprepare a module for StableHLO's own TOSA legalization (Moonshine's streaming encoder runs on an NPU throughtosa-torqin one dispatch).Int4maps toi4. - Mixed precision per layer —
dense(64, dtype = BF16) { }andstage("head", dtype = FP16) { }build a layer in another dtype behind aPrecisionBoundarythat casts in and out (inference only). The genericdense<T>()/stage<T>()overloads, which could only throw, are deprecated atERRORlevel. - Re-encoding storages —
repackTo(Dense(4))decodes every packed encoding with a dequantizer bit-exactly, andrequantizeTois the explicit lossy direction. - New ops and layers —
convTranspose2dwith backward and StableHLO lowering, theGroupNormalizationforward pass,sliceCopy {}, and precision, recall and F1 metrics with binary, macro and micro averaging. - ONNX —
LeakyRelu,Silu,Clip,BatchNormalization,PadandConvTransposeimport as executable ops, andOnnxModelReader.openreads metadata up front and each tensor on demand. - Fixes — the JVM tokenizer's
Splitregex follows Unicode character classes, andrandnnever returnsInf/NaN.
See CHANGELOG.md for full release notes, including every prior release.
- Q1 2026: Comprehensive documentation ✅
- Q2 2026: TurboQuant KV-cache compression ✅ (shipped in 0.18.0); Qwen/LLaMA tokenizers ✅ (shipped in 0.20.0)
- Q3 2026: Missing ML features: metrics, optimizers, and training utilities.
- Q4 2026: On-Device AI, small LLMs improvements
We love contributions! Whether it's a new operator, documentation, or a bug fix:
- Read Getting started as a contributor (five minutes), then the Contribution Guide when you need the procedure.
- Pick an open good first issue — every one names the file to copy the pattern from and the exact Gradle task to run. Comment on it to claim it.
- Open a discussion or issue on GitHub; the issue chooser has templates for DARC features, lane tasks and SKEEP proposals.
Browse the full codebase documentation on DeepWiki.
- Michal Harakal (@michalharakal) — the block-quant StableHLO export and its
fusion pass with the Torq board checks, the StableHLO-phase and TOSA preparation passes, mixed precision and
repackTo/requantizeTo(SKEEP-008),convTranspose2d, the ONNX op mappings and streaming reader,GroupNormalization,sliceCopy, the tokenizer andrandnfixes, and the release - Edwin (@EdinEd) — the precision, recall and F1 metrics with the
Averagingstrategy and the shared confusion-matrix accumulator (#1222) - @S0L0-R00T-DEV — the removal of the unused
TensorDefineDslImpl.create()stub (#1471)
- Michal Harakal (@michalharakal) — the Accelerate/BLAS routing of the JVM's dense FP32 products, the AArch64 register tiles for the native dense FP32 kernels with their tests, and the release
- Michal Harakal (@michalharakal) — the buffered GGUF metadata reader, the one-pass tokenizer build, the AVX2/FMA dense FP32 SGEMM lane with their tests and measurements, and the release
- Michal Harakal (@michalharakal) — the schedule-driven and AVX2/FMA
dense FP32 lanes with their tests and measurements, the
CoroutineSchedulefix, the added-token index, and the release
- Michal Harakal (@michalharakal) — the native packed-kernel binding fix and its regression test, the YOLO move-out, the compile-flexibility explanation, the SKEEP-008 draft, and the release
- Michal Harakal (@michalharakal) — the JVM ↔ wasm parity
series: SIMD128 bodies for every sidecar kernel, the ternary and direct
x · Wᵀkernels, resident activations with the elementwise, fused and convolution kernels on both tiers, the browser IO parity,skainet-data-sourceon JS and wasm, the operator coverage matrix, the BitNet decode allocation fixes, and the release - Edwin (@EdinEd) — the
ClassificationSupporthelpers extracted fromAccuracy, preparing the precision, recall and F1 metrics (#1222)
- Michal Harakal (@michalharakal) — SKEEP-007: the Tier 0
dispatch-order and dense-path fixes, quantize-at-load for the SafeTensors and GGUF loaders with the
Q8_0 quantizer and
skainet-quantize, theskainet-backend-wasm-simdsidecar kernel pack, the scheduled SKEEP-006 removal, and the release
- Michal Harakal (@michalharakal) — key-padding lengths
and window metadata for
scaledDotProductAttentionacross the CPU kernel, recorder, executor and StableHLO lowering, the n-ary composite seam with theskainet.sdpacomposite, the SKEEP-006 deprecation step, and the release
- Michal Harakal (@michalharakal) — exact GELU and
erfacross the CPU kernels, graph executor and backward rules, the lowering-metadata path with composite emission and the legalization pass, and the release
- Michal Harakal (@michalharakal) — SKEEP-006: the front-end and model-identity modules, the tokenizer move with Metaspace BPE, Unigram and WordPiece, the golden corpus, and the release
- Michal Harakal (@michalharakal) — masked grouped-query attention in the StableHLO export, the kotlinx-io API fix, the Kotlin 2.4.20 upgrade, and the publish dry run
- Michal Harakal (@michalharakal) — grouped-query-native
scaledDotProductAttentionand its StableHLO lowering, structural schedule defaults, and schedule-aware graph execution contexts (SKEEP-005 phase 2)
- Michal Harakal (@michalharakal) — the billion-parameter
export arc: allocation-free void tracing, aliased constant extraction, strict StableHLO
conversion, the array-free
BufferHandle.Floatspath, and the sharded SafeTensors loader
- Michal Harakal (@michalharakal) — the Gemma 4 engine-gap
arc: self-healing kernel dispatch and the
ViewKernelPackSPI, dense FP32 for mapped/off-heap storage, the decode-shaped FP32 kernel work, Android-native targets across the downstream chain, and the ternary packs joining the self-healing SPI
- Michal Harakal (@michalharakal) — off-heap ternary
storage, zero-copy mmap for
SEQUENTIALI2_S, the AOT GGUF converter and its IREE-facing counterpart in SKaiNET-IREE-tools, and the scoped dense-FP32 activation matmul-chooser fix
- Michal Harakal (@michalharakal) — the SKEEP-003 memory & storage architecture end to end: M0/M1/M2 milestones, the weight-form and placement-resolution arcs, scope-recycled execution, multi-format footprint analysis, the compile-lane carriage arc, the BitNet/ternary kernel track, and the release docs
- Ajith Goveas (@AjithGoveas) — Iris dataset provider (#1044, #1101), now powering the Android classifier tutorial
- Michal Harakal (@michalharakal) — packed-quant
transpose()block-grid correctness fix, all three kernel tiers (#968, #969)
- Michal Harakal (@michalharakal) — off-heap/mmap tensor storage on Android (#921), GGUF
DEQUANTIZE_TO_FP32over-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)
- Michal Harakal (@michalharakal) — primitive FP32 fast paths for the eager CPU ops (#949), README pointer to SKaiNET-transformers (#923)
- Michal Harakal (@michalharakal) — Android JNI NEON kernel backend with runtime dotprod dispatch (#943, #945), Android
createRandomAccessSourcestreaming 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)
- Michal Harakal (@michalharakal) — dynamic tensor dimensions for streaming KV-cache decode (#891), shared narrow-float BF16/FP16 layer (#886), zero-copy transpose for input-major narrow weights (#895), native FP16 matmul kernel (#896), read-once weight tiling in the native narrow kernels (#897)
- @MacOS — least-privilege permissions on the build workflow (#899), MathJax npm install pinned by version (#889)
- Michal Harakal (@michalharakal) —
Lstmlayer (#824),Dropoutmasking (#867), LR schedules (#866), optional/openLinear(#870, #875), SDPA scale fix (#880), autograd fixes (#877),argMaxDAG spec (#878), tokenizer +gatherfixes (#879), Android native IO targets (#836, #842, #845) - @MacOS — OpenSSF Scorecard workflow and badge (#814), commit-hash pinning across all CI workflows and reproducible docs Docker image (#816, #821, #827, #830–#838, #846, #848, #868)
- @MacOS — REUSE compliance CI workflow and status badge (#806, #807)
- Dhia Chemingui (@dhiaspaner) — Android KMP plugin migration (#385, #386)
If you use SKaiNET in your research, please cite it. Citation metadata lives in
CITATION.cff (GitHub's Cite this repository button exports BibTeX and APA
from it). Ready-to-paste BibTeX:
@misc{skainet,
author = {Harakal, Michal and {SKaiNET-developers contributors}},
title = {{SKaiNET}: A {Kotlin} Multiplatform {AI} Framework},
year = {2026},
note = {Version 0.63.0. MIT License},
howpublished = {\url{https://github.com/SKaiNET-developers/SKaiNET}}
}Please include the version you used (each release is tagged).
MIT — see LICENCE.
