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
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

## [Unreleased]

## [0.54.0] - 2026-09-06

Headline: **every `ExecutionContext` gets a `Schedule` — and the CI run that exercised it found a
real deadlock.** `sk.ainet.context.schedule.Schedule` splits *what* an op computes from *how its
independent chunks spread across cores*: `scaledDotProductAttention` is the first scheduled op,
`parallelChunks` no longer hides a `runBlocking(Dispatchers.Default)` island, and a JVM
`CoroutineSchedule` spreads chunks across a shared pool. Turning it on deadlocked
`skainet-backend-cpu:jvmTest` on CI's 4-vCPU runner for three of the last four `test (jvm)` runs —
not the OOM a first pass assumed, but a `coroutineScope` waiting on children the pool had no thread
left to run. A region is now a shared chunk queue instead: whatever the pool is doing, the caller
can always finish its own region alone. Also in this release: `SafeTensorsParametersLoader`'s
`tensorFilter` reaches parity with the sharded loader, and the `sk.ainet.lang.memory` API drops its
`ExperimentalMemoryApi` opt-in gate now that SKEEP-003's M0–M2 have shipped.

### Added

- **Schedules — the compute-level algorithm/schedule split (SKEEP-005)**
Expand Down Expand Up @@ -40,6 +54,23 @@
complete in 0.49.0. `ExperimentalMemoryApi` is deleted along with every `@OptIn`/
`@ExperimentalMemoryApi` annotation referencing it.

### Fixed

