diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c644f47..9430de31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,19 +2,34 @@ ## [Unreleased] -### Added -- **Composite emission behind `OpGranularityPolicy`** (#1342 part 2): a `LoweringSpec` per fused-able op (`skainet.gelu`, `skainet.erf`) and `ConversionContext.emitComposite` emit `stablehlo.composite "" {composite_attributes, decomposition = @fn, version}` plus one private decomposition function per (op, attributes, type) when a target's policy keeps the op fused; otherwise the same builder emits the decomposition inline. Helper functions go to a module trailer after the entry function so existing text tooling keeps finding `@main` first. First consumer of `OpGranularityPolicy.keepFused`. -- **`GeluApproximationLegalizationPass`** (`skainet-compile-opt`): targets without any erf lowering rewrite `gelu(approximate = false)` to the tanh form explicitly, recording `legalizedFrom` and `maxAbsError = 4.8e-4` on the node and a `GELU_APPROX_LEGALIZED` diagnostic, instead of a silent default. Registered per target through `TargetOptimizers.registerDagPasses`. -- **`@Lowering` annotation** and a *Lowering* column in the generated operator coverage matrix (`composite · decompose · native`); `operators.json` carries the field. +## [0.59.0] - 2026-10-14 + +Headline: **exact GELU and lowering metadata (#1342).** `erf` becomes a first-class op and `gelu` +carries its form (`approximate: Boolean = true`) as op metadata, so DIRECT execution, the compiled +graph and the StableHLO export make the same numeric choice. On the export side a `LoweringSpec` per +fused-able op emits a `stablehlo.composite` when a target keeps the op fused and the inline +decomposition otherwise, an explicit legalization pass replaces the silent exact→tanh substitution, +and `@Lowering` records each op's lowering in the operator matrix. + +Version note: SKaiNET-transformers follows on this release; its Laya adapter replaces the host-side +exact GELU with `gelu(approximate = false)`. The io-core deprecation aliases introduced in 0.58 stay +at WARNING in this release; their schedule moves by one minor (ERROR in 0.60, removed in 0.61) to +match the SKaiNET-transformers schedule. ### Added + - **`TensorOps.erf` and exact GELU** (#1342): `gelu(tensor, approximate: Boolean = true)` keeps the tanh form as the default and selects the exact `0.5·x·(1 + erf(x/√2))` with `approximate = false`, the form BERT-class encoders and the Laya decision models were trained with (the two differ by up to 4.7e-4 near |x| ≈ 2.7). CPU: Abramowitz–Stegun 7.1.26 erf evaluated in double (|err| < 1.5e-7) with a JVM Vector API lane kernel; `GELU(approximate)` module, `network {}` `gelu(approximate)`, Java `TensorJavaOps.gelu(a, approximate)` / `erf(a)` and `SequentialModelBuilder.gelu(approximate)`; exact backward rule and `erfBackward`. - **GELU carries its form as op metadata**: the flag is recorded as `parameters["approximate"]` on the graph node (`GeluOperation`, `ErfOperation`), read by `ComputeGraphExecutor`, the backward rule and the StableHLO converter, so the compiled path cannot silently diverge from eager execution. StableHLO lowers `erf` to `chlo.erf` and exact GELU to `0.5·x·(1 + chlo.erf(x·√½))`; one `GeluLowering` builder emits both forms. +- **Composite emission behind `OpGranularityPolicy`** (#1342 part 2): a `LoweringSpec` per fused-able op (`skainet.gelu`, `skainet.erf`) and `ConversionContext.emitComposite` emit `stablehlo.composite "" {composite_attributes, decomposition = @fn, version}` plus one private decomposition function per (op, attributes, type) when a target's policy keeps the op fused; otherwise the same builder emits the decomposition inline. Helper functions go to a module trailer after the entry function so existing text tooling keeps finding `@main` first. First consumer of `OpGranularityPolicy.keepFused`. +- **`GeluApproximationLegalizationPass`** (`skainet-compile-opt`): targets without any erf lowering rewrite `gelu(approximate = false)` to the tanh form explicitly, recording `legalizedFrom` and `maxAbsError = 4.8e-4` on the node and a `GELU_APPROX_LEGALIZED` diagnostic, instead of a silent default. Registered per target through `TargetOptimizers.registerDagPasses`. +- **`@Lowering` annotation** and a *Lowering* column in the generated operator coverage matrix (`composite · decompose · native`); `operators.json` carries the field. ### Changed + - `TensorOps.gelu` gained the `approximate` parameter: implementors of the interface must update the override (callers are source-compatible through the default). The legacy recording decorator now records `gelu`/`erf` as graph operations instead of delegating them unrecorded. ### Fixed + - `DefaultCpuOps.gelu` printed a literal `${tensor.dtype}` in its unsupported-dtype message. ## [0.58.0] - 2026-10-12 diff --git a/CITATION.cff b/CITATION.cff index 85fe4fa0..4798c4b4 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -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.58.0" -date-released: "2026-10-12" +version: "0.59.0" +date-released: "2026-10-14" license: MIT repository-code: https://github.com/SKaiNET-developers/SKaiNET url: https://skainet-developers.github.io/SKaiNET/ diff --git a/README.md b/README.md index 7dad4e5f..7541539d 100644 --- a/README.md +++ b/README.md @@ -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.58.0")) + implementation(platform("sk.ainet:skainet-bom:0.59.0")) implementation("sk.ainet.core:skainet-lang-core") implementation("sk.ainet.core:skainet-backend-cpu") @@ -308,21 +308,24 @@ val withoutLabel = dataPipeline() --- -## What's New in 0.58.0 - -Input front ends and model identity land as SKEEP-006: - -- **One front-end contract for every input modality** — `skainet-frontend-core` defines - `FrontEnd`, `FrontEndSpec` and the text, audio, image and multimodal contracts; a resolver turns a - checkpoint view into the front ends a model needs. The text lane ships in `skainet-frontend-text`. -- **Tokenizers move out of io-core and cover more files** — Metaspace BPE with byte fallback, - Viterbi Unigram and WordPiece join byte-level BPE, SentencePiece and Tekken. A golden corpus - recorded with the Hugging Face `tokenizers` library guards exact id parity for GPT-2, Qwen2.5, - TinyLlama, T5, BERT, Gemma 4 and the two public Laya tokenizers. -- **Families are identities** — `skainet-model-identity` carries `ModelIdentity`, `CheckpointView` - and an immutable `ModelFamilyRegistry`; the old enum-style detection is deprecated. -- Everything that compiled against 0.57 still compiles: the io-core names are deprecated aliases - that forward to the new homes. +## What's New in 0.59.0 + +Exact GELU and lowering metadata (#1342): + +- **`erf` and the exact GELU** — `gelu(approximate = false)` computes `0.5·x·(1 + erf(x/√2))`, the + form BERT-class encoders and the Laya decision models were trained with, on the CPU scalar kernel + and the JVM Vector API lanes. The default stays the tanh form, byte for byte; the exact backward + rule and `erfBackward` come with it. +- **The form travels as op metadata** — `parameters["approximate"]` sits on the graph node, so the + graph executor, the backward rule and the StableHLO export read the same flag instead of each + picking a default. StableHLO lowers `erf` to `chlo.erf`. +- **Fused ops as `stablehlo.composite`** — a `LoweringSpec` per op (`skainet.gelu`, `skainet.erf`) + emits a composite with a private decomposition function when a target's `OpGranularityPolicy` + keeps the op fused, and the inline decomposition otherwise, both from one builder. An explicit + `GeluApproximationLegalizationPass` records an exact→tanh rewrite on the node instead of + substituting silently, and `@Lowering` adds a *Lowering* column to the operator matrix. +- `TensorOps.gelu` gained a parameter: implementors update the override, callers are + source-compatible. The 0.58 io-core deprecation aliases stay at WARNING (ERROR in 0.60). See [CHANGELOG.md](CHANGELOG.md) for full release notes, including every prior release. @@ -348,6 +351,12 @@ 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.59.0) + +- **Michal Harakal** ([@michalharakal](https://github.com/michalharakal)) — exact GELU and `erf` + across the CPU kernels, graph executor and backward rules, the lowering-metadata path with + composite emission and the legalization pass, and the release + ### Contributors (0.58.0) - **Michal Harakal** ([@michalharakal](https://github.com/michalharakal)) — SKEEP-006: the @@ -437,7 +446,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.58.0. MIT License}, + note = {Version 0.59.0. MIT License}, howpublished = {\url{https://github.com/SKaiNET-developers/SKaiNET}} } ``` diff --git a/docs/antora.yml b/docs/antora.yml index c051ea44..9886acc8 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -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.58.0 + skainet_version: 0.59.0 ksp_version: 2.2.21-2.0.5 dokka_version: 2.1.0 asciidoctorj_version: 3.0.0 diff --git a/docs/modules/ROOT/pages/reference/kernel-support-matrix.adoc b/docs/modules/ROOT/pages/reference/kernel-support-matrix.adoc index a19b179e..7c963814 100644 --- a/docs/modules/ROOT/pages/reference/kernel-support-matrix.adoc +++ b/docs/modules/ROOT/pages/reference/kernel-support-matrix.adoc @@ -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.58.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.59.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). diff --git a/docs/modules/ROOT/pages/reference/operators/generated/index.adoc b/docs/modules/ROOT/pages/reference/operators/generated/index.adoc index c2a05e8f..d7f0c825 100644 --- a/docs/modules/ROOT/pages/reference/operators/generated/index.adoc +++ b/docs/modules/ROOT/pages/reference/operators/generated/index.adoc @@ -1,6 +1,6 @@ = AI-NET Operators Reference -Generated from version `0.58.0` on 2026-10-14 +Generated from version `0.59.0` on 2026-10-14 == Operators by Modality diff --git a/docs/modules/ROOT/pages/reference/ops-status-matrix.adoc b/docs/modules/ROOT/pages/reference/ops-status-matrix.adoc index 05805175..56368ed4 100644 --- a/docs/modules/ROOT/pages/reference/ops-status-matrix.adoc +++ b/docs/modules/ROOT/pages/reference/ops-status-matrix.adoc @@ -1,7 +1,7 @@ = Operator Coverage Matrix :description: Cross-backend status for every operator function in SKaiNET. -Generated from `operators.json` version `0.58.0` on 2026-10-14. +Generated from `operators.json` version `0.59.0` on 2026-10-14. 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"). diff --git a/docs/modules/ROOT/pages/tutorials/android-classifier-getting-started.adoc b/docs/modules/ROOT/pages/tutorials/android-classifier-getting-started.adoc index 81813ea8..795bcebc 100644 --- a/docs/modules/ROOT/pages/tutorials/android-classifier-getting-started.adoc +++ b/docs/modules/ROOT/pages/tutorials/android-classifier-getting-started.adoc @@ -19,12 +19,12 @@ An Android project with Kotlin. Add the SKaiNET modules: [source,kotlin] ---- dependencies { - implementation("sk.ainet.core:skainet-lang-core:0.58.0") // tensors, DSL, training - implementation("sk.ainet.core:skainet-backend-cpu:0.58.0") // CPU ops - implementation("sk.ainet.core:skainet-compile-dag:0.58.0") // autograd (training context) - implementation("sk.ainet.core:skainet-data-api:0.58.0") // Dataset / DataBatch - implementation("sk.ainet.core:skainet-data-simple:0.58.0") // embedded Iris - runtimeOnly("sk.ainet.core:skainet-backend-jni-cpu:0.58.0") // NEON kernels (see below) + implementation("sk.ainet.core:skainet-lang-core:0.59.0") // tensors, DSL, training + implementation("sk.ainet.core:skainet-backend-cpu:0.59.0") // CPU ops + implementation("sk.ainet.core:skainet-compile-dag:0.59.0") // autograd (training context) + implementation("sk.ainet.core:skainet-data-api:0.59.0") // Dataset / DataBatch + implementation("sk.ainet.core:skainet-data-simple:0.59.0") // embedded Iris + runtimeOnly("sk.ainet.core:skainet-backend-jni-cpu:0.59.0") // NEON kernels (see below) } ---- diff --git a/gradle.properties b/gradle.properties index 190f6247..ecc55f67 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ GROUP=sk.ainet.core -VERSION_NAME=0.58.0 +VERSION_NAME=0.59.0 POM_DESCRIPTION=SKaiNET POM_URL=https://github.com/SKaiNET-developers/skainet/