Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,25 @@

## [Unreleased]

Headline: **the browser lane stops falling to the reference kernel (SKEEP-007, Tier 0).**
## [0.61.0] - 2026-10-16

Headline: **the browser lane (SKEEP-007): packed SIMD128 kernels, quantize-at-load, and no more
reference-kernel fallbacks on wasm.** Every SKaiNET browser build used to run scalar Kotlin on one
thread and sent canonical GGUF weights to the per-element decoding reference kernel. This release
reorders the packed-weight paths so the scalar packed kernels serve every target (Tier 0), ships the
engine's own C kernels as a SIMD128 sidecar for wasmJs with pthreads on cross-origin-isolated pages
(Tier 1, `skainet-backend-wasm-simd`), and teaches the SafeTensors and GGUF loaders to quantize dense
weights at load (Tier 2), so Hugging Face checkpoints can be served packed on every target. Measured
on Node: Laya's Q8_0 matmuls move from 0.72 GFLOPS to 27 GFLOPS with four threads, one 22-layer
decision from about 29 s to about 1.2 s.

Version note: the SKEEP-006 compatibility aliases scheduled for removal in 0.61 are removed (see
*Removed*). SKaiNET-transformers follows with the Laya consumer of quantize-at-load and the browser
pack.

### Removed

- **The SKEEP-006 compatibility surface**, an error since 0.60 as scheduled: the `sk.ainet.io.tokenizer.*` aliases (`Tokenizer`, `QwenByteLevelBpeTokenizer`, `SentencePieceTokenizer`, `SpecialTokenSplitter`, `TekkenTokenizer`, `ByteToUnicode`), `sk.ainet.io.model.ModelFormat`, `sk.ainet.io.weights.NameMap` / `RoleTableNameMap` / `TransformerNameMaps`, and in `skainet-io-gguf` the `sk.ainet.io.gguf.registry` package (`ModelArchitecture.fromGguf`, `ModelLoaderRegistry`, `ModelLoaderDescriptor`, `LlamaLoaderDescriptor`, `Gemma3nLoaderDescriptor`). The homes are `sk.ainet.frontend.text`, `sk.ainet.frontend.tokenizer` and `sk.ainet.model`; `TokenizerFactory` and `UnsupportedTokenizerException` stay in `sk.ainet.io.tokenizer` for Java callers, and `sk.ainet.io.weights.asWeightNameResolver` stays.

### Added

