Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
21
vendor/ruvector/npm/packages/spiking-neural/LICENSE
vendored
Normal file
21
vendor/ruvector/npm/packages/spiking-neural/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 rUv
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
351
vendor/ruvector/npm/packages/spiking-neural/README.md
vendored
Normal file
351
vendor/ruvector/npm/packages/spiking-neural/README.md
vendored
Normal file
@@ -0,0 +1,351 @@
|
||||
# spiking-neural
|
||||
|
||||
A high-performance Spiking Neural Network (SNN) library for Node.js with biologically-inspired neuron models and unsupervised learning. SNNs process information through discrete spike events rather than continuous values, making them more energy-efficient and better suited for temporal pattern recognition.
|
||||
|
||||
[](https://www.npmjs.com/package/spiking-neural)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
## What Are Spiking Neural Networks?
|
||||
|
||||
Unlike traditional neural networks that use continuous activation values, SNNs communicate through discrete spike events timed in milliseconds. This mirrors how biological neurons work, enabling:
|
||||
|
||||
- **Temporal pattern recognition** - naturally process time-series data
|
||||
- **Energy efficiency** - event-driven computation (10-100x lower power)
|
||||
- **Online learning** - adapt in real-time with STDP (no batches needed)
|
||||
- **Neuromorphic deployment** - run on specialized hardware like Intel Loihi
|
||||
|
||||
## Key Features
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| **LIF Neurons** | Leaky Integrate-and-Fire model with configurable membrane dynamics |
|
||||
| **STDP Learning** | Spike-Timing-Dependent Plasticity for unsupervised pattern learning |
|
||||
| **Lateral Inhibition** | Winner-take-all competition for sparse representations |
|
||||
| **SIMD Optimization** | Loop-unrolled vector math for 5-54x speedup |
|
||||
| **Multiple Encodings** | Rate coding and temporal coding for flexible input handling |
|
||||
| **Zero Dependencies** | Pure JavaScript SDK works everywhere Node.js runs |
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install spiking-neural
|
||||
```
|
||||
|
||||
**Note**: This package is pure JavaScript and works out of the box. No native compilation required.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### CLI Usage
|
||||
|
||||
```bash
|
||||
# Run pattern recognition demo
|
||||
npx spiking-neural demo pattern
|
||||
|
||||
# Run performance benchmarks
|
||||
npx spiking-neural benchmark
|
||||
|
||||
# Run SIMD vector operation benchmarks
|
||||
npx spiking-neural simd
|
||||
|
||||
# Run validation tests
|
||||
npx spiking-neural test
|
||||
|
||||
# Show help
|
||||
npx spiking-neural help
|
||||
```
|
||||
|
||||
### SDK Usage
|
||||
|
||||
```javascript
|
||||
const {
|
||||
createFeedforwardSNN,
|
||||
rateEncoding,
|
||||
native
|
||||
} = require('spiking-neural');
|
||||
|
||||
// Create a 3-layer feedforward SNN
|
||||
const snn = createFeedforwardSNN([100, 50, 10], {
|
||||
dt: 1.0, // 1ms time step
|
||||
tau: 20.0, // 20ms membrane time constant
|
||||
a_plus: 0.005, // STDP LTP rate
|
||||
a_minus: 0.005, // STDP LTD rate
|
||||
lateral_inhibition: true,
|
||||
inhibition_strength: 10.0
|
||||
});
|
||||
|
||||
// Create input pattern
|
||||
const input = new Float32Array(100).fill(0.5);
|
||||
|
||||
// Run simulation
|
||||
for (let t = 0; t < 100; t++) {
|
||||
const spikes = rateEncoding(input, snn.dt, 100);
|
||||
snn.step(spikes);
|
||||
}
|
||||
|
||||
// Get output
|
||||
const output = snn.getOutput();
|
||||
console.log('Output:', output);
|
||||
```
|
||||
|
||||
## CLI Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `demo <type>` | Run demonstration (pattern, temporal, learning, all) |
|
||||
| `benchmark` | Run SNN performance benchmarks |
|
||||
| `simd` | Run SIMD vector operation benchmarks |
|
||||
| `train` | Train a custom SNN |
|
||||
| `test` | Run validation tests |
|
||||
| `info` | Show system information |
|
||||
| `version` | Show version |
|
||||
| `help` | Show help |
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Pattern recognition with 5x5 pixel patterns
|
||||
npx spiking-neural demo pattern
|
||||
|
||||
# Train custom network
|
||||
npx spiking-neural train --layers 25,50,10 --epochs 10
|
||||
|
||||
# All demos
|
||||
npx spiking-neural demo all
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `createFeedforwardSNN(layer_sizes, params)`
|
||||
|
||||
Create a feedforward spiking neural network.
|
||||
|
||||
```javascript
|
||||
const snn = createFeedforwardSNN([100, 50, 10], {
|
||||
dt: 1.0, // Time step (ms)
|
||||
tau: 20.0, // Membrane time constant (ms)
|
||||
v_rest: -70.0, // Resting potential (mV)
|
||||
v_reset: -75.0, // Reset potential (mV)
|
||||
v_thresh: -50.0, // Spike threshold (mV)
|
||||
resistance: 10.0, // Membrane resistance (MOhm)
|
||||
a_plus: 0.01, // STDP LTP learning rate
|
||||
a_minus: 0.01, // STDP LTD learning rate
|
||||
w_min: 0.0, // Minimum weight
|
||||
w_max: 1.0, // Maximum weight
|
||||
init_weight: 0.5, // Initial weight mean
|
||||
init_std: 0.1, // Initial weight std
|
||||
lateral_inhibition: false, // Enable winner-take-all
|
||||
inhibition_strength: 10.0 // Inhibition strength
|
||||
});
|
||||
```
|
||||
|
||||
### `SpikingNeuralNetwork`
|
||||
|
||||
```javascript
|
||||
// Run one time step
|
||||
const spike_count = snn.step(input_spikes);
|
||||
|
||||
// Run for duration
|
||||
const results = snn.run(100, (time) => inputGenerator(time));
|
||||
|
||||
// Get output spikes
|
||||
const output = snn.getOutput();
|
||||
|
||||
// Get network statistics
|
||||
const stats = snn.getStats();
|
||||
|
||||
// Reset network
|
||||
snn.reset();
|
||||
```
|
||||
|
||||
### `rateEncoding(values, dt, max_rate)`
|
||||
|
||||
Encode values as Poisson spike trains.
|
||||
|
||||
```javascript
|
||||
const input = new Float32Array([0.5, 0.8, 0.2]);
|
||||
const spikes = rateEncoding(input, 1.0, 100); // 100 Hz max rate
|
||||
```
|
||||
|
||||
### `temporalEncoding(values, time, t_start, t_window)`
|
||||
|
||||
Encode values as time-to-first-spike.
|
||||
|
||||
```javascript
|
||||
const input = new Float32Array([0.5, 0.8, 0.2]);
|
||||
const spikes = temporalEncoding(input, currentTime, 0, 50);
|
||||
```
|
||||
|
||||
### `SIMDOps`
|
||||
|
||||
SIMD-optimized vector operations.
|
||||
|
||||
```javascript
|
||||
const { SIMDOps } = require('spiking-neural');
|
||||
|
||||
const a = new Float32Array([1, 2, 3, 4]);
|
||||
const b = new Float32Array([4, 3, 2, 1]);
|
||||
|
||||
SIMDOps.dotProduct(a, b); // Dot product
|
||||
SIMDOps.distance(a, b); // Euclidean distance
|
||||
SIMDOps.cosineSimilarity(a, b); // Cosine similarity
|
||||
```
|
||||
|
||||
### `LIFLayer`
|
||||
|
||||
Low-level LIF neuron layer.
|
||||
|
||||
```javascript
|
||||
const { LIFLayer } = require('spiking-neural');
|
||||
|
||||
const layer = new LIFLayer(100, {
|
||||
tau: 20.0,
|
||||
v_thresh: -50.0,
|
||||
dt: 1.0
|
||||
});
|
||||
|
||||
layer.setCurrents(inputCurrents);
|
||||
const spike_count = layer.update();
|
||||
const spikes = layer.getSpikes();
|
||||
```
|
||||
|
||||
### `SynapticLayer`
|
||||
|
||||
Low-level synaptic connection layer with STDP.
|
||||
|
||||
```javascript
|
||||
const { SynapticLayer } = require('spiking-neural');
|
||||
|
||||
const synapses = new SynapticLayer(100, 50, {
|
||||
a_plus: 0.01,
|
||||
a_minus: 0.01,
|
||||
w_min: 0.0,
|
||||
w_max: 1.0
|
||||
});
|
||||
|
||||
synapses.forward(pre_spikes, post_currents);
|
||||
synapses.learn(pre_spikes, post_spikes);
|
||||
const stats = synapses.getWeightStats();
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
### JavaScript (Auto-vectorization)
|
||||
|
||||
| Operation | 64d | 128d | 256d | 512d |
|
||||
|-----------|-----|------|------|------|
|
||||
| Dot Product | 1.1x | 1.2x | 1.6x | 1.5x |
|
||||
| Distance | 5x | **54x** | 13x | 9x |
|
||||
| Cosine | **2.7x** | 1.0x | 0.9x | 0.9x |
|
||||
|
||||
### Benchmark Results
|
||||
|
||||
| Network Size | Updates/sec | Latency |
|
||||
|--------------|-------------|---------|
|
||||
| 100 neurons | 16,000+ | 0.06ms |
|
||||
| 1,000 neurons | 1,500+ | 0.67ms |
|
||||
| 10,000 neurons | 150+ | 6.7ms |
|
||||
|
||||
## Examples
|
||||
|
||||
### Pattern Recognition
|
||||
|
||||
```javascript
|
||||
const { createFeedforwardSNN, rateEncoding } = require('spiking-neural');
|
||||
|
||||
// 5x5 pattern -> 4 classes
|
||||
const snn = createFeedforwardSNN([25, 20, 4], {
|
||||
a_plus: 0.005,
|
||||
lateral_inhibition: true
|
||||
});
|
||||
|
||||
// Define patterns
|
||||
const cross = [
|
||||
0,0,1,0,0,
|
||||
0,0,1,0,0,
|
||||
1,1,1,1,1,
|
||||
0,0,1,0,0,
|
||||
0,0,1,0,0
|
||||
];
|
||||
|
||||
// Train
|
||||
for (let epoch = 0; epoch < 5; epoch++) {
|
||||
snn.reset();
|
||||
for (let t = 0; t < 100; t++) {
|
||||
snn.step(rateEncoding(cross, 1.0, 100));
|
||||
}
|
||||
}
|
||||
|
||||
// Test
|
||||
snn.reset();
|
||||
for (let t = 0; t < 100; t++) {
|
||||
snn.step(rateEncoding(cross, 1.0, 100));
|
||||
}
|
||||
const output = snn.getOutput();
|
||||
const winner = Array.from(output).indexOf(Math.max(...output));
|
||||
console.log(`Pattern classified as neuron ${winner}`);
|
||||
```
|
||||
|
||||
### Custom Network Architecture
|
||||
|
||||
```javascript
|
||||
const { LIFLayer, SynapticLayer, SpikingNeuralNetwork } = require('spiking-neural');
|
||||
|
||||
// Build custom architecture
|
||||
const input_layer = new LIFLayer(100, { tau: 15.0 });
|
||||
const hidden_layer = new LIFLayer(50, { tau: 20.0 });
|
||||
const output_layer = new LIFLayer(10, { tau: 25.0 });
|
||||
|
||||
const input_hidden = new SynapticLayer(100, 50, { a_plus: 0.01 });
|
||||
const hidden_output = new SynapticLayer(50, 10, { a_plus: 0.005 });
|
||||
|
||||
const layers = [
|
||||
{ neuron_layer: input_layer, synaptic_layer: input_hidden },
|
||||
{ neuron_layer: hidden_layer, synaptic_layer: hidden_output },
|
||||
{ neuron_layer: output_layer, synaptic_layer: null }
|
||||
];
|
||||
|
||||
const snn = new SpikingNeuralNetwork(layers, {
|
||||
lateral_inhibition: true
|
||||
});
|
||||
```
|
||||
|
||||
## Related Demos
|
||||
|
||||
This package is part of the [ruvector](https://github.com/ruvnet/ruvector) meta-cognition examples:
|
||||
|
||||
| Demo | Description | Command |
|
||||
|------|-------------|---------|
|
||||
| Pattern Recognition | 5x5 pixel classification | `npx spiking-neural demo pattern` |
|
||||
| SIMD Benchmarks | Vector operation performance | `npx spiking-neural simd` |
|
||||
| Attention Mechanisms | 5 attention types | See [meta-cognition examples](../examples/meta-cognition-spiking-neural-network) |
|
||||
| Hyperbolic Attention | Poincaré ball model | See [meta-cognition examples](../examples/meta-cognition-spiking-neural-network) |
|
||||
| Self-Discovery | Meta-cognitive systems | See [meta-cognition examples](../examples/meta-cognition-spiking-neural-network) |
|
||||
|
||||
## What are Spiking Neural Networks?
|
||||
|
||||
SNNs are **third-generation neural networks** that model biological neurons:
|
||||
|
||||
| Feature | Traditional ANN | Spiking NN |
|
||||
|---------|-----------------|------------|
|
||||
| Activation | Continuous values | Discrete spikes |
|
||||
| Time | Ignored | Integral to computation |
|
||||
| Learning | Backpropagation | STDP (Hebbian) |
|
||||
| Energy | High | 10-100x lower |
|
||||
| Hardware | GPU/TPU | Neuromorphic chips |
|
||||
|
||||
**Advantages:**
|
||||
- More biologically realistic
|
||||
- Energy efficient (event-driven)
|
||||
- Natural for temporal data
|
||||
- Online learning without batches
|
||||
|
||||
## License
|
||||
|
||||
MIT © [rUv](https://ruv.io)
|
||||
|
||||
## Links
|
||||
|
||||
- **Homepage**: [ruv.io](https://ruv.io)
|
||||
- **GitHub**: [github.com/ruvnet/ruvector](https://github.com/ruvnet/ruvector)
|
||||
- **npm**: [npmjs.com/package/spiking-neural](https://www.npmjs.com/package/spiking-neural)
|
||||
- **SNN Guide**: [Meta-Cognition Examples](https://github.com/ruvnet/ruvector/tree/main/examples/meta-cognition-spiking-neural-network)
|
||||
575
vendor/ruvector/npm/packages/spiking-neural/bin/cli.js
vendored
Executable file
575
vendor/ruvector/npm/packages/spiking-neural/bin/cli.js
vendored
Executable file
@@ -0,0 +1,575 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Spiking Neural Network CLI
|
||||
* Usage: npx spiking-neural <command> [options]
|
||||
*/
|
||||
|
||||
const {
|
||||
createFeedforwardSNN,
|
||||
rateEncoding,
|
||||
temporalEncoding,
|
||||
SIMDOps,
|
||||
native,
|
||||
version
|
||||
} = require('../src/index');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const command = args[0] || 'help';
|
||||
|
||||
// ANSI colors
|
||||
const c = {
|
||||
reset: '\x1b[0m',
|
||||
bold: '\x1b[1m',
|
||||
dim: '\x1b[2m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
cyan: '\x1b[36m',
|
||||
red: '\x1b[31m',
|
||||
magenta: '\x1b[35m'
|
||||
};
|
||||
|
||||
function log(msg = '') { console.log(msg); }
|
||||
function header(title) { log(`\n${c.bold}${c.cyan}${title}${c.reset}\n${'='.repeat(60)}`); }
|
||||
function success(msg) { log(`${c.green}${msg}${c.reset}`); }
|
||||
function warn(msg) { log(`${c.yellow}${msg}${c.reset}`); }
|
||||
function info(msg) { log(`${c.blue}${msg}${c.reset}`); }
|
||||
|
||||
// Commands
|
||||
const commands = {
|
||||
help: showHelp,
|
||||
version: showVersion,
|
||||
demo: runDemo,
|
||||
benchmark: runBenchmark,
|
||||
test: runTest,
|
||||
simd: runSIMDBenchmark,
|
||||
pattern: () => runDemo(['pattern']),
|
||||
train: runTrain,
|
||||
info: showInfo
|
||||
};
|
||||
|
||||
async function main() {
|
||||
if (commands[command]) {
|
||||
await commands[command](args.slice(1));
|
||||
} else {
|
||||
log(`${c.red}Unknown command: ${command}${c.reset}`);
|
||||
showHelp();
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function showHelp() {
|
||||
log(`
|
||||
${c.bold}${c.cyan}Spiking Neural Network CLI${c.reset}
|
||||
${c.dim}High-performance SNN with SIMD optimization${c.reset}
|
||||
|
||||
${c.bold}USAGE:${c.reset}
|
||||
npx spiking-neural <command> [options]
|
||||
snn <command> [options]
|
||||
|
||||
${c.bold}COMMANDS:${c.reset}
|
||||
${c.green}demo${c.reset} <type> Run a demonstration
|
||||
Types: pattern, temporal, learning, all
|
||||
${c.green}benchmark${c.reset} Run performance benchmarks
|
||||
${c.green}simd${c.reset} Run SIMD vector operation benchmarks
|
||||
${c.green}train${c.reset} [opts] Train a custom SNN
|
||||
${c.green}test${c.reset} Run validation tests
|
||||
${c.green}info${c.reset} Show system information
|
||||
${c.green}version${c.reset} Show version
|
||||
${c.green}help${c.reset} Show this help
|
||||
|
||||
${c.bold}EXAMPLES:${c.reset}
|
||||
${c.dim}# Run pattern recognition demo${c.reset}
|
||||
npx spiking-neural demo pattern
|
||||
|
||||
${c.dim}# Run full benchmark suite${c.reset}
|
||||
npx spiking-neural benchmark
|
||||
|
||||
${c.dim}# Train custom network${c.reset}
|
||||
npx spiking-neural train --layers 25,50,10 --epochs 5
|
||||
|
||||
${c.bold}SDK USAGE:${c.reset}
|
||||
const { createFeedforwardSNN, rateEncoding } = require('spiking-neural');
|
||||
|
||||
const snn = createFeedforwardSNN([100, 50, 10], {
|
||||
dt: 1.0,
|
||||
tau: 20.0,
|
||||
lateral_inhibition: true
|
||||
});
|
||||
|
||||
const spikes = rateEncoding(inputData, snn.dt, 100);
|
||||
snn.step(spikes);
|
||||
`);
|
||||
}
|
||||
|
||||
function showVersion() {
|
||||
log(`spiking-neural v${version}`);
|
||||
log(`Native SIMD: ${native ? 'enabled' : 'JavaScript fallback'}`);
|
||||
}
|
||||
|
||||
function showInfo() {
|
||||
header('System Information');
|
||||
log(`
|
||||
${c.bold}Package:${c.reset} spiking-neural v${version}
|
||||
${c.bold}Native SIMD:${c.reset} ${native ? c.green + 'Enabled' : c.yellow + 'JavaScript fallback'}${c.reset}
|
||||
${c.bold}Node.js:${c.reset} ${process.version}
|
||||
${c.bold}Platform:${c.reset} ${process.platform} ${process.arch}
|
||||
${c.bold}Memory:${c.reset} ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB used
|
||||
|
||||
${c.bold}Capabilities:${c.reset}
|
||||
- LIF Neurons with configurable parameters
|
||||
- STDP Learning (unsupervised)
|
||||
- Lateral Inhibition (winner-take-all)
|
||||
- Rate & Temporal Encoding
|
||||
- SIMD-optimized vector operations
|
||||
- Multi-layer feedforward networks
|
||||
`);
|
||||
}
|
||||
|
||||
async function runDemo(demoArgs) {
|
||||
const type = demoArgs[0] || 'pattern';
|
||||
|
||||
if (type === 'pattern' || type === 'all') {
|
||||
await demoPatternRecognition();
|
||||
}
|
||||
if (type === 'temporal' || type === 'all') {
|
||||
await demoTemporalDynamics();
|
||||
}
|
||||
if (type === 'learning' || type === 'all') {
|
||||
await demoSTDPLearning();
|
||||
}
|
||||
}
|
||||
|
||||
async function demoPatternRecognition() {
|
||||
header('Pattern Recognition Demo');
|
||||
|
||||
// Define 5x5 patterns
|
||||
const patterns = {
|
||||
'Cross': [0,0,1,0,0, 0,0,1,0,0, 1,1,1,1,1, 0,0,1,0,0, 0,0,1,0,0],
|
||||
'Square': [1,1,1,1,1, 1,0,0,0,1, 1,0,0,0,1, 1,0,0,0,1, 1,1,1,1,1],
|
||||
'Diagonal': [1,0,0,0,0, 0,1,0,0,0, 0,0,1,0,0, 0,0,0,1,0, 0,0,0,0,1],
|
||||
'X-Shape': [1,0,0,0,1, 0,1,0,1,0, 0,0,1,0,0, 0,1,0,1,0, 1,0,0,0,1]
|
||||
};
|
||||
|
||||
// Visualize patterns
|
||||
log(`\n${c.bold}Patterns:${c.reset}\n`);
|
||||
for (const [name, pattern] of Object.entries(patterns)) {
|
||||
log(`${c.cyan}${name}:${c.reset}`);
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const row = pattern.slice(i * 5, (i + 1) * 5).map(v => v ? '##' : ' ').join('');
|
||||
log(` ${row}`);
|
||||
}
|
||||
log();
|
||||
}
|
||||
|
||||
// Create SNN with parameters tuned for spiking
|
||||
const snn = createFeedforwardSNN([25, 20, 4], {
|
||||
dt: 1.0,
|
||||
tau: 2.0, // Fast integration for quick spiking
|
||||
v_rest: 0.0, // Simplified: rest at 0
|
||||
v_reset: 0.0, // Reset to 0
|
||||
v_thresh: 1.0, // Threshold at 1
|
||||
resistance: 1.0, // Direct current-to-voltage
|
||||
a_plus: 0.02,
|
||||
a_minus: 0.02,
|
||||
init_weight: 0.2, // Strong enough for spike propagation
|
||||
init_std: 0.02,
|
||||
lateral_inhibition: false
|
||||
});
|
||||
|
||||
log(`${c.bold}Network:${c.reset} 25-20-4 (${25*20 + 20*4} synapses)`);
|
||||
log(`${c.bold}Native SIMD:${c.reset} ${native ? c.green + 'Enabled' : c.yellow + 'Fallback'}${c.reset}\n`);
|
||||
|
||||
// Training - use direct pattern as current (scaled)
|
||||
log(`${c.bold}Training (5 epochs):${c.reset}\n`);
|
||||
const pattern_names = Object.keys(patterns);
|
||||
const pattern_arrays = Object.values(patterns);
|
||||
|
||||
for (let epoch = 0; epoch < 5; epoch++) {
|
||||
let total_spikes = 0;
|
||||
for (let p = 0; p < pattern_names.length; p++) {
|
||||
snn.reset();
|
||||
for (let t = 0; t < 50; t++) {
|
||||
// Scale pattern to produce spikes (current * 2 to exceed threshold)
|
||||
const input = new Float32Array(pattern_arrays[p].map(v => v * 2.0));
|
||||
total_spikes += snn.step(input);
|
||||
}
|
||||
}
|
||||
log(` Epoch ${epoch + 1}: ${total_spikes} total spikes`);
|
||||
}
|
||||
|
||||
// Testing
|
||||
log(`\n${c.bold}Testing:${c.reset}\n`);
|
||||
for (let p = 0; p < pattern_names.length; p++) {
|
||||
snn.reset();
|
||||
const output_activity = new Float32Array(4);
|
||||
for (let t = 0; t < 50; t++) {
|
||||
const input = new Float32Array(pattern_arrays[p].map(v => v * 2.0));
|
||||
snn.step(input);
|
||||
const output = snn.getOutput();
|
||||
for (let i = 0; i < 4; i++) output_activity[i] += output[i];
|
||||
}
|
||||
const winner = Array.from(output_activity).indexOf(Math.max(...output_activity));
|
||||
const total = output_activity.reduce((a, b) => a + b, 0);
|
||||
const confidence = total > 0 ? (output_activity[winner] / total * 100) : 0;
|
||||
log(` ${pattern_names[p].padEnd(10)} -> Neuron ${winner} (${confidence.toFixed(1)}%)`);
|
||||
}
|
||||
|
||||
success('\nPattern recognition complete!');
|
||||
}
|
||||
|
||||
async function demoTemporalDynamics() {
|
||||
header('Temporal Dynamics Demo');
|
||||
|
||||
const snn = createFeedforwardSNN([10, 10], {
|
||||
dt: 1.0,
|
||||
tau: 10.0,
|
||||
v_rest: 0.0,
|
||||
v_reset: 0.0,
|
||||
v_thresh: 1.0,
|
||||
resistance: 1.0
|
||||
});
|
||||
|
||||
log(`\nSimulating 50ms with constant input:\n`);
|
||||
log('Time (ms) | Input Sum | Output Spikes');
|
||||
log('-'.repeat(40));
|
||||
|
||||
const input_pattern = new Float32Array(10).fill(1.5); // Strong enough to spike
|
||||
|
||||
for (let t = 0; t < 50; t += 5) {
|
||||
snn.step(input_pattern);
|
||||
|
||||
const stats = snn.getStats();
|
||||
const in_sum = input_pattern.reduce((a, b) => a + b, 0);
|
||||
const out_count = stats.layers[1].neurons.spike_count;
|
||||
|
||||
log(`${t.toString().padStart(9)} | ${in_sum.toFixed(1).padStart(9)} | ${out_count.toString().padStart(13)}`);
|
||||
}
|
||||
|
||||
success('\nTemporal dynamics complete!');
|
||||
}
|
||||
|
||||
async function demoSTDPLearning() {
|
||||
header('STDP Learning Demo');
|
||||
|
||||
const snn = createFeedforwardSNN([10, 5], {
|
||||
dt: 1.0,
|
||||
tau: 10.0,
|
||||
v_rest: 0.0,
|
||||
v_reset: 0.0,
|
||||
v_thresh: 1.0,
|
||||
resistance: 1.0,
|
||||
a_plus: 0.02,
|
||||
a_minus: 0.02
|
||||
});
|
||||
|
||||
log('\nWeight evolution during learning:\n');
|
||||
|
||||
for (let epoch = 0; epoch < 10; epoch++) {
|
||||
const pattern = new Float32Array(10).map(() => Math.random() > 0.5 ? 2.0 : 0);
|
||||
|
||||
for (let t = 0; t < 50; t++) {
|
||||
snn.step(pattern);
|
||||
}
|
||||
|
||||
const stats = snn.getStats();
|
||||
const w = stats.layers[0].synapses;
|
||||
log(` Epoch ${(epoch + 1).toString().padStart(2)}: mean=${w.mean.toFixed(3)}, min=${w.min.toFixed(3)}, max=${w.max.toFixed(3)}`);
|
||||
}
|
||||
|
||||
success('\nSTDP learning complete!');
|
||||
}
|
||||
|
||||
async function runBenchmark() {
|
||||
header('Performance Benchmark');
|
||||
|
||||
const sizes = [100, 500, 1000, 2000];
|
||||
const iterations = 100;
|
||||
|
||||
log(`\n${c.bold}Network Size Scaling:${c.reset}\n`);
|
||||
log('Neurons | Time/Step | Spikes/Step | Ops/Sec');
|
||||
log('-'.repeat(50));
|
||||
|
||||
for (const size of sizes) {
|
||||
const snn = createFeedforwardSNN([size, Math.floor(size / 2), 10], {
|
||||
dt: 1.0,
|
||||
tau: 10.0,
|
||||
v_rest: 0.0,
|
||||
v_reset: 0.0,
|
||||
v_thresh: 1.0,
|
||||
resistance: 1.0,
|
||||
lateral_inhibition: false
|
||||
});
|
||||
|
||||
const input = new Float32Array(size).fill(1.5);
|
||||
|
||||
// Warmup
|
||||
for (let i = 0; i < 10; i++) {
|
||||
snn.step(input);
|
||||
}
|
||||
|
||||
// Benchmark
|
||||
const start = performance.now();
|
||||
let total_spikes = 0;
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
total_spikes += snn.step(input);
|
||||
}
|
||||
const elapsed = performance.now() - start;
|
||||
|
||||
const time_per_step = elapsed / iterations;
|
||||
const spikes_per_step = total_spikes / iterations;
|
||||
const ops_per_sec = Math.round(1000 / time_per_step);
|
||||
|
||||
log(`${size.toString().padStart(7)} | ${time_per_step.toFixed(3).padStart(9)}ms | ${spikes_per_step.toFixed(1).padStart(11)} | ${ops_per_sec.toString().padStart(7)}`);
|
||||
}
|
||||
|
||||
log(`\n${c.bold}Native SIMD:${c.reset} ${native ? c.green + 'Enabled (10-50x faster)' : c.yellow + 'Disabled (use npm run build:native)'}${c.reset}`);
|
||||
|
||||
success('\nBenchmark complete!');
|
||||
}
|
||||
|
||||
async function runSIMDBenchmark() {
|
||||
header('SIMD Vector Operations Benchmark');
|
||||
|
||||
const dimensions = [64, 128, 256, 512];
|
||||
const iterations = 10000;
|
||||
|
||||
log(`\n${c.bold}Dot Product Performance:${c.reset}\n`);
|
||||
log('Dimension | Naive (ms) | SIMD (ms) | Speedup');
|
||||
log('-'.repeat(50));
|
||||
|
||||
for (const dim of dimensions) {
|
||||
const a = new Float32Array(dim).map(() => Math.random());
|
||||
const b = new Float32Array(dim).map(() => Math.random());
|
||||
|
||||
// Naive
|
||||
let start = performance.now();
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
let sum = 0;
|
||||
for (let j = 0; j < dim; j++) sum += a[j] * b[j];
|
||||
}
|
||||
const naiveTime = performance.now() - start;
|
||||
|
||||
// SIMD
|
||||
start = performance.now();
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
SIMDOps.dotProduct(a, b);
|
||||
}
|
||||
const simdTime = performance.now() - start;
|
||||
|
||||
const speedup = naiveTime / simdTime;
|
||||
log(`${dim.toString().padStart(9)} | ${naiveTime.toFixed(2).padStart(10)} | ${simdTime.toFixed(2).padStart(9)} | ${speedup.toFixed(2)}x${speedup > 1.2 ? ' *' : ''}`);
|
||||
}
|
||||
|
||||
log(`\n${c.bold}Euclidean Distance:${c.reset}\n`);
|
||||
log('Dimension | Naive (ms) | SIMD (ms) | Speedup');
|
||||
log('-'.repeat(50));
|
||||
|
||||
for (const dim of dimensions) {
|
||||
const a = new Float32Array(dim).map(() => Math.random());
|
||||
const b = new Float32Array(dim).map(() => Math.random());
|
||||
|
||||
// Naive
|
||||
let start = performance.now();
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
let sum = 0;
|
||||
for (let j = 0; j < dim; j++) {
|
||||
const d = a[j] - b[j];
|
||||
sum += d * d;
|
||||
}
|
||||
Math.sqrt(sum);
|
||||
}
|
||||
const naiveTime = performance.now() - start;
|
||||
|
||||
// SIMD
|
||||
start = performance.now();
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
SIMDOps.distance(a, b);
|
||||
}
|
||||
const simdTime = performance.now() - start;
|
||||
|
||||
const speedup = naiveTime / simdTime;
|
||||
log(`${dim.toString().padStart(9)} | ${naiveTime.toFixed(2).padStart(10)} | ${simdTime.toFixed(2).padStart(9)} | ${speedup.toFixed(2)}x${speedup > 1.5 ? ' **' : speedup > 1.2 ? ' *' : ''}`);
|
||||
}
|
||||
|
||||
success('\nSIMD benchmark complete!');
|
||||
}
|
||||
|
||||
async function runTest() {
|
||||
header('Validation Tests');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
log(` ${c.green}PASS${c.reset} ${name}`);
|
||||
passed++;
|
||||
} catch (e) {
|
||||
log(` ${c.red}FAIL${c.reset} ${name}: ${e.message}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
function assert(condition, msg) {
|
||||
if (!condition) throw new Error(msg);
|
||||
}
|
||||
|
||||
log('\n');
|
||||
|
||||
// LIF Layer tests
|
||||
test('LIFLayer creation', () => {
|
||||
const layer = require('../src/index').LIFLayer;
|
||||
const l = new layer(10);
|
||||
assert(l.n_neurons === 10, 'Wrong neuron count');
|
||||
assert(l.voltages.length === 10, 'Wrong voltage array size');
|
||||
});
|
||||
|
||||
test('LIFLayer update', () => {
|
||||
const { LIFLayer } = require('../src/index');
|
||||
const l = new LIFLayer(10);
|
||||
l.currents.fill(100); // Strong input
|
||||
const spikes = l.update();
|
||||
assert(typeof spikes === 'number', 'Update should return spike count');
|
||||
});
|
||||
|
||||
// Synaptic Layer tests
|
||||
test('SynapticLayer creation', () => {
|
||||
const { SynapticLayer } = require('../src/index');
|
||||
const s = new SynapticLayer(10, 5);
|
||||
assert(s.weights.length === 50, 'Wrong weight matrix size');
|
||||
});
|
||||
|
||||
test('SynapticLayer forward', () => {
|
||||
const { SynapticLayer } = require('../src/index');
|
||||
const s = new SynapticLayer(10, 5);
|
||||
const pre = new Float32Array(10).fill(1);
|
||||
const post = new Float32Array(5);
|
||||
s.forward(pre, post);
|
||||
assert(post.some(v => v !== 0), 'Forward should produce output');
|
||||
});
|
||||
|
||||
// Network tests
|
||||
test('createFeedforwardSNN', () => {
|
||||
const snn = createFeedforwardSNN([10, 5, 2]);
|
||||
assert(snn.layers.length === 3, 'Wrong layer count');
|
||||
});
|
||||
|
||||
test('SNN step', () => {
|
||||
const snn = createFeedforwardSNN([10, 5, 2]);
|
||||
const input = new Float32Array(10).fill(1);
|
||||
const spikes = snn.step(input);
|
||||
assert(typeof spikes === 'number', 'Step should return spike count');
|
||||
});
|
||||
|
||||
test('SNN getOutput', () => {
|
||||
const snn = createFeedforwardSNN([10, 5, 2]);
|
||||
snn.step(new Float32Array(10).fill(1));
|
||||
const output = snn.getOutput();
|
||||
assert(output.length === 2, 'Output should match last layer size');
|
||||
});
|
||||
|
||||
test('SNN reset', () => {
|
||||
const snn = createFeedforwardSNN([10, 5, 2]);
|
||||
snn.step(new Float32Array(10).fill(1));
|
||||
snn.reset();
|
||||
assert(snn.time === 0, 'Reset should zero time');
|
||||
});
|
||||
|
||||
// Encoding tests
|
||||
test('rateEncoding', () => {
|
||||
const spikes = rateEncoding([1, 0, 0.5], 1.0, 100);
|
||||
assert(spikes.length === 3, 'Output should match input length');
|
||||
assert(spikes.every(v => v === 0 || v === 1), 'Should produce binary spikes');
|
||||
});
|
||||
|
||||
test('temporalEncoding', () => {
|
||||
const spikes = temporalEncoding([1, 0, 0.5], 0, 0, 50);
|
||||
assert(spikes.length === 3, 'Output should match input length');
|
||||
});
|
||||
|
||||
// SIMD tests
|
||||
test('SIMDOps.dotProduct', () => {
|
||||
const a = new Float32Array([1, 2, 3, 4]);
|
||||
const b = new Float32Array([1, 1, 1, 1]);
|
||||
const result = SIMDOps.dotProduct(a, b);
|
||||
assert(Math.abs(result - 10) < 0.001, `Expected 10, got ${result}`);
|
||||
});
|
||||
|
||||
test('SIMDOps.distance', () => {
|
||||
const a = new Float32Array([0, 0, 0]);
|
||||
const b = new Float32Array([3, 4, 0]);
|
||||
const result = SIMDOps.distance(a, b);
|
||||
assert(Math.abs(result - 5) < 0.001, `Expected 5, got ${result}`);
|
||||
});
|
||||
|
||||
log('\n' + '-'.repeat(40));
|
||||
log(`${c.bold}Results:${c.reset} ${c.green}${passed} passed${c.reset}, ${failed > 0 ? c.red : c.dim}${failed} failed${c.reset}`);
|
||||
|
||||
if (failed > 0) process.exit(1);
|
||||
success('\nAll tests passed!');
|
||||
}
|
||||
|
||||
async function runTrain(trainArgs) {
|
||||
// Parse arguments
|
||||
let layers = [25, 20, 4];
|
||||
let epochs = 5;
|
||||
|
||||
for (let i = 0; i < trainArgs.length; i++) {
|
||||
if (trainArgs[i] === '--layers' && trainArgs[i + 1]) {
|
||||
layers = trainArgs[i + 1].split(',').map(Number);
|
||||
i++;
|
||||
}
|
||||
if (trainArgs[i] === '--epochs' && trainArgs[i + 1]) {
|
||||
epochs = parseInt(trainArgs[i + 1]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
header(`Training SNN [${layers.join('-')}]`);
|
||||
|
||||
const snn = createFeedforwardSNN(layers, {
|
||||
dt: 1.0,
|
||||
tau: 20.0,
|
||||
a_plus: 0.005,
|
||||
a_minus: 0.005,
|
||||
lateral_inhibition: true
|
||||
});
|
||||
|
||||
const input_size = layers[0];
|
||||
log(`\n${c.bold}Configuration:${c.reset}`);
|
||||
log(` Layers: ${layers.join(' -> ')}`);
|
||||
log(` Epochs: ${epochs}`);
|
||||
log(` Learning: STDP (unsupervised)`);
|
||||
log(` Native SIMD: ${native ? 'enabled' : 'disabled'}\n`);
|
||||
|
||||
log(`${c.bold}Training:${c.reset}\n`);
|
||||
|
||||
for (let epoch = 0; epoch < epochs; epoch++) {
|
||||
let total_spikes = 0;
|
||||
|
||||
// Generate random patterns for each epoch
|
||||
for (let p = 0; p < 10; p++) {
|
||||
const pattern = new Float32Array(input_size).map(() => Math.random() > 0.5 ? 1 : 0);
|
||||
snn.reset();
|
||||
|
||||
for (let t = 0; t < 100; t++) {
|
||||
const input = rateEncoding(pattern, snn.dt, 100);
|
||||
total_spikes += snn.step(input);
|
||||
}
|
||||
}
|
||||
|
||||
const stats = snn.getStats();
|
||||
const w = stats.layers[0].synapses;
|
||||
log(` Epoch ${epoch + 1}/${epochs}: ${total_spikes} spikes, weights: mean=${w.mean.toFixed(3)}, range=[${w.min.toFixed(3)}, ${w.max.toFixed(3)}]`);
|
||||
}
|
||||
|
||||
success('\nTraining complete!');
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(`${c.red}Error:${c.reset}`, err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
27
vendor/ruvector/npm/packages/spiking-neural/binding.gyp
vendored
Normal file
27
vendor/ruvector/npm/packages/spiking-neural/binding.gyp
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "snn_simd",
|
||||
"sources": ["native/snn_simd.cpp"],
|
||||
"include_dirs": [
|
||||
"<!@(node -p \"require('node-addon-api').include\")"
|
||||
],
|
||||
"defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"],
|
||||
"cflags!": ["-fno-exceptions"],
|
||||
"cflags_cc!": ["-fno-exceptions"],
|
||||
"cflags_cc": ["-std=c++17", "-O3", "-msse4.1", "-mavx", "-ffast-math"],
|
||||
"xcode_settings": {
|
||||
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
|
||||
"CLANG_CXX_LIBRARY": "libc++",
|
||||
"MACOSX_DEPLOYMENT_TARGET": "10.15",
|
||||
"OTHER_CFLAGS": ["-msse4.1", "-mavx", "-O3"]
|
||||
},
|
||||
"msvs_settings": {
|
||||
"VCCLCompilerTool": {
|
||||
"ExceptionHandling": 1,
|
||||
"AdditionalOptions": ["/arch:AVX"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
69
vendor/ruvector/npm/packages/spiking-neural/examples/basic.js
vendored
Normal file
69
vendor/ruvector/npm/packages/spiking-neural/examples/basic.js
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Basic Spiking Neural Network Example
|
||||
*
|
||||
* Demonstrates the fundamental usage of the spiking-neural SDK.
|
||||
*/
|
||||
|
||||
const {
|
||||
createFeedforwardSNN,
|
||||
rateEncoding,
|
||||
native,
|
||||
version
|
||||
} = require('spiking-neural');
|
||||
|
||||
console.log(`\nSpiking Neural Network SDK v${version}`);
|
||||
console.log(`Native SIMD: ${native ? 'Enabled' : 'JavaScript fallback'}\n`);
|
||||
console.log('='.repeat(50));
|
||||
|
||||
// Create a 3-layer feedforward SNN
|
||||
const snn = createFeedforwardSNN([100, 50, 10], {
|
||||
dt: 1.0, // 1ms time step
|
||||
tau: 20.0, // 20ms membrane time constant
|
||||
a_plus: 0.005, // STDP LTP rate
|
||||
a_minus: 0.005, // STDP LTD rate
|
||||
lateral_inhibition: true,
|
||||
inhibition_strength: 10.0
|
||||
});
|
||||
|
||||
console.log('\nNetwork created: 100 -> 50 -> 10 neurons');
|
||||
console.log(`Total synapses: ${100 * 50 + 50 * 10}`);
|
||||
|
||||
// Create input pattern (random)
|
||||
const input_pattern = new Float32Array(100).map(() => Math.random());
|
||||
|
||||
console.log('\nRunning 100ms simulation...\n');
|
||||
|
||||
// Run for 100ms
|
||||
let total_spikes = 0;
|
||||
for (let t = 0; t < 100; t++) {
|
||||
// Encode input as spike train
|
||||
const spikes = rateEncoding(input_pattern, snn.dt, 100);
|
||||
total_spikes += snn.step(spikes);
|
||||
}
|
||||
|
||||
// Get network statistics
|
||||
const stats = snn.getStats();
|
||||
|
||||
console.log('Results:');
|
||||
console.log(` Simulation time: ${stats.time}ms`);
|
||||
console.log(` Total spikes: ${total_spikes}`);
|
||||
console.log(` Avg spikes/ms: ${(total_spikes / stats.time).toFixed(2)}`);
|
||||
|
||||
// Layer statistics
|
||||
console.log('\nLayer Statistics:');
|
||||
for (const layer of stats.layers) {
|
||||
if (layer.neurons) {
|
||||
console.log(` Layer ${layer.index}: ${layer.neurons.count} neurons, ${layer.neurons.spike_count} current spikes`);
|
||||
}
|
||||
if (layer.synapses) {
|
||||
console.log(` Weights: mean=${layer.synapses.mean.toFixed(3)}, range=[${layer.synapses.min.toFixed(3)}, ${layer.synapses.max.toFixed(3)}]`);
|
||||
}
|
||||
}
|
||||
|
||||
// Get final output
|
||||
const output = snn.getOutput();
|
||||
console.log('\nOutput layer activity:', Array.from(output).map(v => v.toFixed(2)).join(', '));
|
||||
|
||||
console.log('\nDone!\n');
|
||||
140
vendor/ruvector/npm/packages/spiking-neural/examples/benchmark.js
vendored
Normal file
140
vendor/ruvector/npm/packages/spiking-neural/examples/benchmark.js
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Spiking Neural Network Performance Benchmark
|
||||
*
|
||||
* Tests performance across different network sizes and configurations.
|
||||
*/
|
||||
|
||||
const {
|
||||
createFeedforwardSNN,
|
||||
rateEncoding,
|
||||
SIMDOps,
|
||||
native,
|
||||
version
|
||||
} = require('spiking-neural');
|
||||
|
||||
console.log(`\nSNN Performance Benchmark v${version}`);
|
||||
console.log(`Native SIMD: ${native ? 'Enabled (10-50x faster)' : 'JavaScript fallback'}\n`);
|
||||
console.log('='.repeat(60));
|
||||
|
||||
// Network scaling benchmark
|
||||
console.log('\n--- NETWORK SCALING ---\n');
|
||||
|
||||
const sizes = [100, 500, 1000, 2000];
|
||||
const iterations = 100;
|
||||
|
||||
console.log('Neurons | Time/Step | Spikes/Step | Steps/Sec');
|
||||
console.log('-'.repeat(50));
|
||||
|
||||
for (const size of sizes) {
|
||||
const snn = createFeedforwardSNN([size, Math.floor(size / 2), 10], {
|
||||
dt: 1.0,
|
||||
lateral_inhibition: true
|
||||
});
|
||||
|
||||
const input = new Float32Array(size).fill(0.5);
|
||||
|
||||
// Warmup
|
||||
for (let i = 0; i < 10; i++) {
|
||||
snn.step(rateEncoding(input, snn.dt, 100));
|
||||
}
|
||||
|
||||
// Benchmark
|
||||
const start = performance.now();
|
||||
let total_spikes = 0;
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
total_spikes += snn.step(rateEncoding(input, snn.dt, 100));
|
||||
}
|
||||
const elapsed = performance.now() - start;
|
||||
|
||||
const time_per_step = elapsed / iterations;
|
||||
const spikes_per_step = total_spikes / iterations;
|
||||
const steps_per_sec = Math.round(1000 / time_per_step);
|
||||
|
||||
console.log(`${size.toString().padStart(7)} | ${time_per_step.toFixed(3).padStart(9)}ms | ${spikes_per_step.toFixed(1).padStart(11)} | ${steps_per_sec.toString().padStart(9)}`);
|
||||
}
|
||||
|
||||
// SIMD vector operations
|
||||
console.log('\n--- SIMD VECTOR OPERATIONS ---\n');
|
||||
|
||||
const dimensions = [64, 128, 256, 512];
|
||||
const vecIterations = 10000;
|
||||
|
||||
console.log('Dimension | Naive (ms) | SIMD (ms) | Speedup');
|
||||
console.log('-'.repeat(50));
|
||||
|
||||
for (const dim of dimensions) {
|
||||
const a = new Float32Array(dim).map(() => Math.random());
|
||||
const b = new Float32Array(dim).map(() => Math.random());
|
||||
|
||||
// Naive dot product
|
||||
let start = performance.now();
|
||||
for (let i = 0; i < vecIterations; i++) {
|
||||
let sum = 0;
|
||||
for (let j = 0; j < dim; j++) sum += a[j] * b[j];
|
||||
}
|
||||
const naiveTime = performance.now() - start;
|
||||
|
||||
// SIMD dot product
|
||||
start = performance.now();
|
||||
for (let i = 0; i < vecIterations; i++) {
|
||||
SIMDOps.dotProduct(a, b);
|
||||
}
|
||||
const simdTime = performance.now() - start;
|
||||
|
||||
const speedup = naiveTime / simdTime;
|
||||
console.log(`${dim.toString().padStart(9)} | ${naiveTime.toFixed(2).padStart(10)} | ${simdTime.toFixed(2).padStart(9)} | ${speedup.toFixed(2)}x`);
|
||||
}
|
||||
|
||||
// Distance benchmark
|
||||
console.log('\n--- EUCLIDEAN DISTANCE ---\n');
|
||||
|
||||
console.log('Dimension | Naive (ms) | SIMD (ms) | Speedup');
|
||||
console.log('-'.repeat(50));
|
||||
|
||||
for (const dim of dimensions) {
|
||||
const a = new Float32Array(dim).map(() => Math.random());
|
||||
const b = new Float32Array(dim).map(() => Math.random());
|
||||
|
||||
// Naive
|
||||
let start = performance.now();
|
||||
for (let i = 0; i < vecIterations; i++) {
|
||||
let sum = 0;
|
||||
for (let j = 0; j < dim; j++) {
|
||||
const d = a[j] - b[j];
|
||||
sum += d * d;
|
||||
}
|
||||
Math.sqrt(sum);
|
||||
}
|
||||
const naiveTime = performance.now() - start;
|
||||
|
||||
// SIMD
|
||||
start = performance.now();
|
||||
for (let i = 0; i < vecIterations; i++) {
|
||||
SIMDOps.distance(a, b);
|
||||
}
|
||||
const simdTime = performance.now() - start;
|
||||
|
||||
const speedup = naiveTime / simdTime;
|
||||
console.log(`${dim.toString().padStart(9)} | ${naiveTime.toFixed(2).padStart(10)} | ${simdTime.toFixed(2).padStart(9)} | ${speedup.toFixed(2)}x`);
|
||||
}
|
||||
|
||||
// Memory usage
|
||||
console.log('\n--- MEMORY USAGE ---\n');
|
||||
|
||||
const memBefore = process.memoryUsage().heapUsed;
|
||||
const largeSnn = createFeedforwardSNN([1000, 500, 100], {});
|
||||
const memAfter = process.memoryUsage().heapUsed;
|
||||
const memUsed = (memAfter - memBefore) / 1024 / 1024;
|
||||
|
||||
console.log(`1000-500-100 network: ${memUsed.toFixed(2)} MB`);
|
||||
console.log(`Per neuron: ${(memUsed * 1024 / 1600).toFixed(2)} KB`);
|
||||
|
||||
console.log('\n--- SUMMARY ---\n');
|
||||
console.log('Key findings:');
|
||||
console.log(' - Larger networks have better amortized overhead');
|
||||
console.log(' - SIMD provides 1.2-2x speedup for vector ops');
|
||||
console.log(` - Native addon: ${native ? '10-50x faster (enabled)' : 'not built (run npm run build:native)'}`);
|
||||
|
||||
console.log('\nBenchmark complete!\n');
|
||||
171
vendor/ruvector/npm/packages/spiking-neural/examples/pattern-recognition.js
vendored
Normal file
171
vendor/ruvector/npm/packages/spiking-neural/examples/pattern-recognition.js
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Pattern Recognition with Spiking Neural Networks
|
||||
*
|
||||
* This example demonstrates:
|
||||
* - Rate-coded input encoding
|
||||
* - STDP learning (unsupervised)
|
||||
* - Pattern classification
|
||||
* - Lateral inhibition for winner-take-all
|
||||
*/
|
||||
|
||||
const {
|
||||
createFeedforwardSNN,
|
||||
rateEncoding,
|
||||
native,
|
||||
version
|
||||
} = require('spiking-neural');
|
||||
|
||||
console.log(`\nPattern Recognition with SNNs v${version}`);
|
||||
console.log(`Native SIMD: ${native ? 'Enabled' : 'JavaScript fallback'}\n`);
|
||||
console.log('='.repeat(60));
|
||||
|
||||
// Define 5x5 patterns
|
||||
const patterns = {
|
||||
'Cross': [
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0
|
||||
],
|
||||
'Square': [
|
||||
1, 1, 1, 1, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 1
|
||||
],
|
||||
'Diagonal': [
|
||||
1, 0, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 1
|
||||
],
|
||||
'X-Shape': [
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 0, 1, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 1, 0, 1, 0,
|
||||
1, 0, 0, 0, 1
|
||||
]
|
||||
};
|
||||
|
||||
// Visualize patterns
|
||||
console.log('\nPatterns:\n');
|
||||
for (const [name, pattern] of Object.entries(patterns)) {
|
||||
console.log(`${name}:`);
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const row = pattern.slice(i * 5, (i + 1) * 5).map(v => v ? '##' : ' ').join('');
|
||||
console.log(` ${row}`);
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
|
||||
// Create SNN
|
||||
const n_input = 25; // 5x5 pixels
|
||||
const n_hidden = 20; // Hidden layer
|
||||
const n_output = 4; // 4 pattern classes
|
||||
|
||||
const snn = createFeedforwardSNN([n_input, n_hidden, n_output], {
|
||||
dt: 1.0,
|
||||
tau: 20.0,
|
||||
v_thresh: -50.0,
|
||||
v_reset: -70.0,
|
||||
a_plus: 0.005,
|
||||
a_minus: 0.005,
|
||||
init_weight: 0.3,
|
||||
init_std: 0.1,
|
||||
lateral_inhibition: true,
|
||||
inhibition_strength: 15.0
|
||||
});
|
||||
|
||||
console.log(`Network: ${n_input}-${n_hidden}-${n_output} (${n_input * n_hidden + n_hidden * n_output} synapses)`);
|
||||
console.log(`Learning: STDP (unsupervised)`);
|
||||
|
||||
// Training
|
||||
console.log('\n--- TRAINING ---\n');
|
||||
|
||||
const n_epochs = 5;
|
||||
const presentation_time = 100;
|
||||
const pattern_names = Object.keys(patterns);
|
||||
const pattern_arrays = Object.values(patterns);
|
||||
|
||||
for (let epoch = 0; epoch < n_epochs; epoch++) {
|
||||
let total_spikes = 0;
|
||||
|
||||
for (let p = 0; p < pattern_names.length; p++) {
|
||||
const pattern = pattern_arrays[p];
|
||||
snn.reset();
|
||||
|
||||
for (let t = 0; t < presentation_time; t++) {
|
||||
const input_spikes = rateEncoding(pattern, snn.dt, 100);
|
||||
total_spikes += snn.step(input_spikes);
|
||||
}
|
||||
}
|
||||
|
||||
const stats = snn.getStats();
|
||||
const w = stats.layers[0].synapses;
|
||||
console.log(`Epoch ${epoch + 1}/${n_epochs}: ${total_spikes} spikes, weights: mean=${w.mean.toFixed(3)}`);
|
||||
}
|
||||
|
||||
// Testing
|
||||
console.log('\n--- TESTING ---\n');
|
||||
|
||||
const results = [];
|
||||
for (let p = 0; p < pattern_names.length; p++) {
|
||||
const pattern = pattern_arrays[p];
|
||||
snn.reset();
|
||||
|
||||
const output_activity = new Float32Array(n_output);
|
||||
|
||||
for (let t = 0; t < presentation_time; t++) {
|
||||
const input_spikes = rateEncoding(pattern, snn.dt, 100);
|
||||
snn.step(input_spikes);
|
||||
|
||||
const output = snn.getOutput();
|
||||
for (let i = 0; i < n_output; i++) {
|
||||
output_activity[i] += output[i];
|
||||
}
|
||||
}
|
||||
|
||||
const winner = Array.from(output_activity).indexOf(Math.max(...output_activity));
|
||||
const total = output_activity.reduce((a, b) => a + b, 0);
|
||||
const confidence = total > 0 ? (output_activity[winner] / total * 100) : 0;
|
||||
|
||||
results.push({ pattern: pattern_names[p], winner, confidence });
|
||||
console.log(`${pattern_names[p].padEnd(10)} -> Neuron ${winner} (${confidence.toFixed(1)}% confidence)`);
|
||||
}
|
||||
|
||||
// Noise test
|
||||
console.log('\n--- ROBUSTNESS (20% noise) ---\n');
|
||||
|
||||
function addNoise(pattern, noise_level = 0.2) {
|
||||
return pattern.map(v => Math.random() < noise_level ? 1 - v : v);
|
||||
}
|
||||
|
||||
for (let p = 0; p < pattern_names.length; p++) {
|
||||
const noisy_pattern = addNoise(pattern_arrays[p], 0.2);
|
||||
snn.reset();
|
||||
|
||||
const output_activity = new Float32Array(n_output);
|
||||
|
||||
for (let t = 0; t < presentation_time; t++) {
|
||||
const input_spikes = rateEncoding(noisy_pattern, snn.dt, 100);
|
||||
snn.step(input_spikes);
|
||||
|
||||
const output = snn.getOutput();
|
||||
for (let i = 0; i < n_output; i++) {
|
||||
output_activity[i] += output[i];
|
||||
}
|
||||
}
|
||||
|
||||
const winner = Array.from(output_activity).indexOf(Math.max(...output_activity));
|
||||
const correct = winner === results[p].winner;
|
||||
|
||||
console.log(`${pattern_names[p].padEnd(10)} -> Neuron ${winner} ${correct ? '✓' : '✗'}`);
|
||||
}
|
||||
|
||||
console.log('\nDone!\n');
|
||||
546
vendor/ruvector/npm/packages/spiking-neural/native/snn_simd.cpp
vendored
Normal file
546
vendor/ruvector/npm/packages/spiking-neural/native/snn_simd.cpp
vendored
Normal file
@@ -0,0 +1,546 @@
|
||||
/**
|
||||
* SIMD-Optimized Spiking Neural Network - N-API Implementation
|
||||
*
|
||||
* State-of-the-art SNN with:
|
||||
* - Leaky Integrate-and-Fire (LIF) neurons
|
||||
* - STDP (Spike-Timing-Dependent Plasticity) learning
|
||||
* - SIMD-accelerated membrane potential updates
|
||||
* - Lateral inhibition
|
||||
* - Homeostatic plasticity
|
||||
*
|
||||
* Performance: 10-50x faster than pure JavaScript
|
||||
*/
|
||||
|
||||
#include <node_api.h>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <immintrin.h> // SSE/AVX intrinsics
|
||||
|
||||
// ============================================================================
|
||||
// SIMD Utilities
|
||||
// ============================================================================
|
||||
|
||||
// Check if pointer is 16-byte aligned for SIMD
|
||||
inline bool is_aligned(const void* ptr, size_t alignment = 16) {
|
||||
return (reinterpret_cast<uintptr_t>(ptr) % alignment) == 0;
|
||||
}
|
||||
|
||||
// Align size to SIMD boundary (multiples of 4 for SSE)
|
||||
inline size_t align_size(size_t size) {
|
||||
return (size + 3) & ~3;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Leaky Integrate-and-Fire (LIF) Neuron Model - SIMD Optimized
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Update membrane potentials for a batch of neurons using SIMD
|
||||
*
|
||||
* dV/dt = (-(V - V_rest) + R * I) / tau
|
||||
*
|
||||
* @param voltages Current membrane potentials (V)
|
||||
* @param currents Synaptic currents (I)
|
||||
* @param n_neurons Number of neurons
|
||||
* @param dt Time step (ms)
|
||||
* @param tau Membrane time constant (ms)
|
||||
* @param v_rest Resting potential (mV)
|
||||
* @param resistance Membrane resistance (MOhm)
|
||||
*/
|
||||
void lif_update_simd(
|
||||
float* voltages,
|
||||
const float* currents,
|
||||
size_t n_neurons,
|
||||
float dt,
|
||||
float tau,
|
||||
float v_rest,
|
||||
float resistance
|
||||
) {
|
||||
const size_t n_simd = n_neurons / 4;
|
||||
const size_t n_remainder = n_neurons % 4;
|
||||
|
||||
// SIMD constants
|
||||
const __m128 dt_vec = _mm_set1_ps(dt);
|
||||
const __m128 tau_vec = _mm_set1_ps(tau);
|
||||
const __m128 v_rest_vec = _mm_set1_ps(v_rest);
|
||||
const __m128 r_vec = _mm_set1_ps(resistance);
|
||||
const __m128 decay_vec = _mm_set1_ps(dt / tau);
|
||||
|
||||
// Process 4 neurons at a time with SIMD
|
||||
for (size_t i = 0; i < n_simd; i++) {
|
||||
size_t idx = i * 4;
|
||||
|
||||
// Load 4 voltages and currents
|
||||
__m128 v = _mm_loadu_ps(&voltages[idx]);
|
||||
__m128 i = _mm_loadu_ps(¤ts[idx]);
|
||||
|
||||
// dV = (-(V - V_rest) + R * I) * dt / tau
|
||||
__m128 v_diff = _mm_sub_ps(v, v_rest_vec); // V - V_rest
|
||||
__m128 leak = _mm_mul_ps(v_diff, decay_vec); // leak term
|
||||
__m128 input = _mm_mul_ps(i, r_vec); // R * I
|
||||
__m128 input_scaled = _mm_mul_ps(input, decay_vec); // scale by dt/tau
|
||||
|
||||
// V_new = V - leak + input
|
||||
v = _mm_sub_ps(v, leak);
|
||||
v = _mm_add_ps(v, input_scaled);
|
||||
|
||||
// Store results
|
||||
_mm_storeu_ps(&voltages[idx], v);
|
||||
}
|
||||
|
||||
// Handle remaining neurons (scalar)
|
||||
for (size_t i = n_simd * 4; i < n_neurons; i++) {
|
||||
float dv = (-(voltages[i] - v_rest) + resistance * currents[i]) * dt / tau;
|
||||
voltages[i] += dv;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect spikes and reset neurons - SIMD optimized
|
||||
*
|
||||
* @param voltages Membrane potentials
|
||||
* @param spikes Output spike indicators (1 if spiked, 0 otherwise)
|
||||
* @param n_neurons Number of neurons
|
||||
* @param threshold Spike threshold (mV)
|
||||
* @param v_reset Reset potential (mV)
|
||||
* @return Number of spikes detected
|
||||
*/
|
||||
size_t detect_spikes_simd(
|
||||
float* voltages,
|
||||
float* spikes,
|
||||
size_t n_neurons,
|
||||
float threshold,
|
||||
float v_reset
|
||||
) {
|
||||
size_t spike_count = 0;
|
||||
const size_t n_simd = n_neurons / 4;
|
||||
const size_t n_remainder = n_neurons % 4;
|
||||
|
||||
const __m128 thresh_vec = _mm_set1_ps(threshold);
|
||||
const __m128 reset_vec = _mm_set1_ps(v_reset);
|
||||
const __m128 one_vec = _mm_set1_ps(1.0f);
|
||||
const __m128 zero_vec = _mm_set1_ps(0.0f);
|
||||
|
||||
// Process 4 neurons at a time
|
||||
for (size_t i = 0; i < n_simd; i++) {
|
||||
size_t idx = i * 4;
|
||||
|
||||
__m128 v = _mm_loadu_ps(&voltages[idx]);
|
||||
|
||||
// Compare: spike if v >= threshold
|
||||
__m128 mask = _mm_cmpge_ps(v, thresh_vec);
|
||||
|
||||
// Set spike indicators
|
||||
__m128 spike_vec = _mm_and_ps(mask, one_vec);
|
||||
_mm_storeu_ps(&spikes[idx], spike_vec);
|
||||
|
||||
// Reset spiked neurons
|
||||
v = _mm_blendv_ps(v, reset_vec, mask);
|
||||
_mm_storeu_ps(&voltages[idx], v);
|
||||
|
||||
// Count spikes (check each element in mask)
|
||||
int spike_mask = _mm_movemask_ps(mask);
|
||||
spike_count += __builtin_popcount(spike_mask);
|
||||
}
|
||||
|
||||
// Handle remaining neurons
|
||||
for (size_t i = n_simd * 4; i < n_neurons; i++) {
|
||||
if (voltages[i] >= threshold) {
|
||||
spikes[i] = 1.0f;
|
||||
voltages[i] = v_reset;
|
||||
spike_count++;
|
||||
} else {
|
||||
spikes[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return spike_count;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Synaptic Current Computation - SIMD Optimized
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Compute synaptic currents from spikes and weights
|
||||
*
|
||||
* I_j = sum_i(w_ij * s_i)
|
||||
*
|
||||
* @param currents Output currents (post-synaptic)
|
||||
* @param spikes Input spikes (pre-synaptic)
|
||||
* @param weights Weight matrix [n_post x n_pre]
|
||||
* @param n_pre Number of pre-synaptic neurons
|
||||
* @param n_post Number of post-synaptic neurons
|
||||
*/
|
||||
void compute_currents_simd(
|
||||
float* currents,
|
||||
const float* spikes,
|
||||
const float* weights,
|
||||
size_t n_pre,
|
||||
size_t n_post
|
||||
) {
|
||||
// Zero out currents
|
||||
memset(currents, 0, n_post * sizeof(float));
|
||||
|
||||
// For each post-synaptic neuron
|
||||
for (size_t j = 0; j < n_post; j++) {
|
||||
const float* w_row = &weights[j * n_pre];
|
||||
|
||||
size_t n_simd = n_pre / 4;
|
||||
__m128 sum_vec = _mm_setzero_ps();
|
||||
|
||||
// SIMD: sum 4 synapses at a time
|
||||
for (size_t i = 0; i < n_simd; i++) {
|
||||
size_t idx = i * 4;
|
||||
__m128 s = _mm_loadu_ps(&spikes[idx]);
|
||||
__m128 w = _mm_loadu_ps(&w_row[idx]);
|
||||
__m128 product = _mm_mul_ps(s, w);
|
||||
sum_vec = _mm_add_ps(sum_vec, product);
|
||||
}
|
||||
|
||||
// Horizontal sum of SIMD vector
|
||||
float sum_array[4];
|
||||
_mm_storeu_ps(sum_array, sum_vec);
|
||||
float sum = sum_array[0] + sum_array[1] + sum_array[2] + sum_array[3];
|
||||
|
||||
// Handle remainder
|
||||
for (size_t i = n_simd * 4; i < n_pre; i++) {
|
||||
sum += spikes[i] * w_row[i];
|
||||
}
|
||||
|
||||
currents[j] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// STDP (Spike-Timing-Dependent Plasticity) - SIMD Optimized
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Update synaptic weights using STDP learning rule
|
||||
*
|
||||
* If pre-synaptic spike before post: Δw = A+ * exp(-Δt / tau+) (LTP)
|
||||
* If post-synaptic spike before pre: Δw = -A- * exp(-Δt / tau-) (LTD)
|
||||
*
|
||||
* @param weights Weight matrix [n_post x n_pre]
|
||||
* @param pre_spikes Pre-synaptic spikes
|
||||
* @param post_spikes Post-synaptic spikes
|
||||
* @param pre_trace Pre-synaptic trace
|
||||
* @param post_trace Post-synaptic trace
|
||||
* @param n_pre Number of pre-synaptic neurons
|
||||
* @param n_post Number of post-synaptic neurons
|
||||
* @param a_plus LTP amplitude
|
||||
* @param a_minus LTD amplitude
|
||||
* @param w_min Minimum weight
|
||||
* @param w_max Maximum weight
|
||||
*/
|
||||
void stdp_update_simd(
|
||||
float* weights,
|
||||
const float* pre_spikes,
|
||||
const float* post_spikes,
|
||||
const float* pre_trace,
|
||||
const float* post_trace,
|
||||
size_t n_pre,
|
||||
size_t n_post,
|
||||
float a_plus,
|
||||
float a_minus,
|
||||
float w_min,
|
||||
float w_max
|
||||
) {
|
||||
const __m128 a_plus_vec = _mm_set1_ps(a_plus);
|
||||
const __m128 a_minus_vec = _mm_set1_ps(a_minus);
|
||||
const __m128 w_min_vec = _mm_set1_ps(w_min);
|
||||
const __m128 w_max_vec = _mm_set1_ps(w_max);
|
||||
|
||||
// For each post-synaptic neuron
|
||||
for (size_t j = 0; j < n_post; j++) {
|
||||
float* w_row = &weights[j * n_pre];
|
||||
float post_spike = post_spikes[j];
|
||||
float post_tr = post_trace[j];
|
||||
|
||||
__m128 post_spike_vec = _mm_set1_ps(post_spike);
|
||||
__m128 post_tr_vec = _mm_set1_ps(post_tr);
|
||||
|
||||
size_t n_simd = n_pre / 4;
|
||||
|
||||
// Process 4 synapses at a time
|
||||
for (size_t i = 0; i < n_simd; i++) {
|
||||
size_t idx = i * 4;
|
||||
|
||||
__m128 w = _mm_loadu_ps(&w_row[idx]);
|
||||
__m128 pre_spike = _mm_loadu_ps(&pre_spikes[idx]);
|
||||
__m128 pre_tr = _mm_loadu_ps(&pre_trace[idx]);
|
||||
|
||||
// LTP: pre spike occurred, strengthen based on post trace
|
||||
__m128 ltp = _mm_mul_ps(pre_spike, post_tr_vec);
|
||||
ltp = _mm_mul_ps(ltp, a_plus_vec);
|
||||
|
||||
// LTD: post spike occurred, weaken based on pre trace
|
||||
__m128 ltd = _mm_mul_ps(post_spike_vec, pre_tr);
|
||||
ltd = _mm_mul_ps(ltd, a_minus_vec);
|
||||
|
||||
// Update weight
|
||||
w = _mm_add_ps(w, ltp);
|
||||
w = _mm_sub_ps(w, ltd);
|
||||
|
||||
// Clamp weights
|
||||
w = _mm_max_ps(w, w_min_vec);
|
||||
w = _mm_min_ps(w, w_max_vec);
|
||||
|
||||
_mm_storeu_ps(&w_row[idx], w);
|
||||
}
|
||||
|
||||
// Handle remainder
|
||||
for (size_t i = n_simd * 4; i < n_pre; i++) {
|
||||
float ltp = pre_spikes[i] * post_tr * a_plus;
|
||||
float ltd = post_spike * pre_trace[i] * a_minus;
|
||||
w_row[i] += ltp - ltd;
|
||||
w_row[i] = std::max(w_min, std::min(w_max, w_row[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update spike traces (exponential decay)
|
||||
*
|
||||
* trace(t) = trace(t-1) * exp(-dt/tau) + spike(t)
|
||||
*
|
||||
* @param traces Spike traces to update
|
||||
* @param spikes Current spikes
|
||||
* @param n_neurons Number of neurons
|
||||
* @param decay Decay factor (exp(-dt/tau))
|
||||
*/
|
||||
void update_traces_simd(
|
||||
float* traces,
|
||||
const float* spikes,
|
||||
size_t n_neurons,
|
||||
float decay
|
||||
) {
|
||||
const size_t n_simd = n_neurons / 4;
|
||||
const __m128 decay_vec = _mm_set1_ps(decay);
|
||||
|
||||
for (size_t i = 0; i < n_simd; i++) {
|
||||
size_t idx = i * 4;
|
||||
__m128 tr = _mm_loadu_ps(&traces[idx]);
|
||||
__m128 sp = _mm_loadu_ps(&spikes[idx]);
|
||||
|
||||
// trace = trace * decay + spike
|
||||
tr = _mm_mul_ps(tr, decay_vec);
|
||||
tr = _mm_add_ps(tr, sp);
|
||||
|
||||
_mm_storeu_ps(&traces[idx], tr);
|
||||
}
|
||||
|
||||
// Remainder
|
||||
for (size_t i = n_simd * 4; i < n_neurons; i++) {
|
||||
traces[i] = traces[i] * decay + spikes[i];
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Lateral Inhibition - SIMD Optimized
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Apply lateral inhibition: Winner-take-all among nearby neurons
|
||||
*
|
||||
* @param voltages Membrane potentials
|
||||
* @param spikes Recent spikes
|
||||
* @param n_neurons Number of neurons
|
||||
* @param inhibition_strength How much to suppress neighbors
|
||||
*/
|
||||
void lateral_inhibition_simd(
|
||||
float* voltages,
|
||||
const float* spikes,
|
||||
size_t n_neurons,
|
||||
float inhibition_strength
|
||||
) {
|
||||
// Find neurons that spiked
|
||||
for (size_t i = 0; i < n_neurons; i++) {
|
||||
if (spikes[i] > 0.5f) {
|
||||
// Inhibit nearby neurons (simple: all others)
|
||||
const __m128 inhib_vec = _mm_set1_ps(-inhibition_strength);
|
||||
const __m128 self_vec = _mm_set1_ps((float)i);
|
||||
|
||||
size_t n_simd = n_neurons / 4;
|
||||
for (size_t j = 0; j < n_simd; j++) {
|
||||
size_t idx = j * 4;
|
||||
|
||||
// Don't inhibit self
|
||||
float indices[4] = {(float)idx, (float)(idx+1), (float)(idx+2), (float)(idx+3)};
|
||||
__m128 idx_vec = _mm_loadu_ps(indices);
|
||||
__m128 mask = _mm_cmpneq_ps(idx_vec, self_vec);
|
||||
|
||||
__m128 v = _mm_loadu_ps(&voltages[idx]);
|
||||
__m128 inhib = _mm_and_ps(inhib_vec, mask);
|
||||
v = _mm_add_ps(v, inhib);
|
||||
_mm_storeu_ps(&voltages[idx], v);
|
||||
}
|
||||
|
||||
// Remainder
|
||||
for (size_t j = n_simd * 4; j < n_neurons; j++) {
|
||||
if (j != i) {
|
||||
voltages[j] -= inhibition_strength;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// N-API Wrapper Functions
|
||||
// ============================================================================
|
||||
|
||||
// Helper: Get float array from JS TypedArray
|
||||
float* get_float_array(napi_env env, napi_value value, size_t* length) {
|
||||
napi_typedarray_type type;
|
||||
size_t len;
|
||||
void* data;
|
||||
napi_value arraybuffer;
|
||||
size_t byte_offset;
|
||||
|
||||
napi_get_typedarray_info(env, value, &type, &len, &data, &arraybuffer, &byte_offset);
|
||||
|
||||
if (length) *length = len;
|
||||
return static_cast<float*>(data);
|
||||
}
|
||||
|
||||
// N-API: LIF Update
|
||||
napi_value LIFUpdate(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 7;
|
||||
napi_value args[7];
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
size_t n_neurons;
|
||||
float* voltages = get_float_array(env, args[0], &n_neurons);
|
||||
float* currents = get_float_array(env, args[1], nullptr);
|
||||
|
||||
double dt, tau, v_rest, resistance;
|
||||
napi_get_value_double(env, args[2], &dt);
|
||||
napi_get_value_double(env, args[3], &tau);
|
||||
napi_get_value_double(env, args[4], &v_rest);
|
||||
napi_get_value_double(env, args[5], &resistance);
|
||||
|
||||
lif_update_simd(voltages, currents, n_neurons, dt, tau, v_rest, resistance);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// N-API: Detect Spikes
|
||||
napi_value DetectSpikes(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 4;
|
||||
napi_value args[4];
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
size_t n_neurons;
|
||||
float* voltages = get_float_array(env, args[0], &n_neurons);
|
||||
float* spikes = get_float_array(env, args[1], nullptr);
|
||||
|
||||
double threshold, v_reset;
|
||||
napi_get_value_double(env, args[2], &threshold);
|
||||
napi_get_value_double(env, args[3], &v_reset);
|
||||
|
||||
size_t count = detect_spikes_simd(voltages, spikes, n_neurons, threshold, v_reset);
|
||||
|
||||
napi_value result;
|
||||
napi_create_uint32(env, count, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// N-API: Compute Currents
|
||||
napi_value ComputeCurrents(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 3;
|
||||
napi_value args[3];
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
size_t n_post, n_pre;
|
||||
float* currents = get_float_array(env, args[0], &n_post);
|
||||
float* spikes = get_float_array(env, args[1], &n_pre);
|
||||
float* weights = get_float_array(env, args[2], nullptr);
|
||||
|
||||
compute_currents_simd(currents, spikes, weights, n_pre, n_post);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// N-API: STDP Update
|
||||
napi_value STDPUpdate(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 9;
|
||||
napi_value args[9];
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
size_t n_weights, n_pre, n_post;
|
||||
float* weights = get_float_array(env, args[0], &n_weights);
|
||||
float* pre_spikes = get_float_array(env, args[1], &n_pre);
|
||||
float* post_spikes = get_float_array(env, args[2], &n_post);
|
||||
float* pre_trace = get_float_array(env, args[3], nullptr);
|
||||
float* post_trace = get_float_array(env, args[4], nullptr);
|
||||
|
||||
double a_plus, a_minus, w_min, w_max;
|
||||
napi_get_value_double(env, args[5], &a_plus);
|
||||
napi_get_value_double(env, args[6], &a_minus);
|
||||
napi_get_value_double(env, args[7], &w_min);
|
||||
napi_get_value_double(env, args[8], &w_max);
|
||||
|
||||
stdp_update_simd(weights, pre_spikes, post_spikes, pre_trace, post_trace,
|
||||
n_pre, n_post, a_plus, a_minus, w_min, w_max);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// N-API: Update Traces
|
||||
napi_value UpdateTraces(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 3;
|
||||
napi_value args[3];
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
size_t n_neurons;
|
||||
float* traces = get_float_array(env, args[0], &n_neurons);
|
||||
float* spikes = get_float_array(env, args[1], nullptr);
|
||||
|
||||
double decay;
|
||||
napi_get_value_double(env, args[2], &decay);
|
||||
|
||||
update_traces_simd(traces, spikes, n_neurons, decay);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// N-API: Lateral Inhibition
|
||||
napi_value LateralInhibition(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 3;
|
||||
napi_value args[3];
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
size_t n_neurons;
|
||||
float* voltages = get_float_array(env, args[0], &n_neurons);
|
||||
float* spikes = get_float_array(env, args[1], nullptr);
|
||||
|
||||
double strength;
|
||||
napi_get_value_double(env, args[2], &strength);
|
||||
|
||||
lateral_inhibition_simd(voltages, spikes, n_neurons, strength);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Module Initialization
|
||||
// ============================================================================
|
||||
|
||||
napi_value Init(napi_env env, napi_value exports) {
|
||||
napi_property_descriptor desc[] = {
|
||||
{"lifUpdate", nullptr, LIFUpdate, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"detectSpikes", nullptr, DetectSpikes, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"computeCurrents", nullptr, ComputeCurrents, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"stdpUpdate", nullptr, STDPUpdate, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"updateTraces", nullptr, UpdateTraces, nullptr, nullptr, nullptr, napi_default, nullptr},
|
||||
{"lateralInhibition", nullptr, LateralInhibition, nullptr, nullptr, nullptr, napi_default, nullptr}
|
||||
};
|
||||
|
||||
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
|
||||
return exports;
|
||||
}
|
||||
|
||||
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
|
||||
56
vendor/ruvector/npm/packages/spiking-neural/package.json
vendored
Normal file
56
vendor/ruvector/npm/packages/spiking-neural/package.json
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "@ruvector/spiking-neural",
|
||||
"version": "1.0.1",
|
||||
"description": "High-performance Spiking Neural Network (SNN) with SIMD optimization - CLI & SDK",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
"spiking-neural": "./bin/cli.js",
|
||||
"snn": "./bin/cli.js"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node bin/cli.js test",
|
||||
"benchmark": "node bin/cli.js benchmark",
|
||||
"demo": "node bin/cli.js demo pattern",
|
||||
"build:native": "node-gyp rebuild",
|
||||
"prepublishOnly": "npm test"
|
||||
},
|
||||
"keywords": [
|
||||
"spiking-neural-network",
|
||||
"snn",
|
||||
"neural-network",
|
||||
"neuromorphic",
|
||||
"simd",
|
||||
"machine-learning",
|
||||
"ai",
|
||||
"deep-learning",
|
||||
"stdp",
|
||||
"lif-neuron",
|
||||
"biologically-inspired",
|
||||
"pattern-recognition"
|
||||
],
|
||||
"author": "rUv <ruv@ruv.net>",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ruvnet/ruvector.git"
|
||||
},
|
||||
"homepage": "https://ruv.io",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ruvnet/ruvector/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"files": [
|
||||
"src/",
|
||||
"bin/",
|
||||
"examples/",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"node-gyp": "^10.0.0",
|
||||
"node-addon-api": "^7.0.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user