@oxide-js/spiking 1.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.
@@ -0,0 +1,135 @@
1
+ import { Matrix } from "@oxide-js/core";
2
+ import { BaseModel } from "@oxide-js/models";
3
+ import { SpikingEmbedding } from "../layers/SpikingEmbedding.js";
4
+ import { SpikingDense } from "../layers/SpikingDense.js";
5
+
6
+ export interface SpikingSentenceConfig {
7
+ vocabSize: number;
8
+ embedDim: number;
9
+ beta?: number;
10
+ threshold?: number;
11
+ }
12
+
13
+ export class SpikingSentenceEmbedder extends BaseModel {
14
+ public vocabSize: number;
15
+ public embedDim: number;
16
+
17
+ public embedding: SpikingEmbedding;
18
+ public contextLayer: SpikingDense;
19
+
20
+ constructor(config: SpikingSentenceConfig) {
21
+ super();
22
+ this.vocabSize = config.vocabSize;
23
+ this.embedDim = config.embedDim;
24
+ const beta = config.beta ?? 0.9;
25
+ const threshold = config.threshold ?? 1.0;
26
+
27
+ this.embedding = new SpikingEmbedding({
28
+ inputDim: this.vocabSize,
29
+ outputDim: this.embedDim,
30
+ beta: beta,
31
+ threshold: threshold,
32
+ embeddingsInitializer: "glorot_normal"
33
+ });
34
+
35
+ this.contextLayer = new SpikingDense({
36
+ units: this.embedDim,
37
+ beta: beta,
38
+ threshold: threshold,
39
+ useBias: true,
40
+ kernelInitializer: "glorot_normal"
41
+ });
42
+
43
+ this.add(this.embedding);
44
+ this.add(this.contextLayer);
45
+ }
46
+
47
+ public resetState() {
48
+ this.embedding.resetState();
49
+ this.contextLayer.resetState();
50
+ }
51
+
52
+ /**
53
+ * Membaca sebuah kalimat utuh dan mengubahnya menjadi Vektor Semantik tunggal (Spike Count)
54
+ * @param inputs Matrix Token ID dari kalimat (shape: [batch=1, seq_len])
55
+ * @param optionsOrTraining Opsi forward (tidak dipakai di SNN ini, tapi dibutuhkan oleh abstract method)
56
+ * @returns Matrix Vektor berukuran `[1, embedDim]` yang berisi total Spike (Representasi Makna Kalimat)
57
+ */
58
+ public forward(inputs: Matrix, optionsOrTraining?: any): Matrix {
59
+ if (!this.isBuilt) {
60
+ this.build([1, 1]); // SNN layer selalu memproses kata per kata
61
+ }
62
+
63
+ this.resetState();
64
+ const semanticVector = new Float32Array(this.embedDim);
65
+
66
+ const seqLen = inputs._shape.length > 1 ? inputs._shape[1] : inputs._shape[0];
67
+ const inputData = inputs._data;
68
+
69
+ for (let i = 0; i < seqLen; i++) {
70
+ const x = Matrix.fromFlat(new Float32Array([inputData[i]]), [1, 1]);
71
+
72
+ // Default readingTime = 3 timestep
73
+ for (let t = 0; t < 3; t++) {
74
+ const wordSpikes = this.embedding.forward(x) as Matrix;
75
+ const contextSpikes = this.contextLayer.forward(wordSpikes) as Matrix;
76
+
77
+ const outData = contextSpikes._data;
78
+ for(let j=0; j<this.embedDim; j++) {
79
+ semanticVector[j] += outData[j];
80
+ }
81
+ }
82
+ }
83
+
84
+ return Matrix.fromFlat(semanticVector, [1, this.embedDim]);
85
+ }
86
+
87
+ /**
88
+ * Melatih model sentence embedder menggunakan prinsip Word2Vec CBOW-style Hebbian Contrastive Learning.
89
+ * Secara otomatis mengambil rata-rata vektor konteks kalimat saat ini (Positive),
90
+ * dan menolaknya dari list mean vector negatif (NegativeContexts).
91
+ * Metode ini bypass context layer dan langsung melatih embedding untuk mencegah representation collapse.
92
+ *
93
+ * @returns Float32Array rata-rata vektor (meanVec) dari kalimat saat ini yang dapat disimpan ke historyBuffer.
94
+ */
95
+ public learnContrastive(
96
+ tokens: number[] | Float32Array,
97
+ negativeContexts: Float32Array[],
98
+ learningRate: number = 0.01,
99
+ marginPositive: number = 0.1,
100
+ marginNegative: number = 0.05
101
+ ): Float32Array {
102
+ const wordEmbeddings: { tokenId: number, vec: Float32Array }[] = [];
103
+ const kernel = this.embedding.getParameter('kernel')!._data;
104
+ const dim = this.embedDim;
105
+
106
+ for (let i = 0; i < tokens.length; i++) {
107
+ const tokenId = Math.round(tokens[i]);
108
+ if (tokenId >= 0 && tokenId < this.vocabSize) {
109
+ const offset = tokenId * dim;
110
+ const vec = new Float32Array(dim);
111
+ for (let j = 0; j < dim; j++) vec[j] = kernel[offset + j];
112
+ wordEmbeddings.push({ tokenId, vec });
113
+ }
114
+ }
115
+
116
+ const meanVec = new Float32Array(dim);
117
+ if (wordEmbeddings.length > 0) {
118
+ for (const w of wordEmbeddings) {
119
+ for (let j = 0; j < dim; j++) meanVec[j] += w.vec[j];
120
+ }
121
+ for (let j = 0; j < dim; j++) meanVec[j] /= wordEmbeddings.length;
122
+
123
+ this.embedding.learnHebbian(
124
+ tokens,
125
+ meanVec,
126
+ negativeContexts,
127
+ learningRate,
128
+ marginPositive,
129
+ marginNegative
130
+ );
131
+ }
132
+
133
+ return meanVec;
134
+ }
135
+ }
@@ -0,0 +1,90 @@
1
+ import { createRequire } from "node:module";
2
+ const require = createRequire(import.meta.url);
3
+
4
+ let native: any = null;
5
+ const disableNativeByEnv = process.env.ML_DISABLE_NATIVE === "1";
6
+
7
+ if (!disableNativeByEnv) {
8
+ try {
9
+ native = require("../../index.js");
10
+ if (native != null && Object.keys(native).length === 0) {
11
+ native = null;
12
+ }
13
+ } catch {
14
+ // Silently ignore if native binding fails to load
15
+ }
16
+ }
17
+
18
+ export const isNativeAvailable = () => native !== null;
19
+
20
+ export const dotProductAddOnlyNativeWrapper = (
21
+ aData: Float32Array,
22
+ aRows: number,
23
+ aCols: number,
24
+ bData: Float32Array,
25
+ bRows: number,
26
+ bCols: number,
27
+ transA: boolean,
28
+ transB: boolean,
29
+ outData: Float32Array
30
+ ): void => {
31
+ if (!native) throw new Error("Spiking Native backend not available");
32
+ native.dotProductAddOnlyNative(
33
+ aData,
34
+ aRows,
35
+ aCols,
36
+ bData,
37
+ bRows,
38
+ bCols,
39
+ transA,
40
+ transB,
41
+ outData
42
+ );
43
+ };
44
+
45
+ export const lifStepNativeWrapper = (
46
+ potentials: Float32Array,
47
+ dot: Float32Array,
48
+ spikes: Float32Array,
49
+ lastPotentials: Float32Array,
50
+ beta: number,
51
+ threshold: number
52
+ ): void => {
53
+ if (!native) throw new Error("Spiking Native backend not available");
54
+ native.lifStepNative(potentials, dot, spikes, lastPotentials, beta, threshold);
55
+ };
56
+
57
+ export const maskSurrogateNativeWrapper = (
58
+ errorSignal: Float32Array,
59
+ potentials: Float32Array,
60
+ threshold: number,
61
+ windowSize: number
62
+ ): void => {
63
+ if (!native) throw new Error("Spiking Native backend not available");
64
+ native.maskSurrogateNative(errorSignal, potentials, threshold, windowSize);
65
+ };
66
+
67
+ export const applyAddOnlyDeltaNativeWrapper = (
68
+ kernel: Float32Array,
69
+ bias: Float32Array,
70
+ inputs: Float32Array,
71
+ errorSignal: Float32Array,
72
+ learningRate: number,
73
+ batch: number,
74
+ inFeatures: number,
75
+ units: number,
76
+ useBias: boolean
77
+ ): void => {
78
+ if (!native) throw new Error("Spiking Native backend not available");
79
+ native.applyAddOnlyDeltaNative(
80
+ kernel,
81
+ bias,
82
+ inputs,
83
+ errorSignal,
84
+ learningRate,
85
+ batch,
86
+ inFeatures,
87
+ units,
88
+ useBias
89
+ );
90
+ };
@@ -0,0 +1,324 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "aho-corasick"
7
+ version = "1.1.4"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
10
+ dependencies = [
11
+ "memchr",
12
+ ]
13
+
14
+ [[package]]
15
+ name = "bitflags"
16
+ version = "2.11.1"
17
+ source = "registry+https://github.com/rust-lang/crates.io-index"
18
+ checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
19
+
20
+ [[package]]
21
+ name = "cfg-if"
22
+ version = "1.0.4"
23
+ source = "registry+https://github.com/rust-lang/crates.io-index"
24
+ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
25
+
26
+ [[package]]
27
+ name = "convert_case"
28
+ version = "0.6.0"
29
+ source = "registry+https://github.com/rust-lang/crates.io-index"
30
+ checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
31
+ dependencies = [
32
+ "unicode-segmentation",
33
+ ]
34
+
35
+ [[package]]
36
+ name = "crossbeam-deque"
37
+ version = "0.8.6"
38
+ source = "registry+https://github.com/rust-lang/crates.io-index"
39
+ checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
40
+ dependencies = [
41
+ "crossbeam-epoch",
42
+ "crossbeam-utils",
43
+ ]
44
+
45
+ [[package]]
46
+ name = "crossbeam-epoch"
47
+ version = "0.9.18"
48
+ source = "registry+https://github.com/rust-lang/crates.io-index"
49
+ checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
50
+ dependencies = [
51
+ "crossbeam-utils",
52
+ ]
53
+
54
+ [[package]]
55
+ name = "crossbeam-utils"
56
+ version = "0.8.21"
57
+ source = "registry+https://github.com/rust-lang/crates.io-index"
58
+ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
59
+
60
+ [[package]]
61
+ name = "ctor"
62
+ version = "0.2.9"
63
+ source = "registry+https://github.com/rust-lang/crates.io-index"
64
+ checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
65
+ dependencies = [
66
+ "quote",
67
+ "syn",
68
+ ]
69
+
70
+ [[package]]
71
+ name = "either"
72
+ version = "1.16.0"
73
+ source = "registry+https://github.com/rust-lang/crates.io-index"
74
+ checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
75
+
76
+ [[package]]
77
+ name = "itoa"
78
+ version = "1.0.18"
79
+ source = "registry+https://github.com/rust-lang/crates.io-index"
80
+ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
81
+
82
+ [[package]]
83
+ name = "libloading"
84
+ version = "0.8.9"
85
+ source = "registry+https://github.com/rust-lang/crates.io-index"
86
+ checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
87
+ dependencies = [
88
+ "cfg-if",
89
+ "windows-link",
90
+ ]
91
+
92
+ [[package]]
93
+ name = "memchr"
94
+ version = "2.8.0"
95
+ source = "registry+https://github.com/rust-lang/crates.io-index"
96
+ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
97
+
98
+ [[package]]
99
+ name = "napi"
100
+ version = "2.16.17"
101
+ source = "registry+https://github.com/rust-lang/crates.io-index"
102
+ checksum = "55740c4ae1d8696773c78fdafd5d0e5fe9bc9f1b071c7ba493ba5c413a9184f3"
103
+ dependencies = [
104
+ "bitflags",
105
+ "ctor",
106
+ "napi-derive",
107
+ "napi-sys",
108
+ "once_cell",
109
+ "serde",
110
+ "serde_json",
111
+ ]
112
+
113
+ [[package]]
114
+ name = "napi-build"
115
+ version = "2.3.2"
116
+ source = "registry+https://github.com/rust-lang/crates.io-index"
117
+ checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1"
118
+
119
+ [[package]]
120
+ name = "napi-derive"
121
+ version = "2.16.13"
122
+ source = "registry+https://github.com/rust-lang/crates.io-index"
123
+ checksum = "7cbe2585d8ac223f7d34f13701434b9d5f4eb9c332cccce8dee57ea18ab8ab0c"
124
+ dependencies = [
125
+ "cfg-if",
126
+ "convert_case",
127
+ "napi-derive-backend",
128
+ "proc-macro2",
129
+ "quote",
130
+ "syn",
131
+ ]
132
+
133
+ [[package]]
134
+ name = "napi-derive-backend"
135
+ version = "1.0.75"
136
+ source = "registry+https://github.com/rust-lang/crates.io-index"
137
+ checksum = "1639aaa9eeb76e91c6ae66da8ce3e89e921cd3885e99ec85f4abacae72fc91bf"
138
+ dependencies = [
139
+ "convert_case",
140
+ "once_cell",
141
+ "proc-macro2",
142
+ "quote",
143
+ "regex",
144
+ "semver",
145
+ "syn",
146
+ ]
147
+
148
+ [[package]]
149
+ name = "napi-sys"
150
+ version = "2.4.0"
151
+ source = "registry+https://github.com/rust-lang/crates.io-index"
152
+ checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3"
153
+ dependencies = [
154
+ "libloading",
155
+ ]
156
+
157
+ [[package]]
158
+ name = "once_cell"
159
+ version = "1.21.4"
160
+ source = "registry+https://github.com/rust-lang/crates.io-index"
161
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
162
+
163
+ [[package]]
164
+ name = "proc-macro2"
165
+ version = "1.0.106"
166
+ source = "registry+https://github.com/rust-lang/crates.io-index"
167
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
168
+ dependencies = [
169
+ "unicode-ident",
170
+ ]
171
+
172
+ [[package]]
173
+ name = "quote"
174
+ version = "1.0.45"
175
+ source = "registry+https://github.com/rust-lang/crates.io-index"
176
+ checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
177
+ dependencies = [
178
+ "proc-macro2",
179
+ ]
180
+
181
+ [[package]]
182
+ name = "rayon"
183
+ version = "1.12.0"
184
+ source = "registry+https://github.com/rust-lang/crates.io-index"
185
+ checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
186
+ dependencies = [
187
+ "either",
188
+ "rayon-core",
189
+ ]
190
+
191
+ [[package]]
192
+ name = "rayon-core"
193
+ version = "1.13.0"
194
+ source = "registry+https://github.com/rust-lang/crates.io-index"
195
+ checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
196
+ dependencies = [
197
+ "crossbeam-deque",
198
+ "crossbeam-utils",
199
+ ]
200
+
201
+ [[package]]
202
+ name = "regex"
203
+ version = "1.12.3"
204
+ source = "registry+https://github.com/rust-lang/crates.io-index"
205
+ checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
206
+ dependencies = [
207
+ "aho-corasick",
208
+ "memchr",
209
+ "regex-automata",
210
+ "regex-syntax",
211
+ ]
212
+
213
+ [[package]]
214
+ name = "regex-automata"
215
+ version = "0.4.14"
216
+ source = "registry+https://github.com/rust-lang/crates.io-index"
217
+ checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
218
+ dependencies = [
219
+ "aho-corasick",
220
+ "memchr",
221
+ "regex-syntax",
222
+ ]
223
+
224
+ [[package]]
225
+ name = "regex-syntax"
226
+ version = "0.8.10"
227
+ source = "registry+https://github.com/rust-lang/crates.io-index"
228
+ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
229
+
230
+ [[package]]
231
+ name = "semver"
232
+ version = "1.0.28"
233
+ source = "registry+https://github.com/rust-lang/crates.io-index"
234
+ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
235
+
236
+ [[package]]
237
+ name = "serde"
238
+ version = "1.0.228"
239
+ source = "registry+https://github.com/rust-lang/crates.io-index"
240
+ checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
241
+ dependencies = [
242
+ "serde_core",
243
+ "serde_derive",
244
+ ]
245
+
246
+ [[package]]
247
+ name = "serde_core"
248
+ version = "1.0.228"
249
+ source = "registry+https://github.com/rust-lang/crates.io-index"
250
+ checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
251
+ dependencies = [
252
+ "serde_derive",
253
+ ]
254
+
255
+ [[package]]
256
+ name = "serde_derive"
257
+ version = "1.0.228"
258
+ source = "registry+https://github.com/rust-lang/crates.io-index"
259
+ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
260
+ dependencies = [
261
+ "proc-macro2",
262
+ "quote",
263
+ "syn",
264
+ ]
265
+
266
+ [[package]]
267
+ name = "serde_json"
268
+ version = "1.0.150"
269
+ source = "registry+https://github.com/rust-lang/crates.io-index"
270
+ checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
271
+ dependencies = [
272
+ "itoa",
273
+ "memchr",
274
+ "serde",
275
+ "serde_core",
276
+ "zmij",
277
+ ]
278
+
279
+ [[package]]
280
+ name = "spiking-native"
281
+ version = "1.0.0"
282
+ dependencies = [
283
+ "napi",
284
+ "napi-build",
285
+ "napi-derive",
286
+ "rayon",
287
+ "serde",
288
+ "serde_json",
289
+ ]
290
+
291
+ [[package]]
292
+ name = "syn"
293
+ version = "2.0.117"
294
+ source = "registry+https://github.com/rust-lang/crates.io-index"
295
+ checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
296
+ dependencies = [
297
+ "proc-macro2",
298
+ "quote",
299
+ "unicode-ident",
300
+ ]
301
+
302
+ [[package]]
303
+ name = "unicode-ident"
304
+ version = "1.0.24"
305
+ source = "registry+https://github.com/rust-lang/crates.io-index"
306
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
307
+
308
+ [[package]]
309
+ name = "unicode-segmentation"
310
+ version = "1.13.2"
311
+ source = "registry+https://github.com/rust-lang/crates.io-index"
312
+ checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
313
+
314
+ [[package]]
315
+ name = "windows-link"
316
+ version = "0.2.1"
317
+ source = "registry+https://github.com/rust-lang/crates.io-index"
318
+ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
319
+
320
+ [[package]]
321
+ name = "zmij"
322
+ version = "1.0.21"
323
+ source = "registry+https://github.com/rust-lang/crates.io-index"
324
+ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
@@ -0,0 +1,17 @@
1
+ [package]
2
+ name = "spiking-native"
3
+ version = "1.0.0"
4
+ edition = "2021"
5
+
6
+ [lib]
7
+ crate-type = ["cdylib"]
8
+
9
+ [dependencies]
10
+ napi = { version = "2.16.4", default-features = false, features = ["napi4", "napi8", "serde-json"] }
11
+ napi-derive = "2.16.2"
12
+ rayon = "1.10.0"
13
+ serde = { version = "1.0.198", features = ["derive"] }
14
+ serde_json = "1.0.116"
15
+
16
+ [build-dependencies]
17
+ napi-build = "2.1.2"
@@ -0,0 +1,5 @@
1
+ extern crate napi_build;
2
+
3
+ fn main() {
4
+ napi_build::setup();
5
+ }