summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Peter Christen <mc@yacy.net>2025-11-23 15:35:46 +0100
committerMichael Peter Christen <mc@yacy.net>2025-11-23 15:35:46 +0100
commitf1d17d94608ae50d9f3139a6f63c9435abd7d6ea (patch)
treeaec637459bf250e81921ae246781cd85d8b228f5
parentdf6e72b83ce15a631a512376febfcbc4463bb831 (diff)
refactoring
-rw-r--r--source/net/yacy/ai/llama3/Llama.java101
-rw-r--r--source/net/yacy/ai/llama3/Llama3.java2
-rw-r--r--source/net/yacy/ai/llama3/Model/GGMLTensorEntry.java11
-rw-r--r--source/net/yacy/ai/llama3/Model/GGUF.java8
-rw-r--r--source/net/yacy/ai/llama3/Model/ModelLoader.java8
-rw-r--r--source/net/yacy/ai/llama3/Model/Vocabulary.java4
-rw-r--r--source/net/yacy/ai/llama3/Sampler.java9
-rw-r--r--source/net/yacy/ai/llama3/Tensor/AbstractFloatTensor.java269
-rw-r--r--source/net/yacy/ai/llama3/Tensor/ArrayFloatTensor.java26
-rw-r--r--source/net/yacy/ai/llama3/Tensor/BF16FloatTensor.java2
-rw-r--r--source/net/yacy/ai/llama3/Tensor/DirectBufferFloatTensor.java6
-rw-r--r--source/net/yacy/ai/llama3/Tensor/F16FloatTensor.java2
-rw-r--r--source/net/yacy/ai/llama3/Tensor/FloatTensor.java248
-rw-r--r--source/net/yacy/ai/llama3/Tensor/Q4_0FloatTensor.java12
-rw-r--r--source/net/yacy/ai/llama3/Tensor/Q8_0FloatTensor.java12
-rw-r--r--source/net/yacy/ai/llama3/Tensor/Tensor.java100
16 files changed, 409 insertions, 411 deletions
diff --git a/source/net/yacy/ai/llama3/Llama.java b/source/net/yacy/ai/llama3/Llama.java
index fa35ff418..6735d1d3e 100644
--- a/source/net/yacy/ai/llama3/Llama.java
+++ b/source/net/yacy/ai/llama3/Llama.java
@@ -32,8 +32,9 @@ import java.util.stream.Stream;
import net.yacy.ai.llama3.Model.Arch;
import net.yacy.ai.llama3.Model.Tokenizer;
+import net.yacy.ai.llama3.Tensor.AbstractFloatTensor;
import net.yacy.ai.llama3.Tensor.DirectBufferFloatTensor;
-import net.yacy.ai.llama3.Tensor.Tensor;
+import net.yacy.ai.llama3.Tensor.FloatTensor;
public final class Llama {
@@ -108,36 +109,36 @@ public final class Llama {
public static final class Weights {
// token embedding table
- public final Tensor token_embedding_table; // (vocab_size, dim)
+ public final FloatTensor token_embedding_table; // (vocab_size, dim)
// weights for rmsnorms
public final FloatBuffer[] rms_att_weight; // (layer, dim) rmsnorm weights
// weights for matmuls
- public final Tensor[] wq; // (layer, n_heads * head_size)
- public final Tensor[] wk; // (layer, n_kv_heads, head_size)
- public final Tensor[] wv; // (layer, n_kv_heads * head_size)
- public final Tensor[] wo; // (layer, n_heads * head_size, dim)
- public final Tensor[] q_bias; // (layer, dim)
- public final Tensor[] k_bias; // (layer, kv_dim)
- public final Tensor[] v_bias; // (layer, kv_dim)
+ public final FloatTensor[] wq; // (layer, n_heads * head_size)
+ public final FloatTensor[] wk; // (layer, n_kv_heads, head_size)
+ public final FloatTensor[] wv; // (layer, n_kv_heads * head_size)
+ public final FloatTensor[] wo; // (layer, n_heads * head_size, dim)
+ public final FloatTensor[] q_bias; // (layer, dim)
+ public final FloatTensor[] k_bias; // (layer, kv_dim)
+ public final FloatTensor[] v_bias; // (layer, kv_dim)
public final FloatBuffer[] rms_ffn_weight; // (layer, dim)
// weights for ffn
- public final Tensor[] w1; // (layer, hidden_dim, dim)
- public final Tensor[] w2; // (layer, dim, hidden_dim)
- public final Tensor[] w3; // (layer, hidden_dim, dim)
+ public final FloatTensor[] w1; // (layer, hidden_dim, dim)
+ public final FloatTensor[] w2; // (layer, dim, hidden_dim)
+ public final FloatTensor[] w3; // (layer, hidden_dim, dim)
// public final rmsnorm
public final FloatBuffer rms_final_weight; // (dim,)
// freq_cis for RoPE relatively positional embeddings
public final FloatBuffer freq_cis_real; // (seq_len, head_size/2)
public final FloatBuffer freq_cis_imag; // (seq_len, head_size/2)
// (optional) classifier weights for the logits, on the last layer
- public final Tensor wcls; // (vocab_size, dim)
-
- public Weights(Tensor token_embedding_table, FloatBuffer[] rms_att_weight, Tensor[] wq,
- Tensor[] wk, Tensor[] wv,
- Tensor[] q_bias, Tensor[] k_bias, Tensor[] v_bias,
- Tensor[] wo, FloatBuffer[] rms_ffn_weight,
- Tensor[] w1, Tensor[] w2, Tensor[] w3, FloatBuffer rms_final_weight,
- FloatBuffer freq_cis_real, FloatBuffer freq_cis_imag, Tensor wcls) {
+ public final FloatTensor wcls; // (vocab_size, dim)
+
+ public Weights(FloatTensor token_embedding_table, FloatBuffer[] rms_att_weight, FloatTensor[] wq,
+ FloatTensor[] wk, FloatTensor[] wv,
+ FloatTensor[] q_bias, FloatTensor[] k_bias, FloatTensor[] v_bias,
+ FloatTensor[] wo, FloatBuffer[] rms_ffn_weight,
+ FloatTensor[] w1, FloatTensor[] w2, FloatTensor[] w3, FloatBuffer rms_final_weight,
+ FloatBuffer freq_cis_real, FloatBuffer freq_cis_imag, FloatTensor wcls) {
this.token_embedding_table = token_embedding_table;
this.rms_att_weight = rms_att_weight;
this.wq = wq;
@@ -163,20 +164,20 @@ public final class Llama {
// current wave of activations
public final int batchsize;
- public final Tensor[] x; // activation at current time stamp (dim,)
- public final Tensor[] xb; // same, but inside a residual branch (dim,)
- public final Tensor[] xb2; // an additional buffer just for convenience (dim,)
- public final Tensor[] hb; // buffer for hidden dimension in the ffn (hidden_dim,)
- public final Tensor[] hb2; // buffer for hidden dimension in the ffn (hidden_dim,)
- public final Tensor[] q; // query (dim,)
- public final Tensor[] k; // key (dim,)
- public final Tensor[] v; // value (dim,)
- public final Tensor[] att; // buffer for scores/attention values (n_heads, seq_len)
- public final Tensor logits; // output logits
+ public final FloatTensor[] x; // activation at current time stamp (dim,)
+ public final FloatTensor[] xb; // same, but inside a residual branch (dim,)
+ public final FloatTensor[] xb2; // an additional buffer just for convenience (dim,)
+ public final FloatTensor[] hb; // buffer for hidden dimension in the ffn (hidden_dim,)
+ public final FloatTensor[] hb2; // buffer for hidden dimension in the ffn (hidden_dim,)
+ public final FloatTensor[] q; // query (dim,)
+ public final FloatTensor[] k; // key (dim,)
+ public final FloatTensor[] v; // value (dim,)
+ public final FloatTensor[] att; // buffer for scores/attention values (n_heads, seq_len)
+ public final FloatTensor logits; // output logits
// kv cache
- public final Tensor[] keyCache; // (n_layer, seq_len, kv_dim)
- public final Tensor[] valueCache; // (n_layer, seq_len, kv_dim)
+ public final FloatTensor[] keyCache; // (n_layer, seq_len, kv_dim)
+ public final FloatTensor[] valueCache; // (n_layer, seq_len, kv_dim)
/** last index in previous block */
int idxPrevBlock;
@@ -198,19 +199,19 @@ public final class Llama {
this.logits = DirectBufferFloatTensor.allocate(config.vocabularySize);
int kvDim = (config.dim * config.numberOfKeyValueHeads) / config.numberOfHeads;
- this.keyCache = Stream.generate(() -> DirectBufferFloatTensor.allocate(config.contextLength, kvDim)).limit(config.numberOfLayers).toArray(Tensor[]::new);
- this.valueCache = Stream.generate(() -> DirectBufferFloatTensor.allocate(config.contextLength, kvDim)).limit(config.numberOfLayers).toArray(Tensor[]::new);
+ this.keyCache = Stream.generate(() -> DirectBufferFloatTensor.allocate(config.contextLength, kvDim)).limit(config.numberOfLayers).toArray(FloatTensor[]::new);
+ this.valueCache = Stream.generate(() -> DirectBufferFloatTensor.allocate(config.contextLength, kvDim)).limit(config.numberOfLayers).toArray(FloatTensor[]::new);
}
- private static Tensor[] allocate(int numTokens, int... dims) {
+ private static FloatTensor[] allocate(int numTokens, int... dims) {
return IntStream.range(0, numTokens)
.mapToObj(i -> DirectBufferFloatTensor.allocate(dims))
- .toArray(Tensor[]::new);
+ .toArray(FloatTensor[]::new);
}
}
- static void rmsnorm(Tensor out, Tensor x, FloatBuffer weight, int size, float rmsNormEps) {
+ static void rmsnorm(FloatTensor out, FloatTensor x, FloatBuffer weight, int size, float rmsNormEps) {
// calculate sum of squares
float ss = x.reduce(0, size, 0f, (acc, xi) -> acc + xi * xi);
ss /= size;
@@ -222,7 +223,7 @@ public final class Llama {
}
}
- static Tensor forward(Llama model, State state, int[] tokens, int position, boolean computeLogits) {
+ static FloatTensor forward(Llama model, State state, int[] tokens, int position, boolean computeLogits) {
// a few convenience variables
Configuration config = model.configuration();
Weights weights = model.weights();
@@ -234,7 +235,7 @@ public final class Llama {
final int nTokens = tokens.length;
// copy the token embedding into x
- Tensor.parallelFor(0, nTokens, t ->
+ AbstractFloatTensor.parallelFor(0, nTokens, t ->
weights.token_embedding_table.copyTo(tokens[t] * dim, state.x[t], 0, dim)
);
@@ -243,7 +244,7 @@ public final class Llama {
// attention rmsnorm
final int curLayer = l;
- Tensor.parallelFor(0, nTokens, t ->
+ AbstractFloatTensor.parallelFor(0, nTokens, t ->
rmsnorm(state.xb[t], state.x[t], weights.rms_att_weight[curLayer], dim, config.rmsNormEps)
);
@@ -253,14 +254,14 @@ public final class Llama {
weights.wv[l].matmul(nTokens, state.xb, state.v, kvDim, dim);
// RoPE relative positional encoding: complex-valued rotate q and k in each head
- Tensor.parallelFor(0, nTokens, t -> {
+ AbstractFloatTensor.parallelFor(0, nTokens, t -> {
for (int i = 0; i < dim; i += 2) {
int head_dim = i % headSize;
float fcr = weights.freq_cis_real.get((position + t) * (headSize / 2) + (head_dim / 2));
float fci = weights.freq_cis_imag.get((position + t) * (headSize / 2) + (head_dim / 2));
int rotn = i < kvDim ? 2 : 1; // how many vectors? 2 = q & k, 1 = q only
for (int vi = 0; vi < rotn; vi++) {
- Tensor vec = vi == 0 ? state.q[t] : state.k[t]; // the vector to rotate (query or key)
+ FloatTensor vec = vi == 0 ? state.q[t] : state.k[t]; // the vector to rotate (query or key)
float v0 = vec.getFloat(i);
float v1 = vec.getFloat(i + 1);
vec.setFloat(i, v0 * fcr - v1 * fci);
@@ -270,7 +271,7 @@ public final class Llama {
});
// save key,value at this time step (position) to our kv cache
- Tensor.parallelFor(0, nTokens, t -> {
+ AbstractFloatTensor.parallelFor(0, nTokens, t -> {
state.k[t].copyTo(0, state.keyCache[curLayer], (position + t) * kvDim, kvDim);
state.v[t].copyTo(0, state.valueCache[curLayer], (position + t) * kvDim, kvDim);
});
@@ -282,7 +283,7 @@ public final class Llama {
}
// multihead attention. iterate over all heads
- Tensor.parallelForLong(0, (long) nTokens * (long) config.numberOfHeads, ht -> {
+ AbstractFloatTensor.parallelForLong(0, (long) nTokens * (long) config.numberOfHeads, ht -> {
int token = (int) (ht / config.numberOfHeads);
int h = (int) (ht % config.numberOfHeads);
int qOffset = h * headSize;
@@ -311,12 +312,12 @@ public final class Llama {
weights.wo[l].matmul(nTokens, state.xb, state.xb2, dim, dim);
// residual connection back into x
- Tensor.parallelFor(0, nTokens, t -> {
+ AbstractFloatTensor.parallelFor(0, nTokens, t -> {
state.x[t].addInPlace(state.xb2[t]);
});
// ffn rmsnorm
- Tensor.parallelFor(0, nTokens, t -> {
+ AbstractFloatTensor.parallelFor(0, nTokens, t -> {
rmsnorm(state.xb[t], state.x[t], weights.rms_ffn_weight[curLayer], dim, config.rmsNormEps);
});
@@ -325,12 +326,12 @@ public final class Llama {
weights.w3[l].matmul(nTokens, state.xb, state.hb2, config.hiddenDim, dim);
// SwiGLU non-linearity
- Tensor.parallelFor(0, nTokens, t -> {
+ AbstractFloatTensor.parallelFor(0, nTokens, t -> {
state.hb[t].mapInPlace(value -> value / (float) (1.0 + Math.exp(-value)));
});
// elementwise multiply with w3(x)
- Tensor.parallelFor(0, nTokens, t -> {
+ AbstractFloatTensor.parallelFor(0, nTokens, t -> {
state.hb[t].multiplyInPlace(state.hb2[t]);
});
@@ -338,13 +339,13 @@ public final class Llama {
weights.w2[l].matmul(nTokens, state.hb, state.xb, dim, config.hiddenDim);
// residual connection
- Tensor.parallelFor(0, nTokens, t -> {
+ AbstractFloatTensor.parallelFor(0, nTokens, t -> {
state.x[t].addInPlace(state.xb[t]);
});
}
// final rmsnorm
- Tensor.parallelFor(0, nTokens, t -> {
+ AbstractFloatTensor.parallelFor(0, nTokens, t -> {
rmsnorm(state.x[t], state.x[t], weights.rms_final_weight, dim, config.rmsNormEps);
});
diff --git a/source/net/yacy/ai/llama3/Llama3.java b/source/net/yacy/ai/llama3/Llama3.java
index 0982ec067..065e5ced0 100644
--- a/source/net/yacy/ai/llama3/Llama3.java
+++ b/source/net/yacy/ai/llama3/Llama3.java
@@ -152,7 +152,7 @@ public class Llama3 {
// JVM version 21.0.7+6-LTS: 17.6 T/s
// performance on M4 Max:
- // JVM version 21.0.5+11-LTS: 24.5 T/s
+ // JVM version 21.0.5+11-LTS: 26.3 T/s
System.out.println("JVM version " + Runtime.version());
diff --git a/source/net/yacy/ai/llama3/Model/GGMLTensorEntry.java b/source/net/yacy/ai/llama3/Model/GGMLTensorEntry.java
index 83f7abe1b..48564d7e9 100644
--- a/source/net/yacy/ai/llama3/Model/GGMLTensorEntry.java
+++ b/source/net/yacy/ai/llama3/Model/GGMLTensorEntry.java
@@ -31,7 +31,8 @@ import java.util.Objects;
import net.yacy.ai.llama3.Tensor.Q4_0FloatTensor;
import net.yacy.ai.llama3.Tensor.Q8_0FloatTensor;
-import net.yacy.ai.llama3.Tensor.Tensor;
+import net.yacy.ai.llama3.Tensor.AbstractFloatTensor;
+import net.yacy.ai.llama3.Tensor.FloatTensor;
public final class GGMLTensorEntry {
@@ -73,12 +74,12 @@ public final class GGMLTensorEntry {
}
}
- public Tensor loadQuantized() {
- Tensor tensor = null;
+ public FloatTensor loadQuantized() {
+ FloatTensor tensor = null;
switch (ggmlType) {
//case F32: return new F32FloatTensor(FloatTensor.numberOfElements(entry.shape()), entry.memorySegment());
- case Q8_0: tensor = new Q8_0FloatTensor(Tensor.numberOfElements(this.shape()), this.buffer); break;
- case Q4_0: tensor = new Q4_0FloatTensor(Tensor.numberOfElements(this.shape()), this.buffer); break;
+ case Q8_0: tensor = new Q8_0FloatTensor(AbstractFloatTensor.numberOfElements(this.shape()), this.buffer); break;
+ case Q4_0: tensor = new Q4_0FloatTensor(AbstractFloatTensor.numberOfElements(this.shape()), this.buffer); break;
default: throw new UnsupportedOperationException("Quantization format " + ggmlType);
}
return tensor;
diff --git a/source/net/yacy/ai/llama3/Model/GGUF.java b/source/net/yacy/ai/llama3/Model/GGUF.java
index ae277a71f..aa61ea9b6 100644
--- a/source/net/yacy/ai/llama3/Model/GGUF.java
+++ b/source/net/yacy/ai/llama3/Model/GGUF.java
@@ -38,7 +38,7 @@ import java.util.Comparator;
import java.util.Objects;
import java.util.stream.Collectors;
-import net.yacy.ai.llama3.Tensor.Tensor;
+import net.yacy.ai.llama3.Tensor.AbstractFloatTensor;
/*
* GGUF File Reader. For specification see https://github.com/ggml-org/ggml/blob/master/docs/gguf.md
@@ -203,7 +203,7 @@ public final class GGUF {
GGUFTensorInfo ti = entry.getValue();
long offset = ti.offset();
int sizeInBytes = Math.toIntExact(ti.ggmlType().byteSizeFor(
- Tensor.numberOfElements(ti.dimensions())));
+ AbstractFloatTensor.numberOfElements(ti.dimensions())));
MappedByteBuffer tensorBuffer = (MappedByteBuffer) fullBuffer.duplicate();
tensorBuffer.position((int)offset);
@@ -224,7 +224,7 @@ public final class GGUF {
List<Long> boundaries = new ArrayList<>();
for (GGUFTensorInfo ti : tensorInfos.values()) {
long start = ti.offset();
- long end = start + ti.ggmlType().byteSizeFor(Tensor.numberOfElements(ti.dimensions()));
+ long end = start + ti.ggmlType().byteSizeFor(AbstractFloatTensor.numberOfElements(ti.dimensions()));
boundaries.add(start);
boundaries.add(end);
}
@@ -276,7 +276,7 @@ public final class GGUF {
GGUFTensorInfo ti = entry.getValue();
String name = ti.name();
long tensorOffset = ti.offset() + tensorDataOffset;
- long tensorSize = ti.ggmlType().byteSizeFor(Tensor.numberOfElements(ti.dimensions()));
+ long tensorSize = ti.ggmlType().byteSizeFor(AbstractFloatTensor.numberOfElements(ti.dimensions()));
long tensorEnd = tensorOffset + tensorSize;
// Find all segments that overlap with this tensor
diff --git a/source/net/yacy/ai/llama3/Model/ModelLoader.java b/source/net/yacy/ai/llama3/Model/ModelLoader.java
index a66597d0f..4cd7ce40b 100644
--- a/source/net/yacy/ai/llama3/Model/ModelLoader.java
+++ b/source/net/yacy/ai/llama3/Model/ModelLoader.java
@@ -35,7 +35,7 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
import net.yacy.ai.llama3.Llama;
-import net.yacy.ai.llama3.Tensor.Tensor;
+import net.yacy.ai.llama3.Tensor.FloatTensor;
public final class ModelLoader {
private static final String TOKENIZER_LLAMA_3_MODEL = "gpt2";
@@ -169,7 +169,7 @@ public final class ModelLoader {
float[] ropeFreqsImag = ropeFreqs.second();
- Tensor tokenEmbeddingTable = tensorEntries.get("token_embd.weight").loadQuantized();
+ FloatTensor tokenEmbeddingTable = tensorEntries.get("token_embd.weight").loadQuantized();
Llama.Weights qw = new Llama.Weights(
tokenEmbeddingTable,
loadArrayOfFloatBuffer(config.numberOfLayers, i -> tensorEntries.get("blk." + i + ".attn_norm.weight")),
@@ -256,8 +256,8 @@ public final class ModelLoader {
}
- private static Tensor[] loadArrayOfQuantized(int size, IntFunction<GGMLTensorEntry> getTensorEntry) {
- Tensor[] array = new Tensor[size];
+ private static FloatTensor[] loadArrayOfQuantized(int size, IntFunction<GGMLTensorEntry> getTensorEntry) {
+ FloatTensor[] array = new FloatTensor[size];
for (int i = 0; i < size; i++) {
array[i] = getTensorEntry.apply(i).loadQuantized();
}
diff --git a/source/net/yacy/ai/llama3/Model/Vocabulary.java b/source/net/yacy/ai/llama3/Model/Vocabulary.java
index f2ca7e4dd..9f139be3a 100644
--- a/source/net/yacy/ai/llama3/Model/Vocabulary.java
+++ b/source/net/yacy/ai/llama3/Model/Vocabulary.java
@@ -27,13 +27,13 @@ import java.util.stream.*;
public final class Vocabulary {
private final String[] tokens;
- private final float[] scores;
+ //private final float[] scores;
private final Map<String, Integer> tokenToIndex;
// Primary constructor
public Vocabulary(String[] tokens, float[] scores, Map<String, Integer> tokenToIndex) {
this.tokens = tokens == null ? null : Arrays.copyOf(tokens, tokens.length);
- this.scores = scores == null ? null : Arrays.copyOf(scores, scores.length);
+ //this.scores = scores == null ? null : Arrays.copyOf(scores, scores.length);
this.tokenToIndex = tokenToIndex == null ? null : new HashMap<>(tokenToIndex);
}
diff --git a/source/net/yacy/ai/llama3/Sampler.java b/source/net/yacy/ai/llama3/Sampler.java
index 5eda5cc58..c94876cab 100644
--- a/source/net/yacy/ai/llama3/Sampler.java
+++ b/source/net/yacy/ai/llama3/Sampler.java
@@ -26,13 +26,12 @@ import java.util.Comparator;
import java.util.Random;
import net.yacy.ai.llama3.Tensor.FloatTensor;
-import net.yacy.ai.llama3.Tensor.Tensor;
@FunctionalInterface
interface Sampler {
- int sampleToken(Tensor logits);
+ int sampleToken(FloatTensor logits);
- Sampler ARGMAX = Tensor::argmax;
+ Sampler ARGMAX = FloatTensor::argmax;
static Sampler selectSampler(int vocabularySize, float temperature, float topp, long rngSeed) {
Sampler sampler;
@@ -71,7 +70,7 @@ interface Sampler {
}
@Override
- public int sampleToken(Tensor logits) {
+ public int sampleToken(FloatTensor logits) {
// sample index from probabilities (they must sum to 1!)
float random0to1 = rng.nextFloat();
float cdf = 0.0f;
@@ -120,7 +119,7 @@ interface Sampler {
}
@Override
- public int sampleToken(Tensor logits) {
+ public int sampleToken(FloatTensor logits) {
// top-p sampling (or "nucleus sampling") samples from the smallest set of
// tokens that exceed probability topp. This way we never sample tokens that
// have very low probabilities and are less likely to go "off the rails".
diff --git a/source/net/yacy/ai/llama3/Tensor/AbstractFloatTensor.java b/source/net/yacy/ai/llama3/Tensor/AbstractFloatTensor.java
new file mode 100644
index 000000000..afe9fef73
--- /dev/null
+++ b/source/net/yacy/ai/llama3/Tensor/AbstractFloatTensor.java
@@ -0,0 +1,269 @@
+/**
+ * FloatTensor.java
+
+ * This file was extracted from the llama3/qwen2 projects
+ * https://github.com/mukel/llama3.java
+ * https://github.com/mukel/qwen2.svm.java
+ *
+ * License: MIT License
+ *
+ * Copyright (c) 2024 Andrej Karpathy (for llama2.c)
+ * Copyright (c) 2024 Alfonso² Peterssen (for llama3/qwen2)
+ * Copyright (c) 2023 Georgi Gerganov et al. (for llama.cpp)
+ * Copyright (c) 2025 Michael Peter Christen for modifications:
+ * The code was modified to fit the YaCy AI project:
+ * - back-port to Java 11 (removal of Vector API operations and record types)
+ * - removal of interactive mode and system.out printing
+ * - separation of the classes in the single java and refactoring
+ * - run-time performance optimizations for dot product computation of quantized values
+ * - joining of llama3/qwen2 into one code base; multi-arch options
+ * - alignment with code from https://github.com/ggml-org/llama.cpp/
+ */
+
+package net.yacy.ai.llama3.Tensor;
+
+import java.util.Arrays;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+
+import net.yacy.ai.llama3.Model.GGMLType;
+
+/**
+ * Over-simplified, shapeless, float tensor.
+ * <p>
+ * Not a strict tensor, but rather just a sequence of floats, not required to be backed by memory
+ * e.g. can represent a sequence of quantized floats.
+ */
+public abstract class AbstractFloatTensor implements FloatTensor {
+
+ /**
+ * Converts a 16-bit float (half-precision) to a 32-bit float (single-precision).
+ *
+ * @param h the half-precision float as a short
+ * @return the single-precision float
+ */
+ public final static float float16ToFloat(short h) {
+
+ final int hBits = h & 0xFFFF; // treat as unsigned
+ final int sign = (hBits >>> 15) & 0x00000001;
+
+ int exp = (hBits >>> 10) & 0x0000001F;
+ int mant = hBits & 0x000003FF;
+ int fBits;
+
+ if (exp == 0) {
+ if (mant == 0) {
+ // zero
+ fBits = sign << 31;
+ } else {
+ // subnormal
+ while ((mant & 0x00000400) == 0) {
+ mant <<= 1;
+ exp -= 1;
+ }
+ exp += 1;
+ mant &= ~0x00000400;
+ fBits = (sign << 31) | ((exp + 127 - 15) << 23) | (mant << 13);
+ }
+ } else if (exp == 31) {
+ // Inf/NaN
+ fBits = (sign << 31) | 0x7F800000 | (mant << 13);
+ } else {
+ // normalized number
+ fBits = (sign << 31) | ((exp + 127 - 15) << 23) | (mant << 13);
+ }
+
+ return Float.intBitsToFloat(fBits);
+ }
+
+ /**
+ * Converts a 32-bit float (single-precision) to a 16-bit float (half-precision).
+ *
+ * @param f the single-precision float
+ * @return the half-precision float as a short
+ */
+ public final static short floatToFloat16(final float f) {
+ final int fBits = Float.floatToIntBits(f);
+ final int sign = (fBits >>> 31) & 0x00000001;
+ final int exp = (fBits >>> 23) & 0x000000FF;
+ final int mant = fBits & 0x007FFFFF;
+
+ short hBits;
+
+ if (exp == 0xFF) {
+ // Inf/NaN
+ hBits = (short) ((sign << 15) | 0x7C00 | (mant >>> 13));
+ } else if (exp < 112) {
+ // subnormal or zero
+ hBits = (short) (sign << 15);
+ } else if (exp > 143) {
+ // overflow to Inf
+ hBits = (short) ((sign << 15) | 0x7C00);
+ } else {
+ // normalized number
+ hBits = (short) ((sign << 15) | ((exp - 112) << 10) | (mant >>> 13));
+ }
+
+ return hBits;
+ }
+
+ public abstract int size();
+
+ public abstract float getFloat(final int index);
+
+ public abstract void setFloat(final int index, final float value);
+
+ abstract GGMLType type();
+
+ public static int numberOfElements(final int... dimensions) {
+ assert Arrays.stream(dimensions).allMatch(i -> i > 0);
+ return Arrays.stream(dimensions).reduce(Math::multiplyExact).orElseThrow();
+ }
+
+ public static void parallelFor(final int startInclusive, final int endExclusive, final IntConsumer action) {
+ if (startInclusive == 0 && endExclusive == 1) {
+ action.accept(0);
+ return;
+ }
+ IntStream.range(startInclusive, endExclusive).parallel().forEach(action);
+ }
+
+ public static void parallelForLong(final long startInclusive, final long endExclusive, final LongConsumer action) {
+ if (startInclusive == 0 && endExclusive == 1) {
+ action.accept(0);
+ return;
+ }
+ LongStream.range(startInclusive, endExclusive).parallel().forEach(action);
+ }
+
+ public float dot(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) {
+ float result = 0f;
+ for (int j = 0; j < size; j++) {
+ result += this.getFloat(thisOffset + j) * that.getFloat(thatOffset + j);
+ }
+ return result;
+ }
+
+ public void matmul(final FloatTensor that, final FloatTensor out, final int dim0, final int dim1) {
+ parallelFor(0, dim0, i -> out.setFloat(i, dot(i * dim1, that, 0, dim1)));
+ }
+
+ public void matmul(final int context, final FloatTensor[] that, final FloatTensor[] out, final int dim0, final int dim1) {
+ if (that.length != out.length) {
+ throw new IllegalArgumentException(String.format("that.len=%d, out.len=%d", that.length, out.length));
+ }
+ parallelForLong(0, dim0 * context, ti -> {
+ int idxArr = (int) (ti / dim0);
+ int i = (int) (ti % dim0);
+ out[idxArr].setFloat(i, dot(i * dim1, that[idxArr], 0, dim1));
+ });
+ }
+
+ @FunctionalInterface
+ public interface AggregateFunction {
+ float apply(float acc, float value);
+ }
+
+ public float reduce(final int thisOffset, final int size, final float seed, final AggregateFunction reduce) {
+ float result = seed;
+ for (int i = 0; i < size; ++i) {
+ result = reduce.apply(result, getFloat(thisOffset + i));
+ }
+ return result;
+ }
+
+ private float sum(final int thisOffset, final int size) {
+ return reduce(thisOffset, size, 0f, Float::sum);
+ }
+
+ private float max(final int thisOffset, final int size) {
+ return reduce(thisOffset, size, Float.NEGATIVE_INFINITY, Float::max);
+ }
+
+ public void copyTo(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) {
+ int endOffset = thatOffset + size;
+ for (int i = thatOffset; i < endOffset; ++i) {
+ that.setFloat(i, this.getFloat(i - thatOffset + thisOffset));
+ }
+ }
+
+ public int argmax() {
+ int size = this.size();
+ assert size > 0;
+ int maxIndex = 0;
+ float maxValue = this.getFloat(maxIndex);
+ int endIndex = size;
+ for (int i = 0; i < endIndex; ++i) {
+ float f = this.getFloat(i);
+ if (f > maxValue) {
+ maxValue = f;
+ maxIndex = i;
+ }
+ }
+ return maxIndex;
+ }
+
+ public FloatTensor mapInPlace(final int thisOffset, final int size, MapFunction mapFunction) {
+ int endIndex = thisOffset + size;
+ for (int i = thisOffset; i < endIndex; ++i) {
+ this.setFloat(i, mapFunction.apply(this.getFloat(i)));
+ }
+ return this;
+ }
+
+ public final FloatTensor mapInPlace(final MapFunction mapFunction) {
+ return mapInPlace(0, size(), mapFunction);
+ }
+
+ public FloatTensor mapWithIndexInPlace(final int thisOffset, final int size, final FloatTensor.MapWithIndexFunction mapWithIndexFunction) {
+ int endOffset = thisOffset + size;
+ for (int i = thisOffset; i < endOffset; ++i) {
+ this.setFloat(i, mapWithIndexFunction.apply(this.getFloat(i), i));
+ }
+ return this;
+ }
+
+ private final FloatTensor addInPlace(final int thisOffset, final FloatTensor that, final int thatOffset, int size) {
+ return mapWithIndexInPlace(thisOffset, size, (value, index) -> value + that.getFloat(index - thisOffset + thatOffset));
+ }
+
+ public final FloatTensor addInPlace(final FloatTensor that) {
+ return addInPlace(0, that, 0, size());
+ }
+
+ private final FloatTensor multiplyInPlace(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) {
+ return mapWithIndexInPlace(thisOffset, size, (value, index) -> value * that.getFloat(index - thisOffset + thatOffset));
+ }
+
+ public final FloatTensor multiplyInPlace(final FloatTensor that) {
+ return multiplyInPlace(0, that, 0, size());
+ }
+
+ public final FloatTensor divideInPlace(final int thisOffset, final int size, final float value) {
+ return mapInPlace(thisOffset, size, f -> f / value);
+ }
+
+ public FloatTensor fillInPlace(final int thisOffset, final int size, final float value) {
+ return mapInPlace(thisOffset, size, unused -> value);
+ }
+
+ public final FloatTensor softmaxInPlace(final int thisOffset, final int size) {
+ // find max value (for numerical stability)
+ float maxVal = max(thisOffset, size);
+ // exp and sum
+ mapInPlace(thisOffset, size, f -> (float) Math.exp(f - maxVal));
+ float sum = sum(thisOffset, size);
+ // normalize
+ return divideInPlace(thisOffset, size, sum);
+ }
+
+ public FloatTensor saxpyInPlace(final int thisOffset, final FloatTensor that, final int thatOffset, final int size, final float a) {
+ // this[thatOffset ... thatOffset + size) = a * that[thatOffset ... thatOffset + size) + this[thisOffset ... thisOffset + size)
+ for (int i = 0; i < size; ++i) {
+ this.setFloat(thisOffset + i, a * that.getFloat(thatOffset + i) + this.getFloat(thisOffset + i));
+ }
+ return this;
+ }
+} \ No newline at end of file
diff --git a/source/net/yacy/ai/llama3/Tensor/ArrayFloatTensor.java b/source/net/yacy/ai/llama3/Tensor/ArrayFloatTensor.java
index a66e60e91..90c747182 100644
--- a/source/net/yacy/ai/llama3/Tensor/ArrayFloatTensor.java
+++ b/source/net/yacy/ai/llama3/Tensor/ArrayFloatTensor.java
@@ -28,7 +28,7 @@ import java.lang.invoke.VarHandle;
import net.yacy.ai.llama3.Model.GGMLType;
-public final class ArrayFloatTensor extends FloatTensor implements Tensor {
+public final class ArrayFloatTensor extends AbstractFloatTensor implements FloatTensor {
public final float[] values;
private static final VarHandle FLOAT_ARRAY_HANDLE;
@@ -45,8 +45,8 @@ public final class ArrayFloatTensor extends FloatTensor implements Tensor {
this.values = values;
}
- public static Tensor allocate(final int... dims) {
- int numberOfElements = Tensor.numberOfElements(dims);
+ public static FloatTensor allocate(final int... dims) {
+ int numberOfElements = AbstractFloatTensor.numberOfElements(dims);
return new ArrayFloatTensor(new float[numberOfElements]);
}
@@ -71,13 +71,13 @@ public final class ArrayFloatTensor extends FloatTensor implements Tensor {
}
@Override
- public final FloatTensor fillInPlace(final int thisOffset, final int size, final float value) {
+ public final AbstractFloatTensor fillInPlace(final int thisOffset, final int size, final float value) {
Arrays.fill(this.values, thisOffset, thisOffset + size, value);
return this;
}
@Override
- public final FloatTensor mapInPlace(final int thisOffset, final int size, MapFunction mapFunction) {
+ public final AbstractFloatTensor mapInPlace(final int thisOffset, final int size, MapFunction mapFunction) {
int endIndex = thisOffset + size;
for (int i = thisOffset; i < endIndex; ++i) {
this.values[i] = mapFunction.apply(this.values[i]);
@@ -86,7 +86,7 @@ public final class ArrayFloatTensor extends FloatTensor implements Tensor {
}
@Override
- public final void copyTo(final int thisOffset, final Tensor that, final int thatOffset, final int size) {
+ public final void copyTo(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) {
final int delta = thisOffset - thatOffset;
if (that instanceof ArrayFloatTensor) {
final ArrayFloatTensor aft = (ArrayFloatTensor) that;
@@ -103,7 +103,7 @@ public final class ArrayFloatTensor extends FloatTensor implements Tensor {
}
@Override
- public final float dot(final int thisOffset, final Tensor that, final int thatOffset, final int size) {
+ public final float dot(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) {
float result = 0f;
if (that instanceof ArrayFloatTensor) {
final ArrayFloatTensor aft = (ArrayFloatTensor) that;
@@ -126,20 +126,20 @@ public final class ArrayFloatTensor extends FloatTensor implements Tensor {
}
@Override
- public final void matmul(final Tensor that, final Tensor out, final int dim0, final int dim1) {
+ public final void matmul(final FloatTensor that, final FloatTensor out, final int dim0, final int dim1) {
if (that instanceof ArrayFloatTensor) {
- Tensor.parallelFor(0, dim0, i -> ((ArrayFloatTensor) out).values[i] = this.dot(i * dim1, that, 0, dim1));
+ AbstractFloatTensor.parallelFor(0, dim0, i -> ((ArrayFloatTensor) out).values[i] = this.dot(i * dim1, that, 0, dim1));
} else {
- Tensor.parallelFor(0, dim0, i -> out.setFloat(i, this.dot(i * dim1, that, 0, dim1)));
+ AbstractFloatTensor.parallelFor(0, dim0, i -> out.setFloat(i, this.dot(i * dim1, that, 0, dim1)));
}
}
@Override
- public final void matmul(final int context, final Tensor[] that, final Tensor[] out, final int dim0, final int dim1) {
+ public final void matmul(final int context, final FloatTensor[] that, final FloatTensor[] out, final int dim0, final int dim1) {
if (that.length != out.length) {
throw new IllegalArgumentException(String.format("that.len=%d, out.len=%d", that.length, out.length));
}
- Tensor.parallelForLong(0, dim0 * context, ti -> {
+ AbstractFloatTensor.parallelForLong(0, dim0 * context, ti -> {
int idxArr = (int) (ti / dim0);
int i = (int) (ti % dim0);
out[idxArr].setFloat(i, this.dot(i * dim1, that[idxArr], 0, dim1));
@@ -147,7 +147,7 @@ public final class ArrayFloatTensor extends FloatTensor implements Tensor {
}
@Override
- public final Tensor saxpyInPlace(final int thisOffset, final Tensor that, final int thatOffset, final int size, final float a) {
+ public final FloatTensor saxpyInPlace(final int thisOffset, final FloatTensor that, final int thatOffset, final int size, final float a) {
if (that instanceof Q4_0FloatTensor) {
Q4_0FloatTensor qft = (Q4_0FloatTensor) that;
final float[] decodedBlock = Q4_0FloatTensor.scratchBuffer.get();
diff --git a/source/net/yacy/ai/llama3/Tensor/BF16FloatTensor.java b/source/net/yacy/ai/llama3/Tensor/BF16FloatTensor.java
index b8315ef48..f711f566a 100644
--- a/source/net/yacy/ai/llama3/Tensor/BF16FloatTensor.java
+++ b/source/net/yacy/ai/llama3/Tensor/BF16FloatTensor.java
@@ -27,7 +27,7 @@ import java.nio.ByteOrder;
import net.yacy.ai.llama3.Model.GGMLType;
-public final class BF16FloatTensor extends FloatTensor implements Tensor {
+public final class BF16FloatTensor extends AbstractFloatTensor implements FloatTensor {
final int size;
final ByteBuffer buffer;
diff --git a/source/net/yacy/ai/llama3/Tensor/DirectBufferFloatTensor.java b/source/net/yacy/ai/llama3/Tensor/DirectBufferFloatTensor.java
index f16781986..c123b5efc 100644
--- a/source/net/yacy/ai/llama3/Tensor/DirectBufferFloatTensor.java
+++ b/source/net/yacy/ai/llama3/Tensor/DirectBufferFloatTensor.java
@@ -28,7 +28,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import net.yacy.ai.llama3.Model.GGMLType;
-public class DirectBufferFloatTensor extends FloatTensor implements Tensor {
+public class DirectBufferFloatTensor extends AbstractFloatTensor implements FloatTensor {
final ByteBuffer byteBuffer; // must be direct
@@ -52,8 +52,8 @@ public class DirectBufferFloatTensor extends FloatTensor implements Tensor {
}
}
- public static Tensor allocate(final int... dims) {
- int numberOfElements = Tensor.numberOfElements(dims);
+ public static FloatTensor allocate(final int... dims) {
+ int numberOfElements = AbstractFloatTensor.numberOfElements(dims);
int bytesNeeded = numberOfElements * Float.BYTES;
ByteBuffer buffer = ByteBuffer.allocateDirect(bytesNeeded).order(ByteOrder.nativeOrder());
return new DirectBufferFloatTensor(buffer);
diff --git a/source/net/yacy/ai/llama3/Tensor/F16FloatTensor.java b/source/net/yacy/ai/llama3/Tensor/F16FloatTensor.java
index 109434789..62048a3c8 100644
--- a/source/net/yacy/ai/llama3/Tensor/F16FloatTensor.java
+++ b/source/net/yacy/ai/llama3/Tensor/F16FloatTensor.java
@@ -28,7 +28,7 @@ import java.nio.ByteOrder;
import net.yacy.ai.llama3.Model.GGMLType;
-public final class F16FloatTensor extends FloatTensor implements Tensor {
+public final class F16FloatTensor extends AbstractFloatTensor implements FloatTensor {
final int size;
final ByteBuffer buffer;
diff --git a/source/net/yacy/ai/llama3/Tensor/FloatTensor.java b/source/net/yacy/ai/llama3/Tensor/FloatTensor.java
index eff7e3052..281765bae 100644
--- a/source/net/yacy/ai/llama3/Tensor/FloatTensor.java
+++ b/source/net/yacy/ai/llama3/Tensor/FloatTensor.java
@@ -1,5 +1,5 @@
/**
- * FloatTensor.java
+ * Tensor.java
* This file was extracted from the llama3/qwen2 projects
* https://github.com/mukel/llama3.java
@@ -20,227 +20,55 @@
* - alignment with code from https://github.com/ggml-org/llama.cpp/
*/
-package net.yacy.ai.llama3.Tensor;
-
-import net.yacy.ai.llama3.Model.GGMLType;
-
-/**
- * Over-simplified, shapeless, float tensor.
- * <p>
- * Not a strict tensor, but rather just a sequence of floats, not required to be backed by memory
- * e.g. can represent a sequence of quantized floats.
- */
-public abstract class FloatTensor implements Tensor {
-
- /**
- * Converts a 16-bit float (half-precision) to a 32-bit float (single-precision).
- *
- * @param h the half-precision float as a short
- * @return the single-precision float
- */
- public final static float float16ToFloat(short h) {
- final int hBits = h & 0xFFFF; // treat as unsigned
- final int sign = (hBits >>> 15) & 0x00000001;
+package net.yacy.ai.llama3.Tensor;
- int exp = (hBits >>> 10) & 0x0000001F;
- int mant = hBits & 0x000003FF;
- int fBits;
+import net.yacy.ai.llama3.Tensor.AbstractFloatTensor.AggregateFunction;
- if (exp == 0) {
- if (mant == 0) {
- // zero
- fBits = sign << 31;
- } else {
- // subnormal
- while ((mant & 0x00000400) == 0) {
- mant <<= 1;
- exp -= 1;
- }
- exp += 1;
- mant &= ~0x00000400;
- fBits = (sign << 31) | ((exp + 127 - 15) << 23) | (mant << 13);
- }
- } else if (exp == 31) {
- // Inf/NaN
- fBits = (sign << 31) | 0x7F800000 | (mant << 13);
- } else {
- // normalized number
- fBits = (sign << 31) | ((exp + 127 - 15) << 23) | (mant << 13);
- }
+public interface FloatTensor {
- return Float.intBitsToFloat(fBits);
+ @FunctionalInterface
+ public interface MapFunction {
+ float apply(float value);
}
-
- /**
- * Converts a 32-bit float (single-precision) to a 16-bit float (half-precision).
- *
- * @param f the single-precision float
- * @return the half-precision float as a short
- */
- public final static short floatToFloat16(final float f) {
- final int fBits = Float.floatToIntBits(f);
- final int sign = (fBits >>> 31) & 0x00000001;
- final int exp = (fBits >>> 23) & 0x000000FF;
- final int mant = fBits & 0x007FFFFF;
-
- short hBits;
- if (exp == 0xFF) {
- // Inf/NaN
- hBits = (short) ((sign << 15) | 0x7C00 | (mant >>> 13));
- } else if (exp < 112) {
- // subnormal or zero
- hBits = (short) (sign << 15);
- } else if (exp > 143) {
- // overflow to Inf
- hBits = (short) ((sign << 15) | 0x7C00);
- } else {
- // normalized number
- hBits = (short) ((sign << 15) | ((exp - 112) << 10) | (mant >>> 13));
- }
-
- return hBits;
+ @FunctionalInterface
+ public interface MapWithIndexFunction {
+ float apply(float value, int index);
}
- public abstract int size();
+ public int size();
- public abstract float getFloat(final int index);
-
- public abstract void setFloat(final int index, final float value);
+ public int argmax();
- abstract GGMLType type();
-
- public static float scalarDot(final FloatTensor thiz, final int thisOffset, final Tensor that, final int thatOffset, final int size) {
- float result = 0f;
- for (int j = 0; j < size; j++) {
- result += thiz.getFloat(thisOffset + j) * that.getFloat(thatOffset + j);
- }
- return result;
- }
-
- public float dot(final int thisOffset, final Tensor that, final int thatOffset, final int size) {
- return scalarDot(this, thisOffset, that, thatOffset, size);
- }
-
- public void matmul(final Tensor that, final Tensor out, final int dim0, final int dim1) {
- Tensor.parallelFor(0, dim0, i -> out.setFloat(i, dot(i * dim1, that, 0, dim1)));
- }
-
- public void matmul(final int context, final Tensor[] that, final Tensor[] out, final int dim0, final int dim1) {
- if (that.length != out.length) {
- throw new IllegalArgumentException(String.format("that.len=%d, out.len=%d", that.length, out.length));
- }
- Tensor.parallelForLong(0, dim0 * context, ti -> {
- int idxArr = (int) (ti / dim0);
- int i = (int) (ti % dim0);
- out[idxArr].setFloat(i, dot(i * dim1, that[idxArr], 0, dim1));
- });
- }
-
- @FunctionalInterface
- public interface AggregateFunction {
- float apply(float acc, float value);
- }
-
- public float reduce(final int thisOffset, final int size, final float seed, final AggregateFunction reduce) {
- float result = seed;
- for (int i = 0; i < size; ++i) {
- result = reduce.apply(result, getFloat(thisOffset + i));
- }
- return result;
- }
-
- private float sum(final int thisOffset, final int size) {
- return reduce(thisOffset, size, 0f, Float::sum);
- }
-
- private float max(final int thisOffset, final int size) {
- return reduce(thisOffset, size, Float.NEGATIVE_INFINITY, Float::max);
- }
-
- public void copyTo(final int thisOffset, final Tensor that, final int thatOffset, final int size) {
- int endOffset = thatOffset + size;
- for (int i = thatOffset; i < endOffset; ++i) {
- that.setFloat(i, this.getFloat(i - thatOffset + thisOffset));
- }
- }
+ public float getFloat(final int index);
- public int argmax() {
- int size = this.size();
- assert size > 0;
- int maxIndex = 0;
- float maxValue = this.getFloat(maxIndex);
- int endIndex = size;
- for (int i = 0; i < endIndex; ++i) {
- float f = this.getFloat(i);
- if (f > maxValue) {
- maxValue = f;
- maxIndex = i;
- }
- }
- return maxIndex;
- }
-
- public Tensor mapInPlace(final int thisOffset, final int size, MapFunction mapFunction) {
- int endIndex = thisOffset + size;
- for (int i = thisOffset; i < endIndex; ++i) {
- this.setFloat(i, mapFunction.apply(this.getFloat(i)));
- }
- return this;
- }
-
- public final Tensor mapInPlace(final MapFunction mapFunction) {
- return mapInPlace(0, size(), mapFunction);
- }
-
- public Tensor mapWithIndexInPlace(final int thisOffset, final int size, final Tensor.MapWithIndexFunction mapWithIndexFunction) {
- int endOffset = thisOffset + size;
- for (int i = thisOffset; i < endOffset; ++i) {
- this.setFloat(i, mapWithIndexFunction.apply(this.getFloat(i), i));
- }
- return this;
- }
-
- private final Tensor addInPlace(final int thisOffset, final Tensor that, final int thatOffset, int size) {
- return mapWithIndexInPlace(thisOffset, size, (value, index) -> value + that.getFloat(index - thisOffset + thatOffset));
- }
-
- public final Tensor addInPlace(final Tensor that) {
- return addInPlace(0, that, 0, size());
- }
-
- private final Tensor multiplyInPlace(final int thisOffset, final Tensor that, final int thatOffset, final int size) {
- return mapWithIndexInPlace(thisOffset, size, (value, index) -> value * that.getFloat(index - thisOffset + thatOffset));
- }
-
- public final Tensor multiplyInPlace(final Tensor that) {
- return multiplyInPlace(0, that, 0, size());
- }
+ public void setFloat(final int index, final float value);
- public final Tensor divideInPlace(final int thisOffset, final int size, final float value) {
- return mapInPlace(thisOffset, size, f -> f / value);
- }
-
- public Tensor fillInPlace(final int thisOffset, final int size, final float value) {
- return mapInPlace(thisOffset, size, unused -> value);
- }
+ public void copyTo(final int thisOffset, final FloatTensor that, final int thatOffset, final int size);
+
+ public float dot(final int thisOffset, final FloatTensor that, final int thatOffset, final int size);
+
+ public void matmul(final FloatTensor that, final FloatTensor out, final int dim0, final int dim1);
+
+ public void matmul(final int context, final FloatTensor[] that, final FloatTensor[] out, final int dim0, final int dim1);
+
+ public float reduce(final int thisOffset, final int size, final float seed, final AggregateFunction reduce);
- public final Tensor softmaxInPlace(final int thisOffset, final int size) {
- // find max value (for numerical stability)
- float maxVal = max(thisOffset, size);
- // exp and sum
- mapInPlace(thisOffset, size, f -> (float) Math.exp(f - maxVal));
- float sum = sum(thisOffset, size);
- // normalize
- return divideInPlace(thisOffset, size, sum);
- }
+ public FloatTensor mapInPlace(final MapFunction mapFunction);
+
+ public FloatTensor mapInPlace(final int thisOffset, final int size, MapFunction mapFunction);
+
+ public FloatTensor addInPlace(final FloatTensor that);
+
+ public FloatTensor multiplyInPlace(final FloatTensor that);
+
+ public FloatTensor divideInPlace(final int thisOffset, final int size, final float value);
+
+ public FloatTensor fillInPlace(final int thisOffset, final int size, final float value);
+
+ public FloatTensor softmaxInPlace(final int thisOffset, final int size);
+
+ public FloatTensor saxpyInPlace(final int thisOffset, final FloatTensor that, final int thatOffset, final int size, final float a);
- public Tensor saxpyInPlace(final int thisOffset, final Tensor that, final int thatOffset, final int size, final float a) {
- // this[thatOffset ... thatOffset + size) = a * that[thatOffset ... thatOffset + size) + this[thisOffset ... thisOffset + size)
- for (int i = 0; i < size; ++i) {
- this.setFloat(thisOffset + i, a * that.getFloat(thatOffset + i) + this.getFloat(thisOffset + i));
- }
- return this;
- }
} \ No newline at end of file
diff --git a/source/net/yacy/ai/llama3/Tensor/Q4_0FloatTensor.java b/source/net/yacy/ai/llama3/Tensor/Q4_0FloatTensor.java
index e413f2436..c06f20313 100644
--- a/source/net/yacy/ai/llama3/Tensor/Q4_0FloatTensor.java
+++ b/source/net/yacy/ai/llama3/Tensor/Q4_0FloatTensor.java
@@ -28,7 +28,7 @@ import java.lang.invoke.VarHandle;
import net.yacy.ai.llama3.Model.GGMLType;
-public final class Q4_0FloatTensor extends FloatTensor implements Tensor {
+public final class Q4_0FloatTensor extends AbstractFloatTensor implements FloatTensor {
private final int size;
private final ByteBuffer buffer;
@@ -74,7 +74,7 @@ public final class Q4_0FloatTensor extends FloatTensor implements Tensor {
int blockIndex = index >>> LOG2_QUANT_BLOCK_SIZE; // index / QUANT_BLOCK_SIZE;
int blockOffset = blockIndex * GGMLType.Q4_0.typeSize;
final long offset = blockOffset;
- float scale = FloatTensor.float16ToFloat(buffer.getShort((int) offset));
+ float scale = AbstractFloatTensor.float16ToFloat(buffer.getShort((int) offset));
final int modIndex = index & (GGMLType.Q4_0.blockSize - 1); //index % QUANT_BLOCK_SIZE;
final boolean isLow = modIndex < QUANT_HALF_BLOCK;
final int adjustedIndex = modIndex - (isLow ? 0 : QUANT_HALF_BLOCK);
@@ -93,7 +93,7 @@ public final class Q4_0FloatTensor extends FloatTensor implements Tensor {
while (inPos < end) {
final int blockIndex = inPos >>> LOG2_QUANT_BLOCK_SIZE;
final int blockOffset = blockIndex * GGMLType.Q4_0.typeSize;
- float scale = FloatTensor.float16ToFloat(buffer.getShort((int) (long) blockOffset));
+ float scale = AbstractFloatTensor.float16ToFloat(buffer.getShort((int) (long) blockOffset));
final int blockStart = blockIndex * GGMLType.Q4_0.blockSize;
final int blockEnd = Math.min(blockStart + GGMLType.Q4_0.blockSize, end);
@@ -142,7 +142,7 @@ public final class Q4_0FloatTensor extends FloatTensor implements Tensor {
public static final ThreadLocal<float[]> scratchBuffer = ThreadLocal.withInitial(() -> new float[GGMLType.Q4_0.blockSize]);
@Override
- public void copyTo(final int thisOffset, final Tensor that, final int thatOffset, final int size) {
+ public void copyTo(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) {
final float[] decoded = scratchBuffer.get();
int remaining = size;
int srcIndex = thisOffset;
@@ -172,7 +172,7 @@ public final class Q4_0FloatTensor extends FloatTensor implements Tensor {
* dot product which has the getFloat method inlined in such a way that it processes full blocks at once.
* This gains a > 2.5 times token/s performance increase compared to the generic dot-getFloat implementation.
*/
- public final float dot(final int thisOffset, final Tensor that, final int thatOffset, final int size) {
+ public final float dot(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) {
float result = 0.0f;
int index = 0;
final int blockLimit = size - (size % GGMLType.Q4_0.blockSize);
@@ -183,7 +183,7 @@ public final class Q4_0FloatTensor extends FloatTensor implements Tensor {
// Get this block
final int thisBlockIndex = (thisOffset + index) >>> LOG2_QUANT_BLOCK_SIZE; // (thisOffset + index) / QUANT_BLOCK_SIZE;
final int thisBlockOffset = thisBlockIndex * GGMLType.Q4_0.typeSize;
- final float thisScale = FloatTensor.float16ToFloat(buffer.getShort((int) (long) thisBlockOffset));
+ final float thisScale = AbstractFloatTensor.float16ToFloat(buffer.getShort((int) (long) thisBlockOffset));
// Process block: read all quantized values from this block at once
final int quantOffset = thisBlockOffset + QUANT_FLOAT16_BYTES;
diff --git a/source/net/yacy/ai/llama3/Tensor/Q8_0FloatTensor.java b/source/net/yacy/ai/llama3/Tensor/Q8_0FloatTensor.java
index 04e26ae5c..f388f03a3 100644
--- a/source/net/yacy/ai/llama3/Tensor/Q8_0FloatTensor.java
+++ b/source/net/yacy/ai/llama3/Tensor/Q8_0FloatTensor.java
@@ -26,7 +26,7 @@ import java.nio.ByteBuffer;
import net.yacy.ai.llama3.Model.GGMLType;
-public final class Q8_0FloatTensor extends FloatTensor {
+public final class Q8_0FloatTensor extends AbstractFloatTensor {
final int size;
final ByteBuffer buffer;
@@ -60,12 +60,12 @@ public final class Q8_0FloatTensor extends FloatTensor {
int blockOffset = blockIndex * GGMLType.Q8_0.typeSize;
byte quant = buffer.get((int) (long) (blockOffset + GGMLType.FLOAT16_BYTES + withinBlockIndex));
final long offset = blockOffset;
- float scale = FloatTensor.float16ToFloat(buffer.getShort((int) offset));
+ float scale = AbstractFloatTensor.float16ToFloat(buffer.getShort((int) offset));
return quant * scale;
}
@Override
- public float dot(final int thisOffset, final Tensor that, final int thatOffset, final int size) {
+ public float dot(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) {
assert 0 <= thisOffset && thisOffset + size <= this.size;
assert 0 <= thatOffset && thatOffset + size <= that.size();
@@ -86,7 +86,7 @@ public final class Q8_0FloatTensor extends FloatTensor {
// Get common scale factor for this block
int blockOffset = block * GGMLType.Q8_0.typeSize;
final long offset = blockOffset;
- float thisScale = FloatTensor.float16ToFloat(buffer.getShort((int) offset));
+ float thisScale = AbstractFloatTensor.float16ToFloat(buffer.getShort((int) offset));
// Compute sum of products for this block
float blockSum = 0f;
@@ -113,7 +113,7 @@ public final class Q8_0FloatTensor extends FloatTensor {
}
@Override
- public void copyTo(final int thisOffset, final Tensor that, final int thatOffset, final int size) {
+ public void copyTo(final int thisOffset, final FloatTensor that, final int thatOffset, final int size) {
assert 0 <= thisOffset && thisOffset + size <= this.size;
assert 0 <= thatOffset && thatOffset + size <= that.size();
@@ -127,7 +127,7 @@ public final class Q8_0FloatTensor extends FloatTensor {
byte quant = buffer.get((int) (long) (blockOffset + GGMLType.FLOAT16_BYTES + withinBlockIndex));
final long offset = blockOffset;
- float scale = FloatTensor.float16ToFloat(buffer.getShort((int) offset));
+ float scale = AbstractFloatTensor.float16ToFloat(buffer.getShort((int) offset));
that.setFloat(i, quant * scale);
}
}
diff --git a/source/net/yacy/ai/llama3/Tensor/Tensor.java b/source/net/yacy/ai/llama3/Tensor/Tensor.java
deleted file mode 100644
index dcbe32476..000000000
--- a/source/net/yacy/ai/llama3/Tensor/Tensor.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- * Tensor.java
-
- * This file was extracted from the llama3/qwen2 projects
- * https://github.com/mukel/llama3.java
- * https://github.com/mukel/qwen2.svm.java
- *
- * License: MIT License
- *
- * Copyright (c) 2024 Andrej Karpathy (for llama2.c)
- * Copyright (c) 2024 Alfonso² Peterssen (for llama3/qwen2)
- * Copyright (c) 2023 Georgi Gerganov et al. (for llama.cpp)
- * Copyright (c) 2025 Michael Peter Christen for modifications:
- * The code was modified to fit the YaCy AI project:
- * - back-port to Java 11 (removal of Vector API operations and record types)
- * - removal of interactive mode and system.out printing
- * - separation of the classes in the single java and refactoring
- * - run-time performance optimizations for dot product computation of quantized values
- * - joining of llama3/qwen2 into one code base; multi-arch options
- * - alignment with code from https://github.com/ggml-org/llama.cpp/
- */
-
-
-package net.yacy.ai.llama3.Tensor;
-
-import java.util.Arrays;
-import java.util.function.IntConsumer;
-import java.util.function.LongConsumer;
-import java.util.stream.IntStream;
-import java.util.stream.LongStream;
-
-import net.yacy.ai.llama3.Tensor.FloatTensor.AggregateFunction;
-
-public interface Tensor {
-
- @FunctionalInterface
- public interface MapFunction {
- float apply(float value);
- }
-
- @FunctionalInterface
- public interface MapWithIndexFunction {
- float apply(float value, int index);
- }
-
- public int size();
-
- public int argmax();
-
- public float getFloat(final int index);
-
- public void setFloat(final int index, final float value);
-
- public void copyTo(final int thisOffset, final Tensor that, final int thatOffset, final int size);
-
- public float dot(final int thisOffset, final Tensor that, final int thatOffset, final int size);
-
- public void matmul(final Tensor that, final Tensor out, final int dim0, final int dim1);
-
- public void matmul(final int context, final Tensor[] that, final Tensor[] out, final int dim0, final int dim1);
-
- public float reduce(final int thisOffset, final int size, final float seed, final AggregateFunction reduce);
-
- public Tensor mapInPlace(final MapFunction mapFunction);
-
- public Tensor mapInPlace(final int thisOffset, final int size, MapFunction mapFunction);
-
- public Tensor addInPlace(final Tensor that);
-
- public Tensor multiplyInPlace(final Tensor that);
-
- public Tensor divideInPlace(final int thisOffset, final int size, final float value);
-
- public Tensor fillInPlace(final int thisOffset, final int size, final float value);
-
- public Tensor softmaxInPlace(final int thisOffset, final int size);
-
- public Tensor saxpyInPlace(final int thisOffset, final Tensor that, final int thatOffset, final int size, final float a);
-
- public static int numberOfElements(final int... dimensions) {
- assert Arrays.stream(dimensions).allMatch(i -> i > 0);
- return Arrays.stream(dimensions).reduce(Math::multiplyExact).orElseThrow();
- }
-
- public static void parallelFor(final int startInclusive, final int endExclusive, final IntConsumer action) {
- if (startInclusive == 0 && endExclusive == 1) {
- action.accept(0);
- return;
- }
- IntStream.range(startInclusive, endExclusive).parallel().forEach(action);
- }
-
- public static void parallelForLong(final long startInclusive, final long endExclusive, final LongConsumer action) {
- if (startInclusive == 0 && endExclusive == 1) {
- action.accept(0);
- return;
- }
- LongStream.range(startInclusive, endExclusive).parallel().forEach(action);
- }
-} \ No newline at end of file