diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8fec037e4..29de43db4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,10 +76,11 @@ jobs: # associates with dumps (#1171 merged a stale dump exactly that way; #1174 repaired it). - name: api-compatibility tasks: apiCheck - # Every NotImplementedError / TODO( in production code is either @NotImplemented - # (so it shows in the ops coverage matrix) or listed in config/stub-allowlist.txt. + # Both guards scan sources only, so they share one leg: stubs are either @NotImplemented + # or in config/stub-allowlist.txt, and downcasts to concrete TensorData classes outside + # the data package (SKEEP-003) are in config/tensor-data-downcast-allowlist.txt. - name: stub-markers - tasks: checkUnmarkedStubs + tasks: checkUnmarkedStubs checkTensorDataDowncasts permissions: contents: read diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ac786a7b..6770b9fc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ ## [Unreleased] +### Added + +- **Guards for SKEEP-003's acceptance criteria** (build, `skainet-backend-cpu`, docs, #1501): `./gradlew checkTensorDataDowncasts` counts the downcasts to concrete `TensorData` classes outside the data package (162 in 21 files today) against `config/tensor-data-downcast-allowlist.txt`, so a new one fails the build and the list can only shrink; it runs in the `stub-markers` CI leg. `ForwardScopeFlatMemoryTest` runs 300 forward passes under a forward scope on the JVM and asserts that direct memory, the live heap and the slab stay flat. `reference/benchmark-baseline.adoc` records the JMH numbers for matmul, elementwise and reductions that a dispatch or storage change is compared against, replacing the baseline deleted in #1106. + +### Changed + +- **SKEEP-003 is back at `Accepted`, with an implementation-status table** (docs, #1500): an audit at 0.64.0 found the new storage model in use and the old layer (`TensorStorage`, `BufferHandle`, `TensorData` downcasts in the ops) still in place, so three of the five acceptance criteria are open. The SKEEP now says which, and what tracks each (#932). SKEEP-003a no longer lists #1148 as open, SKEEP-002's status records the Q4_K / Q6_K mapped staging of #1190 and its device measurement, `memory-model.adoc` drops a limit that #1190 removed, and the `TensorStorage` KDoc stops telling new code to target it. + +### Removed + +- **`LogicalDType`** (`skainet-lang-core`, SKEEP-003 decision 13, #1502): the storage-side dtype enum, deprecated since 0.49.0, is removed together with `DType.toLogicalDType()`, `LogicalDType.fromDType` / `toDType`, the `logicalType` properties and the `LogicalDType` overloads of `TensorStorageFactory.fromRawBytes` / `fromRawBytesOwned` / `fileBacked` and `PackedBlockStorage.toTensorStorage`. `TensorStorage` and `StorageMemoryReport` hold a `DType` as their constructor property `dtype`; replace `logicalType = LogicalDType.FLOAT32` with `dtype = FP32`. `StorageMemoryReport.toString()` prints the dtype's name (`Float32`) where it printed the enum constant. + ## [0.64.0] - 2026-11-11 Headline: **every differentiable op has a backward rule, and mixed precision is finished: it trains, exports without diff --git a/build-logic/convention/src/main/kotlin/CheckTensorDataDowncastsTask.kt b/build-logic/convention/src/main/kotlin/CheckTensorDataDowncastsTask.kt new file mode 100644 index 000000000..04179ab29 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/CheckTensorDataDowncastsTask.kt @@ -0,0 +1,140 @@ +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.* +import java.io.File + +/** + * Guards a SKEEP-003 acceptance criterion: no dispatch site downcasts to a concrete `TensorData` + * class outside the data package itself. Kernels are meant to be chosen from a view's + * `Format(dtype, encoding)`, not from the Kotlin class that happens to hold the bytes. + * + * A downcast is an `is` / `!is` / `as` / `as?` whose target is a class named `…TensorData` + * (the generic base `TensorData` itself does not count) or one of the storage markers in + * [markerTypes]. Production code only; the packages that define the classes are exempt. + * + * The sites that exist today are listed in [allowlistFile] as `path|count|reason`, where `count` + * is the exact number of downcasts in the file. A new downcast fails the build until the entry is + * raised, which a reviewer sees; removing one fails until the entry is lowered, so the list can + * only describe the code as it is. The goal is an empty list. + */ +@CacheableTask +abstract class CheckTensorDataDowncastsTask : DefaultTask() { + + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val sources: ConfigurableFileCollection + + /** Repository root that report and allowlist paths are relative to. */ + @get:Internal + abstract val rootDirectory: DirectoryProperty + + @get:InputFile + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val allowlistFile: RegularFileProperty + + @get:OutputFile + abstract val reportFile: RegularFileProperty + + private val cast = Regex("""(?:!is|\bis|\bas\?|\bas)\s+(?:[A-Za-z_][\w]*\.)*([A-Z]\w*)""") + private val mainSourceDir = Regex("""/src/[A-Za-z0-9]*[Mm]ain/""") + + /** Marker interfaces that stand for a concrete storage class in a dispatch `when`. */ + private val markerTypes = setOf( + "PackedBlockStorage", + "RowDequantSource", + "MemorySegmentBackedData", + "Q4MemorySegmentMarker", + "Q8MemorySegmentMarker", + ) + + /** Packages that define the classes and may inspect them. */ + private val exemptPackages = listOf( + "/sk/ainet/lang/tensor/data/", + "/sk/ainet/lang/tensor/storage/", + "/sk/ainet/lang/memory/", + ) + + @TaskAction + fun check() { + val root = rootDirectory.get().asFile + val allowlist = parseAllowlist(allowlistFile.get().asFile) + val found = mutableMapOf>() + + sources.files + .filter { it.extension == "kt" } + .forEach { file -> + val rel = file.relativeTo(root).invariantSeparatorsPath + if (!mainSourceDir.containsMatchIn("/$rel")) return@forEach + if (exemptPackages.any { it in "/$rel" }) return@forEach + file.readLines().forEachIndexed { index, line -> + val trimmed = line.trimStart() + if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) return@forEachIndexed + val code = stripStrings(line).substringBefore("//") + val hits = cast.findAll(code).count { isConcreteStorageType(it.groupValues[1]) } + repeat(hits) { found.getOrPut(rel) { mutableListOf() } += index + 1 } + } + } + + val problems = mutableListOf() + found.toSortedMap().forEach { (path, hits) -> + val expected = allowlist[path] + when { + expected == null -> hits.forEach { problems += "$path:$it — downcast to a concrete TensorData class" } + expected != hits.size -> problems += "$path — ${hits.size} downcasts, allowlist expects $expected (lines ${hits.joinToString()})" + } + } + allowlist.keys.filter { it !in found }.sorted().forEach { path -> + problems += "$path — allowlisted but has no downcasts any more; remove the entry" + } + + reportFile.get().asFile.apply { parentFile.mkdirs() }.writeText( + found.toSortedMap().entries.joinToString("\n", postfix = "\n") { (path, hits) -> "$path|${hits.size}|${hits.joinToString(",")}" }, + ) + + if (problems.isNotEmpty()) { + throw GradleException( + buildString { + appendLine("Downcasts to concrete TensorData classes outside the data package (SKEEP-003):") + problems.forEach { appendLine(" $it") } + appendLine() + appendLine("Choose the kernel from the view's Format(dtype, encoding) instead of the class. If the") + appendLine("count went down, lower it in ${allowlistFile.get().asFile.relativeTo(root).invariantSeparatorsPath} (path|count|reason).") + }, + ) + } + logger.lifecycle("✅ TensorData downcasts match the allowlist (${found.values.sumOf { it.size }} in ${found.size} files)") + } + + private fun isConcreteStorageType(name: String): Boolean = + name in markerTypes || (name.endsWith("TensorData") && name != "TensorData") + + /** Blank out the contents of `"…"` literals on one line (escape-aware). */ + private fun stripStrings(line: String): String = buildString { + var inString = false + var i = 0 + while (i < line.length) { + val ch = line[i] + when { + inString && ch == '\\' -> i++ + ch == '"' -> { inString = !inString; append(ch) } + !inString -> append(ch) + } + i++ + } + } + + private fun parseAllowlist(file: File): Map = + file.readLines() + .map { it.trim() } + .filter { it.isNotEmpty() && !it.startsWith("#") } + .associate { line -> + val parts = line.split("|").map { it.trim() } + if (parts.size < 3 || parts[2].isEmpty()) { + throw GradleException("Malformed allowlist line (want path|count|reason): $line") + } + parts[0] to (parts[1].toIntOrNull() ?: throw GradleException("Bad count in allowlist line: $line")) + } +} diff --git a/build-logic/convention/src/main/kotlin/DocumentationPlugin.kt b/build-logic/convention/src/main/kotlin/DocumentationPlugin.kt index b2fbd42f4..e7bef0eb1 100644 --- a/build-logic/convention/src/main/kotlin/DocumentationPlugin.kt +++ b/build-logic/convention/src/main/kotlin/DocumentationPlugin.kt @@ -57,6 +57,22 @@ class DocumentationPlugin : Plugin { } }) + // Guard (SKEEP-003): ops choose kernels from a view's Format, not by downcasting to a + // concrete TensorData class. Today's sites are allowlisted; the list may only shrink. + project.tasks.register("checkTensorDataDowncasts", CheckTensorDataDowncastsTask::class.java, object : Action { + override fun execute(task: CheckTensorDataDowncastsTask) { + task.group = "verification" + task.description = "Fail on downcasts to concrete TensorData classes that are not in the allowlist" + val root = project.rootProject.layout.projectDirectory + project.rootProject.allprojects.forEach { p -> + task.sources.from(p.fileTree("src") { include("**/*.kt") }) + } + task.rootDirectory.set(root) + task.allowlistFile.set(root.file("config/tensor-data-downcast-allowlist.txt")) + task.reportFile.set(project.layout.buildDirectory.file("reports/downcasts/tensor-data-downcasts.txt")) + } + }) + // Register schema validation task in plugin (migrated from skainet-lang-export-ops) val validateTaskProvider = project.tasks.register("validateOperatorSchema", SchemaValidationTask::class.java, object : Action { override fun execute(task: SchemaValidationTask) { diff --git a/config/tensor-data-downcast-allowlist.txt b/config/tensor-data-downcast-allowlist.txt new file mode 100644 index 000000000..cfc51ac5e --- /dev/null +++ b/config/tensor-data-downcast-allowlist.txt @@ -0,0 +1,34 @@ +# Downcasts to concrete TensorData classes outside the data package. Checked by +# `./gradlew checkTensorDataDowncasts`. +# +# SKEEP-003 acceptance criterion: no dispatch site downcasts to a concrete `TensorData` class +# outside the storage/data package itself; a kernel is chosen from the view's +# Format(dtype, encoding). These are the sites that still do. The goal is an empty file. +# +# Format: path|count|issue or reason +# count = exact number of `is` / `!is` / `as` / `as?` whose target is a `…TensorData` class +# or a storage marker (PackedBlockStorage, RowDequantSource, MemorySegmentBackedData, +# Q4/Q8MemorySegmentMarker). Removing a downcast means lowering the count (or deleting the +# line). Do not raise a count: dispatch on the format instead. + +skainet-apps/skainet-grayscale-cli/src/main/kotlin/sk/ainet/apps/grayscale/HloCompiler.kt|1|#1507 +skainet-apps/skainet-grayscale-cli/src/main/kotlin/sk/ainet/apps/grayscale/TensorConversionPipeline.kt|1|#1507 +skainet-apps/skainet-tensor-tools/src/main/kotlin/sk/ainet/apps/tool/quantize/QuantizeCli.kt|3|#1507 +skainet-backends/skainet-backend-cpu/src/appleMain/kotlin/sk/ainet/exec/tensor/ops/AccelerateCpuOps.kt|18|#1507 +skainet-backends/skainet-backend-cpu/src/commonMain/kotlin/sk/ainet/exec/tensor/ops/DefaultCpuOps.kt|51|#1507 +skainet-backends/skainet-backend-cpu/src/jvmMain/kotlin/sk/ainet/exec/tensor/ops/DefaultCpuOpsJvm.kt|48|#1507 +skainet-backends/skainet-backend-wasm-simd/src/wasmJsMain/kotlin/sk/ainet/exec/wasm/WasmCpuOps.kt|10|#1507 +skainet-compile/skainet-compile-dag/src/commonMain/kotlin/sk/ainet/lang/trace/TraceToGraphBuilder.kt|3|#1507 +skainet-compile/skainet-compile-hlo/src/commonMain/kotlin/sk/ainet/compile/hlo/converters/BlockQuantMatmulConverter.kt|1|#1507 +skainet-compile/skainet-compile-hlo/src/commonMain/kotlin/sk/ainet/compile/hlo/converters/ConstantOperationsConverter.kt|1|#1507 +skainet-compile/skainet-compile-opt/src/commonMain/kotlin/sk/ainet/compile/opt/passes/BlockQuantMatmulFusionPass.kt|1|#1507 +skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/StreamingGgufParametersLoader.kt|2|#1507 +skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/export/GGUFWriter.kt|2|#1507 +skainet-io/skainet-io-safetensors/src/commonMain/kotlin/sk/ainet/io/safetensors/SafeTensorsMaterializer.kt|1|#1507 +skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/IndexTensor.kt|1|#1507 +skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/ops/QuantizedMatmul.kt|9|#1507 +skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/ops/TensorSpecEncoding.kt|1|#1507 +skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/ops/TernaryMatmul.kt|3|#1507 +skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/trace/TraceSession.kt|1|#1507 +skainet-lang/skainet-lang-core/src/jvmMain/kotlin/sk/ainet/lang/tensor/StorageBenchmarks.kt|3|#1507 +skainet-test/skainet-test-groundtruth/src/commonMain/kotlin/sk/ainet/test/groundtruth/TensorAssertions.kt|1|#1507 diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index aabdb2bef..7b22053bf 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -30,6 +30,7 @@ ** xref:reference/operators/generated/index.adoc[Operator reference] ** xref:reference/ops-status-matrix.adoc[Operator coverage matrix] ** xref:reference/kernel-support-matrix.adoc[Kernel × platform support] +** xref:reference/benchmark-baseline.adoc[Benchmark baseline] ** xref:reference/api.adoc[API reference (Dokka)] * Explanation ** xref:explanation/skainet-for-ai.adoc[SKaiNET for AI/ML] diff --git a/docs/modules/ROOT/pages/explanation/memory-model.adoc b/docs/modules/ROOT/pages/explanation/memory-model.adoc index 8aa0e8d73..c6b72d028 100644 --- a/docs/modules/ROOT/pages/explanation/memory-model.adoc +++ b/docs/modules/ROOT/pages/explanation/memory-model.adoc @@ -204,7 +204,7 @@ ARMv8.2 Cortex-A55 reference board. [cols="2,3",options="header"] |=== | Delivered | Notes -| `Storage` / `Scope` / `TensorView` / `Format` / `Layout` | All targets; `TensorData` implementations are façades over views +| `Storage` / `Scope` / `TensorView` / `Format` / `Layout` | All targets; every `TensorData` family used by the kernels exposes a view. Scoped activations (`StorageFloatTensorData`) sit on a `Storage`; the other classes still own their bytes and wrap the view around them (xref:skeep:003-unified-tensor-storage.adoc[SKEEP-003], implementation status) | `KernelKey` dispatch for matmul | Reference kernel plus packed and ternary kernel packs | Ternary encodings and `bitnet_gemv` | `TQ1_0`, `TQ2_0`, BitNet b1.58; reference kernel plus a NEON pack on Android | Memory plan, device profiles, fit check | `skainet-plan`, mobile/desktop/native profiles, two-pool device check @@ -247,9 +247,10 @@ anything is loaded rather than after an OOM. Known limits, stated rather than implied: -* *Packed weights still reach the managed heap under mapped staging.* The packed matmul SPI takes -`ByteArray`s, so a buffer-aware kernel is future work. Mapping therefore lifts the Android heap -ceiling for dense checkpoints, not yet for a Q4_K_M one. +* *Some packed weights still reach the managed heap under mapped staging.* Q4_K and Q6_K weights +stay in the file mapping (`BufferPackedTensorData`, read by row-major kernels through a direct +buffer, #1190), so a Q4_K_M checkpoint larger than the Android heap loads. The other packed +formats are still handed to their kernels as `ByteArray`s. * *Whole-file mapping only.* Files larger than 2 GB are refused rather than mapped in windows. * *Graph-level planning* (allocating a whole forward pass at once) stays in the downstream compiler by decision (#1134). Device placement itself was resolved in 0.49.0: `AllocationResolver` diff --git a/docs/modules/ROOT/pages/reference/benchmark-baseline.adoc b/docs/modules/ROOT/pages/reference/benchmark-baseline.adoc new file mode 100644 index 000000000..c46aba3dd --- /dev/null +++ b/docs/modules/ROOT/pages/reference/benchmark-baseline.adoc @@ -0,0 +1,77 @@ += Benchmark baseline +:description: Reference JMH numbers for the matmul, elementwise and reduction hot paths, to compare against when a change touches dispatch or storage. + +These are the numbers a change to kernel dispatch or tensor storage is compared against. +xref:skeep:003-unified-tensor-storage.adoc[SKEEP-003] sets the budget: matmul within noise, +elementwise at most 3 % slower. + +== How to compare + +. Run the suite on the commit before your change and on the commit with it, on the same machine, + with nothing else building: ++ +[source,bash] +---- +./gradlew :skainet-backends:benchmarks:jvm-cpu-jmh:jmh \ + -PjmhIncludes='sk.ainet.bench.(MatmulBench|KernelMatmulBench|QuantizedMatmulBench|ElementwiseAdd1MBench|Reductions1MBench).*' +---- ++ +The results are in `skainet-backends/benchmarks/jvm-cpu-jmh/build/results/jmh/results.txt`. +`scripts/pr-gate.sh --bench` runs the whole suite. +. Put both tables in the pull request. The table below tells you what the numbers looked like on + the reference machine; numbers from another machine are only comparable with each other. +. When a change moves a number on purpose, record a new baseline here in the same pull request. + +Not part of this baseline: `:skainet-lang:skainet-lang-core:jvmBenchmark` (`StorageBenchmarks`, +`TensorBenchmarks`, `TurboQuantBenchmarks`). With the module's default iteration settings a full +run takes hours; run it filtered per class when a change touches the storage hot paths. + +== Baseline of 2026-11-11 + +* Commit: `edadc84b` (`develop`, 0.64.0) +* Machine: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz, 12 threads, Linux 6.8.0-60, OpenJDK 21.0.13 +* Settings: JMH, fork 1, 3 warm-up and 5 measurement iterations, + `--enable-preview --add-modules jdk.incubator.vector` + +[cols="3,3,4,1,2,2,1",options="header"] +|=== +| Benchmark | Class | Parameters | Mode | Score | ± | Unit + +| `add_1M_fp32` | ElementwiseAdd1MBench | vectorEnabled=true | thrpt | 2.061 | 0.023 | ops/ms +| `add_1M_fp32` | ElementwiseAdd1MBench | vectorEnabled=false | thrpt | 1.195 | 0.019 | ops/ms +| `mean_1M_fp32` | Reductions1MBench | vectorEnabled=true | thrpt | 1.027 | 0.004 | ops/ms +| `mean_1M_fp32` | Reductions1MBench | vectorEnabled=false | thrpt | 1.027 | 0.003 | ops/ms +| `sum_1M_fp32` | Reductions1MBench | vectorEnabled=true | thrpt | 1.027 | 0.006 | ops/ms +| `sum_1M_fp32` | Reductions1MBench | vectorEnabled=false | thrpt | 1.030 | 0.005 | ops/ms +| `matmul_fp32_square` | KernelMatmulBench | provider=scalar, size=256 | avgt | 22.468 | 0.182 | ms/op +| `matmul_fp32_square` | KernelMatmulBench | provider=scalar, size=512 | avgt | 292.669 | 0.428 | ms/op +| `matmul_fp32_square` | KernelMatmulBench | provider=scalar, size=1024 | avgt | 1869.206 | 13.642 | ms/op +| `matmul_fp32_square` | KernelMatmulBench | provider=panama, size=256 | avgt | 1.257 | 0.014 | ms/op +| `matmul_fp32_square` | KernelMatmulBench | provider=panama, size=512 | avgt | 9.881 | 0.144 | ms/op +| `matmul_fp32_square` | KernelMatmulBench | provider=panama, size=1024 | avgt | 87.219 | 1.669 | ms/op +| `matmul_fp32_square` | MatmulBench | blasEnabled=true, size=256, vectorEnabled=true | avgt | 8.548 | 0.072 | ms/op +| `matmul_fp32_square` | MatmulBench | blasEnabled=true, size=256, vectorEnabled=false | avgt | 7.187 | 0.129 | ms/op +| `matmul_fp32_square` | MatmulBench | blasEnabled=true, size=512, vectorEnabled=true | avgt | 67.587 | 0.494 | ms/op +| `matmul_fp32_square` | MatmulBench | blasEnabled=true, size=512, vectorEnabled=false | avgt | 62.912 | 0.486 | ms/op +| `matmul_fp32_square` | MatmulBench | blasEnabled=true, size=1024, vectorEnabled=true | avgt | 544.217 | 6.110 | ms/op +| `matmul_fp32_square` | MatmulBench | blasEnabled=true, size=1024, vectorEnabled=false | avgt | 480.891 | 2.660 | ms/op +| `matmul_fp32_square` | MatmulBench | blasEnabled=false, size=256, vectorEnabled=true | avgt | 1.269 | 0.015 | ms/op +| `matmul_fp32_square` | MatmulBench | blasEnabled=false, size=256, vectorEnabled=false | avgt | 7.080 | 0.047 | ms/op +| `matmul_fp32_square` | MatmulBench | blasEnabled=false, size=512, vectorEnabled=true | avgt | 10.382 | 0.181 | ms/op +| `matmul_fp32_square` | MatmulBench | blasEnabled=false, size=512, vectorEnabled=false | avgt | 59.932 | 1.181 | ms/op +| `matmul_fp32_square` | MatmulBench | blasEnabled=false, size=1024, vectorEnabled=true | avgt | 87.704 | 1.994 | ms/op +| `matmul_fp32_square` | MatmulBench | blasEnabled=false, size=1024, vectorEnabled=false | avgt | 480.785 | 6.122 | ms/op +| `matmul_q4k_panama` | QuantizedMatmulBench | shape=1024-1024 | avgt | 0.110 | 0.001 | ms/op +| `matmul_q4k_panama` | QuantizedMatmulBench | shape=4096-1024 | avgt | 0.365 | 0.002 | ms/op +| `matmul_q4k_panama` | QuantizedMatmulBench | shape=4096-4096 | avgt | 1.388 | 0.020 | ms/op +|=== + +`thrpt` is throughput, higher is better; `avgt` is the average time per call, lower is better. + +One thing the table shows that is not a storage matter: on this Linux machine `MatmulBench` with +`blasEnabled=true` is about seven times slower than the Panama kernel at every size +(8.5 ms against 1.3 ms at 256). BLAS routing is off by default outside macOS. + +The baseline recorded on 2026-08-22, before the storage migration, was removed with +`docs/design/` (#1106); it is in the history at commit `3c0ee71d`. It was taken on the same +machine with OpenJDK 25, so the two are not directly comparable. diff --git a/docs/modules/skeep/pages/002-android-offheap-tensor-storage.adoc b/docs/modules/skeep/pages/002-android-offheap-tensor-storage.adoc index 40f447733..44cd59885 100644 --- a/docs/modules/skeep/pages/002-android-offheap-tensor-storage.adoc +++ b/docs/modules/skeep/pages/002-android-offheap-tensor-storage.adoc @@ -232,14 +232,13 @@ keeps working unchanged and the overload is purely opt-in. Expected consumers: (packed quant blocks) and a synthesized SafeTensors file (dense F32/F16/BF16) produce bit-identical tensors under either placement. -== Implementation status (updated 2026-08-26, for 0.49.0) +== Implementation status (updated 2026-11-11, at 0.64.0) Tracked by SKEEP-003's M2 slice https://github.com/SKaiNET-developers/SKaiNET/issues/1038[#1038]. This SKEEP stays *Draft*: the mechanism is in `develop` (and since 0.49.0 the loader speaks one `WeightForm` — the legacy -`StagingPolicy`/`QuantPolicy`/`WeightOrientation` axes were removed in #1159), the acceptance -criteria that need a physical device are not met yet, and one of them cannot be met until an -unrelated contract is fixed. +`StagingPolicy`/`QuantPolicy`/`WeightOrientation` axes were removed in #1159), one acceptance +criterion has a device measurement behind it, and the others are not recorded. Landed: @@ -257,20 +256,22 @@ Landed: `AndroidGguf.fits(context, path, ctx)` answers from the GGUF header before a byte of payload is read, naming the pool that runs out and what to do about it. +* *Phase 4 for Q4_K and Q6_K* — `BufferPackedTensorData` keeps a packed weight in the file + mapping, and row-major kernels read it through a direct `ByteBuffer` + (https://github.com/SKaiNET-developers/SKaiNET/issues/1189[#1189], PR #1190). Measured on a Pixel 8a with the 256 MB heap cap + (https://github.com/SKaiNET-developers/SKaiNET/issues/1130[#1130]): Qwen2.5-1.5B Q4_K_M, 1.0 GB on disk, loads with 566 KB of + weights on the managed heap and decodes at 153 ms per step; before, it ran out of memory at + load. That is the acceptance criterion "a Q4_K model larger than the heap loads and generates". + Not landed, and why: -* *Packed weights still reach the managed heap.* Mapped staging serves dense F32 tensors as - zero-heap views, but every quantized tensor is still handed to its kernel as a `ByteArray`, - because the packed matmul SPI takes arrays. That is Phase 4, and it is blocked on - https://github.com/SKaiNET-developers/SKaiNET/issues/973[#973] — the packed-quant byte-order - contract — since a buffer-aware kernel first needs an unambiguous answer to *which* byte order it - is reading. Until then the heap ceiling is lifted for dense checkpoints, not for a Q4_K_M one, - which is exactly the acceptance criterion below that remains open. -* *The device numbers.* "≤ 40 MB managed heap for SmolLM2-135M Q8_0" and "a ~600 MB Q4_K model - loads on a 256 MB heap" are measurements on a physical device; the repository's CI has none. They - belong to the `skainet-decode` sample in SKaiNET-transformers, which owns a model and a device - lane. `androidHostTest` covers what can be proven without hardware: the Android compilation - builds, maps, loads, and produces bit-identical tensors under either staging. +* *Other packed formats still reach the managed heap.* Q8_0, Q4_0, Q5_0, Q5_1, Q5_K and the + ternary encodings are handed to their kernels as `ByteArray`s under mapped staging; only Q4_K + and Q6_K have the buffer-reading kernels. +* *The SmolLM2-135M Q8_0 heap figure and the mapped-versus-heap throughput comparison* are not + recorded. Q8_0 is one of the formats above, so the "less than 40 MB" criterion cannot hold yet. +* *Whole-file mapping only.* A file larger than 2 GB is refused instead of being mapped in + windows (https://github.com/SKaiNET-developers/SKaiNET/issues/1422[#1422] asks for the same in the IRPA writer). == Risks diff --git a/docs/modules/skeep/pages/003-unified-tensor-storage.adoc b/docs/modules/skeep/pages/003-unified-tensor-storage.adoc index 86a75a0c9..09d7bc470 100644 --- a/docs/modules/skeep/pages/003-unified-tensor-storage.adoc +++ b/docs/modules/skeep/pages/003-unified-tensor-storage.adoc @@ -1,11 +1,64 @@ = SKEEP-003: Unifying the tensor storage model — one byte-owner, enforced ownership, coherent dtype/encoding :description: SKaiNET proposal to converge the TensorData and TensorStorage layers into one storage model with real ownership, a single view mechanism, and dtype/encoding coherence. -Status: Implemented (accepted 2026-08-22; M0/M1/M2 + the P7/P8 resolution shipped in 0.49.0) + +Status: Accepted (2026-08-22; M0/M1/M2 and the placement resolution shipped in 0.49.0; the removal of the old layer is open, see <>) + Audience: SKaiNET maintainers and contributors + Created: 2026-08-10 + Tracking issue: https://github.com/SKaiNET-developers/SKaiNET/issues/932[#932] (umbrella) → https://github.com/SKaiNET-developers/SKaiNET/issues/1001[#1001 M0] · https://github.com/SKaiNET-developers/SKaiNET/issues/1002[#1002 M1] · https://github.com/SKaiNET-developers/SKaiNET/issues/1003[#1003 M2] + -Implemented: the model as built is documented in xref:ROOT:explanation/memory-model.adoc[The memory model] and xref:ROOT:explanation/packed-weight-layout.adoc[Packed weight layout]; the proposal and milestone PRD that led there are in the pull requests and issues linked below (#932, #1001, #1002, #1003). +As built: the model is documented in xref:ROOT:explanation/memory-model.adoc[The memory model] and xref:ROOT:explanation/packed-weight-layout.adoc[Packed weight layout]; the proposal and milestone PRD that led there are in the pull requests and issues linked below (#932, #1001, #1002, #1003). + +[#implementation-status] +== Implementation status (audited 2026-11-11, at 0.64.0) + +The model is built and in use on every target: `Storage`, `TensorView`, `Format`, `Scope`, the +memory plan, the tracer, mapped staging. The old layer it was meant to replace is still in the +code, so this proposal is back at `Accepted` until the open rows below are closed. The umbrella +https://github.com/SKaiNET-developers/SKaiNET/issues/932[#932] lists the work. + +[cols="3,1,3",options="header"] +|=== +| Acceptance criterion | State | Where it stands + +| One abstraction owns bytes, ownership enforced +| Partly +| Enforcement is tested (`StorageTest`, `ScopeTest`, `TensorViewTest`, `JvmStorageTest`). `TensorStorage` and `BufferHandle` still exist beside `Storage`, and the streaming readers' `loadTensorStorage*` return the old type (https://github.com/SKaiNET-developers/SKaiNET/issues/1502[#1502], https://github.com/SKaiNET-developers/SKaiNET/issues/1507[#1507]). + +| One view mechanism +| Partly +| `OneViewMechanismTest` covers slice, transpose and unsqueeze on the new view. The public `slice()` family still returns the deprecated `SlicedTensorView` (https://github.com/SKaiNET-developers/SKaiNET/issues/1502[#1502]). + +| A packed tensor reports FP32 and its encoding; no dispatch site downcasts to a concrete `TensorData` class outside the data package +| Not met +| The first half holds (`FormatTest`). About 130 downcasts remain in the CPU, Accelerate and wasm ops, and matmul reaches `KernelKey` dispatch only as its last fallback (https://github.com/SKaiNET-developers/SKaiNET/issues/1501[#1501] for the guard, https://github.com/SKaiNET-developers/SKaiNET/issues/1507[#1507] for the migration). + +| No kernel regression against a baseline +| Partly +| The benchmarks exist; the recorded baseline was deleted with `docs/design/` in #1106 (https://github.com/SKaiNET-developers/SKaiNET/issues/1501[#1501]). + +| Flat direct memory under a forward scope on the JVM +| No test +| Slab reuse is tested (`ScopedOpOutputsTest`), direct memory only under the GC arena (`MemSegArenaLeakTest`) (https://github.com/SKaiNET-developers/SKaiNET/issues/1501[#1501]). +|=== + +Decisions 1, 2 (naming and package), 6, 9, 11, 12 and 13 are delivered; `LogicalDType` is +deprecated and its removal is https://github.com/SKaiNET-developers/SKaiNET/issues/1502[#1502]. The rest: + +[cols="1,4,3",options="header"] +|=== +| # | Open part | Tracked by + +| 1 | Façades: only `StorageFloatTensorData` sits on a `Storage`; the other `TensorData` classes own their bytes and wrap a view around them on each read | https://github.com/SKaiNET-developers/SKaiNET/issues/1507[#1507] +| 2 | `sk.ainet.lang.graph.Layout` is not deprecated; `BlockOrderLayout` implements it now | SKEEP-004, which owns the compile-side layout +| 3 | `get()` on a packed view decodes; the debug-mode warning on hot paths does not exist | https://github.com/SKaiNET-developers/SKaiNET/issues/1503[#1503] +| 4 | The `(head, tail)` pair and its gather fallback exist (`WindowedKV`); only the reference attention consumes them | https://github.com/SKaiNET-developers/SKaiNET/issues/1504[#1504], outside this SKEEP +| 5 | `BlockSpec.activation` drives the eager dispatcher; the StableHLO export does not read it | https://github.com/SKaiNET-developers/SKaiNET/issues/1505[#1505], outside this SKEEP +| 7 | `SuspendingRandomAccessSource` exists in `skainet-io-core`; there is no remote module | https://github.com/SKaiNET-developers/SKaiNET/issues/1506[#1506], outside this SKEEP +| 8 | Axis labels on `Shape` | https://github.com/SKaiNET-developers/SKaiNET/issues/1503[#1503] +| 10 | Activation ids exist when tracing, not in eager debug mode | https://github.com/SKaiNET-developers/SKaiNET/issues/1503[#1503] +|=== + +Compiled parity (P7 below) was closed as superseded: IREE validation lives in the IREE-tools and +conformity repositories (#1148), and this repository has no eager-versus-compiled parity test. == Decision (2026-08-22) diff --git a/docs/modules/skeep/pages/003a-placement-and-planning-resolution.adoc b/docs/modules/skeep/pages/003a-placement-and-planning-resolution.adoc index 082c289de..9ef9a4e44 100644 --- a/docs/modules/skeep/pages/003a-placement-and-planning-resolution.adoc +++ b/docs/modules/skeep/pages/003a-placement-and-planning-resolution.adoc @@ -98,9 +98,9 @@ The carry work landed as the #1178/#1179/#1180 arc (0.49.0): `TensorRef` carries encoding and block order; `TensorSpec` metadata accessors follow the untyped precedent; the structural `skainet.tensor_layouts` module attribute and `ExternalParameterRef.blockOrder` declare the facts to the consumer; and `LayoutAssignmentPass` runs on the production path via -`HloGenerator.generate(target = …)`, populating the pre-built `ResolvedComputeGraph` seams. Until -the conformity pipeline exists (#1148, the future IREE-validation SKEEP), the carriage tests are -the metadata's reader. +`HloGenerator.generate(target = …)`, populating the pre-built `ResolvedComputeGraph` seams. The +conformity pipeline that reads the metadata lives outside this repository (#1148 was closed as +superseded); here, the carriage tests are the metadata's reader. == Where everything landed @@ -113,7 +113,7 @@ the metadata's reader. | `memoryScope` wired into creation | #1145 | PR #1156 | Op outputs through the scope + offset-aware SIMD | #1146, #1173 | landed (PRs #1168/#1175) | Layout/placement carriage to StableHLO | #1147 (#1178/#1179/#1180) | landed (PRs #1182/#1183/#1184) -| `.vmfb` parity acceptance run | #1148 | open (follow-up) +| `.vmfb` parity acceptance run | #1148 | closed as superseded 2026-09-02: IREE validation lives in the IREE-tools and conformity repositories; no eager-versus-compiled parity test in this repository |=== The SKEEP-003 items "`StorageSpec`, `MemoryPlanner` and the annotations are deleted unless wired" diff --git a/docs/modules/skeep/pages/index.adoc b/docs/modules/skeep/pages/index.adoc index 1083211bb..9c2f381d4 100644 --- a/docs/modules/skeep/pages/index.adoc +++ b/docs/modules/skeep/pages/index.adoc @@ -116,12 +116,12 @@ Every proposal should include: | Off-heap tensor storage on Android | xref:skeep:003-unified-tensor-storage.adoc[SKEEP-003] -| Implemented +| Accepted | Unifying the tensor storage model | xref:skeep:003a-placement-and-planning-resolution.adoc[SKEEP-003a] -| Resolved -| Placement, eager lifetime and graph-level planning — the P7/P8 answers +| Implemented +| Addendum to SKEEP-003: placement, eager lifetime and graph-level planning | xref:skeep:004-virtual-tensor-layout.adoc[SKEEP-004] | Draft diff --git a/scripts/pr-gate.sh b/scripts/pr-gate.sh index 1ddd5d0bf..2dc8463ea 100755 --- a/scripts/pr-gate.sh +++ b/scripts/pr-gate.sh @@ -68,7 +68,7 @@ step "Android host tests" "${GRADLE[@]}" testAndroidHostTest if [[ "$mode" == "--bench" ]]; then - step "benchmarks (compare against the committed baseline before/after)" + step "benchmarks (compare with docs/modules/ROOT/pages/reference/benchmark-baseline.adoc)" "${GRADLE[@]}" :skainet-lang:skainet-lang-core:jvmBenchmark "${GRADLE[@]}" :skainet-backends:benchmarks:jvm-cpu-jmh:jmh fi diff --git a/skainet-backends/skainet-backend-cpu/src/jvmTest/kotlin/sk/ainet/exec/tensor/ops/ForwardScopeFlatMemoryTest.kt b/skainet-backends/skainet-backend-cpu/src/jvmTest/kotlin/sk/ainet/exec/tensor/ops/ForwardScopeFlatMemoryTest.kt new file mode 100644 index 000000000..3f8c5a554 --- /dev/null +++ b/skainet-backends/skainet-backend-cpu/src/jvmTest/kotlin/sk/ainet/exec/tensor/ops/ForwardScopeFlatMemoryTest.kt @@ -0,0 +1,109 @@ +package sk.ainet.exec.tensor.ops + +import java.lang.management.BufferPoolMXBean +import java.lang.management.ManagementFactory +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import sk.ainet.context.DirectCpuExecutionContext +import sk.ainet.context.ExecutionContext +import sk.ainet.context.forwardScope +import sk.ainet.lang.memory.MemoryProbe +import sk.ainet.lang.tensor.Shape +import sk.ainet.lang.tensor.Tensor +import sk.ainet.lang.tensor.data.StorageFloatTensorData +import sk.ainet.lang.types.FP32 + +/** + * SKEEP-003 acceptance criterion (#1501): repeated forward passes under a forward scope keep + * memory flat on the JVM. This is the failure that forced the retreat to the garbage collector: + * a shared arena grew with every op output, and per-call arenas leaked per matmul. + * + * The loop is a decode step in miniature — weights outside the scope, activations inside it, + * `reset()` at the step boundary — at a size where a leak is unmistakable: one step produces + * about 3 MB of activations, so 300 steps would be about 900 MB if anything accumulated. + * Asserted: the direct buffer pool, the live heap after a collection, and the slab's peak. + */ +class ForwardScopeFlatMemoryTest { + + private val n = 512 + private val steps = 300 + private val warmup = 20 + + private val directPool: BufferPoolMXBean = + ManagementFactory.getPlatformMXBeans(BufferPoolMXBean::class.java).first { it.name == "direct" } + + /** Live heap after a collection: what the loop still holds, not what it has churned through. */ + private fun liveHeap(): Long { + val rt = Runtime.getRuntime() + repeat(3) { System.gc(); Thread.sleep(50) } + return rt.totalMemory() - rt.freeMemory() + } + + private fun mb(bytes: Long): String = "%.1f MB".format(bytes / (1024.0 * 1024.0)) + + private fun step(x: Tensor, w: Tensor, b: Tensor): Tensor { + val ops = x.ops + return ops.relu(ops.add(ops.matmul(x, w), b)) + } + + private fun weights(ctx: ExecutionContext): Pair, Tensor> { + val w = ctx.fromFloatArray(Shape(n, n), FP32::class, FloatArray(n * n) { ((it * 31) % 17 - 8) * 0.01f }) + val b = ctx.fromFloatArray(Shape(n), FP32::class, FloatArray(n) { (it % 5 - 2) * 0.1f }) + return w to b + } + + @Test + fun repeatedForwardPassesKeepDirectMemoryAndTheSlabFlat() { + val base = DirectCpuExecutionContext() + val (w, b) = weights(base) + val input = FloatArray(n * n) { ((it * 13) % 11 - 5) * 0.05f } + val stepBytes = 3L * n * n * Float.SIZE_BYTES // input, matmul and add outputs; relu reuses or adds one more + + base.forwardScope(slabFloats = 8 * n * n) { scoped, scope -> + var directAfterWarmup = 0L + var rssAfterWarmup: Long? = null + var heapAfterWarmup = 0L + var peakAfterWarmup = 0 + var directPeak = 0L + + repeat(steps) { stepNo -> + val x = scoped.fromFloatArray(Shape(n, n), FP32::class, input) + val y = step(x, w, b) + assertTrue(y.data is StorageFloatTensorData<*>, "step $stepNo: activations must come from the slab") + if (stepNo == 0) assertTrue(y.data.copyToFloatArray().any { it > 0f }, "the step must compute something") + assertEquals(0L, scope.overflowBytes, "step $stepNo: nothing may spill past the slab") + scope.reset() + + val direct = directPool.memoryUsed + if (stepNo == warmup) { + directAfterWarmup = direct + rssAfterWarmup = MemoryProbe.rssBytes() + heapAfterWarmup = liveHeap() + peakAfterWarmup = scope.peakFloats + } + if (stepNo > warmup) { + directPeak = maxOf(directPeak, direct) + assertEquals(peakAfterWarmup, scope.peakFloats, "step $stepNo: the slab must not grow in steady state") + } + } + + val directGrowth = directPeak - directAfterWarmup + val heapGrowth = liveHeap() - heapAfterWarmup + val rssGrowth = rssAfterWarmup?.let { before -> MemoryProbe.rssBytes()?.minus(before) } + println( + "[forward-scope] ${steps - warmup} steady steps of ${mb(stepBytes)}: direct pool ${mb(directAfterWarmup)} -> peak ${mb(directPeak)}, " + + "live heap growth ${mb(heapGrowth)}, rss growth ${rssGrowth?.let(::mb) ?: "n/a"}, slab peak ${scope.peakFloats} floats", + ) + + // Flat: less than one step's worth of direct memory appears over 280 steps. + assertTrue(directGrowth < stepBytes, "direct memory grew by ${mb(directGrowth)} over ${steps - warmup} steps") + // A leak of every activation would be about 840 MB here. Live heap after a collection + // is what the loop still holds; allow a tenth of the leak for whatever else the test + // JVM keeps. RSS is printed, not asserted: a JVM grows its committed heap lazily, so + // RSS moves with garbage that is already dead (seen on a CI runner). + val leakIfEverythingStayed = stepBytes * (steps - warmup) + assertTrue(heapGrowth < leakIfEverythingStayed / 10, "live heap grew by ${mb(heapGrowth)} over ${steps - warmup} steps") + } + } +} diff --git a/skainet-io/skainet-io-gguf/src/jvmTest/kotlin/sk/ainet/io/gguf/StorageIntegrationTest.kt b/skainet-io/skainet-io-gguf/src/jvmTest/kotlin/sk/ainet/io/gguf/StorageIntegrationTest.kt index 955286555..5a3548939 100644 --- a/skainet-io/skainet-io-gguf/src/jvmTest/kotlin/sk/ainet/io/gguf/StorageIntegrationTest.kt +++ b/skainet-io/skainet-io-gguf/src/jvmTest/kotlin/sk/ainet/io/gguf/StorageIntegrationTest.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.io.gguf import org.junit.Test @@ -109,7 +107,7 @@ class StorageIntegrationTest { // F32 tensor val f32Storage = reader.loadTensorStorage("weight_f32") - assertEquals(LogicalDType.FLOAT32, f32Storage.logicalType) + assertEquals(FP32, f32Storage.dtype) assertSame(FP32, f32Storage.dtype) assertEquals(TensorEncoding.Dense(4), f32Storage.encoding) assertEquals(Ownership.BORROWED, f32Storage.ownership) @@ -119,7 +117,7 @@ class StorageIntegrationTest { // Q8_0 tensor val q80Storage = reader.loadTensorStorage("weight_q80") - assertEquals(LogicalDType.FLOAT32, q80Storage.logicalType) // packed weights are logically FP32 + assertEquals(FP32, q80Storage.dtype) // packed weights are logically FP32 assertSame(FP32, q80Storage.dtype) assertEquals(TensorEncoding.Q8_0, q80Storage.encoding) assertEquals(Ownership.BORROWED, q80Storage.ownership) diff --git a/skainet-io/skainet-io-safetensors/src/commonTest/kotlin/sk/ainet/io/safetensors/StorageAwareSafeTensorsLoaderTest.kt b/skainet-io/skainet-io-safetensors/src/commonTest/kotlin/sk/ainet/io/safetensors/StorageAwareSafeTensorsLoaderTest.kt index 6e66fd7ac..936a95448 100644 --- a/skainet-io/skainet-io-safetensors/src/commonTest/kotlin/sk/ainet/io/safetensors/StorageAwareSafeTensorsLoaderTest.kt +++ b/skainet-io/skainet-io-safetensors/src/commonTest/kotlin/sk/ainet/io/safetensors/StorageAwareSafeTensorsLoaderTest.kt @@ -1,9 +1,6 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.io.safetensors import sk.ainet.io.RandomAccessSource -import sk.ainet.lang.tensor.storage.LogicalDType import sk.ainet.lang.tensor.storage.MemoryDomain import sk.ainet.lang.tensor.storage.Ownership import kotlin.test.Test @@ -11,6 +8,7 @@ import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertFailsWith import kotlin.test.assertTrue +import sk.ainet.lang.types.FP32 /** * Tests for [StorageAwareSafeTensorsLoader]. @@ -105,7 +103,7 @@ class StorageAwareSafeTensorsLoaderTest { assertTrue(tensors.containsKey("weight")) val storage = tensors["weight"]!! - assertEquals(LogicalDType.FLOAT32, storage.logicalType) + assertEquals(FP32, storage.dtype) assertEquals(Ownership.BORROWED, storage.ownership) assertFalse(storage.isFileBacked) assertEquals(6L, storage.elementCount) // 2 * 3 diff --git a/skainet-lang/skainet-lang-core/api/jvm/skainet-lang-core.api b/skainet-lang/skainet-lang-core/api/jvm/skainet-lang-core.api index b305d8ca7..ce1e3cbf6 100644 --- a/skainet-lang/skainet-lang-core/api/jvm/skainet-lang-core.api +++ b/skainet-lang/skainet-lang-core/api/jvm/skainet-lang-core.api @@ -4885,7 +4885,6 @@ public final class sk/ainet/lang/tensor/data/BitNetB158TensorData : sk/ainet/lan public fun set ([IB)V public synthetic fun set ([ILjava/lang/Object;)V public fun toFloatArray ()[F - public fun toTensorStorage (Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public fun toTensorStorage (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -4919,7 +4918,6 @@ public final class sk/ainet/lang/tensor/data/BitNetPlanesTensorData : sk/ainet/l public fun set ([IF)V public synthetic fun set ([ILjava/lang/Object;)V public fun toFloatArray ()[F - public fun toTensorStorage (Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public fun toTensorStorage (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -4949,7 +4947,6 @@ public final class sk/ainet/lang/tensor/data/BufferPackedTensorData : sk/ainet/l public fun set ([IF)V public synthetic fun set ([ILjava/lang/Object;)V public fun toFloatArray ()[F - public fun toTensorStorage (Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public fun toTensorStorage (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -5279,7 +5276,6 @@ public final class sk/ainet/lang/tensor/data/Q4_0BlockTensorData : sk/ainet/lang public fun set ([IB)V public synthetic fun set ([ILjava/lang/Object;)V public fun toFloatArray ()[F - public fun toTensorStorage (Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public fun toTensorStorage (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -5345,7 +5341,6 @@ public final class sk/ainet/lang/tensor/data/Q4_KBlockTensorData : sk/ainet/lang public fun set ([IB)V public synthetic fun set ([ILjava/lang/Object;)V public fun toFloatArray ()[F - public fun toTensorStorage (Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public fun toTensorStorage (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -5407,7 +5402,6 @@ public final class sk/ainet/lang/tensor/data/Q5_0BlockTensorData : sk/ainet/lang public fun set ([IB)V public synthetic fun set ([ILjava/lang/Object;)V public fun toFloatArray ()[F - public fun toTensorStorage (Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public fun toTensorStorage (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -5456,7 +5450,6 @@ public final class sk/ainet/lang/tensor/data/Q5_1BlockTensorData : sk/ainet/lang public fun set ([IB)V public synthetic fun set ([ILjava/lang/Object;)V public fun toFloatArray ()[F - public fun toTensorStorage (Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public fun toTensorStorage (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -5510,7 +5503,6 @@ public final class sk/ainet/lang/tensor/data/Q5_KBlockTensorData : sk/ainet/lang public fun set ([IB)V public synthetic fun set ([ILjava/lang/Object;)V public fun toFloatArray ()[F - public fun toTensorStorage (Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public fun toTensorStorage (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -5579,7 +5571,6 @@ public final class sk/ainet/lang/tensor/data/Q6_KBlockTensorData : sk/ainet/lang public fun set ([IB)V public synthetic fun set ([ILjava/lang/Object;)V public fun toFloatArray ()[F - public fun toTensorStorage (Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public fun toTensorStorage (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -5671,7 +5662,6 @@ public final class sk/ainet/lang/tensor/data/Q8_0BlockTensorData : sk/ainet/lang public fun set ([IB)V public synthetic fun set ([ILjava/lang/Object;)V public fun toFloatArray ()[F - public fun toTensorStorage (Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public fun toTensorStorage (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -5834,7 +5824,6 @@ public final class sk/ainet/lang/tensor/data/Ternary2BitTensorData : sk/ainet/la public fun set ([IB)V public synthetic fun set ([ILjava/lang/Object;)V public fun toFloatArray ()[F - public fun toTensorStorage (Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public fun toTensorStorage (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -7433,10 +7422,6 @@ public final class sk/ainet/lang/tensor/storage/CopySourceStat { public fun toString ()Ljava/lang/String; } -public final class sk/ainet/lang/tensor/storage/DTypeBridge { - public static final fun toLogicalDType (Lsk/ainet/lang/types/DType;)Lsk/ainet/lang/tensor/storage/LogicalDType; -} - public final class sk/ainet/lang/tensor/storage/DefaultBufferResolver : sk/ainet/lang/tensor/storage/BufferResolver { public fun ()V public fun (Lkotlin/jvm/functions/Function1;)V @@ -7639,36 +7624,6 @@ public final class sk/ainet/lang/tensor/storage/KvCacheStore$DefaultImpls { public static synthetic fun valueWindow$default (Lsk/ainet/lang/tensor/storage/KvCacheStore;IIIILjava/lang/Object;)Lsk/ainet/lang/memory/WindowedKV; } -public final class sk/ainet/lang/tensor/storage/LogicalDType : java/lang/Enum { - public static final field BFLOAT16 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field Companion Lsk/ainet/lang/tensor/storage/LogicalDType$Companion; - public static final field FLOAT16 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field FLOAT32 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field FLOAT64 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field INT16 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field INT32 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field INT4 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field INT64 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field INT8 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field TERNARY Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field UINT16 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field UINT32 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field UINT64 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static final field UINT8 Lsk/ainet/lang/tensor/storage/LogicalDType; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public final fun getSizeInBits ()I - public final fun getSizeInBytes ()I - public final fun isFloatingPoint ()Z - public final fun isSigned ()Z - public final fun toDType ()Lsk/ainet/lang/types/DType; - public static fun valueOf (Ljava/lang/String;)Lsk/ainet/lang/tensor/storage/LogicalDType; - public static fun values ()[Lsk/ainet/lang/tensor/storage/LogicalDType; -} - -public final class sk/ainet/lang/tensor/storage/LogicalDType$Companion { - public final fun fromDType (Lsk/ainet/lang/types/DType;)Lsk/ainet/lang/tensor/storage/LogicalDType; -} - public final class sk/ainet/lang/tensor/storage/MemoryDomain : java/lang/Enum { public static final field DEVICE_LOCAL Lsk/ainet/lang/tensor/storage/MemoryDomain; public static final field HOST_HEAP Lsk/ainet/lang/tensor/storage/MemoryDomain; @@ -7714,9 +7669,7 @@ public abstract interface class sk/ainet/lang/tensor/storage/PackedBlockStorage public fun getPhysicalBytes ()J public abstract fun getShape ()Lsk/ainet/lang/tensor/Shape; public fun toFloatArray ()[F - public fun toTensorStorage (Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public fun toTensorStorage (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; - public static synthetic fun toTensorStorage$default (Lsk/ainet/lang/tensor/storage/PackedBlockStorage;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;ILjava/lang/Object;)Lsk/ainet/lang/tensor/storage/TensorStorage; public static synthetic fun toTensorStorage$default (Lsk/ainet/lang/tensor/storage/PackedBlockStorage;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;ILjava/lang/Object;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -7728,9 +7681,7 @@ public final class sk/ainet/lang/tensor/storage/PackedBlockStorage$DefaultImpls public static fun getPackedView (Lsk/ainet/lang/tensor/storage/PackedBlockStorage;)Lsk/ainet/lang/memory/TensorView; public static fun getPhysicalBytes (Lsk/ainet/lang/tensor/storage/PackedBlockStorage;)J public static fun toFloatArray (Lsk/ainet/lang/tensor/storage/PackedBlockStorage;)[F - public static fun toTensorStorage (Lsk/ainet/lang/tensor/storage/PackedBlockStorage;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public static fun toTensorStorage (Lsk/ainet/lang/tensor/storage/PackedBlockStorage;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; - public static synthetic fun toTensorStorage$default (Lsk/ainet/lang/tensor/storage/PackedBlockStorage;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/Placement;ILjava/lang/Object;)Lsk/ainet/lang/tensor/storage/TensorStorage; public static synthetic fun toTensorStorage$default (Lsk/ainet/lang/tensor/storage/PackedBlockStorage;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/Placement;ILjava/lang/Object;)Lsk/ainet/lang/tensor/storage/TensorStorage; } @@ -7769,10 +7720,10 @@ public final class sk/ainet/lang/tensor/storage/Requirement : java/lang/Enum { } public final class sk/ainet/lang/tensor/storage/StorageMemoryReport { - public fun (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/Ownership;Lsk/ainet/lang/tensor/storage/Placement;JJZZZ)V + public fun (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/Ownership;Lsk/ainet/lang/tensor/storage/Placement;JJZZZ)V public final fun component1 ()Lsk/ainet/lang/tensor/Shape; public final fun component10 ()Z - public final fun component2 ()Lsk/ainet/lang/tensor/storage/LogicalDType; + public final fun component2 ()Lsk/ainet/lang/types/DType; public final fun component3 ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public final fun component4 ()Lsk/ainet/lang/tensor/storage/Ownership; public final fun component5 ()Lsk/ainet/lang/tensor/storage/Placement; @@ -7780,14 +7731,13 @@ public final class sk/ainet/lang/tensor/storage/StorageMemoryReport { public final fun component7 ()J public final fun component8 ()Z public final fun component9 ()Z - public final fun copy (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/Ownership;Lsk/ainet/lang/tensor/storage/Placement;JJZZZ)Lsk/ainet/lang/tensor/storage/StorageMemoryReport; - public static synthetic fun copy$default (Lsk/ainet/lang/tensor/storage/StorageMemoryReport;Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/Ownership;Lsk/ainet/lang/tensor/storage/Placement;JJZZZILjava/lang/Object;)Lsk/ainet/lang/tensor/storage/StorageMemoryReport; + public final fun copy (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/Ownership;Lsk/ainet/lang/tensor/storage/Placement;JJZZZ)Lsk/ainet/lang/tensor/storage/StorageMemoryReport; + public static synthetic fun copy$default (Lsk/ainet/lang/tensor/storage/StorageMemoryReport;Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/Ownership;Lsk/ainet/lang/tensor/storage/Placement;JJZZZILjava/lang/Object;)Lsk/ainet/lang/tensor/storage/StorageMemoryReport; public fun equals (Ljava/lang/Object;)Z public final fun getCompressionRatio ()D public final fun getDtype ()Lsk/ainet/lang/types/DType; public final fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public final fun getLogicalBytes ()J - public final fun getLogicalType ()Lsk/ainet/lang/tensor/storage/LogicalDType; public final fun getOwnership ()Lsk/ainet/lang/tensor/storage/Ownership; public final fun getPhysicalBytes ()J public final fun getPlacement ()Lsk/ainet/lang/tensor/storage/Placement; @@ -8012,20 +7962,18 @@ public final class sk/ainet/lang/tensor/storage/TensorEncoding$TurboQuantPolarQj } public final class sk/ainet/lang/tensor/storage/TensorStorage { - public fun (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/BufferHandle;Lsk/ainet/lang/tensor/storage/Placement;J[JZ)V - public synthetic fun (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/BufferHandle;Lsk/ainet/lang/tensor/storage/Placement;J[JZILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/BufferHandle;Lsk/ainet/lang/tensor/storage/Placement;J[JZ)V public synthetic fun (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/BufferHandle;Lsk/ainet/lang/tensor/storage/Placement;J[JZILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Lsk/ainet/lang/tensor/Shape; - public final fun component2 ()Lsk/ainet/lang/tensor/storage/LogicalDType; + public final fun component2 ()Lsk/ainet/lang/types/DType; public final fun component3 ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public final fun component4 ()Lsk/ainet/lang/tensor/storage/BufferHandle; public final fun component5 ()Lsk/ainet/lang/tensor/storage/Placement; public final fun component6 ()J public final fun component7 ()[J public final fun component8 ()Z - public final fun copy (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/BufferHandle;Lsk/ainet/lang/tensor/storage/Placement;J[JZ)Lsk/ainet/lang/tensor/storage/TensorStorage; - public static synthetic fun copy$default (Lsk/ainet/lang/tensor/storage/TensorStorage;Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/BufferHandle;Lsk/ainet/lang/tensor/storage/Placement;J[JZILjava/lang/Object;)Lsk/ainet/lang/tensor/storage/TensorStorage; + public final fun copy (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/BufferHandle;Lsk/ainet/lang/tensor/storage/Placement;J[JZ)Lsk/ainet/lang/tensor/storage/TensorStorage; + public static synthetic fun copy$default (Lsk/ainet/lang/tensor/storage/TensorStorage;Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Lsk/ainet/lang/tensor/storage/BufferHandle;Lsk/ainet/lang/tensor/storage/Placement;J[JZILjava/lang/Object;)Lsk/ainet/lang/tensor/storage/TensorStorage; public final fun copyMaterialize ()Lsk/ainet/lang/tensor/storage/TensorStorage; public final fun copyMaterialize (Lsk/ainet/lang/tensor/storage/BufferResolver;)Lsk/ainet/lang/tensor/storage/TensorStorage; public final fun copyToDevice (Lsk/ainet/lang/tensor/storage/DeviceKind;)Lsk/ainet/lang/tensor/storage/TensorStorage; @@ -8038,7 +7986,6 @@ public final class sk/ainet/lang/tensor/storage/TensorStorage { public final fun getElementCount ()J public final fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public final fun getLogicalBytes ()J - public final fun getLogicalType ()Lsk/ainet/lang/tensor/storage/LogicalDType; public final fun getOwnership ()Lsk/ainet/lang/tensor/storage/Ownership; public final fun getPhysicalBytes ()J public final fun getPlacement ()Lsk/ainet/lang/tensor/storage/Placement; @@ -8060,17 +8007,12 @@ public final class sk/ainet/lang/tensor/storage/TensorStorage { public final class sk/ainet/lang/tensor/storage/TensorStorageFactory { public static final field INSTANCE Lsk/ainet/lang/tensor/storage/TensorStorageFactory; public final fun borrowFloatArray (Lsk/ainet/lang/tensor/Shape;[F)Lsk/ainet/lang/tensor/storage/TensorStorage; - public final fun fileBacked (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Ljava/lang/String;JJ)Lsk/ainet/lang/tensor/storage/TensorStorage; public final fun fileBacked (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;Ljava/lang/String;JJ)Lsk/ainet/lang/tensor/storage/TensorStorage; public final fun fromFloatArray (Lsk/ainet/lang/tensor/Shape;[F)Lsk/ainet/lang/tensor/storage/TensorStorage; public final fun fromIntArray (Lsk/ainet/lang/tensor/Shape;[I)Lsk/ainet/lang/tensor/storage/TensorStorage; - public final fun fromRawBytes (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/TensorEncoding;[BLsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public final fun fromRawBytes (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;[BLsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; - public static synthetic fun fromRawBytes$default (Lsk/ainet/lang/tensor/storage/TensorStorageFactory;Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/TensorEncoding;[BLsk/ainet/lang/tensor/storage/Placement;ILjava/lang/Object;)Lsk/ainet/lang/tensor/storage/TensorStorage; public static synthetic fun fromRawBytes$default (Lsk/ainet/lang/tensor/storage/TensorStorageFactory;Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;[BLsk/ainet/lang/tensor/storage/Placement;ILjava/lang/Object;)Lsk/ainet/lang/tensor/storage/TensorStorage; - public final fun fromRawBytesOwned (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/TensorEncoding;[BLsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; public final fun fromRawBytesOwned (Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;[BLsk/ainet/lang/tensor/storage/Placement;)Lsk/ainet/lang/tensor/storage/TensorStorage; - public static synthetic fun fromRawBytesOwned$default (Lsk/ainet/lang/tensor/storage/TensorStorageFactory;Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/LogicalDType;Lsk/ainet/lang/tensor/storage/TensorEncoding;[BLsk/ainet/lang/tensor/storage/Placement;ILjava/lang/Object;)Lsk/ainet/lang/tensor/storage/TensorStorage; public static synthetic fun fromRawBytesOwned$default (Lsk/ainet/lang/tensor/storage/TensorStorageFactory;Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;[BLsk/ainet/lang/tensor/storage/Placement;ILjava/lang/Object;)Lsk/ainet/lang/tensor/storage/TensorStorage; public final fun fromTensorData (Lsk/ainet/lang/tensor/data/TensorData;)Lsk/ainet/lang/tensor/storage/TensorStorage; public final fun toTensorData (Lsk/ainet/lang/tensor/storage/TensorStorage;)Lsk/ainet/lang/tensor/data/TensorData; diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/LogicalDType.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/LogicalDType.kt deleted file mode 100644 index fb2d31b85..000000000 --- a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/LogicalDType.kt +++ /dev/null @@ -1,108 +0,0 @@ -@file:Suppress("DEPRECATION") // this file *is* the deprecated type - -package sk.ainet.lang.tensor.storage - -import sk.ainet.lang.types.BF16 -import sk.ainet.lang.types.DType -import sk.ainet.lang.types.FP16 -import sk.ainet.lang.types.FP32 -import sk.ainet.lang.types.FP64 -import sk.ainet.lang.types.Int16 -import sk.ainet.lang.types.Int32 -import sk.ainet.lang.types.Int4 -import sk.ainet.lang.types.Int64 -import sk.ainet.lang.types.Int8 -import sk.ainet.lang.types.UInt8 -import sk.ainet.lang.types.UInt16 -import sk.ainet.lang.types.UInt32 -import sk.ainet.lang.types.UInt64 -import sk.ainet.lang.types.Ternary - -/** - * Logical numeric type — what the tensor values mean semantically. - * - * This is intentionally separate from [TensorEncoding], which describes how - * values are physically stored. A tensor with logical type [FLOAT32] might - * be encoded as [TensorEncoding.Dense], [TensorEncoding.Q4_K], etc. - */ -@Deprecated( - message = "LogicalDType merges into DType (SKEEP-003 decision #13): use the DType objects (FP32, Int8, …) and the dtype-first constructors/overloads.", - replaceWith = ReplaceWith("DType", "sk.ainet.lang.types.DType"), -) -public enum class LogicalDType( - public val sizeInBits: Int, - public val isFloatingPoint: Boolean, - public val isSigned: Boolean -) { - TERNARY(2, isFloatingPoint = false, isSigned = true), - INT4(4, isFloatingPoint = false, isSigned = true), - INT8(8, isFloatingPoint = false, isSigned = true), - INT16(16, isFloatingPoint = false, isSigned = true), - INT32(32, isFloatingPoint = false, isSigned = true), - INT64(64, isFloatingPoint = false, isSigned = true), - UINT8(8, isFloatingPoint = false, isSigned = false), - UINT16(16, isFloatingPoint = false, isSigned = false), - UINT32(32, isFloatingPoint = false, isSigned = false), - UINT64(64, isFloatingPoint = false, isSigned = false), - FLOAT16(16, isFloatingPoint = true, isSigned = true), - BFLOAT16(16, isFloatingPoint = true, isSigned = true), - FLOAT32(32, isFloatingPoint = true, isSigned = true), - FLOAT64(64, isFloatingPoint = true, isSigned = true); - - public val sizeInBytes: Int get() = (sizeInBits + 7) / 8 - - /** - * The [DType] this logical type corresponds to — the inverse of [fromDType]. - * - * Bridge half 1 of 2 (SKEEP-003 Phase 0, decision #13): [LogicalDType] and [DType] are - * bijective (14 ↔ 14) and will merge into one sealed `DType` that carries its `KClass` - * witness; until then this is the single sanctioned way to go from a storage descriptor's - * logical type to the `DType` the tensor DSL uses. Total — every constant maps to exactly - * one `DType` object — and exhaustive by construction (no `else` branch). - * - * @see sk.ainet.lang.tensor.storage.toLogicalDType - */ - public fun toDType(): DType = when (this) { - TERNARY -> Ternary - INT4 -> Int4 - INT8 -> Int8 - INT16 -> Int16 - INT32 -> Int32 - INT64 -> Int64 - UINT8 -> UInt8 - UINT16 -> UInt16 - UINT32 -> UInt32 - UINT64 -> UInt64 - FLOAT16 -> FP16 - BFLOAT16 -> BF16 - FLOAT32 -> FP32 - FLOAT64 -> FP64 - } - - public companion object { - /** - * The [LogicalDType] for a [DType]. Inverse of [toDType]; prefer the extension - * [sk.ainet.lang.tensor.storage.toLogicalDType] at call sites. - */ - @Deprecated( - message = "LogicalDType merges into DType (SKEEP-003 decision #13); the DType object itself is the logical type.", - replaceWith = ReplaceWith("dtype"), - ) - public fun fromDType(dtype: DType): LogicalDType = when (dtype) { - is Ternary -> TERNARY - is Int4 -> INT4 - is Int8 -> INT8 - is Int16 -> INT16 - is Int32 -> INT32 - is Int64 -> INT64 - is UInt8 -> UINT8 - is UInt16 -> UINT16 - is UInt32 -> UINT32 - is UInt64 -> UINT64 - is FP16 -> FLOAT16 - is BF16 -> BFLOAT16 - is FP32 -> FLOAT32 - is FP64 -> FLOAT64 - } - } -} diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/LogicalDTypeBridge.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/LogicalDTypeBridge.kt deleted file mode 100644 index 7bbfb329e..000000000 --- a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/LogicalDTypeBridge.kt +++ /dev/null @@ -1,27 +0,0 @@ -@file:JvmName("DTypeBridge") -@file:Suppress("DEPRECATION") // the bridge exists to keep the deprecated type usable until removal - -package sk.ainet.lang.tensor.storage - -import sk.ainet.lang.types.DType -import kotlin.jvm.JvmName - -/** - * The [LogicalDType] describing this [DType] — bridge half 2 of 2 (SKEEP-003 Phase 0, - * decision #13). - * - * Lives in the storage package (not in `sk.ainet.lang.types`) so the type package stays a - * leaf; the mapping is total and bijective with [LogicalDType.toDType]: - * - * ``` - * for (l in LogicalDType.entries) check(l.toDType().toLogicalDType() == l) - * ``` - * - * When `LogicalDType` is deprecated (decision #13, separate slice) its `ReplaceWith` targets - * point at `DType` directly and this extension becomes a no-op shim. - */ -@Deprecated( - message = "LogicalDType merges into DType (SKEEP-003 decision #13); keep the DType itself.", - replaceWith = ReplaceWith("this"), -) -public fun DType.toLogicalDType(): LogicalDType = LogicalDType.fromDType(this) diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/PackedBlockStorage.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/PackedBlockStorage.kt index 97811780b..b3062e9e6 100644 --- a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/PackedBlockStorage.kt +++ b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/PackedBlockStorage.kt @@ -127,24 +127,15 @@ public interface PackedBlockStorage { return result } - /** - * Convert this packed storage to a [TensorStorage] descriptor. - */ - @Deprecated(message = "Use toTensorStorage(dtype, placement) (SKEEP-003 decision #13).", replaceWith = ReplaceWith("toTensorStorage(dtype, placement)")) + /** Convert this packed storage to a [TensorStorage] descriptor (packed weights are logically FP32). */ public fun toTensorStorage( - logicalType: LogicalDType = LogicalDType.FLOAT32, + dtype: sk.ainet.lang.types.DType = sk.ainet.lang.types.FP32, placement: Placement = Placement.CPU_HEAP ): TensorStorage = TensorStorage( shape = shape, - logicalType = logicalType, + dtype = dtype, encoding = encoding, buffer = BufferHandle.Borrowed(packedData, isMutable = false), placement = placement ) - - /** Convert this packed storage to a [TensorStorage] descriptor, dtype-first (packed weights are logically [FP32]). */ - public fun toTensorStorage( - dtype: sk.ainet.lang.types.DType, - placement: Placement = Placement.CPU_HEAP - ): TensorStorage = toTensorStorage(dtype.toLogicalDType(), placement) } diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/StorageMemoryReport.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/StorageMemoryReport.kt index 799e28e57..29d8b4e56 100644 --- a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/StorageMemoryReport.kt +++ b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/StorageMemoryReport.kt @@ -12,8 +12,7 @@ import sk.ainet.lang.tensor.Shape */ public data class StorageMemoryReport( val shape: Shape, - @Deprecated(message = "Use dtype (SKEEP-003 decision #13).", replaceWith = ReplaceWith("dtype")) - val logicalType: LogicalDType, + val dtype: sk.ainet.lang.types.DType, val encoding: TensorEncoding, val ownership: Ownership, val placement: Placement, @@ -23,9 +22,6 @@ public data class StorageMemoryReport( val isAlias: Boolean, val isMutable: Boolean ) { - /** The [sk.ainet.lang.types.DType] of [logicalType] (SKEEP-003 Phase 0 bridge). */ - val dtype: sk.ainet.lang.types.DType get() = logicalType.toDType() - /** Compression ratio: logical / physical. >1 means the encoding is smaller than dense. */ val compressionRatio: Double get() = if (physicalBytes > 0) logicalBytes.toDouble() / physicalBytes else 1.0 @@ -33,7 +29,7 @@ public data class StorageMemoryReport( override fun toString(): String = buildString { append("StorageMemoryReport(") append("shape=$shape, ") - append("logical=$logicalType, ") + append("logical=${dtype.name}, ") append("encoding=${encoding.name}, ") append("ownership=$ownership, ") append("placement=${placement.device}/${placement.domain}, ") diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/TensorEncoding.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/TensorEncoding.kt index 6b3bff89c..ed5777401 100644 --- a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/TensorEncoding.kt +++ b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/TensorEncoding.kt @@ -4,7 +4,7 @@ package sk.ainet.lang.tensor.storage * Physical storage encoding — how tensor data is laid out in memory. * * A [TensorEncoding] describes the byte-level format of a buffer, independent - * of the logical numeric type ([LogicalDType]). For example, a FLOAT32 tensor + * of the logical numeric type ([sk.ainet.lang.types.DType]). For example, a FLOAT32 tensor * may be stored as [Dense] (4 bytes per element) or as [Q4_K] (packed 4-bit * blocks with per-block scales). * diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/TensorStorage.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/TensorStorage.kt index e6b575306..be5338ac4 100644 --- a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/TensorStorage.kt +++ b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/TensorStorage.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") // keeps the LogicalDType primary constructor working until the next major - package sk.ainet.lang.tensor.storage import sk.ainet.lang.tensor.Shape @@ -8,27 +6,24 @@ import sk.ainet.lang.types.DType /** * Runtime descriptor for a tensor's backing memory. * - * [TensorStorage] is the main architectural type that replaces ad-hoc - * array passing between loaders, planners, and backends. It carries enough - * information to handle a tensor without inspecting its bytes: + * [TensorStorage] describes a tensor read from a file before it becomes a + * tensor: the streaming GGUF and SafeTensors readers return it. It carries + * enough information to handle a tensor without inspecting its bytes: * - * - **What** the values mean: [logicalType] + * - **What** the values mean: [dtype] * - **How** they are stored: [encoding] * - **Where** the bytes live: [buffer] + [placement] * - **Layout**: [shape], [byteOffset], [strides], [isContiguous] * - **Ownership**: via [buffer]'s [BufferHandle] subtype * - * Existing [sk.ainet.lang.tensor.data.TensorData] remains as a - * compatibility façade. New loaders, planners, and backends should target - * [TensorStorage] directly. + * This is the older of two storage descriptions. The model new code targets is + * [sk.ainet.lang.memory.Storage] (owns bytes) with + * [sk.ainet.lang.memory.TensorView] (interprets them); SKEEP-003 replaces this + * type with a view once the readers return one (#1507). */ public data class TensorStorage( val shape: Shape, - @Deprecated( - message = "Use dtype (SKEEP-003 decision #13); the LogicalDType view stays until the next major.", - replaceWith = ReplaceWith("dtype"), - ) - val logicalType: LogicalDType, + val dtype: DType, val encoding: TensorEncoding, val buffer: BufferHandle, val placement: Placement = Placement.CPU_HEAP, @@ -36,29 +31,11 @@ public data class TensorStorage( val strides: LongArray? = null, val isContiguous: Boolean = true ) { - /** - * Construct from a [DType] (SKEEP-003 Phase 0): the dtype-first form every new call site uses; - * the [LogicalDType] primary constructor stays for source compatibility until the next major. - */ - public constructor( - shape: Shape, - dtype: DType, - encoding: TensorEncoding, - buffer: BufferHandle, - placement: Placement = Placement.CPU_HEAP, - byteOffset: Long = 0, - strides: LongArray? = null, - isContiguous: Boolean = true, - ) : this(shape, dtype.toLogicalDType(), encoding, buffer, placement, byteOffset, strides, isContiguous) - - /** The [DType] of this storage — what the values mean (SKEEP-003 Phase 0 bridge over [logicalType]). */ - val dtype: DType get() = logicalType.toDType() - /** Number of logical elements in this tensor. */ val elementCount: Long get() = shape.volume.toLong() /** Logical size: number of elements x logical element size. */ - val logicalBytes: Long get() = elementCount * logicalType.sizeInBytes + val logicalBytes: Long get() = elementCount * dtype.sizeInBytes /** Physical size: actual bytes consumed in the buffer for this tensor. */ val physicalBytes: Long get() = encoding.physicalBytes(elementCount) ?: buffer.sizeInBytes @@ -81,7 +58,7 @@ public data class TensorStorage( */ public fun memoryReport(): StorageMemoryReport = StorageMemoryReport( shape = shape, - logicalType = logicalType, + dtype = dtype, encoding = encoding, ownership = ownership, placement = placement, @@ -198,7 +175,7 @@ public data class TensorStorage( * first, then quantized. The target encoding and shape are validated before the buffer is * read, with the loader's messages. * - * The result is owned packed host storage; [logicalType], shape and placement device are + * The result is owned packed host storage; [dtype], shape and placement device are * kept. [BufferHandle.FileBacked] sources need the overload that takes a [BufferResolver]. */ public fun requantizeTo(targetEncoding: TensorEncoding): TensorStorage = @@ -215,7 +192,7 @@ public data class TensorStorage( ) return TensorStorage( shape = shape, - logicalType = logicalType, + dtype = dtype, encoding = targetEncoding, buffer = BufferHandleFactory.owned(packed.packedData), placement = placement.copy(domain = MemoryDomain.HOST_HEAP), @@ -241,7 +218,7 @@ public data class TensorStorage( if (this === other) return true if (other !is TensorStorage) return false return shape == other.shape && - logicalType == other.logicalType && + dtype == other.dtype && encoding == other.encoding && buffer == other.buffer && placement == other.placement && @@ -252,7 +229,7 @@ public data class TensorStorage( override fun hashCode(): Int { var result = shape.hashCode() - result = 31 * result + logicalType.hashCode() + result = 31 * result + dtype.hashCode() result = 31 * result + encoding.hashCode() result = 31 * result + buffer.hashCode() result = 31 * result + placement.hashCode() diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/TensorStorageFactory.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/TensorStorageFactory.kt index 98976f473..99a1857d7 100644 --- a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/TensorStorageFactory.kt +++ b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/TensorStorageFactory.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") // the LogicalDType overloads stay as delegating shims until the next major - package sk.ainet.lang.tensor.storage import sk.ainet.lang.tensor.Shape @@ -81,86 +79,55 @@ public object TensorStorageFactory { * Create storage from raw bytes with explicit encoding. * The byte array is borrowed (not copied). */ - @Deprecated(message = "Use the DType overload (SKEEP-003 decision #13).", replaceWith = ReplaceWith("fromRawBytes(shape, dtype, encoding, …)")) public fun fromRawBytes( shape: Shape, - logicalType: LogicalDType, + dtype: DType, encoding: TensorEncoding, data: ByteArray, placement: Placement = Placement.CPU_HEAP ): TensorStorage = TensorStorage( shape = shape, - logicalType = logicalType, + dtype = dtype, encoding = encoding, buffer = BufferHandleFactory.borrow(data), placement = placement ) - /** Create storage from raw bytes with explicit encoding, dtype-first (the byte array is borrowed). */ - public fun fromRawBytes( - shape: Shape, - dtype: DType, - encoding: TensorEncoding, - data: ByteArray, - placement: Placement = Placement.CPU_HEAP - ): TensorStorage = fromRawBytes(shape, dtype.toLogicalDType(), encoding, data, placement) - /** * Create storage from raw bytes with explicit encoding (owned copy). */ - @Deprecated(message = "Use the DType overload (SKEEP-003 decision #13).", replaceWith = ReplaceWith("fromRawBytesOwned(shape, dtype, encoding, …)")) public fun fromRawBytesOwned( shape: Shape, - logicalType: LogicalDType, + dtype: DType, encoding: TensorEncoding, data: ByteArray, placement: Placement = Placement.CPU_HEAP ): TensorStorage = TensorStorage( shape = shape, - logicalType = logicalType, + dtype = dtype, encoding = encoding, buffer = BufferHandleFactory.owned(data), placement = placement ) - /** Create storage from raw bytes with explicit encoding (owned copy), dtype-first. */ - public fun fromRawBytesOwned( - shape: Shape, - dtype: DType, - encoding: TensorEncoding, - data: ByteArray, - placement: Placement = Placement.CPU_HEAP - ): TensorStorage = fromRawBytesOwned(shape, dtype.toLogicalDType(), encoding, data, placement) - /** * Create file-backed storage (for memory-mapped model weights). */ - @Deprecated(message = "Use the DType overload (SKEEP-003 decision #13).", replaceWith = ReplaceWith("fileBacked(shape, dtype, encoding, …)")) public fun fileBacked( shape: Shape, - logicalType: LogicalDType, + dtype: DType, encoding: TensorEncoding, path: String, fileOffset: Long, sizeInBytes: Long ): TensorStorage = TensorStorage( shape = shape, - logicalType = logicalType, + dtype = dtype, encoding = encoding, buffer = BufferHandleFactory.fileBacked(path, fileOffset, sizeInBytes), placement = Placement.MMAP_WEIGHTS ) - /** Create file-backed storage (for memory-mapped model weights), dtype-first. */ - public fun fileBacked( - shape: Shape, - dtype: DType, - encoding: TensorEncoding, - path: String, - fileOffset: Long, - sizeInBytes: Long - ): TensorStorage = fileBacked(shape, dtype.toLogicalDType(), encoding, path, fileOffset, sizeInBytes) - /** * Bridge: create a [TensorStorage] descriptor from an existing [TensorData]. * @@ -183,25 +150,25 @@ public object TensorStorageFactory { return when (data) { is FloatArrayTensorData<*> -> TensorStorage( shape = data.shape, - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(bytesPerElement = 4), buffer = BufferHandleFactory.owned(data.buffer) ) is IntArrayTensorData<*> -> TensorStorage( shape = data.shape, - logicalType = LogicalDType.INT32, + dtype = Int32, encoding = TensorEncoding.Dense(bytesPerElement = 4), buffer = BufferHandleFactory.owned(data.buffer) ) is Q4_KTensorData -> TensorStorage( shape = data.shape, - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Q4_K, buffer = BufferHandleFactory.borrow(data.packedData) ) is Q8_0TensorData -> TensorStorage( shape = data.shape, - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Q8_0, buffer = BufferHandleFactory.borrow(data.packedData) ) diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/types/DType.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/types/DType.kt index 93ed4de88..aa48f5b4a 100644 --- a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/types/DType.kt +++ b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/types/DType.kt @@ -8,8 +8,8 @@ import kotlin.reflect.KClass * * `DType` is sealed with exactly fourteen `object` members, so it is switchable like an enum * (`when (dtype) { FP32 -> … }`) while each member still serves as the generic witness of - * `Tensor` through [witness]. SKEEP-003 (decision #13): this single type replaces - * the storage-side `LogicalDType` enum; `Format = (DType, TensorEncoding)` has exactly one dtype + * `Tensor` through [witness]. SKEEP-003 (decision #13): this single type replaced + * the storage-side `LogicalDType` enum (removed in #1502); `Format = (DType, TensorEncoding)` has exactly one dtype * type. */ public sealed interface DType { diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/AcceptanceCriteriaTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/AcceptanceCriteriaTest.kt index 4ebce7538..ed8245294 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/AcceptanceCriteriaTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/AcceptanceCriteriaTest.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.tensor.storage import sk.ainet.lang.tensor.Shape @@ -11,6 +9,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue +import sk.ainet.lang.types.FP16 /** * End-to-end acceptance criteria tests for the Memory Architecture PRD. @@ -43,7 +42,7 @@ class AcceptanceCriteriaTest { fun ac2_fileBackedStoragePreservesPlacement() { val storage = TensorStorageFactory.fileBacked( shape = Shape(1024, 768), - logicalType = LogicalDType.FLOAT16, + dtype = FP16, encoding = TensorEncoding.Dense(2), path = "/model/weights.bin", fileOffset = 0, @@ -74,7 +73,7 @@ class AcceptanceCriteriaTest { // source array must be visible through the storage handle. val bytes = ByteArray(12) val storage = TensorStorageFactory.fromRawBytes( - Shape(3), LogicalDType.FLOAT32, TensorEncoding.Dense(4), bytes + Shape(3), FP32, TensorEncoding.Dense(4), bytes ) assertEquals(Ownership.BORROWED, storage.ownership) bytes[0] = 42 @@ -141,7 +140,7 @@ class AcceptanceCriteriaTest { val storage = TensorStorageFactory.fromTensorData(td) val report = storage.memoryReport() - assertEquals(LogicalDType.FLOAT32, report.logicalType) + assertEquals(FP32, report.dtype) assertEquals("Dense(4B)", report.encoding.name) assertEquals(Ownership.OWNED, report.ownership) assertEquals(24L, report.logicalBytes) @@ -155,14 +154,14 @@ class AcceptanceCriteriaTest { fun ac5_packedQ4KReportsAllFields() { val storage = TensorStorage( shape = Shape(512), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Q4_K, buffer = BufferHandle.Borrowed(ByteArray(288)), // 2 blocks placement = Placement.CPU_HEAP ) val report = storage.memoryReport() - assertEquals(LogicalDType.FLOAT32, report.logicalType) + assertEquals(FP32, report.dtype) assertEquals("Q4_K", report.encoding.name) assertEquals(Ownership.BORROWED, report.ownership) assertEquals(DeviceKind.CPU, report.placement.device) @@ -176,7 +175,7 @@ class AcceptanceCriteriaTest { fun ac5_fileBackedReportsAllFields() { val storage = TensorStorage( shape = Shape(1000), - logicalType = LogicalDType.FLOAT16, + dtype = FP16, encoding = TensorEncoding.Dense(2), buffer = BufferHandle.FileBacked("/model.bin", 4096, 2000), placement = Placement.MMAP_WEIGHTS @@ -195,7 +194,7 @@ class AcceptanceCriteriaTest { fun ac6_weightsAreImmutableAndPersistent() { val weights = TensorStorage( shape = Shape(768, 768), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.FileBacked("/model.bin", 0, 768L * 768 * 4), placement = Placement.MMAP_WEIGHTS @@ -209,7 +208,7 @@ class AcceptanceCriteriaTest { fun ac6_activationsAreMutableAndTransient() { val activations = TensorStorage( shape = Shape(32, 768), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.Owned(ByteArray(32 * 768 * 4)), placement = Placement.CPU_HEAP @@ -228,13 +227,13 @@ class AcceptanceCriteriaTest { // Load two tensors — one borrowed, one owned (copy) tracker.record("borrowed_weight", TensorStorage( shape = Shape(100), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.Borrowed(ByteArray(400)) )) tracker.record("copied_activation", TensorStorage( shape = Shape(100), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.Owned(ByteArray(400)) )) diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/BufferHandleFactoryTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/BufferHandleFactoryTest.kt index 6eda00edb..80f12896c 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/BufferHandleFactoryTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/BufferHandleFactoryTest.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.tensor.storage import sk.ainet.lang.tensor.Shape @@ -13,6 +11,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue +import sk.ainet.lang.types.FP16 class BufferHandleFactoryTest { @@ -101,7 +100,7 @@ class TensorStorageFactoryTest { val storage = TensorStorageFactory.fromFloatArray(shape, data) assertEquals(shape, storage.shape) - assertEquals(LogicalDType.FLOAT32, storage.logicalType) + assertEquals(FP32, storage.dtype) assertEquals(TensorEncoding.Dense(4), storage.encoding) assertEquals(Ownership.OWNED, storage.ownership) assertEquals(24L, storage.logicalBytes) @@ -115,7 +114,7 @@ class TensorStorageFactoryTest { val data = intArrayOf(1, 2, 3, 4) val storage = TensorStorageFactory.fromIntArray(shape, data) - assertEquals(LogicalDType.INT32, storage.logicalType) + assertEquals(Int32, storage.dtype) assertEquals(Ownership.OWNED, storage.ownership) assertEquals(16L, storage.physicalBytes) // 4 * 4 } @@ -125,7 +124,7 @@ class TensorStorageFactoryTest { val data = ByteArray(144) val storage = TensorStorageFactory.fromRawBytes( shape = Shape(256), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Q4_K, data = data ) @@ -141,7 +140,7 @@ class TensorStorageFactoryTest { fun fileBackedCreatesImmutableStorage() { val storage = TensorStorageFactory.fileBacked( shape = Shape(512, 512), - logicalType = LogicalDType.FLOAT16, + dtype = FP16, encoding = TensorEncoding.Dense(2), path = "/model.bin", fileOffset = 0, @@ -159,7 +158,7 @@ class TensorStorageFactoryTest { val tensorData = DenseFloatArrayTensorData(Shape(3), floatArrayOf(1f, 2f, 3f)) val storage = TensorStorageFactory.fromTensorData(tensorData) - assertEquals(LogicalDType.FLOAT32, storage.logicalType) + assertEquals(FP32, storage.dtype) assertEquals(TensorEncoding.Dense(4), storage.encoding) assertEquals(3L, storage.elementCount) assertEquals(12L, storage.physicalBytes) @@ -173,7 +172,7 @@ class TensorStorageFactoryTest { val tensorData = DenseIntArrayTensorData(Shape(2), intArrayOf(10, 20)) val storage = TensorStorageFactory.fromTensorData(tensorData) - assertEquals(LogicalDType.INT32, storage.logicalType) + assertEquals(Int32, storage.dtype) assertEquals(TensorEncoding.Dense(4), storage.encoding) assertEquals(Ownership.OWNED, storage.ownership) } @@ -184,7 +183,7 @@ class TensorStorageFactoryTest { val tensorData = Q4_KBlockTensorData.fromRawBytes(Shape(256), packedData) val storage = TensorStorageFactory.fromTensorData(tensorData) - assertEquals(LogicalDType.FLOAT32, storage.logicalType) + assertEquals(FP32, storage.dtype) assertEquals(TensorEncoding.Q4_K, storage.encoding) assertEquals(Ownership.BORROWED, storage.ownership) assertEquals(144L, storage.physicalBytes) @@ -209,7 +208,7 @@ class TensorStorageFactoryTest { val tensorData = Q8_0BlockTensorData.fromRawBytes(Shape(32), packedData) val storage = TensorStorageFactory.fromTensorData(tensorData) - assertEquals(LogicalDType.FLOAT32, storage.logicalType) + assertEquals(FP32, storage.dtype) assertEquals(TensorEncoding.Q8_0, storage.encoding) assertEquals(Ownership.BORROWED, storage.ownership) assertEquals(34L, storage.physicalBytes) @@ -219,7 +218,7 @@ class TensorStorageFactoryTest { fun memoryReportFromFactory() { val storage = TensorStorageFactory.fromRawBytes( shape = Shape(256), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Q4_K, data = ByteArray(144) ) diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/CompressedKvAttentionTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/CompressedKvAttentionTest.kt index 3aac6a090..abe81bf07 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/CompressedKvAttentionTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/CompressedKvAttentionTest.kt @@ -1,11 +1,10 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.tensor.storage import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue +import sk.ainet.lang.types.FP32 /** * Tests for [CompressedKvAttention] — the bridge between KvCacheStore and SDPA. @@ -72,11 +71,11 @@ class CompressedKvAttentionTest { bridge.storeKeyValue(0, FloatArray(8), FloatArray(8)) val keyStorage = bridge.loadKeyStorageRaw(0) - assertEquals(LogicalDType.FLOAT32, keyStorage.logicalType) + assertEquals(FP32, keyStorage.dtype) assertEquals(Ownership.OWNED, keyStorage.ownership) val valueStorage = bridge.loadValueStorageRaw(0) - assertEquals(LogicalDType.FLOAT32, valueStorage.logicalType) + assertEquals(FP32, valueStorage.dtype) } @Test diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/ExplicitCopyApiTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/ExplicitCopyApiTest.kt index c6fc7b38f..1cf84ece1 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/ExplicitCopyApiTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/ExplicitCopyApiTest.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.tensor.storage import sk.ainet.lang.tensor.Shape @@ -83,7 +81,7 @@ class ExplicitCopyApiTest { val rawData = ByteArray(144) // Q4_K block val storage = TensorStorageFactory.fromRawBytes( shape = Shape(256), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Q4_K, data = rawData ) @@ -95,7 +93,7 @@ class ExplicitCopyApiTest { val rawData = ByteArray(144) val storage = TensorStorageFactory.fromRawBytesOwned( shape = Shape(256), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Q4_K, data = rawData ) diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/KvCacheStoreTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/KvCacheStoreTest.kt index b5570e2df..94cdd4837 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/KvCacheStoreTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/KvCacheStoreTest.kt @@ -1,11 +1,10 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.tensor.storage import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertTrue +import sk.ainet.lang.types.FP32 /** * Tests for [KvCacheStore] contract and [DefaultKvCacheStore] implementation. @@ -123,7 +122,7 @@ class KvCacheStoreTest { store.appendToken(0, FloatArray(8) { it.toFloat() }, FloatArray(8)) val storage = store.readKeyStorage(0) - assertEquals(LogicalDType.FLOAT32, storage.logicalType) + assertEquals(FP32, storage.dtype) assertEquals(Ownership.OWNED, storage.ownership) assertTrue(storage.isMutable) } diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/LogicalDTypeBridgeTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/LogicalDTypeBridgeTest.kt deleted file mode 100644 index ec35ec5ef..000000000 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/LogicalDTypeBridgeTest.kt +++ /dev/null @@ -1,106 +0,0 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - -package sk.ainet.lang.tensor.storage - -import sk.ainet.lang.tensor.Shape -import sk.ainet.lang.types.BF16 -import sk.ainet.lang.types.DType -import sk.ainet.lang.types.FP16 -import sk.ainet.lang.types.FP32 -import sk.ainet.lang.types.FP64 -import sk.ainet.lang.types.Int16 -import sk.ainet.lang.types.Int32 -import sk.ainet.lang.types.Int4 -import sk.ainet.lang.types.Int64 -import sk.ainet.lang.types.Int8 -import sk.ainet.lang.types.Ternary -import sk.ainet.lang.types.UInt16 -import sk.ainet.lang.types.UInt32 -import sk.ainet.lang.types.UInt64 -import sk.ainet.lang.types.UInt8 -import sk.ainet.lang.types.isFloatingPoint -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertSame - -/** - * SKEEP-003 Phase 0, decision #13: the two-way `LogicalDType` <-> `DType` bridge must be total - * and bijective so that `LogicalDType` can later be merged into `DType` without a semantic gap. - */ -class LogicalDTypeBridgeTest { - - private val expectedPairs: List> = listOf( - LogicalDType.TERNARY to Ternary, - LogicalDType.INT4 to Int4, - LogicalDType.INT8 to Int8, - LogicalDType.INT16 to Int16, - LogicalDType.INT32 to Int32, - LogicalDType.INT64 to Int64, - LogicalDType.UINT8 to UInt8, - LogicalDType.UINT16 to UInt16, - LogicalDType.UINT32 to UInt32, - LogicalDType.UINT64 to UInt64, - LogicalDType.FLOAT16 to FP16, - LogicalDType.BFLOAT16 to BF16, - LogicalDType.FLOAT32 to FP32, - LogicalDType.FLOAT64 to FP64, - ) - - @Test - fun bothTypeSystemsHaveFourteenMembers() { - assertEquals(14, LogicalDType.entries.size) - assertEquals(14, DType.getAllTypes().size) - assertEquals(14, expectedPairs.size) - } - - @Test - fun explicitPairTable() { - for ((logical, dtype) in expectedPairs) { - assertSame(dtype, logical.toDType(), "toDType of $logical") - assertEquals(logical, dtype.toLogicalDType(), "toLogicalDType of ${dtype.name}") - } - } - - @Test - fun logicalToDTypeAndBackIsIdentity() { - for (logical in LogicalDType.entries) { - assertEquals(logical, logical.toDType().toLogicalDType(), "round trip of $logical") - } - } - - @Test - fun dtypeToLogicalAndBackIsIdentity() { - for (dtype in DType.getAllTypes().values) { - assertSame(dtype, dtype.toLogicalDType().toDType(), "round trip of ${dtype.name}") - } - } - - @Test - fun mappingIsBijective() { - val images = LogicalDType.entries.map { it.toDType() }.toSet() - assertEquals(LogicalDType.entries.size, images.size, "toDType must be injective") - val preImages = DType.getAllTypes().values.map { it.toLogicalDType() }.toSet() - assertEquals(DType.getAllTypes().size, preImages.size, "toLogicalDType must be injective") - } - - @Test - fun widthAndFloatnessAgreeAcrossTheBridge() { - for (logical in LogicalDType.entries) { - val dtype = logical.toDType() - assertEquals(logical.sizeInBits, dtype.sizeInBits, "sizeInBits of $logical") - assertEquals(logical.isFloatingPoint, dtype.isFloatingPoint(), "isFloatingPoint of $logical") - } - } - - @Test - fun descriptorsExposeTheDType() { - val storage = TensorStorage( - shape = Shape(2, 3), - logicalType = LogicalDType.FLOAT32, - encoding = TensorEncoding.Dense(4), - buffer = BufferHandle.Owned(ByteArray(24)), - ) - assertSame(FP32, storage.dtype) - assertSame(FP32, storage.memoryReport().dtype) - } -} diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/MemoryTrackerTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/MemoryTrackerTest.kt index f75b9b90b..dfb97e34b 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/MemoryTrackerTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/MemoryTrackerTest.kt @@ -1,10 +1,10 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.tensor.storage import sk.ainet.lang.tensor.Shape import kotlin.test.Test import kotlin.test.assertEquals +import sk.ainet.lang.types.FP16 +import sk.ainet.lang.types.FP32 class MemoryTrackerTest { @@ -14,13 +14,13 @@ class MemoryTrackerTest { val s1 = TensorStorage( shape = Shape(100), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.Owned(ByteArray(400)) ) val s2 = TensorStorage( shape = Shape(256), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Q4_K, buffer = BufferHandle.Borrowed(ByteArray(144)) ) @@ -52,7 +52,7 @@ class MemoryTrackerTest { val tracker = MemoryTracker() tracker.record("x", TensorStorage( shape = Shape(10), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.Owned(ByteArray(40)) )) @@ -69,7 +69,7 @@ class MemoryTrackerTest { val tracker = MemoryTracker() tracker.record("mmap_weight", TensorStorage( shape = Shape(1000), - logicalType = LogicalDType.FLOAT16, + dtype = FP16, encoding = TensorEncoding.Dense(2), buffer = BufferHandle.FileBacked("/model.bin", 0, 2000), placement = Placement.MMAP_WEIGHTS diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/NarrowFloatStorageDecodeTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/NarrowFloatStorageDecodeTest.kt index eccae6879..84d678bb4 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/NarrowFloatStorageDecodeTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/NarrowFloatStorageDecodeTest.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.tensor.storage import sk.ainet.lang.tensor.Shape @@ -37,7 +35,7 @@ class NarrowFloatStorageDecodeTest { val bytes = packed(values, codec) return TensorStorage( shape = Shape(values.size), - logicalType = if (codec === Fp16Codec) LogicalDType.FLOAT16 else LogicalDType.BFLOAT16, + dtype = if (codec === Fp16Codec) FP16 else BF16, encoding = TensorEncoding.Dense(2), buffer = BufferHandle.Owned(bytes, 0, bytes.size.toLong()), ) @@ -79,14 +77,14 @@ class NarrowFloatStorageDecodeTest { @Test fun bf16_and_fp16_decode_differently_for_the_same_bytes() { - // Proves the codec is actually selected by logicalType rather than one being aliased + // Proves the codec is actually selected by dtype rather than one being aliased // to the other: the bit pattern 0x3C00 is 1.0 in fp16 but ~0.0078 in bf16. val bytes = byteArrayOf(0x00, 0x3C) val asFp16 = TensorStorageFactory.toTensorData( - TensorStorage(Shape(1), LogicalDType.FLOAT16, TensorEncoding.Dense(2), BufferHandle.Owned(bytes, 0, 2)), + TensorStorage(Shape(1), FP16, TensorEncoding.Dense(2), BufferHandle.Owned(bytes, 0, 2)), ).copyToFloatArray()[0] val asBf16 = TensorStorageFactory.toTensorData( - TensorStorage(Shape(1), LogicalDType.BFLOAT16, TensorEncoding.Dense(2), BufferHandle.Owned(bytes, 0, 2)), + TensorStorage(Shape(1), BF16, TensorEncoding.Dense(2), BufferHandle.Owned(bytes, 0, 2)), ).copyToFloatArray()[0] assertEquals(1.0f, asFp16) diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/NonContiguousStorageTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/NonContiguousStorageTest.kt index 921292e34..0dff522c9 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/NonContiguousStorageTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/NonContiguousStorageTest.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.tensor.storage import sk.ainet.lang.tensor.Shape @@ -9,6 +7,7 @@ import kotlin.test.assertFalse import kotlin.test.assertNotEquals import kotlin.test.assertNull import kotlin.test.assertTrue +import sk.ainet.lang.types.FP32 class NonContiguousStorageTest { @@ -16,7 +15,7 @@ class NonContiguousStorageTest { fun defaultStorage_stridesNull_isContiguousTrue() { val storage = TensorStorage( shape = Shape(4, 4), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.Owned(ByteArray(64)) ) @@ -29,7 +28,7 @@ class NonContiguousStorageTest { val strides = longArrayOf(768, 1) val storage = TensorStorage( shape = Shape(1024, 768), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.Owned(ByteArray(1024 * 768 * 4)), strides = strides, @@ -45,14 +44,14 @@ class NonContiguousStorageTest { val buf = BufferHandle.Owned(ByteArray(64)) val s1 = TensorStorage( shape = Shape(4, 4), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = buf, strides = longArrayOf(4, 1) ) val s2 = TensorStorage( shape = Shape(4, 4), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = buf, strides = longArrayOf(1, 4) // transposed strides @@ -63,16 +62,16 @@ class NonContiguousStorageTest { @Test fun equalityNullStridesMatch() { val buf = BufferHandle.Owned(ByteArray(16)) - val s1 = TensorStorage(Shape(4), LogicalDType.FLOAT32, TensorEncoding.Dense(4), buf) - val s2 = TensorStorage(Shape(4), LogicalDType.FLOAT32, TensorEncoding.Dense(4), buf) + val s1 = TensorStorage(Shape(4), FP32, TensorEncoding.Dense(4), buf) + val s2 = TensorStorage(Shape(4), FP32, TensorEncoding.Dense(4), buf) assertEquals(s1, s2) } @Test fun hashCodeDiffersWithDifferentStrides() { val buf = BufferHandle.Owned(ByteArray(64)) - val s1 = TensorStorage(Shape(4, 4), LogicalDType.FLOAT32, TensorEncoding.Dense(4), buf, strides = longArrayOf(4, 1)) - val s2 = TensorStorage(Shape(4, 4), LogicalDType.FLOAT32, TensorEncoding.Dense(4), buf, strides = longArrayOf(1, 4)) + val s1 = TensorStorage(Shape(4, 4), FP32, TensorEncoding.Dense(4), buf, strides = longArrayOf(4, 1)) + val s2 = TensorStorage(Shape(4, 4), FP32, TensorEncoding.Dense(4), buf, strides = longArrayOf(1, 4)) // Not guaranteed by contract but highly likely for different strides assertNotEquals(s1.hashCode(), s2.hashCode()) } @@ -81,7 +80,7 @@ class NonContiguousStorageTest { fun memoryReport_nonContiguous_reportsCorrectBytes() { val storage = TensorStorage( shape = Shape(8, 8), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.Owned(ByteArray(256)), strides = longArrayOf(8, 1), diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/PackedBlockStorageTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/PackedBlockStorageTest.kt index 7c0e1f80c..17e00b21d 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/PackedBlockStorageTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/PackedBlockStorageTest.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.tensor.storage import sk.ainet.lang.tensor.Shape @@ -8,6 +6,7 @@ import sk.ainet.lang.tensor.data.Q8_0BlockTensorData import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue +import sk.ainet.lang.types.FP32 class PackedBlockStorageTest { @@ -87,7 +86,7 @@ class PackedBlockStorageTest { val packed = td as PackedBlockStorage val storage = packed.toTensorStorage() - assertEquals(LogicalDType.FLOAT32, storage.logicalType) + assertEquals(FP32, storage.dtype) assertEquals(TensorEncoding.Q4_K, storage.encoding) assertEquals(Ownership.BORROWED, storage.ownership) assertEquals(144L, storage.physicalBytes) diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/StorageToTensorDataTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/StorageToTensorDataTest.kt index f64afe432..8192be975 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/StorageToTensorDataTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/StorageToTensorDataTest.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.tensor.storage import sk.ainet.lang.tensor.Shape @@ -98,7 +96,7 @@ class StorageToTensorDataTest { val storage = TensorStorageFactory.fromRawBytes( shape = Shape(3), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), data = floatBytes ) diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/TensorStorageContractTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/TensorStorageContractTest.kt index 589429936..f52763ee6 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/TensorStorageContractTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/TensorStorageContractTest.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.tensor.storage import sk.ainet.lang.tensor.Shape @@ -11,35 +9,24 @@ import kotlin.test.assertTrue class TensorStorageContractTest { - // --- LogicalDType --- - - @Test - fun logicalDTypeFromDTypeRoundTrips() { - assertEquals(LogicalDType.FLOAT32, LogicalDType.fromDType(FP32)) - assertEquals(LogicalDType.FLOAT16, LogicalDType.fromDType(FP16)) - assertEquals(LogicalDType.BFLOAT16, LogicalDType.fromDType(BF16)) - assertEquals(LogicalDType.INT32, LogicalDType.fromDType(Int32)) - assertEquals(LogicalDType.INT4, LogicalDType.fromDType(Int4)) - assertEquals(LogicalDType.TERNARY, LogicalDType.fromDType(Ternary)) - assertEquals(LogicalDType.UINT8, LogicalDType.fromDType(UInt8)) - } + // --- DType as the storage type --- @Test - fun logicalDTypeSizeInBytes() { - assertEquals(4, LogicalDType.FLOAT32.sizeInBytes) - assertEquals(2, LogicalDType.FLOAT16.sizeInBytes) - assertEquals(2, LogicalDType.BFLOAT16.sizeInBytes) - assertEquals(4, LogicalDType.INT32.sizeInBytes) - assertEquals(1, LogicalDType.INT8.sizeInBytes) - assertEquals(1, LogicalDType.INT4.sizeInBytes) // 4 bits rounds up to 1 byte + fun dtypeSizeInBytes() { + assertEquals(4, FP32.sizeInBytes) + assertEquals(2, FP16.sizeInBytes) + assertEquals(2, BF16.sizeInBytes) + assertEquals(4, Int32.sizeInBytes) + assertEquals(1, Int8.sizeInBytes) + assertEquals(1, Int4.sizeInBytes) // 4 bits rounds up to 1 byte } @Test - fun logicalDTypeProperties() { - assertTrue(LogicalDType.FLOAT32.isFloatingPoint) - assertTrue(LogicalDType.FLOAT32.isSigned) - assertFalse(LogicalDType.UINT8.isSigned) - assertFalse(LogicalDType.INT32.isFloatingPoint) + fun dtypeProperties() { + assertTrue(FP32.isFloatingPoint()) + assertTrue(FP32.isSigned) + assertFalse(UInt8.isSigned) + assertFalse(Int32.isFloatingPoint()) } // --- TensorEncoding --- @@ -152,7 +139,7 @@ class TensorStorageContractTest { val data = ByteArray(24) // 6 elements * 4 bytes val storage = TensorStorage( shape = shape, - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.Owned(data) ) @@ -171,7 +158,7 @@ class TensorStorageContractTest { val data = ByteArray(144) // 1 Q4_K block val storage = TensorStorage( shape = shape, - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Q4_K, buffer = BufferHandle.Borrowed(data) ) @@ -187,7 +174,7 @@ class TensorStorageContractTest { val shape = Shape(1024, 768) val storage = TensorStorage( shape = shape, - logicalType = LogicalDType.FLOAT16, + dtype = FP16, encoding = TensorEncoding.Dense(2), buffer = BufferHandle.FileBacked("/model.bin", fileOffset = 4096, sizeInBytes = 1024L * 768 * 2), placement = Placement.MMAP_WEIGHTS @@ -204,12 +191,12 @@ class TensorStorageContractTest { val shape = Shape(256) val storage = TensorStorage( shape = shape, - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Q4_K, buffer = BufferHandle.Borrowed(ByteArray(144)) ) val report = storage.memoryReport() - assertEquals(LogicalDType.FLOAT32, report.logicalType) + assertEquals(FP32, report.dtype) assertEquals("Q4_K", report.encoding.name) assertEquals(Ownership.BORROWED, report.ownership) assertEquals(1024L, report.logicalBytes) @@ -225,15 +212,15 @@ class TensorStorageContractTest { fun dtypeConstructorEqualsLogicalTypeConstructor() { val shape = Shape(2, 3) val buffer = BufferHandle.Borrowed(ByteArray(24)) // one instance: BufferHandle equality is identity - val viaLogical = TensorStorage(shape, LogicalDType.FLOAT32, TensorEncoding.Dense(4), buffer) + val viaLogical = TensorStorage(shape, FP32, TensorEncoding.Dense(4), buffer) val viaDType = TensorStorage(shape, FP32, TensorEncoding.Dense(4), buffer) assertEquals(viaLogical, viaDType) assertEquals(FP32, viaDType.dtype) - assertEquals(LogicalDType.FLOAT32, viaDType.logicalType) + assertEquals(FP32, viaDType.dtype) assertEquals(viaLogical.memoryReport(), viaDType.memoryReport()) val bf16 = TensorStorage(shape, BF16, TensorEncoding.Dense(2), BufferHandle.Borrowed(ByteArray(12)), Placement.CPU_HEAP) - assertEquals(LogicalDType.BFLOAT16, bf16.logicalType) + assertEquals(BF16, bf16.dtype) assertEquals(12L, bf16.logicalBytes) } @@ -242,17 +229,17 @@ class TensorStorageContractTest { val shape = Shape(4) val bytes = ByteArray(16) // BufferHandle subclasses have identity equality, so compare the descriptor fields. - fun sig(s: TensorStorage) = listOf(s.shape, s.logicalType, s.dtype, s.encoding, s.ownership, s.placement, s.physicalBytes) + fun sig(s: TensorStorage) = listOf(s.shape, s.dtype, s.dtype, s.encoding, s.ownership, s.placement, s.physicalBytes) assertEquals( - sig(TensorStorageFactory.fromRawBytes(shape, LogicalDType.INT32, TensorEncoding.Dense(4), bytes)), + sig(TensorStorageFactory.fromRawBytes(shape, Int32, TensorEncoding.Dense(4), bytes)), sig(TensorStorageFactory.fromRawBytes(shape, Int32, TensorEncoding.Dense(4), bytes)), ) assertEquals( - sig(TensorStorageFactory.fromRawBytesOwned(shape, LogicalDType.FLOAT16, TensorEncoding.Dense(2), ByteArray(8))), + sig(TensorStorageFactory.fromRawBytesOwned(shape, FP16, TensorEncoding.Dense(2), ByteArray(8))), sig(TensorStorageFactory.fromRawBytesOwned(shape, FP16, TensorEncoding.Dense(2), ByteArray(8))), ) assertEquals( - sig(TensorStorageFactory.fileBacked(shape, LogicalDType.FLOAT32, TensorEncoding.Dense(4), "/m.gguf", 128L, 16L)), + sig(TensorStorageFactory.fileBacked(shape, FP32, TensorEncoding.Dense(4), "/m.gguf", 128L, 16L)), sig(TensorStorageFactory.fileBacked(shape, FP32, TensorEncoding.Dense(4), "/m.gguf", 128L, 16L)), ) assertEquals(FP32, TensorStorageFactory.fromFloatArray(shape, FloatArray(4)).dtype) diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/TransferOpsTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/TransferOpsTest.kt index 71498f8bc..c70ffd106 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/TransferOpsTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/tensor/storage/TransferOpsTest.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.tensor.storage import sk.ainet.lang.tensor.Shape @@ -8,12 +6,13 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNotSame import kotlin.test.assertSame +import sk.ainet.lang.types.FP32 class TransferOpsTest { private fun ownedStorage(bytes: ByteArray = ByteArray(16) { it.toByte() }) = TensorStorage( shape = Shape(4), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.Owned(bytes), placement = Placement.CPU_HEAP @@ -21,7 +20,7 @@ class TransferOpsTest { private fun borrowedStorage() = TensorStorage( shape = Shape(4), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.Borrowed(ByteArray(16) { it.toByte() }), placement = Placement(device = DeviceKind.CPU, domain = MemoryDomain.MMAP_FILE) @@ -37,7 +36,7 @@ class TransferOpsTest { assertEquals(Ownership.OWNED, copy.ownership) assertEquals(storage.shape, copy.shape) - assertEquals(storage.logicalType, copy.logicalType) + assertEquals(storage.dtype, copy.dtype) assertEquals(storage.encoding, copy.encoding) assertEquals(MemoryDomain.HOST_HEAP, copy.placement.domain) @@ -64,7 +63,7 @@ class TransferOpsTest { val parent = BufferHandle.Owned(parentBytes) val storage = TensorStorage( shape = Shape(4), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.Aliased(parent, byteOffset = 8, sizeInBytes = 16), placement = Placement.CPU_HEAP @@ -90,7 +89,7 @@ class TransferOpsTest { }) val storage = TensorStorage( shape = Shape(4), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.FileBacked("/model.bin", 0, 16), placement = Placement.MMAP_WEIGHTS @@ -106,7 +105,7 @@ class TransferOpsTest { fun copyMaterialize_fileBackedBuffer_withoutResolver_throwsHelpfully() { val storage = TensorStorage( shape = Shape(4), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.FileBacked("/model.bin", 0, 16), placement = Placement.MMAP_WEIGHTS @@ -120,7 +119,7 @@ class TransferOpsTest { fun copyMaterialize_deviceResidentBuffer_throwsUnsupported() { val storage = TensorStorage( shape = Shape(4), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.DeviceResident("gpu:0", "opaque", 16, true) ) @@ -139,7 +138,7 @@ class TransferOpsTest { }) val storage = TensorStorage( shape = Shape(4), - logicalType = LogicalDType.FLOAT32, + dtype = FP32, encoding = TensorEncoding.Dense(4), buffer = BufferHandle.FileBacked("/model.bin", 0, 16), placement = Placement.MMAP_WEIGHTS diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/types/DTypeWitnessTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/types/DTypeWitnessTest.kt index aa89c2150..4233db569 100644 --- a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/types/DTypeWitnessTest.kt +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/types/DTypeWitnessTest.kt @@ -1,9 +1,5 @@ -@file:Suppress("DEPRECATION") // LogicalDType legacy path kept under test until removal (SKEEP-003 #1014) - package sk.ainet.lang.types -import sk.ainet.lang.tensor.storage.LogicalDType -import sk.ainet.lang.tensor.storage.toLogicalDType import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -26,10 +22,6 @@ class DTypeWitnessTest { listOf(Ternary, Int4, Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, FP16, BF16, FP32, FP64), DType.entries, ) - // same order as LogicalDType, so the two enumerations line up index by index - for ((i, logical) in LogicalDType.entries.withIndex()) { - assertSame(DType.entries[i], logical.toDType(), "entries[$i] vs $logical") - } } @Test @@ -69,11 +61,9 @@ class DTypeWitnessTest { } @Test - fun signednessAndByteWidthMatchTheStorageEnum() { + fun signednessAndByteWidth() { for (d in DType.entries) { - val logical = d.toLogicalDType() - assertEquals(logical.isSigned, d.isSigned, "isSigned of ${d.name}") - assertEquals(logical.sizeInBytes, d.sizeInBytes, "sizeInBytes of ${d.name}") + assertEquals((d.sizeInBits + 7) / 8, d.sizeInBytes, "sizeInBytes of ${d.name}") } assertFalse(UInt8.isSigned); assertFalse(UInt16.isSigned); assertFalse(UInt32.isSigned); assertFalse(UInt64.isSigned) assertTrue(Int8.isSigned); assertTrue(FP32.isSigned); assertTrue(Ternary.isSigned)