@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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # @oxide-js/spiking
2
+
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7db4802: Spiking Neural Networks (SNN) & JSON Compression
8
+
9
+ - Added new `@oxide-js/spiking` package for Biomimetic AI, featuring LIF dynamics and Hebbian Contrastive Learning.
10
+ - Optimized `BaseModel.serialize()` and `setWeights()` in `@oxide-js/models` to compress Float32Array into regular arrays, achieving up to 75% JSON size reductions.
11
+ - Added `mj.argmax` and `mj.threshold` primitives in `@oxide-js/core`.
12
+ - Included `"threshold"` activation typing inside `ActivationType`.
@@ -0,0 +1,101 @@
1
+ import { SpikingNetwork } from "../src/core/SpikingNetwork.js";
2
+ import { STDP } from "../src/learning/STDP.js";
3
+ import { PoissonEncoder } from "../src/encoding/PoissonEncoder.js";
4
+
5
+ /**
6
+ * Simulasi Pengenalan Pola Sederhana menggunakan SNN
7
+ * --------------------------------------------------
8
+ * 1. Kita memiliki 3 neuron Input dan 1 neuron Output (Total 4 Neuron)
9
+ * 2. Input merepresentasikan intensitas sinyal (misalnya: [0.9, 0.1, 0.8])
10
+ * 3. Neuron Input terhubung ke Neuron Output
11
+ * 4. STDP akan mengamati dan memperkuat bobot dari input yang sering "menyala" (0.9 dan 0.8)
12
+ */
13
+ async function runDemo() {
14
+ console.log("=== Memulai Simulasi SNN Add-Only ===\n");
15
+
16
+ // Konfigurasi
17
+ const NUM_INPUTS = 3;
18
+ const NUM_OUTPUTS = 1;
19
+ const TOTAL_NEURONS = NUM_INPUTS + NUM_OUTPUTS;
20
+ const TIME_STEPS = 100;
21
+
22
+ // 1. Inisialisasi Jaringan & Komponen
23
+ // Threshold di-set rendah agar output mudah menembakkan spike
24
+ const net = new SpikingNetwork(TOTAL_NEURONS, 0.8, 2.0);
25
+ const stdp = new STDP(net, {
26
+ learningRate: 0.05,
27
+ aPlus: 1.5,
28
+ aMinus: 0.5, // LTD diturunkan agar sinyal yang sering aktif (berkorelasi kuat) membesar
29
+ wMax: 10.0,
30
+ wMin: 0.0 // Bobot tidak boleh negatif dalam contoh ini
31
+ });
32
+ const encoder = new PoissonEncoder(1.0);
33
+
34
+ // Neuron Output berada di indeks ke-3 (karena 0,1,2 adalah input)
35
+ const OUTPUT_NEURON = 3;
36
+
37
+ // 2. Hubungkan Input ke Output dengan bobot awal yang seragam (kecil)
38
+ console.log("Membangun koneksi awal:");
39
+ for (let i = 0; i < NUM_INPUTS; i++) {
40
+ net.connect(i, OUTPUT_NEURON, 1.0);
41
+ console.log(`- Input Neuron ${i} -> Output Neuron ${OUTPUT_NEURON} (Bobot Awal: 1.0)`);
42
+ }
43
+ console.log("\n");
44
+
45
+ // 3. Menyiapkan Pola Input
46
+ // Neuron 0 (Sinyal Kuat), Neuron 1 (Sinyal Lemah), Neuron 2 (Sinyal Kuat)
47
+ const inputPattern = [0.9, 0.1, 0.8];
48
+
49
+ let totalOutputSpikes = 0;
50
+
51
+ console.log(`Menjalankan simulasi selama ${TIME_STEPS} time-steps...`);
52
+
53
+ for (let t = 0; t < TIME_STEPS; t++) {
54
+ // A. Encode sinyal input kontinu menjadi Spike
55
+ const currentSpikes = encoder.encodeArray(inputPattern);
56
+
57
+ // B. Injeksi spike input secara langsung ke neuron input
58
+ // (Beri potensial yang cukup agar mereka seketika menembakkan spike di step ini)
59
+ for (let i = 0; i < NUM_INPUTS; i++) {
60
+ if (currentSpikes[i] === 1) {
61
+ // Injeksi arus yang jauh di atas threshold (2.0) agar pasti spike
62
+ net.injectCurrent(i, 10.0);
63
+ }
64
+ }
65
+
66
+ // C. Evaluasi Jaringan (Add-Only Propagation)
67
+ net.step();
68
+
69
+ // D. Hitung Spike Output
70
+ if (net.spikes[OUTPUT_NEURON] === 1) {
71
+ totalOutputSpikes++;
72
+ }
73
+
74
+ // E. Lakukan Proses Pembelajaran (Plasticity)
75
+ stdp.updateWeights();
76
+ }
77
+
78
+ // 4. Hasil Simulasi
79
+ console.log("\n=== Hasil Simulasi ===");
80
+ console.log(`Total tembakan (spikes) dari Neuron Output: ${totalOutputSpikes}`);
81
+ console.log("\nPerubahan Bobot Akhir (Setelah proses Belajar STDP):");
82
+
83
+ for (let i = 0; i < NUM_INPUTS; i++) {
84
+ // Bobot ke-0 dari array koneksi neuron input 'i'
85
+ const finalWeight = net.weights[i][0];
86
+ const initialInput = inputPattern[i];
87
+
88
+ let status = "";
89
+ if (finalWeight > 1.0) status = "📈 Diperkuat (LTP)";
90
+ else if (finalWeight < 1.0) status = "📉 Diperlemah (LTD)";
91
+ else status = "âž– Tetap";
92
+
93
+ console.log(`Neuron ${i} (Intensitas Sinyal: ${initialInput}) -> Bobot Akhir: ${finalWeight.toFixed(4)} ${status}`);
94
+ }
95
+
96
+ console.log("\nKesimpulan:");
97
+ console.log("Seperti yang terlihat, SNN secara otomatis mengenali dan memperkuat koneksi dari neuron input yang aktif (Intensitas 0.9 & 0.8),");
98
+ console.log("sementara neuron yang jarang aktif (Intensitas 0.1) bobotnya melemah secara natural melalui aturan STDP.");
99
+ }
100
+
101
+ runDemo().catch(console.error);
package/index.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ export declare function dotProductAddOnlyNative(aData: Float32Array, aRowsOrig: number, aColsOrig: number, bData: Float32Array, bRowsOrig: number, bColsOrig: number, transA: boolean, transB: boolean, outData: Float32Array): void
7
+ export declare class NativeSpikingNetwork {
8
+ constructor(numNeurons: number, beta: number, defaultThreshold: number)
9
+ connect(pre: number, post: number, weight: number): void
10
+ injectCurrent(neuronIdx: number, current: number): void
11
+ inhibitRange(startIdx: number, endIdx: number, exceptIdx: number, current: number): void
12
+ step(): void
13
+ resetState(): void
14
+ getSpikes(): Array<number>
15
+ getPotentials(): Array<number>
16
+ updateStdp(learningRate: number, tauPlus: number, tauMinus: number, aPlus: number, aMinus: number, wMax: number, wMin: number): void
17
+ saveToFile(filepath: string): void
18
+ loadFromFile(filepath: string): void
19
+ }
package/index.js ADDED
@@ -0,0 +1,316 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /* prettier-ignore */
4
+
5
+ /* auto-generated by NAPI-RS */
6
+
7
+ const { existsSync, readFileSync } = require('fs')
8
+ const { join } = require('path')
9
+
10
+ const { platform, arch } = process
11
+
12
+ let nativeBinding = null
13
+ let localFileExisted = false
14
+ let loadError = null
15
+
16
+ function isMusl() {
17
+ // For Node 10
18
+ if (!process.report || typeof process.report.getReport !== 'function') {
19
+ try {
20
+ const lddPath = require('child_process').execSync('which ldd').toString().trim()
21
+ return readFileSync(lddPath, 'utf8').includes('musl')
22
+ } catch (e) {
23
+ return true
24
+ }
25
+ } else {
26
+ const { glibcVersionRuntime } = process.report.getReport().header
27
+ return !glibcVersionRuntime
28
+ }
29
+ }
30
+
31
+ switch (platform) {
32
+ case 'android':
33
+ switch (arch) {
34
+ case 'arm64':
35
+ localFileExisted = existsSync(join(__dirname, 'spiking-native.android-arm64.node'))
36
+ try {
37
+ if (localFileExisted) {
38
+ nativeBinding = require('./spiking-native.android-arm64.node')
39
+ } else {
40
+ nativeBinding = require('@oxide-js/spiking-android-arm64')
41
+ }
42
+ } catch (e) {
43
+ loadError = e
44
+ }
45
+ break
46
+ case 'arm':
47
+ localFileExisted = existsSync(join(__dirname, 'spiking-native.android-arm-eabi.node'))
48
+ try {
49
+ if (localFileExisted) {
50
+ nativeBinding = require('./spiking-native.android-arm-eabi.node')
51
+ } else {
52
+ nativeBinding = require('@oxide-js/spiking-android-arm-eabi')
53
+ }
54
+ } catch (e) {
55
+ loadError = e
56
+ }
57
+ break
58
+ default:
59
+ throw new Error(`Unsupported architecture on Android ${arch}`)
60
+ }
61
+ break
62
+ case 'win32':
63
+ switch (arch) {
64
+ case 'x64':
65
+ localFileExisted = existsSync(
66
+ join(__dirname, 'spiking-native.win32-x64-msvc.node')
67
+ )
68
+ try {
69
+ if (localFileExisted) {
70
+ nativeBinding = require('./spiking-native.win32-x64-msvc.node')
71
+ } else {
72
+ nativeBinding = require('@oxide-js/spiking-win32-x64-msvc')
73
+ }
74
+ } catch (e) {
75
+ loadError = e
76
+ }
77
+ break
78
+ case 'ia32':
79
+ localFileExisted = existsSync(
80
+ join(__dirname, 'spiking-native.win32-ia32-msvc.node')
81
+ )
82
+ try {
83
+ if (localFileExisted) {
84
+ nativeBinding = require('./spiking-native.win32-ia32-msvc.node')
85
+ } else {
86
+ nativeBinding = require('@oxide-js/spiking-win32-ia32-msvc')
87
+ }
88
+ } catch (e) {
89
+ loadError = e
90
+ }
91
+ break
92
+ case 'arm64':
93
+ localFileExisted = existsSync(
94
+ join(__dirname, 'spiking-native.win32-arm64-msvc.node')
95
+ )
96
+ try {
97
+ if (localFileExisted) {
98
+ nativeBinding = require('./spiking-native.win32-arm64-msvc.node')
99
+ } else {
100
+ nativeBinding = require('@oxide-js/spiking-win32-arm64-msvc')
101
+ }
102
+ } catch (e) {
103
+ loadError = e
104
+ }
105
+ break
106
+ default:
107
+ throw new Error(`Unsupported architecture on Windows: ${arch}`)
108
+ }
109
+ break
110
+ case 'darwin':
111
+ localFileExisted = existsSync(join(__dirname, 'spiking-native.darwin-universal.node'))
112
+ try {
113
+ if (localFileExisted) {
114
+ nativeBinding = require('./spiking-native.darwin-universal.node')
115
+ } else {
116
+ nativeBinding = require('@oxide-js/spiking-darwin-universal')
117
+ }
118
+ break
119
+ } catch {}
120
+ switch (arch) {
121
+ case 'x64':
122
+ localFileExisted = existsSync(join(__dirname, 'spiking-native.darwin-x64.node'))
123
+ try {
124
+ if (localFileExisted) {
125
+ nativeBinding = require('./spiking-native.darwin-x64.node')
126
+ } else {
127
+ nativeBinding = require('@oxide-js/spiking-darwin-x64')
128
+ }
129
+ } catch (e) {
130
+ loadError = e
131
+ }
132
+ break
133
+ case 'arm64':
134
+ localFileExisted = existsSync(
135
+ join(__dirname, 'spiking-native.darwin-arm64.node')
136
+ )
137
+ try {
138
+ if (localFileExisted) {
139
+ nativeBinding = require('./spiking-native.darwin-arm64.node')
140
+ } else {
141
+ nativeBinding = require('@oxide-js/spiking-darwin-arm64')
142
+ }
143
+ } catch (e) {
144
+ loadError = e
145
+ }
146
+ break
147
+ default:
148
+ throw new Error(`Unsupported architecture on macOS: ${arch}`)
149
+ }
150
+ break
151
+ case 'freebsd':
152
+ if (arch !== 'x64') {
153
+ throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
154
+ }
155
+ localFileExisted = existsSync(join(__dirname, 'spiking-native.freebsd-x64.node'))
156
+ try {
157
+ if (localFileExisted) {
158
+ nativeBinding = require('./spiking-native.freebsd-x64.node')
159
+ } else {
160
+ nativeBinding = require('@oxide-js/spiking-freebsd-x64')
161
+ }
162
+ } catch (e) {
163
+ loadError = e
164
+ }
165
+ break
166
+ case 'linux':
167
+ switch (arch) {
168
+ case 'x64':
169
+ if (isMusl()) {
170
+ localFileExisted = existsSync(
171
+ join(__dirname, 'spiking-native.linux-x64-musl.node')
172
+ )
173
+ try {
174
+ if (localFileExisted) {
175
+ nativeBinding = require('./spiking-native.linux-x64-musl.node')
176
+ } else {
177
+ nativeBinding = require('@oxide-js/spiking-linux-x64-musl')
178
+ }
179
+ } catch (e) {
180
+ loadError = e
181
+ }
182
+ } else {
183
+ localFileExisted = existsSync(
184
+ join(__dirname, 'spiking-native.linux-x64-gnu.node')
185
+ )
186
+ try {
187
+ if (localFileExisted) {
188
+ nativeBinding = require('./spiking-native.linux-x64-gnu.node')
189
+ } else {
190
+ nativeBinding = require('@oxide-js/spiking-linux-x64-gnu')
191
+ }
192
+ } catch (e) {
193
+ loadError = e
194
+ }
195
+ }
196
+ break
197
+ case 'arm64':
198
+ if (isMusl()) {
199
+ localFileExisted = existsSync(
200
+ join(__dirname, 'spiking-native.linux-arm64-musl.node')
201
+ )
202
+ try {
203
+ if (localFileExisted) {
204
+ nativeBinding = require('./spiking-native.linux-arm64-musl.node')
205
+ } else {
206
+ nativeBinding = require('@oxide-js/spiking-linux-arm64-musl')
207
+ }
208
+ } catch (e) {
209
+ loadError = e
210
+ }
211
+ } else {
212
+ localFileExisted = existsSync(
213
+ join(__dirname, 'spiking-native.linux-arm64-gnu.node')
214
+ )
215
+ try {
216
+ if (localFileExisted) {
217
+ nativeBinding = require('./spiking-native.linux-arm64-gnu.node')
218
+ } else {
219
+ nativeBinding = require('@oxide-js/spiking-linux-arm64-gnu')
220
+ }
221
+ } catch (e) {
222
+ loadError = e
223
+ }
224
+ }
225
+ break
226
+ case 'arm':
227
+ if (isMusl()) {
228
+ localFileExisted = existsSync(
229
+ join(__dirname, 'spiking-native.linux-arm-musleabihf.node')
230
+ )
231
+ try {
232
+ if (localFileExisted) {
233
+ nativeBinding = require('./spiking-native.linux-arm-musleabihf.node')
234
+ } else {
235
+ nativeBinding = require('@oxide-js/spiking-linux-arm-musleabihf')
236
+ }
237
+ } catch (e) {
238
+ loadError = e
239
+ }
240
+ } else {
241
+ localFileExisted = existsSync(
242
+ join(__dirname, 'spiking-native.linux-arm-gnueabihf.node')
243
+ )
244
+ try {
245
+ if (localFileExisted) {
246
+ nativeBinding = require('./spiking-native.linux-arm-gnueabihf.node')
247
+ } else {
248
+ nativeBinding = require('@oxide-js/spiking-linux-arm-gnueabihf')
249
+ }
250
+ } catch (e) {
251
+ loadError = e
252
+ }
253
+ }
254
+ break
255
+ case 'riscv64':
256
+ if (isMusl()) {
257
+ localFileExisted = existsSync(
258
+ join(__dirname, 'spiking-native.linux-riscv64-musl.node')
259
+ )
260
+ try {
261
+ if (localFileExisted) {
262
+ nativeBinding = require('./spiking-native.linux-riscv64-musl.node')
263
+ } else {
264
+ nativeBinding = require('@oxide-js/spiking-linux-riscv64-musl')
265
+ }
266
+ } catch (e) {
267
+ loadError = e
268
+ }
269
+ } else {
270
+ localFileExisted = existsSync(
271
+ join(__dirname, 'spiking-native.linux-riscv64-gnu.node')
272
+ )
273
+ try {
274
+ if (localFileExisted) {
275
+ nativeBinding = require('./spiking-native.linux-riscv64-gnu.node')
276
+ } else {
277
+ nativeBinding = require('@oxide-js/spiking-linux-riscv64-gnu')
278
+ }
279
+ } catch (e) {
280
+ loadError = e
281
+ }
282
+ }
283
+ break
284
+ case 's390x':
285
+ localFileExisted = existsSync(
286
+ join(__dirname, 'spiking-native.linux-s390x-gnu.node')
287
+ )
288
+ try {
289
+ if (localFileExisted) {
290
+ nativeBinding = require('./spiking-native.linux-s390x-gnu.node')
291
+ } else {
292
+ nativeBinding = require('@oxide-js/spiking-linux-s390x-gnu')
293
+ }
294
+ } catch (e) {
295
+ loadError = e
296
+ }
297
+ break
298
+ default:
299
+ throw new Error(`Unsupported architecture on Linux: ${arch}`)
300
+ }
301
+ break
302
+ default:
303
+ throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
304
+ }
305
+
306
+ if (!nativeBinding) {
307
+ if (loadError) {
308
+ throw loadError
309
+ }
310
+ throw new Error(`Failed to load native binding`)
311
+ }
312
+
313
+ const { NativeSpikingNetwork, dotProductAddOnlyNative } = nativeBinding
314
+
315
+ module.exports.NativeSpikingNetwork = NativeSpikingNetwork
316
+ module.exports.dotProductAddOnlyNative = dotProductAddOnlyNative
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@oxide-js/spiking",
3
+ "version": "1.1.0",
4
+ "type": "module",
5
+ "description": "Oxide-JS Spiking: Add-Only Event-Driven Spiking Neural Network implementation.",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/Akhyar11/Oxide-JS.git",
9
+ "directory": "packages/spiking"
10
+ },
11
+ "main": "dist/index.js",
12
+ "types": "dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "import": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ }
19
+ },
20
+ "napi": {
21
+ "name": "spiking-native"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "build:rust": "napi build --platform --release --cargo-cwd src-rust",
26
+ "postinstall": "node -e \"try{require('child_process').execSync('npm run build:rust',{stdio:'inherit'})}catch(e){console.warn('[oxide-js/spiking] Rust native build skipped')}\""
27
+ },
28
+ "keywords": [
29
+ "snn",
30
+ "spiking",
31
+ "neuromorphic",
32
+ "add-only",
33
+ "oxide-js"
34
+ ],
35
+ "author": "Akhyar",
36
+ "license": "ISC",
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "dependencies": {
41
+ "@napi-rs/cli": "^2.18.0",
42
+ "@napi-rs/wasm-runtime": "^0.2.4"
43
+ },
44
+ "devDependencies": {
45
+ "typescript": "^6.0.3"
46
+ }
47
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+
2
+ export { default as dotProductAddOnly } from "./math/dotProductAddOnly.js";
3
+ export { SpikingDense, type SpikingDenseConfig } from "./layers/SpikingDense.js";
4
+ export { SpikingEmbedding, type SpikingEmbeddingConfig } from "./layers/SpikingEmbedding.js";
5
+ export { SpikingSentenceEmbedder, type SpikingSentenceConfig } from "./models/SpikingSentenceEmbedder.js";