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 54a621657..29be4c0d9 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 @@ -634,6 +634,75 @@ public final class sk/ainet/java/TrainingResult { public fun toString ()Ljava/lang/String; } +public final class sk/ainet/lang/memory/AllocationSpec { + public static final field Companion Lsk/ainet/lang/memory/AllocationSpec$Companion; + public static final field DEFAULT_ALIGNMENT I + public fun (Lsk/ainet/lang/memory/Format;JLsk/ainet/lang/tensor/storage/MemoryDomain;Lsk/ainet/lang/memory/ScopeKind;ZI)V + public synthetic fun (Lsk/ainet/lang/memory/Format;JLsk/ainet/lang/tensor/storage/MemoryDomain;Lsk/ainet/lang/memory/ScopeKind;ZIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lsk/ainet/lang/memory/Format; + public final fun component2 ()J + public final fun component3 ()Lsk/ainet/lang/tensor/storage/MemoryDomain; + public final fun component4 ()Lsk/ainet/lang/memory/ScopeKind; + public final fun component5 ()Z + public final fun component6 ()I + public final fun copy (Lsk/ainet/lang/memory/Format;JLsk/ainet/lang/tensor/storage/MemoryDomain;Lsk/ainet/lang/memory/ScopeKind;ZI)Lsk/ainet/lang/memory/AllocationSpec; + public static synthetic fun copy$default (Lsk/ainet/lang/memory/AllocationSpec;Lsk/ainet/lang/memory/Format;JLsk/ainet/lang/tensor/storage/MemoryDomain;Lsk/ainet/lang/memory/ScopeKind;ZIILjava/lang/Object;)Lsk/ainet/lang/memory/AllocationSpec; + public fun equals (Ljava/lang/Object;)Z + public final fun getAlignment ()I + public final fun getBytes ()J + public final fun getBytesOrNull ()Ljava/lang/Long; + public final fun getDomain ()Lsk/ainet/lang/tensor/storage/MemoryDomain; + public final fun getElementCount ()J + public final fun getFormat ()Lsk/ainet/lang/memory/Format; + public final fun getMutable ()Z + public final fun getScope ()Lsk/ainet/lang/memory/ScopeKind; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class sk/ainet/lang/memory/AllocationSpec$Companion { + public final fun of (Lsk/ainet/lang/memory/Format;Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/MemoryDomain;Lsk/ainet/lang/memory/ScopeKind;ZI)Lsk/ainet/lang/memory/AllocationSpec; + public static synthetic fun of$default (Lsk/ainet/lang/memory/AllocationSpec$Companion;Lsk/ainet/lang/memory/Format;Lsk/ainet/lang/tensor/Shape;Lsk/ainet/lang/tensor/storage/MemoryDomain;Lsk/ainet/lang/memory/ScopeKind;ZIILjava/lang/Object;)Lsk/ainet/lang/memory/AllocationSpec; +} + +public abstract interface annotation class sk/ainet/lang/memory/ExperimentalMemoryApi : java/lang/annotation/Annotation { +} + +public final class sk/ainet/lang/memory/Format { + public static final field Companion Lsk/ainet/lang/memory/Format$Companion; + public fun (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;)V + public final fun component1 ()Lsk/ainet/lang/types/DType; + public final fun component2 ()Lsk/ainet/lang/tensor/storage/TensorEncoding; + public final fun copy (Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;)Lsk/ainet/lang/memory/Format; + public static synthetic fun copy$default (Lsk/ainet/lang/memory/Format;Lsk/ainet/lang/types/DType;Lsk/ainet/lang/tensor/storage/TensorEncoding;ILjava/lang/Object;)Lsk/ainet/lang/memory/Format; + public fun equals (Ljava/lang/Object;)Z + public final fun getDtype ()Lsk/ainet/lang/types/DType; + public final fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; + public fun hashCode ()I + public final fun isDense ()Z + public final fun physicalBytes (J)Ljava/lang/Long; + public fun toString ()Ljava/lang/String; +} + +public final class sk/ainet/lang/memory/Format$Companion { + public final fun dense (Lsk/ainet/lang/types/DType;)Lsk/ainet/lang/memory/Format; +} + +public final class sk/ainet/lang/memory/FormatKt { + public static final fun getFormat (Lsk/ainet/lang/tensor/Tensor;)Lsk/ainet/lang/memory/Format; + public static final fun getFormat (Lsk/ainet/lang/tensor/storage/TensorStorage;)Lsk/ainet/lang/memory/Format; + public static final fun getFormatOrNull (Lsk/ainet/lang/tensor/Tensor;)Lsk/ainet/lang/memory/Format; +} + +public final class sk/ainet/lang/memory/ScopeKind : java/lang/Enum { + public static final field AMBIENT Lsk/ainet/lang/memory/ScopeKind; + public static final field FORWARD Lsk/ainet/lang/memory/ScopeKind; + public static final field MODEL Lsk/ainet/lang/memory/ScopeKind; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lsk/ainet/lang/memory/ScopeKind; + public static fun values ()[Lsk/ainet/lang/memory/ScopeKind; +} + public final class sk/ainet/lang/nn/AvgPool2d : sk/ainet/lang/nn/Module { public fun (Lkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;ZLjava/lang/String;)V public synthetic fun (Lkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -2906,6 +2975,7 @@ public final class sk/ainet/lang/tensor/data/Bf16TensorData$Companion { public final class sk/ainet/lang/tensor/data/Bf16TensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/Bf16TensorData;)[F public static fun getCodec (Lsk/ainet/lang/tensor/data/Bf16TensorData;)Lsk/ainet/lang/types/NarrowFloatCodec; + public static fun getEncoding (Lsk/ainet/lang/tensor/data/Bf16TensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public final class sk/ainet/lang/tensor/data/Bf16TensorDataKt { @@ -2918,6 +2988,7 @@ public final class sk/ainet/lang/tensor/data/DenseFloatArrayTensorData : sk/aine public fun get ([I)Ljava/lang/Float; public synthetic fun get ([I)Ljava/lang/Object; public fun getBuffer ()[F + public fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public fun getShape ()Lsk/ainet/lang/tensor/Shape; public fun set ([IF)V public synthetic fun set ([ILjava/lang/Object;)V @@ -2929,6 +3000,7 @@ public final class sk/ainet/lang/tensor/data/DenseIntArrayTensorData : sk/ainet/ public fun get ([I)Ljava/lang/Integer; public synthetic fun get ([I)Ljava/lang/Object; public fun getBuffer ()[I + public fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public fun getShape ()Lsk/ainet/lang/tensor/Shape; public fun set ([II)V public synthetic fun set ([ILjava/lang/Object;)V @@ -2970,6 +3042,7 @@ public abstract interface class sk/ainet/lang/tensor/data/FloatArrayTensorData : public final class sk/ainet/lang/tensor/data/FloatArrayTensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/FloatArrayTensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/FloatArrayTensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public abstract interface class sk/ainet/lang/tensor/data/FloatBufferTensorData : sk/ainet/lang/tensor/data/TensorData { @@ -2978,6 +3051,7 @@ public abstract interface class sk/ainet/lang/tensor/data/FloatBufferTensorData public final class sk/ainet/lang/tensor/data/FloatBufferTensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/FloatBufferTensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/FloatBufferTensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public final class sk/ainet/lang/tensor/data/Fp16DenseTensorData : sk/ainet/lang/tensor/data/NarrowFloatDenseTensorData { @@ -2996,6 +3070,7 @@ public abstract interface class sk/ainet/lang/tensor/data/IntArrayTensorData : s public final class sk/ainet/lang/tensor/data/IntArrayTensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/IntArrayTensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/IntArrayTensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public abstract interface class sk/ainet/lang/tensor/data/ItemsAccessor { @@ -3009,6 +3084,7 @@ public final class sk/ainet/lang/tensor/data/LazyZeroFloatArrayTensorData : sk/a public fun get ([I)Ljava/lang/Float; public synthetic fun get ([I)Ljava/lang/Object; public fun getBuffer ()[F + public fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public fun getShape ()Lsk/ainet/lang/tensor/Shape; public fun set ([IF)V public synthetic fun set ([ILjava/lang/Object;)V @@ -3020,6 +3096,7 @@ public final class sk/ainet/lang/tensor/data/LazyZeroIntArrayTensorData : sk/ain public fun get ([I)Ljava/lang/Integer; public synthetic fun get ([I)Ljava/lang/Object; public fun getBuffer ()[I + public fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public fun getShape ()Lsk/ainet/lang/tensor/Shape; public fun set ([II)V public synthetic fun set ([ILjava/lang/Object;)V @@ -3041,6 +3118,7 @@ public final class sk/ainet/lang/tensor/data/MemorySegmentTensorData : sk/ainet/ public fun get ([I)Ljava/lang/Float; public synthetic fun get ([I)Ljava/lang/Object; public final fun getByteSize ()J + public fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public fun getSegment ()Ljava/lang/foreign/MemorySegment; public fun getSegmentByteOffset ()J public fun getShape ()Lsk/ainet/lang/tensor/Shape; @@ -3076,6 +3154,7 @@ public final class sk/ainet/lang/tensor/data/MmapFloatTensorData : sk/ainet/lang public fun copyToFloatArray ()[F public fun get ([I)Ljava/lang/Float; public synthetic fun get ([I)Ljava/lang/Object; + public fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public fun getFloatBuffer ()Ljava/nio/FloatBuffer; public fun getShape ()Lsk/ainet/lang/tensor/Shape; public fun set ([IF)V @@ -3103,6 +3182,7 @@ public class sk/ainet/lang/tensor/data/NarrowFloatDenseTensorData : sk/ainet/lan public fun get ([I)Ljava/lang/Float; public synthetic fun get ([I)Ljava/lang/Object; public fun getCodec ()Lsk/ainet/lang/types/NarrowFloatCodec; + public fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public fun getPackedData ()[B public fun getShape ()Lsk/ainet/lang/tensor/Shape; public fun set ([IF)V @@ -3120,6 +3200,7 @@ public final class sk/ainet/lang/tensor/data/NarrowFloatInputMajorTensorData : s public fun get ([I)Ljava/lang/Float; public synthetic fun get ([I)Ljava/lang/Object; public fun getCodec ()Lsk/ainet/lang/types/NarrowFloatCodec; + public fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public fun getPackedData ()[B public fun getShape ()Lsk/ainet/lang/tensor/Shape; public fun set ([IF)V @@ -3144,6 +3225,7 @@ public final class sk/ainet/lang/tensor/data/NarrowFloatTensorData$Companion { public final class sk/ainet/lang/tensor/data/NarrowFloatTensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/NarrowFloatTensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/NarrowFloatTensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public final class sk/ainet/lang/tensor/data/NarrowFloatTensorDataKt { @@ -3170,6 +3252,7 @@ public final class sk/ainet/lang/tensor/data/Q4MemorySegmentTensorData : sk/aine public fun getBlockCount ()I public fun getBlockSize ()I public fun getBytesPerBlock ()I + public fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public fun getSegment ()Ljava/lang/foreign/MemorySegment; public fun getSegmentByteOffset ()J public fun getShape ()Lsk/ainet/lang/tensor/Shape; @@ -3232,6 +3315,7 @@ public final class sk/ainet/lang/tensor/data/Q4_0TensorData$Companion { public final class sk/ainet/lang/tensor/data/Q4_0TensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/Q4_0TensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/Q4_0TensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public final class sk/ainet/lang/tensor/data/Q4_0TensorDataKt { @@ -3292,6 +3376,7 @@ public final class sk/ainet/lang/tensor/data/Q4_KTensorData$Companion { public final class sk/ainet/lang/tensor/data/Q4_KTensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/Q4_KTensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/Q4_KTensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public final class sk/ainet/lang/tensor/data/Q4_KTensorDataKt { @@ -3338,6 +3423,7 @@ public final class sk/ainet/lang/tensor/data/Q5_0TensorData$Companion { public final class sk/ainet/lang/tensor/data/Q5_0TensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/Q5_0TensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/Q5_0TensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public final class sk/ainet/lang/tensor/data/Q5_1BlockTensorData : sk/ainet/lang/tensor/data/Q5_1TensorData, sk/ainet/lang/tensor/storage/PackedBlockStorage { @@ -3380,6 +3466,7 @@ public final class sk/ainet/lang/tensor/data/Q5_1TensorData$Companion { public final class sk/ainet/lang/tensor/data/Q5_1TensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/Q5_1TensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/Q5_1TensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public final class sk/ainet/lang/tensor/data/Q5_KBlockTensorData : sk/ainet/lang/tensor/data/Q5_KTensorData, sk/ainet/lang/tensor/storage/PackedBlockStorage { @@ -3440,6 +3527,7 @@ public final class sk/ainet/lang/tensor/data/Q5_KTensorData$Companion { public final class sk/ainet/lang/tensor/data/Q5_KTensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/Q5_KTensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/Q5_KTensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public final class sk/ainet/lang/tensor/data/Q5_KTensorDataKt { @@ -3496,6 +3584,7 @@ public final class sk/ainet/lang/tensor/data/Q6_KTensorData$Companion { public final class sk/ainet/lang/tensor/data/Q6_KTensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/Q6_KTensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/Q6_KTensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public final class sk/ainet/lang/tensor/data/Q6_KTensorDataKt { @@ -3518,6 +3607,7 @@ public final class sk/ainet/lang/tensor/data/Q8MemorySegmentTensorData : sk/aine public fun getBlockCount ()I public fun getBlockSize ()I public fun getBytesPerBlock ()I + public fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public fun getSegment ()Ljava/lang/foreign/MemorySegment; public fun getSegmentByteOffset ()J public fun getShape ()Lsk/ainet/lang/tensor/Shape; @@ -3574,6 +3664,7 @@ public final class sk/ainet/lang/tensor/data/Q8_0TensorData$Companion { public final class sk/ainet/lang/tensor/data/Q8_0TensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/Q8_0TensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/Q8_0TensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public final class sk/ainet/lang/tensor/data/Q8_0TensorDataKt { @@ -3586,11 +3677,13 @@ public abstract interface class sk/ainet/lang/tensor/data/RowDequantSource { public abstract interface class sk/ainet/lang/tensor/data/TensorData : sk/ainet/lang/tensor/data/ItemsAccessor { public fun copyToFloatArray ()[F + public fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public abstract fun getShape ()Lsk/ainet/lang/tensor/Shape; } public final class sk/ainet/lang/tensor/data/TensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/TensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/TensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public abstract interface class sk/ainet/lang/tensor/data/TensorDataFactory { @@ -3661,6 +3754,7 @@ public abstract interface class sk/ainet/lang/tensor/data/TernaryTensorData : sk public final class sk/ainet/lang/tensor/data/TernaryTensorData$DefaultImpls { public static fun copyToFloatArray (Lsk/ainet/lang/tensor/data/TernaryTensorData;)[F + public static fun getEncoding (Lsk/ainet/lang/tensor/data/TernaryTensorData;)Lsk/ainet/lang/tensor/storage/TensorEncoding; } public final class sk/ainet/lang/tensor/data/TernaryTensorDataKt { @@ -3720,6 +3814,7 @@ public final class sk/ainet/lang/tensor/data/views/UnsqueezedTensorData : sk/ain public fun (Lsk/ainet/lang/tensor/data/TensorData;I)V public fun copyToFloatArray ()[F public fun get ([I)Ljava/lang/Object; + public fun getEncoding ()Lsk/ainet/lang/tensor/storage/TensorEncoding; public fun getShape ()Lsk/ainet/lang/tensor/Shape; public fun set ([ILjava/lang/Object;)V } @@ -5495,6 +5590,7 @@ public final class sk/ainet/lang/tensor/storage/StorageSpec { public final fun getOwnership ()Lsk/ainet/lang/tensor/storage/Ownership; public final fun getPlacement ()Lsk/ainet/lang/tensor/storage/Placement; public fun hashCode ()I + public final fun toAllocationSpec (J)Lsk/ainet/lang/memory/AllocationSpec; public fun toString ()Ljava/lang/String; } diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/memory/AllocationSpec.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/memory/AllocationSpec.kt new file mode 100644 index 000000000..118614e1a --- /dev/null +++ b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/memory/AllocationSpec.kt @@ -0,0 +1,74 @@ +package sk.ainet.lang.memory + +import sk.ainet.lang.tensor.Shape +import sk.ainet.lang.tensor.storage.MemoryDomain + +/** + * The lifetime class an allocation belongs to (SKEEP-003 §4.5). `Scope` itself — the object that + * allocates and frees — arrives with milestone M1; this enum is its `kind`, declared now so that + * memory plans and allocation specs can name the lifetime without depending on the allocator. + */ +@ExperimentalMemoryApi +public enum class ScopeKind { + /** Lives until the model is closed: weights, KV-cache backing, embedding tables. */ + MODEL, + /** Recycled every forward pass: activations, attention scratch, adapter outputs. */ + FORWARD, + /** Garbage-collected, no explicit lifetime — the default for notebooks, tests and ad-hoc tensors. */ + AMBIENT, +} + +/** + * What an allocation needs: the [Format] of the elements, how many, and where / for how long the + * bytes should live. Pure description — owns nothing. The single input of the memory plan + * (milestone M0) and, from M1, of `Storage.allocate(spec, scope)`. + * + * Replaces the never-consumed `StorageSpec` (decision recorded in SKEEP-003: "StorageSpec becomes + * the allocation spec"). + * + * @property format dtype + encoding of the elements + * @property elementCount logical number of elements + * @property domain where the bytes should be (heap, off-heap/pinned, mapped file, device …) + * @property scope the lifetime class the allocation belongs to + * @property mutable whether the bytes may be written after allocation + * @property alignment required byte alignment of the start of the buffer (SIMD kernels want 16–64) + */ +@ExperimentalMemoryApi +public data class AllocationSpec( + val format: Format, + val elementCount: Long, + val domain: MemoryDomain = MemoryDomain.HOST_HEAP, + val scope: ScopeKind = ScopeKind.AMBIENT, + val mutable: Boolean = true, + val alignment: Int = DEFAULT_ALIGNMENT, +) { + init { + require(elementCount >= 0) { "elementCount must be >= 0, was $elementCount" } + require(alignment > 0 && (alignment and (alignment - 1)) == 0) { "alignment must be a power of two, was $alignment" } + } + + /** Physical bytes this allocation needs, or `null` when the encoding cannot tell (opaque payloads). */ + val bytesOrNull: Long? get() = format.physicalBytes(elementCount) + + /** + * Physical bytes this allocation needs. + * @throws IllegalStateException for an encoding that cannot compute its size (see [bytesOrNull]) + */ + val bytes: Long + get() = bytesOrNull ?: throw IllegalStateException("Encoding ${format.encoding.name} cannot compute a byte size for $elementCount elements") + + public companion object { + /** 64 bytes: satisfies AVX-512 / NEON / cache-line alignment for every current kernel. */ + public const val DEFAULT_ALIGNMENT: Int = 64 + + /** Spec for a tensor of [shape] in [format]. */ + public fun of( + format: Format, + shape: Shape, + domain: MemoryDomain = MemoryDomain.HOST_HEAP, + scope: ScopeKind = ScopeKind.AMBIENT, + mutable: Boolean = true, + alignment: Int = DEFAULT_ALIGNMENT, + ): AllocationSpec = AllocationSpec(format, shape.volume.toLong(), domain, scope, mutable, alignment) + } +} diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/memory/ExperimentalMemoryApi.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/memory/ExperimentalMemoryApi.kt new file mode 100644 index 000000000..d7406d7cd --- /dev/null +++ b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/memory/ExperimentalMemoryApi.kt @@ -0,0 +1,18 @@ +package sk.ainet.lang.memory + +/** + * Marks the SKEEP-003 memory-architecture API (`sk.ainet.lang.memory`) that is still being + * shaped through milestones M0–M1 (`Format`, `AllocationSpec`, later `Storage`, `Scope`, + * `TensorView`, …). The types are usable and tested, but their shape may still change before the + * compatibility promise applies; opt in explicitly with `@OptIn(ExperimentalMemoryApi::class)`. + */ +@RequiresOptIn( + message = "SKEEP-003 memory-architecture API: usable, but may change until milestone M1 is complete.", + level = RequiresOptIn.Level.WARNING, +) +@Retention(AnnotationRetention.BINARY) +@Target( + AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, + AnnotationTarget.TYPEALIAS, AnnotationTarget.CONSTRUCTOR, +) +public annotation class ExperimentalMemoryApi diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/memory/Format.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/memory/Format.kt new file mode 100644 index 000000000..3991d3e10 --- /dev/null +++ b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/memory/Format.kt @@ -0,0 +1,58 @@ +package sk.ainet.lang.memory + +import sk.ainet.lang.tensor.Tensor +import sk.ainet.lang.tensor.storage.TensorEncoding +import sk.ainet.lang.tensor.storage.TensorStorage +import sk.ainet.lang.types.DType + +/** + * The pair `(dtype, encoding)` — **what** a value means and **how** its bytes are laid out. + * + * A Q4_K weight is `Format(FP32, TensorEncoding.Q4_K)`: logically FP32, stored as Q4_K blocks. A + * plain float tensor is `Format(FP32, TensorEncoding.Dense(4))`. Kernel dispatch keys on formats + * (SKEEP-003 §0, §5); rule 3 — the logical dtype is never erased by a packed encoding. + * + * `Format` is pure metadata: it owns no bytes and carries no shape. + */ +@ExperimentalMemoryApi +public data class Format(val dtype: DType, val encoding: TensorEncoding) { + + /** True when the bytes are the dtype's own dense representation (no block packing). */ + val isDense: Boolean get() = encoding is TensorEncoding.Dense + + /** Physical bytes for [elementCount] elements under this format, or `null` if the encoding cannot tell. */ + public fun physicalBytes(elementCount: Long): Long? = encoding.physicalBytes(elementCount) + + /** `Float32/Q4_K`, `Float32/Dense(4B)` — the form the `toString()` renderer prints. */ + override fun toString(): String = "${dtype.name}/${encoding.name}" + + public companion object { + /** The dense format of [dtype] at its own width (`Dense(dtype.sizeInBytes)`). */ + public fun dense(dtype: DType): Format = Format(dtype, TensorEncoding.Dense(dtype.sizeInBytes)) + } +} + +/** + * The [Format] of this tensor: its [Tensor.dtype] witness mapped back to a [DType] plus the + * encoding its data reports ([sk.ainet.lang.tensor.data.TensorData.encoding], dense at the + * dtype's width when the data reports none). + * + * @throws IllegalStateException if [Tensor.dtype] is not a concrete dtype class (e.g. `DType::class`) + */ +@ExperimentalMemoryApi +public val Tensor<*, *>.format: Format + get() = formatOrNull + ?: throw IllegalStateException("Tensor.dtype ${this.dtype} is not a concrete DType witness; cannot derive a Format") + +/** The [Format] of this tensor, or `null` if its dtype witness is not a concrete dtype class. */ +@ExperimentalMemoryApi +public val Tensor<*, *>.formatOrNull: Format? + get() { + val dt = DType.fromWitnessOrNull(this.dtype) ?: return null + return Format(dt, this.data.encoding ?: TensorEncoding.Dense(dt.sizeInBytes)) + } + +/** The [Format] of this storage descriptor: `(dtype, encoding)`. */ +@ExperimentalMemoryApi +public val TensorStorage.format: Format + get() = Format(dtype, encoding) diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/data/NarrowFloatTensorData.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/data/NarrowFloatTensorData.kt index 1f8f916d8..fce7bdc9b 100644 --- a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/data/NarrowFloatTensorData.kt +++ b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/data/NarrowFloatTensorData.kt @@ -1,6 +1,7 @@ package sk.ainet.lang.tensor.data import sk.ainet.lang.tensor.Shape +import sk.ainet.lang.tensor.storage.TensorEncoding import sk.ainet.lang.types.DType import sk.ainet.lang.types.Fp16Codec import sk.ainet.lang.types.NarrowFloatCodec @@ -58,6 +59,9 @@ public open class NarrowFloatDenseTensorData( private val strides: IntArray = shape.computeStrides() override val packedData: ByteArray get() = data + /** Physically two bytes per element whatever the declared dtype witness. */ + override val encoding: TensorEncoding get() = TensorEncoding.Dense(NarrowFloatTensorData.BYTES_PER_ELEMENT) + init { val requiredBytes = shape.volume * NarrowFloatTensorData.BYTES_PER_ELEMENT require(data.size >= requiredBytes) { diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/data/TensorData.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/data/TensorData.kt index c287d40a5..b22eeab39 100644 --- a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/data/TensorData.kt +++ b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/data/TensorData.kt @@ -68,6 +68,18 @@ public interface TensorData : ItemsAccessor { */ public val shape: Shape + /** + * How this data's bytes are laid out, or `null` when they are the plain dense representation + * of the tensor's logical dtype (the default for array-backed data). + * + * Packed implementations (Q4_0 … Q8_0, Q4_K/Q5_K/Q6_K, ternary) report their block encoding; + * narrow-float data reports `Dense(2)`. Together with the tensor's dtype witness this yields + * the tensor's `Format` (SKEEP-003 rule 3: a packed weight is *logically* FP32 — the encoding + * says how it is stored, it never replaces the dtype). A default member, not an abstract one, + * because `TensorData` is implemented outside this module. + */ + public val encoding: sk.ainet.lang.tensor.storage.TensorEncoding? get() = null + /** * Copies all tensor data to a FloatArray. * diff --git a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/StorageSpec.kt b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/StorageSpec.kt index f2a40c629..927d9fe2b 100644 --- a/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/StorageSpec.kt +++ b/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/storage/StorageSpec.kt @@ -1,5 +1,9 @@ package sk.ainet.lang.tensor.storage +import sk.ainet.lang.memory.AllocationSpec +import sk.ainet.lang.memory.ExperimentalMemoryApi +import sk.ainet.lang.memory.Format +import sk.ainet.lang.memory.ScopeKind import sk.ainet.lang.types.DType /** @@ -11,7 +15,15 @@ import sk.ainet.lang.types.DType * [sk.ainet.lang.tensor.data.TensorFactoryRegistry]). Existing dtype-based * lookups remain as a convenience — they build a default [StorageSpec] * with [TensorEncoding.Dense] and [Ownership.OWNED]. + * + * Deprecated (SKEEP-003 Phase 0): never consumed by any factory; the allocation + * description is [sk.ainet.lang.memory.AllocationSpec] (`Format` + element count + + * domain + scope). Use [toAllocationSpec] to convert. Removed at the next major release. */ +@Deprecated( + message = "StorageSpec was never consumed; describe allocations with sk.ainet.lang.memory.AllocationSpec (SKEEP-003).", + replaceWith = ReplaceWith("AllocationSpec", "sk.ainet.lang.memory.AllocationSpec"), +) public data class StorageSpec( val logicalType: LogicalDType, val encoding: TensorEncoding = TensorEncoding.Dense(logicalType.sizeInBytes), @@ -21,8 +33,24 @@ public data class StorageSpec( /** The [DType] of [logicalType] (SKEEP-003 Phase 0 bridge; see [LogicalDType.toDType]). */ val dtype: DType get() = logicalType.toDType() + /** + * The [AllocationSpec] equivalent of this spec for [elementCount] elements: `Format(dtype, + * encoding)`, the placement's memory domain, `MODEL` scope for persistent placements and + * `AMBIENT` otherwise, mutable only when owned. + */ + @OptIn(ExperimentalMemoryApi::class) + public fun toAllocationSpec(elementCount: Long): AllocationSpec = AllocationSpec( + format = Format(dtype, encoding), + elementCount = elementCount, + domain = placement.domain, + scope = if (placement.residency == Residency.PERSISTENT) ScopeKind.MODEL else ScopeKind.AMBIENT, + mutable = ownership == Ownership.OWNED, + ) + + @Suppress("DEPRECATION") // the factories build the deprecated type on purpose public companion object { /** Build a default spec from a legacy DType (dense, owned, CPU heap). */ + @Deprecated("StorageSpec is deprecated; build an AllocationSpec (sk.ainet.lang.memory).") public fun fromDType(dtype: DType): StorageSpec { val logical = dtype.toLogicalDType() return StorageSpec( @@ -34,6 +62,7 @@ public data class StorageSpec( } /** Spec for borrowed dense data. */ + @Deprecated("StorageSpec is deprecated; build an AllocationSpec (sk.ainet.lang.memory).") public fun borrowed(dtype: DType): StorageSpec { val logical = dtype.toLogicalDType() return StorageSpec( @@ -45,6 +74,7 @@ public data class StorageSpec( } /** Spec for Q4_K packed data. */ + @Deprecated("StorageSpec is deprecated; build an AllocationSpec (sk.ainet.lang.memory).") public fun q4k(placement: Placement = Placement.CPU_HEAP): StorageSpec = StorageSpec( logicalType = LogicalDType.FLOAT32, encoding = TensorEncoding.Q4_K, @@ -53,6 +83,7 @@ public data class StorageSpec( ) /** Spec for Q8_0 packed data. */ + @Deprecated("StorageSpec is deprecated; build an AllocationSpec (sk.ainet.lang.memory).") public fun q80(placement: Placement = Placement.CPU_HEAP): StorageSpec = StorageSpec( logicalType = LogicalDType.FLOAT32, encoding = TensorEncoding.Q8_0, @@ -61,6 +92,7 @@ public data class StorageSpec( ) /** Spec for file-backed weights. */ + @Deprecated("StorageSpec is deprecated; build an AllocationSpec (sk.ainet.lang.memory).") public fun mmapWeights(dtype: DType): StorageSpec { val logical = dtype.toLogicalDType() return StorageSpec( diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/memory/AllocationSpecTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/memory/AllocationSpecTest.kt new file mode 100644 index 000000000..75ffa640e --- /dev/null +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/memory/AllocationSpecTest.kt @@ -0,0 +1,86 @@ +package sk.ainet.lang.memory + +import sk.ainet.lang.tensor.Shape +import sk.ainet.lang.tensor.storage.MemoryDomain +import sk.ainet.lang.tensor.storage.Ownership +import sk.ainet.lang.tensor.storage.Placement +import sk.ainet.lang.tensor.storage.StorageSpec +import sk.ainet.lang.tensor.storage.TensorEncoding +import sk.ainet.lang.types.BF16 +import sk.ainet.lang.types.FP32 +import sk.ainet.lang.types.Int8 +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertFalse +import kotlin.test.assertNull +import kotlin.test.assertTrue + +/** SKEEP-003 Phase 0: `AllocationSpec` replaces the never-consumed `StorageSpec`. */ +@OptIn(ExperimentalMemoryApi::class) +@Suppress("DEPRECATION") // StorageSpec.toAllocationSpec is the migration path under test +class AllocationSpecTest { + + @Test + fun bytesFollowTheEncoding() { + assertEquals(4L * 1000, AllocationSpec(Format.dense(FP32), 1000).bytes) + assertEquals(2L * 1000, AllocationSpec(Format.dense(BF16), 1000).bytes) + assertEquals(1000L, AllocationSpec(Format.dense(Int8), 1000).bytes) + // Q4_K: 144 bytes per 256 elements + assertEquals(144L * 4, AllocationSpec(Format(FP32, TensorEncoding.Q4_K), 1024).bytes) + assertEquals(144L, AllocationSpec(Format(FP32, TensorEncoding.Q4_K), 1).bytes) // partial block rounds up + // Q8_0: 34 bytes per 32 elements + assertEquals(34L * 2, AllocationSpec.of(Format(FP32, TensorEncoding.Q8_0), Shape(2, 32)).bytes) + // TurboQuant 4-bit, block 128: seed(4) + 4 groups × 2 B scales + 64 B codes = 76 per block + val tq = TensorEncoding.TurboQuantPolar(bitsPerElement = 4, blockSize = 128) + assertEquals(tq.physicalBytes(256), AllocationSpec(Format(FP32, tq), 256).bytes) + } + + @Test + fun opaqueEncodingHasNoComputableSize() { + val spec = AllocationSpec(Format(FP32, TensorEncoding.Opaque("IQ2_XXS", 0)), 64) + // Opaque carries its raw byte count; zero is "unknown" → physicalBytes may be null or 0 depending on the encoding + val b = spec.bytesOrNull + assertTrue(b == null || b == 0L) + } + + @Test + fun defaultsAreAmbientHeapMutableAligned64() { + val s = AllocationSpec(Format.dense(FP32), 8) + assertEquals(MemoryDomain.HOST_HEAP, s.domain) + assertEquals(ScopeKind.AMBIENT, s.scope) + assertTrue(s.mutable) + assertEquals(64, s.alignment) + assertFalse(s.format.isDense.not()) + } + + @Test + fun validation() { + assertFailsWith { AllocationSpec(Format.dense(FP32), -1) } + assertFailsWith { AllocationSpec(Format.dense(FP32), 1, alignment = 48) } + assertFailsWith { AllocationSpec(Format.dense(FP32), 1, alignment = 0) } + } + + @Test + fun storageSpecConvertsToAllocationSpec() { + val weights = StorageSpec.q4k(Placement.MMAP_WEIGHTS).toAllocationSpec(1024) + assertEquals(Format(FP32, TensorEncoding.Q4_K), weights.format) + assertEquals(1024L, weights.elementCount) + assertEquals(MemoryDomain.MMAP_FILE, weights.domain) + assertEquals(ScopeKind.MODEL, weights.scope) // persistent placement → model lifetime + assertFalse(weights.mutable) // borrowed packed bytes + + val owned = StorageSpec.fromDType(BF16).toAllocationSpec(10) + assertEquals(Format.dense(BF16), owned.format) + assertEquals(ScopeKind.AMBIENT, owned.scope) + assertTrue(owned.mutable) + assertEquals(20L, owned.bytes) + assertEquals(Ownership.OWNED, StorageSpec.fromDType(BF16).ownership) + } + + @Test + fun scopeKindHasTheThreeLifetimes() { + assertEquals(listOf(ScopeKind.MODEL, ScopeKind.FORWARD, ScopeKind.AMBIENT), ScopeKind.entries) + assertNull(ScopeKind.entries.firstOrNull { it.name == "DEVICE" }) + } +} diff --git a/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/memory/FormatTest.kt b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/memory/FormatTest.kt new file mode 100644 index 000000000..36ef9b7cd --- /dev/null +++ b/skainet-lang/skainet-lang-core/src/commonTest/kotlin/sk/ainet/lang/memory/FormatTest.kt @@ -0,0 +1,98 @@ +package sk.ainet.lang.memory + +import sk.ainet.lang.tensor.Shape +import sk.ainet.lang.tensor.VoidOpsTensor +import sk.ainet.lang.tensor.data.Bf16DenseTensorData +import sk.ainet.lang.tensor.data.DenseFloatArrayTensorData +import sk.ainet.lang.tensor.data.DenseIntArrayTensorData +import sk.ainet.lang.tensor.data.Fp16DenseTensorData +import sk.ainet.lang.tensor.data.Q4_0BlockTensorData +import sk.ainet.lang.tensor.data.Q4_KBlockTensorData +import sk.ainet.lang.tensor.data.Q5_0BlockTensorData +import sk.ainet.lang.tensor.data.Q5_1BlockTensorData +import sk.ainet.lang.tensor.data.Q5_KBlockTensorData +import sk.ainet.lang.tensor.data.Q6_KBlockTensorData +import sk.ainet.lang.tensor.data.Q8_0BlockTensorData +import sk.ainet.lang.tensor.data.TensorData +import sk.ainet.lang.tensor.data.Ternary2BitTensorData +import sk.ainet.lang.tensor.storage.BufferHandle +import sk.ainet.lang.tensor.storage.TensorEncoding +import sk.ainet.lang.tensor.storage.TensorStorage +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.Int32 +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNull +import kotlin.test.assertSame +import kotlin.test.assertTrue + +/** + * SKEEP-003 M0-A3: every tensor reports a coherent `Format(dtype, encoding)`; a packed weight is + * logically FP32 with its block encoding — never a `Byte`-typed tensor. + */ +@OptIn(ExperimentalMemoryApi::class) +class FormatTest { + + @Suppress("UNCHECKED_CAST") + private fun tensor(data: TensorData<*, *>, dtype: kotlin.reflect.KClass) = + VoidOpsTensor(data as TensorData, dtype) + + @Test + fun denseArrayDataReportsNoEncodingAndDenseFormat() { + val f = DenseFloatArrayTensorData(Shape(2, 2), FloatArray(4)) + assertNull(f.encoding) + assertEquals(Format(FP32, TensorEncoding.Dense(4)), tensor(f, FP32::class).format) + assertTrue(tensor(f, FP32::class).format.isDense) + + val i = DenseIntArrayTensorData(Shape(3), IntArray(3)) + assertEquals(Format.dense(Int32), tensor(i, Int32::class).format) + } + + @Test + fun narrowFloatDataIsDenseAtTwoBytes() { + assertEquals(Format(BF16, TensorEncoding.Dense(2)), tensor(Bf16DenseTensorData(Shape(4), ByteArray(8)), BF16::class).format) + assertEquals(Format(FP16, TensorEncoding.Dense(2)), tensor(Fp16DenseTensorData(Shape(4), ByteArray(8)), FP16::class).format) + // the physical encoding is reported even if the tensor is declared at a wider dtype + assertEquals(TensorEncoding.Dense(2), tensor(Bf16DenseTensorData(Shape(4), ByteArray(8)), FP32::class).format.encoding) + } + + @Test + fun packedWeightsAreLogicallyFp32WithTheirBlockEncoding() { + val cases: List, TensorEncoding>> = listOf( + Q4_0BlockTensorData(Shape(2, 32), ByteArray(2 * 18)) to TensorEncoding.Q4_0, + Q5_0BlockTensorData(Shape(2, 32), ByteArray(2 * 22)) to TensorEncoding.Q5_0, + Q5_1BlockTensorData(Shape(2, 32), ByteArray(2 * 24)) to TensorEncoding.Q5_1, + Q8_0BlockTensorData(Shape(2, 32), ByteArray(2 * 34)) to TensorEncoding.Q8_0, + Q4_KBlockTensorData(Shape(1, 256), ByteArray(144)) to TensorEncoding.Q4_K, + Q5_KBlockTensorData(Shape(1, 256), ByteArray(176)) to TensorEncoding.Q5_K, + Q6_KBlockTensorData(Shape(1, 256), ByteArray(210)) to TensorEncoding.Q6_K, + Ternary2BitTensorData.zeros(Shape(2, 8)) to TensorEncoding.TernaryPacked, + ) + for ((data, enc) in cases) { + assertEquals(enc, data.encoding, "TensorData.encoding of ${data::class.simpleName}") + val fmt = tensor(data, FP32::class).format + assertEquals(Format(FP32, enc), fmt, "Format of ${data::class.simpleName}") + assertSame(FP32, fmt.dtype) + assertEquals("Float32/${enc.name}", fmt.toString()) + } + } + + @Test + fun storageFormatPairsDtypeAndEncoding() { + val s = TensorStorage(Shape(1, 256), FP32, TensorEncoding.Q4_K, BufferHandle.Borrowed(ByteArray(144))) + assertEquals(Format(FP32, TensorEncoding.Q4_K), s.format) + assertEquals(144L, s.format.physicalBytes(256)) + assertEquals(Format.dense(FP32), TensorStorage(Shape(2), FP32, TensorEncoding.Dense(4), BufferHandle.Borrowed(ByteArray(8))).format) + } + + @Test + fun nonConcreteWitnessHasNoFormat() { + val t = VoidOpsTensor(DenseFloatArrayTensorData(Shape(1), FloatArray(1)), DType::class) + assertNull(t.formatOrNull) + assertFailsWith { t.format } + } +} 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 index 70b09b388..f03767e0a 100644 --- 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 @@ -25,6 +25,7 @@ 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. */ +@Suppress("DEPRECATION") // StorageSpec is exercised on purpose: the bridge must keep the legacy descriptor working class LogicalDTypeBridgeTest { private val expectedPairs: List> = listOf( diff --git a/skainet-lang/skainet-lang-core/src/jvmMain/kotlin/sk/ainet/lang/tensor/data/Q4MemorySegmentTensorData.kt b/skainet-lang/skainet-lang-core/src/jvmMain/kotlin/sk/ainet/lang/tensor/data/Q4MemorySegmentTensorData.kt index e2125903a..8834b99d3 100644 --- a/skainet-lang/skainet-lang-core/src/jvmMain/kotlin/sk/ainet/lang/tensor/data/Q4MemorySegmentTensorData.kt +++ b/skainet-lang/skainet-lang-core/src/jvmMain/kotlin/sk/ainet/lang/tensor/data/Q4MemorySegmentTensorData.kt @@ -1,6 +1,7 @@ package sk.ainet.lang.tensor.data import sk.ainet.lang.tensor.Shape +import sk.ainet.lang.tensor.storage.TensorEncoding import sk.ainet.lang.types.DType import java.lang.foreign.Arena import java.lang.foreign.MemorySegment @@ -44,6 +45,8 @@ public class Q4MemorySegmentTensorData( override val shape: Shape = Shape(initialShape.dimensions.copyOf()) private val strides: IntArray = shape.computeStrides() + override val encoding: TensorEncoding get() = TensorEncoding.Q4_0 + override val blockSize: Int = 32 override val bytesPerBlock: Int = 18 // 2 scale + 16 codes override val blockCount: Int = (shape.volume + blockSize - 1) / blockSize diff --git a/skainet-lang/skainet-lang-core/src/jvmMain/kotlin/sk/ainet/lang/tensor/data/Q8MemorySegmentTensorData.kt b/skainet-lang/skainet-lang-core/src/jvmMain/kotlin/sk/ainet/lang/tensor/data/Q8MemorySegmentTensorData.kt index e99b14bad..3a7e46896 100644 --- a/skainet-lang/skainet-lang-core/src/jvmMain/kotlin/sk/ainet/lang/tensor/data/Q8MemorySegmentTensorData.kt +++ b/skainet-lang/skainet-lang-core/src/jvmMain/kotlin/sk/ainet/lang/tensor/data/Q8MemorySegmentTensorData.kt @@ -1,6 +1,7 @@ package sk.ainet.lang.tensor.data import sk.ainet.lang.tensor.Shape +import sk.ainet.lang.tensor.storage.TensorEncoding import sk.ainet.lang.types.DType import java.lang.foreign.Arena import java.lang.foreign.MemorySegment @@ -42,6 +43,8 @@ public class Q8MemorySegmentTensorData( override val shape: Shape = Shape(initialShape.dimensions.copyOf()) private val strides: IntArray = shape.computeStrides() + override val encoding: TensorEncoding get() = TensorEncoding.Q8_0 + override val blockSize: Int = 32 override val bytesPerBlock: Int = 34 // 2 scale + 32 codes override val blockCount: Int = (shape.volume + blockSize - 1) / blockSize