Expand Down
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ authors:
orcid: https://orcid.org/0009-0006-7981-4075
- name: SKaiNET-developers contributors
website: https://github.com/SKaiNET-developers
version: "0.60.0"
date-released: "2026-10-15"
version: "0.61.0"
date-released: "2026-10-16"
license: MIT
repository-code: https://github.com/SKaiNET-developers/SKaiNET
url: https://skainet-developers.github.io/SKaiNET/
Expand Down
52 changes: 29 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Add the core dependencies (Gradle Kotlin DSL):
```kotlin
dependencies {
// Recommended: import the umbrella BOM and drop versions on the engine modules.
implementation(platform("sk.ainet:skainet-bom:0.60.0"))
implementation(platform("sk.ainet:skainet-bom:0.61.0"))

implementation("sk.ainet.core:skainet-lang-core")
implementation("sk.ainet.core:skainet-backend-cpu")
Expand Down Expand Up @@ -308,27 +308,26 @@ val withoutLabel = dataPipeline<RawDataset>()

---

## What's New in 0.60.0

Key-padding lengths and window metadata for attention (#1343):

- **`scaledDotProductAttention(keyLengths, window, rightContext)`** — padding (a `[batch]`
lengths tensor), the sliding-window band and `causal` are rules over positions defined once in
`AttentionMask` and shared by the CPU kernel, the recorded graph and the StableHLO export. The
CPU kernel computes scores only inside each row's attended key range, so a padded batch equals
the unpadded single passes; the dense additive mask stays the generic fallback.
- **The rules travel as op metadata** — the recorded node carries `maskKind`, `window` and
`rightContext` with the lengths as an operand, so a compiled target regenerates the cheapest
legal form (`iota`/`compare`/`select` on the export) instead of inheriting a dense
`[batch, 1, Sq, Sk]` mask that scales with the batch.
- **`skainet.sdpa` composite** — the composite seam is n-ary (`HloDecomposition` takes every
operand), and `SdpaLowering.spec` emits `stablehlo.composite "skainet.sdpa"` with `scale`,
`causal`, `window`, `rightContext` and `maskKind` attributes when a target's
`OpGranularityPolicy` keeps attention fused, from the same builder as the inline form.
- The SKEEP-006 compatibility aliases (`sk.ainet.io.tokenizer.*`, `sk.ainet.io.model.ModelFormat`,
`sk.ainet.io.weights.NameMap`, the GGUF `ModelLoaderRegistry`) are errors since this release and
are removed in 0.61. Fixed on the way: a batched dense attention mask on the CPU read batch
row 0 for every batch.
## What's New in 0.61.0

The browser lane (SKEEP-007): packed SIMD128 kernels, quantize-at-load, no reference-kernel
fallbacks on wasm.

- **`skainet-backend-wasm-simd`** — the engine's own C kernels compiled by Emscripten into a
SIMD128 sidecar for wasmJs, with pthreads on cross-origin-isolated pages and under Node. One call,
`WasmSimd.install()`, registers the kernel pack and the provider and makes every context built
afterwards use them. Measured on Node: Laya's Q8_0 matmuls from 0.72 GFLOPS (scalar) to 8.5
single-threaded and 27 with four threads — a 22-layer decision from about 29 s to about 1.2 s.
- **Quantize at load** — `WeightForm(RequantizeTo(Q8_0))` on the SafeTensors loaders (and every
encoding with a quantizer on the GGUF loader) turns dense F32/F16/BF16 weight matrices into packed
Q8_0 or Q4_0 at load; `PlannerProfile.quantizeDenseTo` lets a browser or phone profile ask for it;
`skainet-quantize` converts offline. Hugging Face checkpoints can be served packed on every target.
- **The scalar lane is no longer the reference lane** — a canonical packed weight on a target without
a row-major kernel pack is relaid once into kernel feed order and served by the scalar packed
kernels; dense `[out, in]` weights are multiplied in place without a transpose copy; 16-bit
token tables are read row by row. `ByteArrayRandomAccessSource` is the engine's in-memory source.
- The SKEEP-006 compatibility aliases, errors since 0.60, are removed as scheduled; `TokenizerFactory`
stays in `sk.ainet.io.tokenizer` for Java callers.

See [CHANGELOG.md](CHANGELOG.md) for full release notes, including every prior release.

Expand All @@ -354,6 +353,13 @@ We love contributions! Whether it's a new operator, documentation, or a bug fix:

Browse the full codebase documentation on [DeepWiki](https://deepwiki.com/SKaiNET-developers/SKaiNET).

### Contributors (0.61.0)

- **Michal Harakal** ([@michalharakal](https://github.com/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`, the `skainet-backend-wasm-simd` sidecar kernel pack, the
scheduled SKEEP-006 removal, and the release

### Contributors (0.60.0)