- **`CoroutineSchedule` deadlock when a region is entered from its own pool**
([#1262](https://github.com/SKaiNET-developers/SKaiNET/issues/1262),
[#1264](https://github.com/SKaiNET-developers/SKaiNET/issues/1264),
[#1266](https://github.com/SKaiNET-developers/SKaiNET/pull/1266)): `CoroutineSchedule.forRange`
ran a region as `runBlocking { coroutineScope { launch(Dispatchers.Default) … } }`, and a
`coroutineScope` waits for every child — including ones the pool never got a thread for. Once
every `Dispatchers.Default` worker was itself inside a region (routine on a 4-vCPU CI runner;
never reproduced on a many-core laptop), nobody was left to run the children and the JVM parked
forever — the intermittent `test (jvm)` timeout that #1264's "force fully serial" fix mistook for
an OOM hang. A region is now a shared chunk queue: `tasks - 1` helpers dispatch to the pool, the
caller runs chunk 0 and then drains the queue itself, and waits only for chunks a thread has
already claimed — a caller can always finish its own region alone, whatever the pool is doing.
Contract unchanged (first failure wins, nested regions run inline, writes happen-before return);
public API unchanged.

## [0.53.0] - 2026-09-02

Headline: **the export pipeline emits billion-parameter models.** Tracing a 4.5B-parameter
Expand Down
50 changes: 28 additions & 22 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.53.0"))
implementation(platform("sk.ainet:skainet-bom:0.54.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.53.0

The export pipeline emits billion-parameter models:

- **Full Gemma 3n E2B export** — the DSL → tape → StableHLO path traced a 4.5B-parameter model
into an OOM at a 46 GB heap; the same repro now exports the whole model in under a minute.
Shape-only tracing no longer materializes zero buffers, graph constants alias the live weights
instead of copying them, and ≥2 GiB constants travel as `BufferHandle.Floats` — an aliased
float array — because the tied embedding is exactly one byte over what a JVM byte array holds.
- **Conversion fails loudly** — `StableHloConverter` is strict by default: an unconvertible node
throws `HloConversionException`, an unresolved operand throws `MissingOperandException`, and a
packed weight reaching constant extraction throws `PackedConstantException` instead of silently
becoming a function argument. `ConversionErrorPolicy.LENIENT` restores the old
comment-and-continue behavior for inspection.
- **Sharded SafeTensors in the engine** — `ShardedSafeTensorsParametersLoader` consumes
`model.safetensors.index.json` with the single-file loader's BF16/FP16 policies, a fail-fast
dtype pre-scan, and a `tensorFilter` hook; the per-family hand-rolled loaders downstream can
collapse onto it.
- **Registry gaps the strictness surfaced** — `clamp` and the camelCase `indexSelect` the tracer
actually emits now lower, and `createBasic` registers the neural-net converter like
`createExtended` does.
## What's New in 0.54.0

Structured concurrency lands as a first-class citizen — and the CI run that exercised it found a
real deadlock:

- **`Schedule` on every `ExecutionContext`** (SKEEP-005) — `sk.ainet.context.schedule.Schedule`
splits *what* an op computes from *how its independent chunks spread across cores*.
`scaledDotProductAttention` is the first scheduled op, `parallelChunks` no longer hides a
`runBlocking(Dispatchers.Default)` island, and the JVM `CoroutineSchedule.hardware()` default
spreads chunks across cores while `Schedule.Sequential` keeps every kernel single-threaded.
- **A real deadlock, found by turning scheduling on** — `CoroutineSchedule.forRange`'s region
waited on children the pool had no thread left to run, once every worker was itself inside a
region; it looked like the OOM hang a first CI fix assumed. A region is now a shared chunk
queue, so a caller can always finish its own region alone, whatever the pool is doing.
- **`tensorFilter` on the single-file `SafeTensorsParametersLoader`** — parity with the sharded
loader; lets a family load selectively from a checkpoint that carries tensors the requested
dtype can't accept.
- **`ExperimentalMemoryApi` opt-in gate removed** — SKEEP-003's M0–M2 shipped complete back in
0.49.0, so `Storage`, `Scope`, `Format`, `TensorView`, `WeightForm`, and the rest of
`sk.ainet.lang.memory` no longer need `@OptIn`.

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.54.0)

- **Michal Harakal** ([@michalharakal](https://github.com/michalharakal)) — the SKEEP-005
structured-concurrency Schedule API, the coroutine-pool deadlock it uncovered on CI and its
shared-chunk-queue fix, `SafeTensorsParametersLoader` `tensorFilter` parity, and retiring the
`ExperimentalMemoryApi` opt-in gate now that SKEEP-003 has shipped

### Contributors (0.53.0)

- **Michal Harakal** ([@michalharakal](https://github.com/michalharakal)) — the billion-parameter
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.53.0
skainet_version: 0.54.0
ksp_version: 2.2.21-2.0.5
dokka_version: 2.1.0
asciidoctorj_version: 3.0.0
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.53.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.54.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.53.0` on 2026-09-02
Generated from version `0.54.0` on 2026-09-06

== 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.53.0` on 2026-09-02.
Generated from `operators.json` version `0.54.0` on 2026-09-06.

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]). 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.53.0") // tensors, DSL, training
implementation("sk.ainet.core:skainet-backend-cpu:0.53.0") // CPU ops
implementation("sk.ainet.core:skainet-compile-dag:0.53.0") // autograd (training context)
implementation("sk.ainet.core:skainet-data-api:0.53.0") // Dataset / DataBatch
implementation("sk.ainet.core:skainet-data-simple:0.53.0") // embedded Iris
runtimeOnly("sk.ainet.core:skainet-backend-jni-cpu:0.53.0") // NEON kernels (see below)
implementation("sk.ainet.core:skainet-lang-core:0.54.0") // tensors, DSL, training
implementation("sk.ainet.core:skainet-backend-cpu:0.54.0") // CPU ops
implementation("sk.ainet.core:skainet-compile-dag:0.54.0") // autograd (training context)
implementation("sk.ainet.core:skainet-data-api:0.54.0") // Dataset / DataBatch
implementation("sk.ainet.core:skainet-data-simple:0.54.0") // embedded Iris
runtimeOnly("sk.ainet.core:skainet-backend-jni-cpu:0.54.0") // NEON kernels (see below)
}
----

Expand Down
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.53.0
VERSION_NAME=0.54.0
POM_DESCRIPTION=SKaiNET

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