warpvector 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1298 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ IntentAdapter: () => IntentAdapter,
24
+ IntentTrainer: () => IntentTrainer,
25
+ LoraIntentAdapter: () => LoraIntentAdapter,
26
+ MigrationTrainer: () => MigrationTrainer,
27
+ ProjectionAdapter: () => ProjectionAdapter,
28
+ VectorDBAdapter: () => VectorDBAdapter,
29
+ applyActivationToVector: () => applyActivationToVector,
30
+ assertDimension: () => assertDimension,
31
+ cosineSimilarity: () => cosineSimilarity,
32
+ flattenMatrix: () => flattenMatrix,
33
+ innerProduct: () => innerProduct,
34
+ normalize: () => normalize,
35
+ reject: () => reject,
36
+ slerp: () => slerp,
37
+ softmax: () => softmax
38
+ });
39
+ module.exports = __toCommonJS(index_exports);
40
+
41
+ // src/utils.ts
42
+ function normalize(vector) {
43
+ const dim = vector.length;
44
+ let sumOfSquares = 0;
45
+ for (let i = 0; i < dim; i++) {
46
+ const val = vector[i];
47
+ sumOfSquares += val * val;
48
+ }
49
+ const norm = Math.sqrt(sumOfSquares);
50
+ const result = new Float32Array(dim);
51
+ if (norm === 0) {
52
+ return result;
53
+ }
54
+ for (let i = 0; i < dim; i++) {
55
+ result[i] = vector[i] / norm;
56
+ }
57
+ return result;
58
+ }
59
+ function slerp(v1, v2, t) {
60
+ const dim = v1.length;
61
+ if (dim !== v2.length) {
62
+ throw new Error("Vectors must have the same dimension for slerp.");
63
+ }
64
+ let dot = 0;
65
+ let norm1Sq = 0;
66
+ let norm2Sq = 0;
67
+ for (let i = 0; i < dim; i++) {
68
+ dot += v1[i] * v2[i];
69
+ norm1Sq += v1[i] * v1[i];
70
+ norm2Sq += v2[i] * v2[i];
71
+ }
72
+ const norm1 = Math.sqrt(norm1Sq);
73
+ const norm2 = Math.sqrt(norm2Sq);
74
+ if (norm1 === 0 || norm2 === 0) {
75
+ throw new Error("Cannot apply slerp on zero vectors.");
76
+ }
77
+ let cosTheta = dot / (norm1 * norm2);
78
+ cosTheta = Math.max(-1, Math.min(1, cosTheta));
79
+ const theta = Math.acos(cosTheta);
80
+ const sinTheta = Math.sin(theta);
81
+ const result = new Float32Array(dim);
82
+ if (sinTheta < 1e-6) {
83
+ for (let i = 0; i < dim; i++) {
84
+ result[i] = v1[i] + t * (v2[i] - v1[i]);
85
+ }
86
+ return result;
87
+ }
88
+ const w1 = Math.sin((1 - t) * theta) / sinTheta;
89
+ const w2 = Math.sin(t * theta) / sinTheta;
90
+ const interpolatedMagnitude = norm1 + t * (norm2 - norm1);
91
+ for (let i = 0; i < dim; i++) {
92
+ const scaledV1 = v1[i] / norm1;
93
+ const scaledV2 = v2[i] / norm2;
94
+ result[i] = (scaledV1 * w1 + scaledV2 * w2) * interpolatedMagnitude;
95
+ }
96
+ return result;
97
+ }
98
+ function innerProduct(v1, v2) {
99
+ const dim = v1.length;
100
+ if (dim !== v2.length) {
101
+ throw new Error("Vectors must have the same dimension.");
102
+ }
103
+ let dot = 0;
104
+ for (let i = 0; i < dim; i++) {
105
+ dot += v1[i] * v2[i];
106
+ }
107
+ return dot;
108
+ }
109
+ function cosineSimilarity(v1, v2) {
110
+ const dim = v1.length;
111
+ if (dim !== v2.length) {
112
+ throw new Error("Vectors must have the same dimension.");
113
+ }
114
+ let dot = 0;
115
+ let norm1Sq = 0;
116
+ let norm2Sq = 0;
117
+ for (let i = 0; i < dim; i++) {
118
+ const val1 = v1[i];
119
+ const val2 = v2[i];
120
+ dot += val1 * val2;
121
+ norm1Sq += val1 * val1;
122
+ norm2Sq += val2 * val2;
123
+ }
124
+ if (norm1Sq === 0 || norm2Sq === 0) return 0;
125
+ return dot / (Math.sqrt(norm1Sq) * Math.sqrt(norm2Sq));
126
+ }
127
+ function reject(baseVector, negativeVector) {
128
+ const dim = baseVector.length;
129
+ if (dim !== negativeVector.length) {
130
+ throw new Error("Vectors must have the same dimension.");
131
+ }
132
+ let dotVU = 0;
133
+ let dotUU = 0;
134
+ for (let i = 0; i < dim; i++) {
135
+ const u = negativeVector[i];
136
+ dotVU += baseVector[i] * u;
137
+ dotUU += u * u;
138
+ }
139
+ if (dotUU === 0) {
140
+ const result2 = new Float32Array(dim);
141
+ result2.set(baseVector);
142
+ return result2;
143
+ }
144
+ const scalar = dotVU / dotUU;
145
+ const result = new Float32Array(dim);
146
+ for (let i = 0; i < dim; i++) {
147
+ result[i] = baseVector[i] - scalar * negativeVector[i];
148
+ }
149
+ return result;
150
+ }
151
+ function applyActivationToVector(vector, activation) {
152
+ if (!activation) return;
153
+ const dim = vector.length;
154
+ if (activation === "relu") {
155
+ for (let i = 0; i < dim; i++) {
156
+ if (vector[i] < 0) vector[i] = 0;
157
+ }
158
+ } else if (activation === "sigmoid") {
159
+ for (let i = 0; i < dim; i++) {
160
+ vector[i] = 1 / (1 + Math.exp(-vector[i]));
161
+ }
162
+ } else if (activation === "tanh") {
163
+ for (let i = 0; i < dim; i++) {
164
+ vector[i] = Math.tanh(vector[i]);
165
+ }
166
+ }
167
+ }
168
+ function softmax(values) {
169
+ if (values.length === 0) return [];
170
+ const max = Math.max(...values);
171
+ let sum = 0;
172
+ const exps = values.map((v) => {
173
+ const e = Math.exp(v - max);
174
+ sum += e;
175
+ return e;
176
+ });
177
+ return exps.map((e) => e / sum);
178
+ }
179
+ function flattenMatrix(matrix, rows, cols, contextName = "Matrix") {
180
+ if (matrix.length !== rows) {
181
+ throw new Error(
182
+ `${contextName}: Row dimension mismatch. Expected ${rows}, got ${matrix.length}.`
183
+ );
184
+ }
185
+ const flat = new Float32Array(rows * cols);
186
+ for (let i = 0; i < rows; i++) {
187
+ if (matrix[i].length !== cols) {
188
+ throw new Error(
189
+ `${contextName}: Column dimension mismatch at row ${i}. Expected ${cols}, got ${matrix[i].length}.`
190
+ );
191
+ }
192
+ for (let j = 0; j < cols; j++) {
193
+ flat[i * cols + j] = matrix[i][j];
194
+ }
195
+ }
196
+ return flat;
197
+ }
198
+ function assertDimension(vector, expectedDimension, contextName = "Vector") {
199
+ if (vector.length !== expectedDimension) {
200
+ throw new Error(
201
+ `${contextName} dimension mismatch. Expected ${expectedDimension}, got ${vector.length}.`
202
+ );
203
+ }
204
+ }
205
+
206
+ // src/wasm/wasm-binary.ts
207
+ var wasmBase64 = "AGFzbQEAAAABGQJgBn9/f39/fwBgDH9/f39/f319fX9/fwADAwIAAQUDAQAABzADDXR1bmVCYXRjaFdhc20AABNzZ2RNb21lbnR1bVN0ZXBXYXNtAAEGbWVtb3J5AgAK4QMCoAECBX8BfQNAIAUgCEoEQEEAIQYDQCAEIAZKBEBDAAAAACELIAQgBmwhCSAEIAhsIQpBACEHA0AgBCAHSgRAIAsgACAHIAlqQQJ0aioCACACIAcgCmpBAnRqKgIAlJIhCyAHQQFqIQcMAQsLIAMgBCAIbCAGakECdGogCyABIAZBAnRqKgIAkjgCACAGQQFqIQYMAQsLIAhBAWohCAwBCwsLvAICBX8DfQNAIAogDUoEQEMAAAAAIREgCSANbCEPQQAhDANAIAkgDEoEQCARIAAgDCAPakECdGoqAgAgBCAMQQJ0aioCAJSSIREgDEEBaiEMDAELCyANQQJ0IgwgC2ogESABIAxqKgIAkjgCACANQQFqIQ0MAQsLA0AgCiAOSgRAIA5BAnQiDCALaioCACAFIAxqKgIAkyERIAggAyAMaiINKgIAlCAGIBGUkyESIA0gEjgCACABIAxqIgwgDCoCACASkjgCACAJIA5sIQ9BACEMA0AgCSAMSgRAIAwgD2pBAnQiECAAaiINKgIAIRIgCCACIBBqIhAqAgCUIAYgESAEIAxBAnRqKgIAlCAHIBKUkpSTIRMgECATOAIAIA0gEiATkjgCACAMQQFqIQwMAQsLIA5BAWohDgwBCwsL";
208
+
209
+ // src/wasm/wasm-loader.ts
210
+ var wasmInstance = null;
211
+ var wasmMemory = null;
212
+ var initPromise = null;
213
+ function getWasmMemory() {
214
+ return wasmMemory;
215
+ }
216
+ function getWasmInstance() {
217
+ return wasmInstance;
218
+ }
219
+ async function initWasm() {
220
+ if (wasmInstance) return wasmInstance;
221
+ if (initPromise) return initPromise;
222
+ try {
223
+ const bytes = Uint8Array.from(atob(wasmBase64), (c) => c.charCodeAt(0));
224
+ const module2 = await WebAssembly.compile(bytes);
225
+ wasmInstance = new WebAssembly.Instance(module2);
226
+ wasmMemory = wasmInstance.exports.memory;
227
+ return wasmInstance;
228
+ } catch (e) {
229
+ console.warn("WASM initialization failed, falling back to JS.", e);
230
+ return null;
231
+ }
232
+ }
233
+ function ensureWasmMemory(requiredBytes) {
234
+ if (!wasmMemory) return false;
235
+ if (requiredBytes <= wasmMemory.buffer.byteLength) {
236
+ return true;
237
+ }
238
+ const currentPages = wasmMemory.buffer.byteLength / 65536;
239
+ const requiredPages = Math.ceil(requiredBytes / 65536);
240
+ try {
241
+ wasmMemory.grow(requiredPages - currentPages);
242
+ return true;
243
+ } catch (e) {
244
+ console.warn("WASM memory grow failed", e);
245
+ return false;
246
+ }
247
+ }
248
+
249
+ // src/IntentAdapter.ts
250
+ var IntentAdapter = class {
251
+ dimension;
252
+ matrices;
253
+ biases;
254
+ routingVectors;
255
+ /**
256
+ * IntentAdapter を初期化します。
257
+ * 行列とバイアスを Float32Array にコンパイルし、キャッシュ局所性と計算速度を最適化します。
258
+ *
259
+ * @constructor
260
+ * @param {Record<string, IntentWeights> | number} intentsOrDimension - 各インテント名と IntentWeights のマッピング、または次元数(空から始める場合)
261
+ * @throws {Error} インテントが一つも提供されておらず、次元数も不明な場合にエラーをスローします。
262
+ */
263
+ constructor(intentsOrDimension) {
264
+ this.matrices = /* @__PURE__ */ new Map();
265
+ this.biases = /* @__PURE__ */ new Map();
266
+ this.routingVectors = /* @__PURE__ */ new Map();
267
+ if (typeof intentsOrDimension === "number") {
268
+ this.dimension = intentsOrDimension;
269
+ } else {
270
+ const intentKeys = Object.keys(intentsOrDimension);
271
+ if (intentKeys.length === 0) {
272
+ throw new Error(
273
+ "At least one intent or a specific dimension must be provided."
274
+ );
275
+ }
276
+ const firstIntent = intentsOrDimension[intentKeys[0]];
277
+ this.dimension = firstIntent.bias.length;
278
+ for (const [intentName, weights] of Object.entries(intentsOrDimension)) {
279
+ this.addIntent(intentName, weights);
280
+ }
281
+ }
282
+ }
283
+ /**
284
+ * 実行時に新しい意図を動的に追加または更新します。
285
+ *
286
+ * @param {string} intentName - 追加する意図の名前
287
+ * @param {IntentWeights} weights - 意図の重み(行列とバイアス、任意でルーティングベクトル)
288
+ * @throws {Error} 行列やバイアスの次元数が現在の次元数と一致しない場合にエラーをスローします。
289
+ * @returns {void}
290
+ */
291
+ addIntent(intentName, weights) {
292
+ const { matrix, bias, routingVector } = weights;
293
+ assertDimension(bias, this.dimension, `Intent '${intentName}' Bias`);
294
+ let flatMatrix;
295
+ if (matrix instanceof Float32Array) {
296
+ assertDimension(
297
+ matrix,
298
+ this.dimension * this.dimension,
299
+ `Intent '${intentName}' Flat matrix`
300
+ );
301
+ flatMatrix = new Float32Array(matrix);
302
+ } else {
303
+ flatMatrix = flattenMatrix(
304
+ matrix,
305
+ this.dimension,
306
+ this.dimension,
307
+ `Intent '${intentName}' Matrix`
308
+ );
309
+ }
310
+ this.matrices.set(intentName, flatMatrix);
311
+ this.biases.set(intentName, new Float32Array(bias));
312
+ if (routingVector) {
313
+ assertDimension(
314
+ routingVector,
315
+ this.dimension,
316
+ `Intent '${intentName}' Routing vector`
317
+ );
318
+ this.routingVectors.set(intentName, new Float32Array(routingVector));
319
+ }
320
+ }
321
+ /**
322
+ * 指定した意図を削除します。
323
+ *
324
+ * @param {string} intentName - 削除する意図の名前
325
+ * @returns {void}
326
+ */
327
+ removeIntent(intentName) {
328
+ this.matrices.delete(intentName);
329
+ this.biases.delete(intentName);
330
+ this.routingVectors.delete(intentName);
331
+ }
332
+ /**
333
+ * 行列とバイアスを用いてベクトルにアフィン変換を適用する内部関数 (x' = W * x + b)
334
+ *
335
+ * @param {Float32Array} matrix - フラット化された変換行列
336
+ * @param {Float32Array} bias - バイアスベクトル
337
+ * @param {number[] | Float32Array} vector - 変換元の入力ベクトル
338
+ * @param {Float32Array} result - 計算結果を格納する配列 (出力先)
339
+ * @returns {void}
340
+ */
341
+ applyAffine(matrix, bias, vector, result) {
342
+ const dim = this.dimension;
343
+ for (let i = 0; i < dim; i++) {
344
+ let sum = 0;
345
+ const rowOffset = i * dim;
346
+ for (let j = 0; j < dim; j++) {
347
+ sum += matrix[rowOffset + j] * vector[j];
348
+ }
349
+ result[i] = sum + bias[i];
350
+ }
351
+ }
352
+ /**
353
+ * 複数の意図を指定された重みでブレンドした一時的な行列とバイアスを計算します。
354
+ * W_blend = Σ(w_i * W_i), b_blend = Σ(w_i * b_i)
355
+ *
356
+ * @param {Record<string, number>} blendWeights - ブレンドする各意図の重みマップ
357
+ * @returns {{matrix: Float32Array, bias: Float32Array}} ブレンドされた合成行列とバイアス
358
+ * @throws {Error} 指定された意図が見つからない場合にエラーをスローします。
359
+ */
360
+ computeBlendedWeights(blendWeights) {
361
+ const dim = this.dimension;
362
+ const blendedMatrix = new Float32Array(dim * dim);
363
+ const blendedBias = new Float32Array(dim);
364
+ for (const [intentName, weight] of Object.entries(blendWeights)) {
365
+ const matrix = this.matrices.get(intentName);
366
+ const bias = this.biases.get(intentName);
367
+ if (!matrix || !bias) {
368
+ throw new Error(`Intent '${intentName}' not found during blending.`);
369
+ }
370
+ for (let i = 0; i < dim; i++) {
371
+ blendedBias[i] += bias[i] * weight;
372
+ const rowOffset = i * dim;
373
+ for (let j = 0; j < dim; j++) {
374
+ blendedMatrix[rowOffset + j] += matrix[rowOffset + j] * weight;
375
+ }
376
+ }
377
+ }
378
+ return { matrix: blendedMatrix, bias: blendedBias };
379
+ }
380
+ /**
381
+ * 指定された意図(intent)に基づいて、ベースベクトルにアフィン変換を適用します。
382
+ * 数式: x' = W_I * x + b_I
383
+ *
384
+ * @param {number[] | Float32Array} baseVector - 変換元のベクトル
385
+ * @param {string} intent - 適用する意図(intent)の名前
386
+ * @param {Activation} [activation] - (オプション)変換後に適用する非線形活性化関数
387
+ * @returns {Float32Array} 変換後のベクトル
388
+ * @throws {Error} ベクトルの次元数が一致しない場合、または指定された意図が存在しない場合にエラーをスローします。
389
+ */
390
+ tune(baseVector, intent, activation) {
391
+ assertDimension(baseVector, this.dimension, "Base vector");
392
+ const matrix = this.matrices.get(intent);
393
+ const bias = this.biases.get(intent);
394
+ if (!matrix || !bias) {
395
+ throw new Error(`Intent '${intent}' not found.`);
396
+ }
397
+ const result = new Float32Array(this.dimension);
398
+ this.applyAffine(matrix, bias, baseVector, result);
399
+ applyActivationToVector(result, activation);
400
+ return result;
401
+ }
402
+ /**
403
+ * 複数のベースベクトルに対して、指定された意図のアフィン変換をバッチ処理で適用します。
404
+ * メモリと条件が許せば、自動的にWASM/SIMDエンジンにオフロードして超高速処理を行います。
405
+ *
406
+ * @param {(number[] | Float32Array)[]} baseVectors - 変換元のベクトルの配列 (2次元配列)
407
+ * @param {string} intent - 適用する意図(intent)の名前
408
+ * @param {Activation} [activation] - (オプション)非線形活性化関数
409
+ * @returns {Float32Array[]} 変換後のベクトルの配列
410
+ * @throws {Error} 指定された意図が存在しない場合や入力ベクトルの次元が不正な場合にエラーをスローします。
411
+ */
412
+ tuneBatch(baseVectors, intent, activation) {
413
+ const matrix = this.matrices.get(intent);
414
+ const bias = this.biases.get(intent);
415
+ if (!matrix || !bias) {
416
+ throw new Error(`Intent '${intent}' not found.`);
417
+ }
418
+ const batchSize = baseVectors.length;
419
+ const instance = getWasmInstance();
420
+ const requiredBytes = (this.dimension * this.dimension + this.dimension + batchSize * this.dimension * 2) * 4;
421
+ if (instance && ensureWasmMemory(requiredBytes)) {
422
+ const wasmMem = getWasmMemory();
423
+ const f32Mem = new Float32Array(wasmMem.buffer);
424
+ let ptr = 0;
425
+ const matrixPtr = ptr;
426
+ f32Mem.set(matrix, ptr);
427
+ ptr += this.dimension * this.dimension;
428
+ const biasPtr = ptr;
429
+ f32Mem.set(bias, ptr);
430
+ ptr += this.dimension;
431
+ const vectorsPtr = ptr;
432
+ for (let k = 0; k < batchSize; k++) {
433
+ f32Mem.set(baseVectors[k], ptr + k * this.dimension);
434
+ }
435
+ ptr += batchSize * this.dimension;
436
+ const resultsPtr = ptr;
437
+ const tuneBatchWasm = instance.exports.tuneBatchWasm;
438
+ tuneBatchWasm(
439
+ matrixPtr * 4,
440
+ biasPtr * 4,
441
+ vectorsPtr * 4,
442
+ resultsPtr * 4,
443
+ this.dimension,
444
+ batchSize
445
+ );
446
+ const results2 = new Array(batchSize);
447
+ for (let k = 0; k < batchSize; k++) {
448
+ const res = f32Mem.slice(
449
+ resultsPtr + k * this.dimension,
450
+ resultsPtr + (k + 1) * this.dimension
451
+ );
452
+ applyActivationToVector(res, activation);
453
+ results2[k] = res;
454
+ }
455
+ return results2;
456
+ }
457
+ const results = new Array(batchSize);
458
+ for (let k = 0; k < batchSize; k++) {
459
+ const baseVector = baseVectors[k];
460
+ assertDimension(baseVector, this.dimension, `Base vector at index ${k}`);
461
+ const result = new Float32Array(this.dimension);
462
+ this.applyAffine(matrix, bias, baseVector, result);
463
+ applyActivationToVector(result, activation);
464
+ results[k] = result;
465
+ }
466
+ return results;
467
+ }
468
+ /**
469
+ * 複数の意図を指定された重みでブレンドし、ベクトルにアフィン変換を適用します。
470
+ *
471
+ * @param {number[] | Float32Array} baseVector - 変換元のベクトル
472
+ * @param {Record<string, number>} blendWeights - 意図の名前と重みのマッピング (例: { riskAnalysis: 0.7, economicImpact: 0.3 })
473
+ * @param {Activation} [activation] - (オプション)非線形活性化関数
474
+ * @returns {Float32Array} 変換後のベクトル
475
+ * @throws {Error} 入力ベクトルの次元が不正な場合、または指定された意図が存在しない場合にエラーをスローします。
476
+ */
477
+ tuneBlended(baseVector, blendWeights, activation) {
478
+ assertDimension(baseVector, this.dimension, "Base vector");
479
+ const { matrix, bias } = this.computeBlendedWeights(blendWeights);
480
+ const result = new Float32Array(this.dimension);
481
+ this.applyAffine(matrix, bias, baseVector, result);
482
+ applyActivationToVector(result, activation);
483
+ return result;
484
+ }
485
+ /**
486
+ * 複数の意図を指定された重みでブレンドし、複数のベクトルに一括で適用します。
487
+ * これもバッチ処理と同様に自動的にWASM最適化が有効になります。
488
+ *
489
+ * @param {(number[] | Float32Array)[]} baseVectors - 変換元のベクトルの配列
490
+ * @param {Record<string, number>} blendWeights - 意図の名前と重みのマッピング
491
+ * @param {Activation} [activation] - (オプション)非線形活性化関数
492
+ * @returns {Float32Array[]} 変換後のベクトルの配列
493
+ * @throws {Error} ベクトルの次元数が一致しない場合、または指定された意図が存在しない場合にエラーをスローします。
494
+ */
495
+ tuneBatchBlended(baseVectors, blendWeights, activation) {
496
+ const { matrix, bias } = this.computeBlendedWeights(blendWeights);
497
+ const batchSize = baseVectors.length;
498
+ const instance = getWasmInstance();
499
+ const requiredBytes = (this.dimension * this.dimension + this.dimension + batchSize * this.dimension * 2) * 4;
500
+ if (instance && ensureWasmMemory(requiredBytes)) {
501
+ const wasmMem = getWasmMemory();
502
+ const f32Mem = new Float32Array(wasmMem.buffer);
503
+ let ptr = 0;
504
+ const matrixPtr = ptr;
505
+ f32Mem.set(matrix, ptr);
506
+ ptr += this.dimension * this.dimension;
507
+ const biasPtr = ptr;
508
+ f32Mem.set(bias, ptr);
509
+ ptr += this.dimension;
510
+ const vectorsPtr = ptr;
511
+ for (let k = 0; k < batchSize; k++) {
512
+ f32Mem.set(baseVectors[k], ptr + k * this.dimension);
513
+ }
514
+ ptr += batchSize * this.dimension;
515
+ const resultsPtr = ptr;
516
+ const tuneBatchWasm = instance.exports.tuneBatchWasm;
517
+ tuneBatchWasm(
518
+ matrixPtr * 4,
519
+ biasPtr * 4,
520
+ vectorsPtr * 4,
521
+ resultsPtr * 4,
522
+ this.dimension,
523
+ batchSize
524
+ );
525
+ const results2 = new Array(batchSize);
526
+ for (let k = 0; k < batchSize; k++) {
527
+ const res = f32Mem.slice(
528
+ resultsPtr + k * this.dimension,
529
+ resultsPtr + (k + 1) * this.dimension
530
+ );
531
+ applyActivationToVector(res, activation);
532
+ results2[k] = res;
533
+ }
534
+ return results2;
535
+ }
536
+ const results = new Array(batchSize);
537
+ for (let k = 0; k < batchSize; k++) {
538
+ const baseVector = baseVectors[k];
539
+ assertDimension(baseVector, this.dimension, `Base vector at index ${k}`);
540
+ const result = new Float32Array(this.dimension);
541
+ this.applyAffine(matrix, bias, baseVector, result);
542
+ applyActivationToVector(result, activation);
543
+ results[k] = result;
544
+ }
545
+ return results;
546
+ }
547
+ /**
548
+ * 自己アテンション型動的ブレンド (Auto-blending / Routing)
549
+ *
550
+ * クエリとして入力されたベースベクトル自体と、各意図の `routingVector`(代表ベクトル)の
551
+ * 類似度を比較し、Softmax関数により最適なブレンド比率を自動で算出・適用します。
552
+ *
553
+ * @param {number[] | Float32Array} baseVector - ユーザーからのクエリベクトルなど
554
+ * @param {Activation} [activation] - (オプション)非線形活性化関数
555
+ * @returns {Float32Array} 動的ブレンドによって変換されたベクトル
556
+ * @throws {Error} ベースベクトルの次元が異なる場合、またはルーティングベクトルが一つも存在しない場合にエラーをスローします。
557
+ */
558
+ tuneAutoBlended(baseVector, activation) {
559
+ assertDimension(baseVector, this.dimension, "Base vector");
560
+ const intentNames = [];
561
+ const scores = [];
562
+ for (const [intentName, vector] of this.routingVectors.entries()) {
563
+ intentNames.push(intentName);
564
+ scores.push(cosineSimilarity(baseVector, vector));
565
+ }
566
+ if (intentNames.length === 0) {
567
+ throw new Error("No routing vectors available for auto-blending.");
568
+ }
569
+ const weightsArray = softmax(scores);
570
+ const blendWeights = {};
571
+ for (let i = 0; i < intentNames.length; i++) {
572
+ blendWeights[intentNames[i]] = weightsArray[i];
573
+ }
574
+ return this.tuneBlended(baseVector, blendWeights, activation);
575
+ }
576
+ /**
577
+ * 学習済みの意図(IntentWeights)を軽量なバイナリ(Uint8Array)としてシリアライズします。
578
+ * これにより、JSONに比べてファイルサイズが劇的に小さくなり、ロードも高速になります。
579
+ *
580
+ * @param {string} intentName - エクスポートする意図の名前
581
+ * @returns {Uint8Array} シリアライズされたバイナリデータ
582
+ */
583
+ exportIntentBinary(intentName) {
584
+ const matrix = this.matrices.get(intentName);
585
+ const bias = this.biases.get(intentName);
586
+ const routingVector = this.routingVectors.get(intentName);
587
+ if (!matrix || !bias) {
588
+ throw new Error(`Intent '${intentName}' not found.`);
589
+ }
590
+ const dim = this.dimension;
591
+ const hasRouting = routingVector ? 1 : 0;
592
+ const totalBytes = 8 + dim * dim * 4 + dim * 4 + (hasRouting ? dim * 4 : 0);
593
+ const buffer = new ArrayBuffer(totalBytes);
594
+ const dataView = new DataView(buffer);
595
+ const uint8View = new Uint8Array(buffer);
596
+ dataView.setUint32(0, dim, true);
597
+ dataView.setUint8(4, hasRouting);
598
+ let offset = 8;
599
+ uint8View.set(
600
+ new Uint8Array(matrix.buffer, matrix.byteOffset, matrix.byteLength),
601
+ offset
602
+ );
603
+ offset += matrix.byteLength;
604
+ uint8View.set(
605
+ new Uint8Array(bias.buffer, bias.byteOffset, bias.byteLength),
606
+ offset
607
+ );
608
+ offset += bias.byteLength;
609
+ if (routingVector) {
610
+ uint8View.set(
611
+ new Uint8Array(
612
+ routingVector.buffer,
613
+ routingVector.byteOffset,
614
+ routingVector.byteLength
615
+ ),
616
+ offset
617
+ );
618
+ }
619
+ return uint8View;
620
+ }
621
+ /**
622
+ * バイナリデータ(Uint8Array)から IntentWeights を復元し、
623
+ * そのままアダプターに新しい意図として追加します。
624
+ *
625
+ * @param {string} intentName - 追加する意図の名前
626
+ * @param {Uint8Array} binary - exportIntentBinary で生成されたバイナリデータ
627
+ */
628
+ importIntentBinary(intentName, binary) {
629
+ if (binary.length < 8) {
630
+ throw new Error("Invalid binary format: too short.");
631
+ }
632
+ const dataView = new DataView(
633
+ binary.buffer,
634
+ binary.byteOffset,
635
+ binary.byteLength
636
+ );
637
+ const dim = dataView.getUint32(0, true);
638
+ const hasRouting = dataView.getUint8(4);
639
+ if (this.dimension !== void 0 && dim !== this.dimension) {
640
+ throw new Error(
641
+ `Dimension mismatch. Expected ${this.dimension}, got ${dim}.`
642
+ );
643
+ }
644
+ const expectedBytes = 8 + dim * dim * 4 + dim * 4 + (hasRouting ? dim * 4 : 0);
645
+ if (binary.length !== expectedBytes) {
646
+ throw new Error(
647
+ `Invalid binary length. Expected ${expectedBytes}, got ${binary.length}.`
648
+ );
649
+ }
650
+ let offset = 8;
651
+ const matrix = new Float32Array(dim * dim);
652
+ matrix.set(
653
+ new Float32Array(binary.buffer, binary.byteOffset + offset, dim * dim)
654
+ );
655
+ offset += dim * dim * 4;
656
+ const bias = new Float32Array(dim);
657
+ bias.set(new Float32Array(binary.buffer, binary.byteOffset + offset, dim));
658
+ offset += dim * 4;
659
+ let routingVector = void 0;
660
+ if (hasRouting) {
661
+ routingVector = new Float32Array(dim);
662
+ routingVector.set(
663
+ new Float32Array(binary.buffer, binary.byteOffset + offset, dim)
664
+ );
665
+ }
666
+ this.matrices.set(intentName, matrix);
667
+ this.biases.set(intentName, bias);
668
+ if (routingVector) {
669
+ this.routingVectors.set(intentName, routingVector);
670
+ }
671
+ }
672
+ };
673
+
674
+ // src/LoraIntentAdapter.ts
675
+ var LoraIntentAdapter = class {
676
+ dimension;
677
+ rank;
678
+ // フラット化されたAとBの行列、およびバイアスを保存
679
+ matricesA;
680
+ matricesB;
681
+ biases;
682
+ /**
683
+ * LoraIntentAdapter を初期化します。
684
+ * 非常に高い次元(例:1536次元など)の埋め込みベクトルに対して、
685
+ * フルマトリックスの代わりに低ランク行列(A, B)を使用することで
686
+ * メモリ使用量と計算量を劇的に削減します。
687
+ *
688
+ * @constructor
689
+ * @param {number} dimension - ベクトル空間の元の次元数(D)
690
+ * @param {number} rank - 低ランク適応における中間ランク数(r)
691
+ * @param {Record<string, LoraIntentWeights>} [intents] - 初期化時に追加するインテントのマップ
692
+ */
693
+ constructor(dimension, rank, intents) {
694
+ this.dimension = dimension;
695
+ this.rank = rank;
696
+ this.matricesA = /* @__PURE__ */ new Map();
697
+ this.matricesB = /* @__PURE__ */ new Map();
698
+ this.biases = /* @__PURE__ */ new Map();
699
+ if (intents) {
700
+ for (const [intentName, weights] of Object.entries(intents)) {
701
+ this.addIntent(intentName, weights);
702
+ }
703
+ }
704
+ }
705
+ /**
706
+ * 実行時に新しいLoRA意図を動的に追加または更新します。
707
+ *
708
+ * @param {string} intentName - 追加または更新する意図の名前
709
+ * @param {LoraIntentWeights} weights - LoRA意図の重み(行列A、行列B、バイアス)
710
+ * @throws {Error} 行列またはバイアスの次元数が一致しない場合にエラーをスローします。
711
+ * @returns {void}
712
+ */
713
+ addIntent(intentName, weights) {
714
+ const { matrixA, matrixB, bias } = weights;
715
+ assertDimension(bias, this.dimension, `Intent '${intentName}' Bias`);
716
+ const flatA = flattenMatrix(
717
+ matrixA,
718
+ this.dimension,
719
+ this.rank,
720
+ `Intent '${intentName}' Matrix A`
721
+ );
722
+ const flatB = flattenMatrix(
723
+ matrixB,
724
+ this.rank,
725
+ this.dimension,
726
+ `Intent '${intentName}' Matrix B`
727
+ );
728
+ this.matricesA.set(intentName, flatA);
729
+ this.matricesB.set(intentName, flatB);
730
+ this.biases.set(intentName, new Float32Array(bias));
731
+ }
732
+ /**
733
+ * 指定したLoRA意図を削除します。
734
+ *
735
+ * @param {string} intentName - 削除する意図の名前
736
+ * @returns {void}
737
+ */
738
+ removeIntent(intentName) {
739
+ this.matricesA.delete(intentName);
740
+ this.matricesB.delete(intentName);
741
+ this.biases.delete(intentName);
742
+ }
743
+ /**
744
+ * LoRAアプローチを用いてベクトルにアフィン変換を適用します。
745
+ * 数式: x' = x + A(Bx) + b
746
+ *
747
+ * @param {number[] | Float32Array} baseVector - 変換元のベクトル
748
+ * @param {string} intent - 適用する意図の名前
749
+ * @returns {Float32Array} LoRA変換と残差結合が適用された新しいベクトル
750
+ * @throws {Error} ベクトルの次元数が一致しない、または指定された意図が存在しない場合にエラーをスローします。
751
+ */
752
+ tune(baseVector, intent) {
753
+ assertDimension(baseVector, this.dimension, "Base vector");
754
+ const matA = this.matricesA.get(intent);
755
+ const matB = this.matricesB.get(intent);
756
+ const bias = this.biases.get(intent);
757
+ if (!matA || !matB || !bias) {
758
+ throw new Error(`Intent '${intent}' not found.`);
759
+ }
760
+ const result = new Float32Array(this.dimension);
761
+ const y = new Float32Array(this.rank);
762
+ for (let i = 0; i < this.rank; i++) {
763
+ let sum = 0;
764
+ const rowOffset = i * this.dimension;
765
+ for (let j = 0; j < this.dimension; j++) {
766
+ sum += matB[rowOffset + j] * baseVector[j];
767
+ }
768
+ y[i] = sum;
769
+ }
770
+ for (let i = 0; i < this.dimension; i++) {
771
+ let sum = 0;
772
+ const rowOffset = i * this.rank;
773
+ for (let j = 0; j < this.rank; j++) {
774
+ sum += matA[rowOffset + j] * y[j];
775
+ }
776
+ result[i] = baseVector[i] + sum + bias[i];
777
+ }
778
+ return result;
779
+ }
780
+ };
781
+
782
+ // src/ProjectionAdapter.ts
783
+ var ProjectionAdapter = class {
784
+ inDimension;
785
+ outDimension;
786
+ // フラット化された射影行列とバイアスを保存
787
+ matrices;
788
+ biases;
789
+ /**
790
+ * ProjectionAdapter を初期化します。
791
+ *
792
+ * @constructor
793
+ * @param {number} inDimension - 変換前の入力ベクトルの次元数
794
+ * @param {number} outDimension - 変換後の出力ベクトルの次元数
795
+ * @param {Record<string, ProjectionWeights>} [projections] - 初期化時に追加する射影設定のマップ
796
+ */
797
+ constructor(inDimension, outDimension, projections) {
798
+ this.inDimension = inDimension;
799
+ this.outDimension = outDimension;
800
+ this.matrices = /* @__PURE__ */ new Map();
801
+ this.biases = /* @__PURE__ */ new Map();
802
+ if (projections) {
803
+ for (const [name, weights] of Object.entries(projections)) {
804
+ this.addProjection(name, weights);
805
+ }
806
+ }
807
+ }
808
+ /**
809
+ * 実行時に新しい射影設定を動的に追加または更新します。
810
+ *
811
+ * @param {string} name - 追加または更新する射影設定の名前
812
+ * @param {ProjectionWeights} weights - 射影変換行列のデータ
813
+ * @throws {Error} 行列のサイズが指定された次元数と一致しない場合にエラーをスローします。
814
+ * @returns {void}
815
+ */
816
+ addProjection(name, weights) {
817
+ const { matrix } = weights;
818
+ const flatMatrix = flattenMatrix(
819
+ matrix,
820
+ this.outDimension,
821
+ this.inDimension,
822
+ `Projection '${name}' Matrix`
823
+ );
824
+ this.matrices.set(name, flatMatrix);
825
+ if (weights.bias) {
826
+ assertDimension(
827
+ weights.bias,
828
+ this.outDimension,
829
+ `Projection '${name}' Bias`
830
+ );
831
+ this.biases.set(name, new Float32Array(weights.bias));
832
+ } else {
833
+ this.biases.delete(name);
834
+ }
835
+ }
836
+ /**
837
+ * 指定した射影設定を削除します。
838
+ *
839
+ * @param {string} name - 削除する射影設定の名前
840
+ * @returns {void}
841
+ */
842
+ removeProjection(name) {
843
+ this.matrices.delete(name);
844
+ this.biases.delete(name);
845
+ }
846
+ /**
847
+ * ベクトルに射影変換を適用し、次元を変更した新しいベクトルを返します。
848
+ * 数式: y = W * x
849
+ *
850
+ * @param {number[] | Float32Array} baseVector - 変換元の入力ベクトル
851
+ * @param {string} projectionName - 適用する射影設定の名前
852
+ * @returns {Float32Array} 射影変換適用後の新しいベクトル
853
+ * @throws {Error} ベクトルの次元数が inDimension と一致しない場合、または射影設定が存在しない場合にエラーをスローします。
854
+ */
855
+ project(baseVector, projectionName) {
856
+ assertDimension(baseVector, this.inDimension, "Base vector");
857
+ const matrix = this.matrices.get(projectionName);
858
+ if (!matrix) {
859
+ throw new Error(`Projection '${projectionName}' not found.`);
860
+ }
861
+ const result = new Float32Array(this.outDimension);
862
+ const bias = this.biases.get(projectionName);
863
+ for (let i = 0; i < this.outDimension; i++) {
864
+ let sum = bias ? bias[i] : 0;
865
+ const rowOffset = i * this.inDimension;
866
+ for (let j = 0; j < this.inDimension; j++) {
867
+ sum += matrix[rowOffset + j] * baseVector[j];
868
+ }
869
+ result[i] = sum;
870
+ }
871
+ return result;
872
+ }
873
+ };
874
+
875
+ // src/BaseTrainer.ts
876
+ var BaseTrainer = class {
877
+ /** 学習用サンプルの配列 */
878
+ examples = [];
879
+ /**
880
+ * 学習用のサンプルデータを追加します。
881
+ * 次元数がソース/ターゲットと一致しない場合はエラーとなります。
882
+ *
883
+ * @param {TExample} example 追加するサンプルデータ
884
+ * @throws {Error} 次元数が一致しない場合にスローされます。
885
+ */
886
+ addExample(example) {
887
+ const { source, target } = this.getInputs(example);
888
+ if (source.length !== this.sourceDimension) {
889
+ throw new Error(
890
+ `Source dimension mismatch. Expected ${this.sourceDimension}.`
891
+ );
892
+ }
893
+ if (target.length !== this.targetDimension) {
894
+ throw new Error(
895
+ `Target dimension mismatch. Expected ${this.targetDimension}.`
896
+ );
897
+ }
898
+ this.examples.push(example);
899
+ }
900
+ /**
901
+ * 追加されたサンプルデータを用いて学習を実行します。
902
+ * 指定されたエポック数だけ SGD + Momentum によるパラメータ更新を行います。
903
+ * パフォーマンスのため、可能であれば内部で WebAssembly (WASM) を使用します。
904
+ *
905
+ * @param {BaseTrainingOptions} [options={}] 学習のハイパーパラメータオプション
906
+ * @returns {Promise<TResult>} 学習済みの重みを返します。
907
+ * @throws {Error} サンプルデータが追加されていない場合にスローされます。
908
+ */
909
+ async train(options = {}) {
910
+ await initWasm();
911
+ if (this.examples.length === 0) {
912
+ throw new Error("No training examples provided.");
913
+ }
914
+ if (options.autoTune) {
915
+ options.learningRate = this.findBestLearningRate(options);
916
+ options.autoTune = false;
917
+ }
918
+ const lr = options.learningRate ?? 0.01;
919
+ const epochs = options.epochs ?? 100;
920
+ const reg = options.regularization ?? 1e-3;
921
+ const momentum = options.momentum ?? 0.9;
922
+ const sDim = this.sourceDimension;
923
+ const tDim = this.targetDimension;
924
+ const flatMatrix = new Float32Array(tDim * sDim);
925
+ for (let i = 0; i < tDim; i++) {
926
+ if (i < sDim) {
927
+ flatMatrix[i * sDim + i] = 1;
928
+ }
929
+ }
930
+ const bias = new Float32Array(tDim);
931
+ const vMatrix = new Float32Array(tDim * sDim);
932
+ const vBias = new Float32Array(tDim);
933
+ for (let epoch = 0; epoch < epochs; epoch++) {
934
+ for (const example of this.examples) {
935
+ const { source, target } = this.getInputs(example);
936
+ this.sgdMomentumStep(
937
+ flatMatrix,
938
+ bias,
939
+ vMatrix,
940
+ vBias,
941
+ source,
942
+ target,
943
+ lr,
944
+ reg,
945
+ momentum
946
+ );
947
+ }
948
+ }
949
+ return this.toWeights(flatMatrix, bias);
950
+ }
951
+ /**
952
+ * サンプルデータに対する短時間のテストランを行い、最も損失(Loss)が小さくなる最適な学習率を自動探索します。
953
+ * `options.autoTune` が true の場合に `train` メソッド内で自動的に呼び出されます。
954
+ *
955
+ * @param {BaseTrainingOptions} options 現在の学習オプション
956
+ * @returns {number} 探索された最適な学習率
957
+ */
958
+ findBestLearningRate(options) {
959
+ const candidateLrs = [0.1, 0.05, 0.01, 5e-3, 1e-3];
960
+ let bestLr = options.learningRate ?? 0.01;
961
+ let minLoss = Infinity;
962
+ const testEpochs = Math.min(10, options.epochs ?? 100);
963
+ const reg = options.regularization ?? 1e-3;
964
+ const momentum = options.momentum ?? 0.9;
965
+ const sDim = this.sourceDimension;
966
+ const tDim = this.targetDimension;
967
+ for (const lr of candidateLrs) {
968
+ const flatMatrix = new Float32Array(tDim * sDim);
969
+ for (let i = 0; i < tDim; i++) {
970
+ if (i < sDim) flatMatrix[i * sDim + i] = 1;
971
+ }
972
+ const bias = new Float32Array(tDim);
973
+ const vMatrix = new Float32Array(tDim * sDim);
974
+ const vBias = new Float32Array(tDim);
975
+ for (let epoch = 0; epoch < testEpochs; epoch++) {
976
+ for (const example of this.examples) {
977
+ const { source, target } = this.getInputs(example);
978
+ this.sgdMomentumStep(
979
+ flatMatrix,
980
+ bias,
981
+ vMatrix,
982
+ vBias,
983
+ source,
984
+ target,
985
+ lr,
986
+ reg,
987
+ momentum
988
+ );
989
+ }
990
+ }
991
+ let currentLoss = 0;
992
+ for (const example of this.examples) {
993
+ const { source, target } = this.getInputs(example);
994
+ const pred = new Float32Array(tDim);
995
+ for (let i = 0; i < tDim; i++) {
996
+ let sum = 0;
997
+ for (let j = 0; j < sDim; j++) {
998
+ sum += flatMatrix[i * sDim + j] * source[j];
999
+ }
1000
+ pred[i] = sum + bias[i];
1001
+ const diff = pred[i] - target[i];
1002
+ currentLoss += diff * diff;
1003
+ }
1004
+ }
1005
+ if (currentLoss < minLoss) {
1006
+ minLoss = currentLoss;
1007
+ bestLr = lr;
1008
+ }
1009
+ }
1010
+ return bestLr;
1011
+ }
1012
+ /**
1013
+ * SGD + Momentum アルゴリズムによる1ステップのパラメータ更新を実行します。
1014
+ * In-place (破壊的) に `matrix` と `bias` を更新します。
1015
+ *
1016
+ * @param {Float32Array} matrix 現在の変換行列 (1次元フラット配列)
1017
+ * @param {Float32Array} bias 現在のバイアスベクトル
1018
+ * @param {Float32Array} vMatrix 行列のモメンタム (速度)
1019
+ * @param {Float32Array} vBias バイアスのモメンタム (速度)
1020
+ * @param {number[] | Float32Array} x 入力ベクトル (ソース)
1021
+ * @param {number[] | Float32Array} y 理想の出力ベクトル (ターゲット)
1022
+ * @param {number} lr 学習率
1023
+ * @param {number} reg L2正則化係数
1024
+ * @param {number} momentum モメンタム係数
1025
+ */
1026
+ sgdMomentumStep(matrix, bias, vMatrix, vBias, x, y, lr, reg, momentum) {
1027
+ const sDim = this.sourceDimension;
1028
+ const tDim = this.targetDimension;
1029
+ const instance = getWasmInstance();
1030
+ const requiredBytes = sDim * tDim * 4 + tDim * 4 + sDim * tDim * 4 + tDim * 4 + sDim * 4 + tDim * 4 + tDim * 4;
1031
+ if (instance && ensureWasmMemory(requiredBytes)) {
1032
+ const wasmMemory2 = getWasmMemory();
1033
+ const f32Mem = new Float32Array(wasmMemory2.buffer);
1034
+ let offset = 0;
1035
+ const matrixOffset = offset;
1036
+ offset += sDim * tDim;
1037
+ const biasOffset = offset;
1038
+ offset += tDim;
1039
+ const vMatrixOffset = offset;
1040
+ offset += sDim * tDim;
1041
+ const vBiasOffset = offset;
1042
+ offset += tDim;
1043
+ const xOffset = offset;
1044
+ offset += sDim;
1045
+ const yOffset = offset;
1046
+ offset += tDim;
1047
+ const predOffset = offset;
1048
+ offset += tDim;
1049
+ f32Mem.set(matrix, matrixOffset);
1050
+ f32Mem.set(bias, biasOffset);
1051
+ f32Mem.set(vMatrix, vMatrixOffset);
1052
+ f32Mem.set(vBias, vBiasOffset);
1053
+ f32Mem.set(x, xOffset);
1054
+ f32Mem.set(y, yOffset);
1055
+ const sgdMomentumStepWasm = instance.exports.sgdMomentumStepWasm;
1056
+ sgdMomentumStepWasm(
1057
+ matrixOffset * 4,
1058
+ biasOffset * 4,
1059
+ vMatrixOffset * 4,
1060
+ vBiasOffset * 4,
1061
+ xOffset * 4,
1062
+ yOffset * 4,
1063
+ lr,
1064
+ reg,
1065
+ momentum,
1066
+ sDim,
1067
+ tDim,
1068
+ predOffset * 4
1069
+ );
1070
+ matrix.set(f32Mem.subarray(matrixOffset, matrixOffset + sDim * tDim));
1071
+ bias.set(f32Mem.subarray(biasOffset, biasOffset + tDim));
1072
+ vMatrix.set(f32Mem.subarray(vMatrixOffset, vMatrixOffset + sDim * tDim));
1073
+ vBias.set(f32Mem.subarray(vBiasOffset, vBiasOffset + tDim));
1074
+ return;
1075
+ }
1076
+ const pred = new Float32Array(tDim);
1077
+ for (let i = 0; i < tDim; i++) {
1078
+ let sum = 0;
1079
+ const rowOffset = i * sDim;
1080
+ for (let j = 0; j < sDim; j++) {
1081
+ sum += matrix[rowOffset + j] * x[j];
1082
+ }
1083
+ pred[i] = sum + bias[i];
1084
+ }
1085
+ for (let i = 0; i < tDim; i++) {
1086
+ const error = pred[i] - y[i];
1087
+ const bGrad = error;
1088
+ vBias[i] = momentum * vBias[i] - lr * bGrad;
1089
+ bias[i] += vBias[i];
1090
+ const rowOffset = i * sDim;
1091
+ for (let j = 0; j < sDim; j++) {
1092
+ const wIdx = rowOffset + j;
1093
+ const wGrad = error * x[j] + reg * matrix[wIdx];
1094
+ vMatrix[wIdx] = momentum * vMatrix[wIdx] - lr * wGrad;
1095
+ matrix[wIdx] += vMatrix[wIdx];
1096
+ }
1097
+ }
1098
+ }
1099
+ };
1100
+
1101
+ // src/trainer.ts
1102
+ var IntentTrainer = class extends BaseTrainer {
1103
+ dimension;
1104
+ /**
1105
+ * IntentTrainer のインスタンスを作成します。
1106
+ * @param {number} dimension ベクトルの次元数(入力・出力ともに同じ次元数となります)
1107
+ */
1108
+ constructor(dimension) {
1109
+ super();
1110
+ this.dimension = dimension;
1111
+ }
1112
+ get sourceDimension() {
1113
+ return this.dimension;
1114
+ }
1115
+ get targetDimension() {
1116
+ return this.dimension;
1117
+ }
1118
+ getInputs(example) {
1119
+ return { source: example.input, target: example.target };
1120
+ }
1121
+ toWeights(flatMatrix, bias) {
1122
+ const dim = this.dimension;
1123
+ const outMatrix = new Array(dim);
1124
+ for (let i = 0; i < dim; i++) {
1125
+ const row = new Array(dim);
1126
+ const rowOffset = i * dim;
1127
+ for (let j = 0; j < dim; j++) {
1128
+ row[j] = flatMatrix[rowOffset + j];
1129
+ }
1130
+ outMatrix[i] = row;
1131
+ }
1132
+ return {
1133
+ matrix: outMatrix,
1134
+ bias: Array.from(bias)
1135
+ };
1136
+ }
1137
+ /**
1138
+ * オンライン学習 (フィードバックループ) 用のメソッド。
1139
+ * ユーザーのクリックなどの 1 回のフィードバックからリアルタイムに重みを微調整します。
1140
+ *
1141
+ * @param {IntentWeights} currentWeights - 現在の重み
1142
+ * @param {number[] | Float32Array} input - 検索されたクエリベクトル
1143
+ * @param {number[] | Float32Array} target - クリックされた(理想の)ドキュメントのベクトル
1144
+ * @param {number} learningRate - 1ステップの学習率 (デフォルト: 0.01)
1145
+ * @param {number} regularization - L2正則化の強さ (デフォルト: 0.001)
1146
+ * @returns {IntentWeights} 微調整された新しい重み
1147
+ */
1148
+ async updateOnline(currentWeights, input, target, learningRate = 0.01, regularization = 1e-3) {
1149
+ await initWasm();
1150
+ if (input.length !== this.dimension || target.length !== this.dimension) {
1151
+ throw new Error(`Dimension mismatch. Expected ${this.dimension}`);
1152
+ }
1153
+ const dim = this.dimension;
1154
+ let flatMatrix;
1155
+ if (currentWeights.matrix instanceof Float32Array) {
1156
+ flatMatrix = new Float32Array(currentWeights.matrix);
1157
+ } else {
1158
+ flatMatrix = flattenMatrix(currentWeights.matrix, dim, dim, "updateOnline Matrix");
1159
+ }
1160
+ const bias = new Float32Array(currentWeights.bias);
1161
+ const vMatrix = new Float32Array(dim * dim);
1162
+ const vBias = new Float32Array(dim);
1163
+ this.sgdMomentumStep(
1164
+ flatMatrix,
1165
+ bias,
1166
+ vMatrix,
1167
+ vBias,
1168
+ input,
1169
+ target,
1170
+ learningRate,
1171
+ regularization,
1172
+ 0
1173
+ // no momentum for 1-shot online update
1174
+ );
1175
+ const newWeights = this.toWeights(flatMatrix, bias);
1176
+ if (currentWeights.routingVector) {
1177
+ newWeights.routingVector = [...currentWeights.routingVector];
1178
+ }
1179
+ return newWeights;
1180
+ }
1181
+ };
1182
+
1183
+ // src/migration.ts
1184
+ var MigrationTrainer = class extends BaseTrainer {
1185
+ _sourceDimension;
1186
+ _targetDimension;
1187
+ /**
1188
+ * @param {number} sourceDimension 移行元の次元数 (例: ada-002なら1536)
1189
+ * @param {number} targetDimension 移行先の次元数 (例: text-embedding-3-smallなら512)
1190
+ */
1191
+ constructor(sourceDimension, targetDimension) {
1192
+ super();
1193
+ this._sourceDimension = sourceDimension;
1194
+ this._targetDimension = targetDimension;
1195
+ }
1196
+ get sourceDimension() {
1197
+ return this._sourceDimension;
1198
+ }
1199
+ get targetDimension() {
1200
+ return this._targetDimension;
1201
+ }
1202
+ getInputs(example) {
1203
+ return { source: example.source, target: example.target };
1204
+ }
1205
+ toWeights(flatMatrix, bias) {
1206
+ const sDim = this.sourceDimension;
1207
+ const tDim = this.targetDimension;
1208
+ const outMatrix = new Array(tDim);
1209
+ for (let i = 0; i < tDim; i++) {
1210
+ const row = new Array(sDim);
1211
+ const rowOffset = i * sDim;
1212
+ for (let j = 0; j < sDim; j++) {
1213
+ row[j] = flatMatrix[rowOffset + j];
1214
+ }
1215
+ outMatrix[i] = row;
1216
+ }
1217
+ return {
1218
+ matrix: outMatrix,
1219
+ bias: Array.from(bias)
1220
+ };
1221
+ }
1222
+ };
1223
+
1224
+ // src/db.ts
1225
+ var VectorDBAdapter = class {
1226
+ /**
1227
+ * PostgreSQL (pgvector) 用のクエリ文字列表現を生成します。
1228
+ * INSERT や SELECT の際に使用できる形式です。
1229
+ *
1230
+ * @example
1231
+ * const sql = `SELECT * FROM items ORDER BY embedding <-> '${VectorDBAdapter.toPgvector(warpedVector)}' LIMIT 5`;
1232
+ *
1233
+ * @param {number[] | Float32Array} vector ワープ変換後のベクトル
1234
+ * @returns {string} `'[0.1, 0.2, 0.3]'` のような文字列表現
1235
+ */
1236
+ static toPgvector(vector) {
1237
+ return `[${Array.from(vector).join(", ")}]`;
1238
+ }
1239
+ /**
1240
+ * Pinecone 用のクエリオブジェクトを生成します。
1241
+ * Pinecone クライアントに直接渡せる形式のオブジェクトを返します。
1242
+ *
1243
+ * @example
1244
+ * const query = VectorDBAdapter.toPineconeQuery(warpedVector, 10, { genre: "comedy" });
1245
+ * await index.query(query);
1246
+ *
1247
+ * @param {number[] | Float32Array} vector 検索クエリベクトル
1248
+ * @param {number} topK 取得する件数 (デフォルト: 10)
1249
+ * @param {Record<string, any>} [filter] メタデータフィルタ(オプション)
1250
+ * @returns {Record<string, any>} Pineconeのqueryメソッド用オブジェクト
1251
+ */
1252
+ static toPineconeQuery(vector, topK = 10, filter) {
1253
+ return {
1254
+ vector: Array.from(vector),
1255
+ topK,
1256
+ ...filter ? { filter } : {}
1257
+ };
1258
+ }
1259
+ /**
1260
+ * Redis (RediSearch) のベクトルフィールド用に、
1261
+ * Float32Array をバイナリ(Uint8Array)に変換します。
1262
+ * Node.js環境では Buffer.from() を使って Buffer に変換して渡してください。
1263
+ *
1264
+ * @example
1265
+ * const blob = Buffer.from(VectorDBAdapter.toRedis(warpedVector));
1266
+ * await redis.call('FT.SEARCH', 'idx', '*=>[KNN 5 @embedding $BLOB]', 'PARAMS', '2', 'BLOB', blob, 'DIALECT', '2');
1267
+ *
1268
+ * @param {number[] | Float32Array} vector ワープ変換後のベクトル
1269
+ * @returns {Uint8Array} バイナリデータ (Float32のバイト表現)
1270
+ */
1271
+ static toRedis(vector) {
1272
+ const f32Array = vector instanceof Float32Array ? vector : new Float32Array(vector);
1273
+ return new Uint8Array(
1274
+ f32Array.buffer.slice(
1275
+ f32Array.byteOffset,
1276
+ f32Array.byteOffset + f32Array.byteLength
1277
+ )
1278
+ );
1279
+ }
1280
+ };
1281
+ // Annotate the CommonJS export names for ESM import in node:
1282
+ 0 && (module.exports = {
1283
+ IntentAdapter,
1284
+ IntentTrainer,
1285
+ LoraIntentAdapter,
1286
+ MigrationTrainer,
1287
+ ProjectionAdapter,
1288
+ VectorDBAdapter,
1289
+ applyActivationToVector,
1290
+ assertDimension,
1291
+ cosineSimilarity,
1292
+ flattenMatrix,
1293
+ innerProduct,
1294
+ normalize,
1295
+ reject,
1296
+ slerp,
1297
+ softmax
1298
+ });