- **Michal Harakal** ([@michalharakal](https://github.com/michalharakal)) — key-padding lengths
Expand Down Expand Up @@ -456,7 +462,7 @@ from it). Ready-to-paste BibTeX:
author = {Harakal, Michal and {SKaiNET-developers contributors}},
title = {{SKaiNET}: A {Kotlin} Multiplatform {AI} Framework},
year = {2026},
note = {Version 0.60.0. MIT License},
note = {Version 0.61.0. MIT License},
howpublished = {\url{https://github.com/SKaiNET-developers/SKaiNET}}
}
```
Expand Down
2 changes: 1 addition & 1 deletion docs/antora.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ asciidoc:
framework_name: SKaiNET
# Current SKaiNET release — bump once per release; referenced as
# {skainet_version} in dependency snippets (blocks need subs="attributes+").
skainet_version: 0.60.0
skainet_version: 0.61.0
ksp_version: 2.2.21-2.0.5
dokka_version: 2.1.0
asciidoctorj_version: 3.0.0
4 changes: 2 additions & 2 deletions docs/modules/ROOT/pages/reference/input-frontends.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ own side files and decorated by its model family.
|===

`skainet-io-core` keeps `sk.ainet.io.tokenizer.*`, `sk.ainet.io.model.ModelFormat` and
`sk.ainet.io.weights.NameMap` as deprecated aliases (WARNING in 0.58 and 0.59, ERROR since 0.60,
removed in 0.61), and `TokenizerFactory` as a forwarding object for Java callers.
`sk.ainet.io.weights.NameMap` as deprecated aliases for two releases (WARNING in 0.58 and 0.59, ERROR
in 0.60, removed in 0.61), and keeps `TokenizerFactory` as a forwarding object for Java callers.

== Resolving a checkpoint's tokenizer

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
= Kernel × platform support matrix
:description: Which compute-kernel provider serves each weight format on each KMP target.

Generated from `kernel-support.json` (version `0.60.0`) by `KernelSupportMatrixTest` — registry introspection of the registered `KernelProvider` implementations. Do not edit by hand; run `./gradlew generateKernelMatrix` to refresh.
Generated from `kernel-support.json` (version `0.61.0`) by `KernelSupportMatrixTest` — registry introspection of the registered `KernelProvider` implementations. Do not edit by hand; run `./gradlew generateKernelMatrix` to refresh.

Each cell is the best (highest-priority) provider that serves `Float32 × format` `matmul` on that platform: *native-ffm* (100) → *panama-vector* (50) → *scalar* (0). An empty cell (`—`) means no provider carries a kernel there (the format is dequant-to-FP32 only).

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= AI-NET Operators Reference

Generated from version `0.60.0` on 2026-10-15
Generated from version `0.61.0` on 2026-10-16

== Operators by Modality

Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/reference/ops-status-matrix.adoc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
= Operator Coverage Matrix
:description: Cross-backend status for every operator function in SKaiNET.

Generated from `operators.json` version `0.60.0` on 2026-10-15.
Generated from `operators.json` version `0.61.0` on 2026-10-16.

Rows are `Operator.function` pairs. The `Validated` column shows whether the function's documentation has been DARC-validated by a reviewer (see xref:contributing/darc-workflow.adoc[DARC workflow]). The `Lowering` column lists the compile-path forms the function declares with `@Lowering` (`composite` = a `stablehlo.composite` when a target keeps it fused, `decompose` = StableHLO/CHLO primitives, `native` = a 1:1 target op); `—` means undeclared. Remaining columns are backends that appear in any function's `statusByBackend` map — a missing entry means the backend makes no claim about the function (treat it as "unknown", not "not supported").

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ An Android project with Kotlin. Add the SKaiNET modules:
[source,kotlin]
----
dependencies {
implementation("sk.ainet.core:skainet-lang-core:0.60.0") // tensors, DSL, training
implementation("sk.ainet.core:skainet-backend-cpu:0.60.0") // CPU ops
implementation("sk.ainet.core:skainet-compile-dag:0.60.0") // autograd (training context)
implementation("sk.ainet.core:skainet-data-api:0.60.0") // Dataset / DataBatch
implementation("sk.ainet.core:skainet-data-simple:0.60.0") // embedded Iris
runtimeOnly("sk.ainet.core:skainet-backend-jni-cpu:0.60.0") // NEON kernels (see below)
implementation("sk.ainet.core:skainet-lang-core:0.61.0") // tensors, DSL, training
implementation("sk.ainet.core:skainet-backend-cpu:0.61.0") // CPU ops
implementation("sk.ainet.core:skainet-compile-dag:0.61.0") // autograd (training context)
implementation("sk.ainet.core:skainet-data-api:0.61.0") // Dataset / DataBatch
implementation("sk.ainet.core:skainet-data-simple:0.61.0") // embedded Iris
runtimeOnly("sk.ainet.core:skainet-backend-jni-cpu:0.61.0") // NEON kernels (see below)
}
----

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
:description: SKaiNET proposal for one resolvable input front-end contract (tokenizers first, audio and image features next) keyed on a string-based model identity with an immutable family registry, so that every model type SKaiNET runs gets its preprocessing from the engine instead of re-deriving it downstream.

Status: Implemented +
Implementation: https://github.com/SKaiNET-developers/SKaiNET/pull/1332[#1332] (text lane, identity, resolver; engine 0.58.0), https://github.com/SKaiNET-developers/SKaiNET-transformers/pull/485[SKaiNET-transformers #485] (consumer, 0.58.0); the io-core compatibility aliases moved to `DeprecationLevel.ERROR` in 0.60.0 (https://github.com/SKaiNET-developers/SKaiNET/pull/1350[#1350]) and leave in 0.61. The audio (F4, F6) and image (F5) front ends are follow-up proposals. +
Implementation: https://github.com/SKaiNET-developers/SKaiNET/pull/1332[#1332] (text lane, identity, resolver; engine 0.58.0), https://github.com/SKaiNET-developers/SKaiNET-transformers/pull/485[SKaiNET-transformers #485] (consumer, 0.58.0); the io-core compatibility aliases moved to `DeprecationLevel.ERROR` in 0.60.0 (https://github.com/SKaiNET-developers/SKaiNET/pull/1350[#1350]) and were removed in 0.61.0. The audio (F4, F6) and image (F5) front ends are follow-up proposals. +
Audience: SKaiNET maintainers and contributors; SKaiNET-transformers maintainers (the first consumer, and the repository whose parallel tokenizer stack this proposal retires); SKaiNET-cartridge maintainers (the facade binds front ends to cartridge ports) +
Created: 2026-10-11 +
Tracking issue: https://github.com/SKaiNET-developers/SKaiNET/issues/1331[#1331] (implementation lanes: https://github.com/SKaiNET-developers/SKaiNET/issues/1321[#1321], https://github.com/SKaiNET-developers/SKaiNET-transformers/issues/471[SKaiNET-transformers #471], https://github.com/SKaiNET-developers/SKaiNET-transformers/issues/330[SKaiNET-transformers #330]) +
Expand Down
4 changes: 2 additions & 2 deletions docs/modules/skeep/pages/007-browser-lane.adoc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
= SKEEP-007: The browser lane — packed SIMD kernels and quantize-at-load
:description: SKaiNET proposal for making inference in a browser usable and quick: stop the scalar wasm lane from falling to the decoding reference kernel, serve dense weights without transpose copies, keep token tables narrow, add a SIMD128 kernel pack with pthreads as a sidecar wasm module, and let the SafeTensors loader quantize at load so Hugging Face checkpoints are served packed on every target.

Status: Draft — Tiers 0, 2 and 1 in review +
Implementation: Tier 0 https://github.com/SKaiNET-developers/SKaiNET/pull/1352[#1352]; Tier 2 quantize-at-load https://github.com/SKaiNET-developers/SKaiNET/pull/1353[#1353]; Tier 1 `skainet-backend-wasm-simd` (the pull request stacked on #1353); the consumer work in SKaiNET-transformers follows, see <<_rollout_plan>>. +
Status: Implemented (Tiers 0, 1 and 2 in engine 0.61.0); the resident-activation step, the SKaiNET-transformers consumer and the WebGPU proposal remain open +
Implementation: Tier 0 https://github.com/SKaiNET-developers/SKaiNET/pull/1352[#1352], Tier 2 quantize-at-load https://github.com/SKaiNET-developers/SKaiNET/pull/1353[#1353], Tier 1 `skainet-backend-wasm-simd` https://github.com/SKaiNET-developers/SKaiNET/pull/1354[#1354] — engine 0.61.0; the consumer work in SKaiNET-transformers follows, see <<_rollout_plan>>. +
Audience: SKaiNET maintainers and contributors; SKaiNET-transformers maintainers (Laya and BERT in the browser); SKaiNET-examples maintainers (every web sample runs through this lane) +
Created: 2026-10-16 +
Origin: the SemanticEmojiFilter example ships its 23M-parameter embedding filter to the web but keeps the 322M-parameter Laya verifier on desktop and Android, because a decision in scalar Kotlin/Wasm is estimated at 10–30 s and the FP32 weights at about 2.5 GB resident. Nothing in the repository measures the wasm lane today.
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/skeep/pages/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,6 @@ Every proposal should include:
| Input front ends and model identity — one resolvable preprocessing contract (tokenizers first) keyed on a string-based family registry

| xref:skeep:007-browser-lane.adoc[SKEEP-007]
| Draft
| Implemented
| The browser lane — packed SIMD128 kernels as a sidecar module, quantize-at-load for SafeTensors, and the common-code fixes that keep wasm off the reference kernel
|===
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
GROUP=sk.ainet.core
VERSION_NAME=0.60.0
VERSION_NAME=0.61.0
POM_DESCRIPTION=SKaiNET

POM_URL=https://github.com/SKaiNET-developers/skainet/
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
@file:Suppress("DEPRECATION", "DEPRECATION_ERROR")

package sk.ainet.io.weights

import sk.ainet.lang.tensor.TensorId

/** Moved to `skainet-model-identity` (SKEEP-006); alias an error since 0.60, removed in 0.61. */
@Deprecated("Moved to sk.ainet.model.NameMap (SKEEP-006); the alias is an error since 0.60 and is removed in 0.61.", ReplaceWith("NameMap", "sk.ainet.model.NameMap"), level = DeprecationLevel.ERROR)
public typealias NameMap = sk.ainet.model.NameMap

/** Moved to `skainet-model-identity` (SKEEP-006); alias an error since 0.60, removed in 0.61. */
@Deprecated("Moved to sk.ainet.model.RoleTableNameMap (SKEEP-006); the alias is an error since 0.60 and is removed in 0.61.", ReplaceWith("RoleTableNameMap", "sk.ainet.model.RoleTableNameMap"), level = DeprecationLevel.ERROR)
public typealias RoleTableNameMap = sk.ainet.model.RoleTableNameMap

/**
* This map as a legacy [WeightNameResolver]: `(modulePath, paramName)` in the slash form
* (`model/layers[3]/attn`, `q_proj.weight`) → checkpoint name. An extension since SKEEP-006
Expand Down

This file was deleted.

Loading
Loading