@oxide-js/spiking 1.1.0 → 1.2.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 +11 -0
- package/index.cjs +322 -0
- package/index.d.ts +4 -13
- package/index.js +5 -2
- package/package.json +1 -1
- package/spiking-native.linux-x64-gnu.node +0 -0
- package/src/index.ts +1 -2
- package/src/layers/SpikingDense.ts +55 -36
- package/src/layers/SpikingEmbedding.ts +123 -135
- package/src/native_backend.ts +22 -3
- package/src-rust/src/delta.rs +51 -0
- package/src-rust/src/dot_product.rs +47 -0
- package/src-rust/src/embedding.rs +28 -0
- package/src-rust/src/lib.rs +14 -460
- package/src-rust/src/lif.rs +44 -0
- package/src-rust/src/surrogate.rs +28 -0
- package/test/test_embedding_overlap.ts +181 -0
- package/examples/demo.ts +0 -101
- package/src/models/SpikingSentenceEmbedder.ts +0 -135
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @oxide-js/spiking
|
|
2
2
|
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f0b623b: Refactored Native Rust Backend to implement Rayon parallelism and added Heterogeneous Neuron Dynamics
|
|
8
|
+
|
|
9
|
+
- **Parallel Computing**: Integrated `rayon` into the native Rust backend to parallelize operations (`lifStep`, `surrogateMask`, `dotProduct`, `deltaUpdates`).
|
|
10
|
+
- **Heterogeneous Neuron Dynamics**: Decoupled `beta` (Leakage) and `threshold` scalars into random per-neuron array buffers for model stochasticity.
|
|
11
|
+
- **Improved Mathematical Stability**: Enforced membrane potential clamping at `1.0` and strict weight/bias clipping within the `[-1.0, 1.0]` range to prevent gradient explosions during Hebbian Updates.
|
|
12
|
+
- **Native Data Races Handled**: Safely extracted slice pointers before processing `Float32Array` buffers with Rayon to guarantee thread-safe (Send + Sync) execution across parallel units.
|
|
13
|
+
|
|
3
14
|
## 1.1.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
package/index.cjs
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
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, lifStepNative, maskSurrogateNative, applyAddOnlyDeltaNative, learnHebbianNative, supervisedStdpNative, learnContrastiveBatchNative } = nativeBinding
|
|
314
|
+
|
|
315
|
+
module.exports.NativeSpikingNetwork = NativeSpikingNetwork
|
|
316
|
+
module.exports.dotProductAddOnlyNative = dotProductAddOnlyNative
|
|
317
|
+
module.exports.lifStepNative = lifStepNative
|
|
318
|
+
module.exports.maskSurrogateNative = maskSurrogateNative
|
|
319
|
+
module.exports.applyAddOnlyDeltaNative = applyAddOnlyDeltaNative
|
|
320
|
+
module.exports.learnHebbianNative = learnHebbianNative
|
|
321
|
+
module.exports.supervisedStdpNative = supervisedStdpNative
|
|
322
|
+
module.exports.learnContrastiveBatchNative = learnContrastiveBatchNative
|
package/index.d.ts
CHANGED
|
@@ -4,16 +4,7 @@
|
|
|
4
4
|
/* auto-generated by NAPI-RS */
|
|
5
5
|
|
|
6
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
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
}
|
|
7
|
+
export declare function lifStepNative(potentials: Float32Array, dot: Float32Array, spikes: Float32Array, lastPotentials: Float32Array, beta: Float32Array, threshold: Float32Array): void
|
|
8
|
+
export declare function maskSurrogateNative(errorSignal: Float32Array, potentials: Float32Array, threshold: Float32Array, windowSize: number): void
|
|
9
|
+
export declare function applyAddOnlyDeltaNative(kernel: Float32Array, bias: Float32Array, inputs: Float32Array, errorSignal: Float32Array, learningRate: number, batch: number, inFeatures: number, units: number, useBias: boolean): void
|
|
10
|
+
export declare function applyEmbeddingDeltaNative(embeddings: Float32Array, inputs: Float32Array, errorSignal: Float32Array, learningRate: number, inputDim: number, outputDim: number): void
|
package/index.js
CHANGED
|
@@ -310,7 +310,10 @@ if (!nativeBinding) {
|
|
|
310
310
|
throw new Error(`Failed to load native binding`)
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
const {
|
|
313
|
+
const { dotProductAddOnlyNative, lifStepNative, maskSurrogateNative, applyAddOnlyDeltaNative, applyEmbeddingDeltaNative } = nativeBinding
|
|
314
314
|
|
|
315
|
-
module.exports.NativeSpikingNetwork = NativeSpikingNetwork
|
|
316
315
|
module.exports.dotProductAddOnlyNative = dotProductAddOnlyNative
|
|
316
|
+
module.exports.lifStepNative = lifStepNative
|
|
317
|
+
module.exports.maskSurrogateNative = maskSurrogateNative
|
|
318
|
+
module.exports.applyAddOnlyDeltaNative = applyAddOnlyDeltaNative
|
|
319
|
+
module.exports.applyEmbeddingDeltaNative = applyEmbeddingDeltaNative
|
package/package.json
CHANGED
|
Binary file
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
1
|
export { default as dotProductAddOnly } from "./math/dotProductAddOnly.js";
|
|
3
2
|
export { SpikingDense, type SpikingDenseConfig } from "./layers/SpikingDense.js";
|
|
4
3
|
export { SpikingEmbedding, type SpikingEmbeddingConfig } from "./layers/SpikingEmbedding.js";
|
|
5
|
-
export { SpikingSentenceEmbedder, type SpikingSentenceConfig } from "./models/SpikingSentenceEmbedder.js";
|
|
4
|
+
// export { SpikingSentenceEmbedder, type SpikingSentenceConfig } from "./models/SpikingSentenceEmbedder.js";
|
|
@@ -12,8 +12,6 @@ export interface SpikingDenseConfig extends LayerConfig {
|
|
|
12
12
|
useBias?: boolean;
|
|
13
13
|
kernelInitializer?: string;
|
|
14
14
|
biasInitializer?: string;
|
|
15
|
-
beta?: number;
|
|
16
|
-
threshold?: number;
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
export class SpikingDense extends BaseLayer {
|
|
@@ -21,8 +19,8 @@ export class SpikingDense extends BaseLayer {
|
|
|
21
19
|
public useBias: boolean;
|
|
22
20
|
public kernelInitializer: string;
|
|
23
21
|
public biasInitializer: string;
|
|
24
|
-
public beta
|
|
25
|
-
public threshold
|
|
22
|
+
public beta!: Float32Array;
|
|
23
|
+
public threshold!: Float32Array;
|
|
26
24
|
|
|
27
25
|
public potentials!: Matrix;
|
|
28
26
|
public lastPotentials?: Matrix;
|
|
@@ -43,8 +41,6 @@ export class SpikingDense extends BaseLayer {
|
|
|
43
41
|
this.useBias = config.useBias ?? true;
|
|
44
42
|
this.kernelInitializer = config.kernelInitializer || "glorot_normal";
|
|
45
43
|
this.biasInitializer = config.biasInitializer || "zeros";
|
|
46
|
-
this.beta = config.beta ?? 0.9;
|
|
47
|
-
this.threshold = config.threshold ?? 1.0;
|
|
48
44
|
}
|
|
49
45
|
|
|
50
46
|
public computeOutputShape(inputShape: number[]): number[] {
|
|
@@ -65,6 +61,14 @@ export class SpikingDense extends BaseLayer {
|
|
|
65
61
|
this.addParameter("bias", biasVal, true, [this.units, 1]);
|
|
66
62
|
}
|
|
67
63
|
|
|
64
|
+
// Inisialisasi beta dan threshold secara acak untuk setiap neuron
|
|
65
|
+
this.beta = new Float32Array(this.units);
|
|
66
|
+
this.threshold = new Float32Array(this.units);
|
|
67
|
+
for (let i = 0; i < this.units; i++) {
|
|
68
|
+
this.beta[i] = 0.8 + Math.random() * 0.19;
|
|
69
|
+
this.threshold[i] = 0.5 + Math.random() * 0.5; // Max 1.0
|
|
70
|
+
}
|
|
71
|
+
|
|
68
72
|
// Inisialisasi state
|
|
69
73
|
this.potentials = Matrix.fromFlat(new Float32Array(this.units), [1, this.units]);
|
|
70
74
|
}
|
|
@@ -112,19 +116,24 @@ export class SpikingDense extends BaseLayer {
|
|
|
112
116
|
} else {
|
|
113
117
|
const potData = this.potentials._data;
|
|
114
118
|
const dotData = dot._data;
|
|
115
|
-
const thresh = this.threshold;
|
|
116
119
|
const lpData = this.lastPotentials._data;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
120
|
+
|
|
121
|
+
for (let b = 0; b < batch; b++) {
|
|
122
|
+
const offset = b * this.units;
|
|
123
|
+
for (let i = 0; i < this.units; i++) {
|
|
124
|
+
const idx = offset + i;
|
|
125
|
+
potData[idx] = Math.min((potData[idx] * this.beta[i]) + dotData[idx], 1.0); // Clamp potential max 1.0
|
|
126
|
+
lpData[idx] = potData[idx];
|
|
127
|
+
}
|
|
128
|
+
for (let i = 0; i < this.units; i++) {
|
|
129
|
+
const idx = offset + i;
|
|
130
|
+
if (potData[idx] >= this.threshold[i]) {
|
|
131
|
+
outData[idx] = 1;
|
|
132
|
+
potData[idx] -= this.threshold[i];
|
|
133
|
+
} else {
|
|
134
|
+
outData[idx] = 0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
128
137
|
}
|
|
129
138
|
}
|
|
130
139
|
|
|
@@ -146,22 +155,26 @@ export class SpikingDense extends BaseLayer {
|
|
|
146
155
|
|
|
147
156
|
// Surrogate Mask: Boxcar (Murni Add-Only mask, tanpa perkalian float!)
|
|
148
157
|
if (this.lastPotentials) {
|
|
158
|
+
const eData = eHidden._data;
|
|
159
|
+
const pData = this.lastPotentials._data;
|
|
160
|
+
const windowSize = 1.0;
|
|
161
|
+
|
|
149
162
|
if (isNativeAvailable()) {
|
|
150
163
|
maskSurrogateNativeWrapper(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
this.threshold,
|
|
154
|
-
|
|
164
|
+
eData,
|
|
165
|
+
pData,
|
|
166
|
+
this.threshold,
|
|
167
|
+
windowSize
|
|
155
168
|
);
|
|
156
169
|
} else {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
170
|
+
const batch = eHidden._shape[0];
|
|
171
|
+
for (let b = 0; b < batch; b++) {
|
|
172
|
+
const offset = b * this.units;
|
|
173
|
+
for (let i = 0; i < this.units; i++) {
|
|
174
|
+
const idx = offset + i;
|
|
175
|
+
if (Math.abs(pData[idx] - this.threshold[i]) > windowSize) {
|
|
176
|
+
eData[idx] = 0;
|
|
177
|
+
}
|
|
165
178
|
}
|
|
166
179
|
}
|
|
167
180
|
}
|
|
@@ -197,6 +210,12 @@ export class SpikingDense extends BaseLayer {
|
|
|
197
210
|
units,
|
|
198
211
|
this.useBias
|
|
199
212
|
);
|
|
213
|
+
|
|
214
|
+
for(let i = 0; i < kernel.length; i++) kernel[i] = Math.max(-1.0, Math.min(1.0, kernel[i]));
|
|
215
|
+
if (this.useBias && this.bias) {
|
|
216
|
+
const biasData = this.bias._data;
|
|
217
|
+
for(let i = 0; i < biasData.length; i++) biasData[i] = Math.max(-1.0, Math.min(1.0, biasData[i]));
|
|
218
|
+
}
|
|
200
219
|
} else {
|
|
201
220
|
// Delta rule add-only
|
|
202
221
|
for (let b = 0; b < batch; b++) {
|
|
@@ -204,11 +223,12 @@ export class SpikingDense extends BaseLayer {
|
|
|
204
223
|
const errOffset = b * units;
|
|
205
224
|
|
|
206
225
|
for (let k = 0; k < inFeatures; k++) {
|
|
207
|
-
// HANYA update jika input menyala (Spike
|
|
208
|
-
if (inputs[inOffset + k]
|
|
226
|
+
// HANYA update jika input menyala (Spike > 0.5) -> Add Only Update!
|
|
227
|
+
if (inputs[inOffset + k] > 0.5) {
|
|
209
228
|
const kOffset = k * units;
|
|
210
229
|
for (let j = 0; j < units; j++) {
|
|
211
230
|
kernel[kOffset + j] += learningRate * err[errOffset + j];
|
|
231
|
+
kernel[kOffset + j] = Math.max(-1.0, Math.min(1.0, kernel[kOffset + j]));
|
|
212
232
|
}
|
|
213
233
|
}
|
|
214
234
|
}
|
|
@@ -216,7 +236,8 @@ export class SpikingDense extends BaseLayer {
|
|
|
216
236
|
if (this.useBias && this.bias) {
|
|
217
237
|
const biasData = this.bias._data;
|
|
218
238
|
for (let j = 0; j < units; j++) {
|
|
219
|
-
biasData[j] += learningRate * err[errOffset + j];
|
|
239
|
+
biasData[j] += (learningRate * err[errOffset + j]) / batch;
|
|
240
|
+
biasData[j] = Math.max(-1.0, Math.min(1.0, biasData[j]));
|
|
220
241
|
}
|
|
221
242
|
}
|
|
222
243
|
}
|
|
@@ -229,9 +250,7 @@ export class SpikingDense extends BaseLayer {
|
|
|
229
250
|
units: this.units,
|
|
230
251
|
useBias: this.useBias,
|
|
231
252
|
kernelInitializer: this.kernelInitializer,
|
|
232
|
-
biasInitializer: this.biasInitializer
|
|
233
|
-
beta: this.beta,
|
|
234
|
-
threshold: this.threshold
|
|
253
|
+
biasInitializer: this.biasInitializer
|
|
235
254
|
};
|
|
236
255
|
}
|
|
237
256
|
}
|