@buley/neural 2.0.3 → 3.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buley/neural",
3
- "version": "2.0.3",
3
+ "version": "3.0.0",
4
4
  "description": "A Transparent, Local-First, WebGPU-Accelerated Neural Graph Database.",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
package/src/engine/gpu.ts CHANGED
@@ -112,7 +112,7 @@ export class GPUEngine {
112
112
  }
113
113
 
114
114
  // Upload Input
115
- this.device.queue.writeBuffer(this.inputBuffer, 0, inputs as unknown as BufferSource);
115
+ this.device.queue.writeBuffer(this.inputBuffer, 0, inputs as BufferSource);
116
116
 
117
117
  // Encode Command
118
118
  const commandEncoder = this.device.createCommandEncoder();
@@ -213,7 +213,7 @@ export class GPUEngine {
213
213
  // Let's assume prepareTrainingBuffers was called ONCE before loop.
214
214
  // We just need to update TARGETS buffer!
215
215
  if (this.targetBuffer) {
216
- this.device?.queue.writeBuffer(this.targetBuffer, 0, targets as unknown as BufferSource);
216
+ this.device?.queue.writeBuffer(this.targetBuffer, 0, targets as BufferSource);
217
217
  }
218
218
 
219
219
  // Run Training Shaders
@@ -229,7 +229,7 @@ export class GPUEngine {
229
229
  }
230
230
 
231
231
  if (deltas && deltas.length > 0) {
232
- this.device.queue.writeBuffer(this.deltaBuffer, 0, deltas as unknown as BufferSource);
232
+ this.device.queue.writeBuffer(this.deltaBuffer, 0, deltas as BufferSource);
233
233
  }
234
234
 
235
235
  const commandEncoder = this.device.createCommandEncoder();
@@ -258,7 +258,7 @@ export class GPUEngine {
258
258
 
259
259
  // We only write what we are given, usually just the first N inputs (Microphone bins)
260
260
  // If data is smaller than buffer, we use queue.writeBuffer which handles partial writes
261
- this.device.queue.writeBuffer(this.inputBuffer, 0, data as unknown as BufferSource);
261
+ this.device.queue.writeBuffer(this.inputBuffer, 0, data as BufferSource);
262
262
 
263
263
  // Trigger a tick? Or let the outer loop do it?
264
264
  // Let's just update the buffer. The UI loop calls runTick() or similar.
@@ -0,0 +1,118 @@
1
+
2
+ /// <reference path="../webnn-types.d.ts" />
3
+
4
+ export class WebNNEngine {
5
+ context: MLContext | null = null;
6
+ builder: MLGraphBuilder | null = null;
7
+ graph: MLGraph | null = null;
8
+
9
+ // Buffers/State
10
+ networkSize: number = 0;
11
+ batchSize: number = 1;
12
+
13
+ // We keep weights/biases in memory to rebuild graph if needed,
14
+ // though for strict WebNN we bakw them into constants.
15
+ weights: Float32Array | null = null;
16
+ biases: Float32Array | null = null;
17
+
18
+ isReady = false;
19
+
20
+ async init() {
21
+ if (!navigator.ml) {
22
+ console.warn("WebNN: navigator.ml not supported");
23
+ return;
24
+ }
25
+
26
+ try {
27
+ // Prefer NPU, fallback to GPU if NPU not available, though we really want NPU for "cool factor"
28
+ // Note: browser support for 'npu' deviceType is bleeding edge.
29
+ this.context = await navigator.ml.createContext({ deviceType: 'npu', powerPreference: 'low-power' });
30
+ console.log("WebNN: NPU Context created");
31
+
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ this.builder = new (window as any).MLGraphBuilder(this.context);
34
+ this.isReady = true;
35
+ } catch (e) {
36
+ console.error("WebNN Init Error (likely no NPU or flag disabled):", e);
37
+ // Fallback? or just stay uninitialized
38
+ }
39
+ }
40
+
41
+ async prepareModel(size: number, weights: Float32Array, biases: Float32Array, batchSize: number = 1) {
42
+ if (!this.context || !this.builder) return;
43
+ this.networkSize = size;
44
+ this.batchSize = batchSize;
45
+ this.weights = weights;
46
+ this.biases = biases;
47
+
48
+ // Build the Computational Graph
49
+ // Model: Y = Tanh((X * W) + B)
50
+ // Shapes:
51
+ // X: [batchSize, networkSize]
52
+ // W: [networkSize, networkSize]
53
+ // B: [networkSize] (Broadcasted)
54
+ // Y: [batchSize, networkSize]
55
+
56
+ try {
57
+ const builder = this.builder;
58
+
59
+ // 1. Input Operand (Variable)
60
+ const inputDesc: MLOperandDescriptor = {
61
+ dataType: 'float32',
62
+ dimensions: [batchSize, size]
63
+ };
64
+ const input = builder.input('input', inputDesc);
65
+
66
+ // 2. Constants (Weights & Biases)
67
+ // Note: WebNN matmul expects typically [I, J] * [J, K] -> [I, K]
68
+ // Our weights are N*N flattened.
69
+ const weightDesc: MLOperandDescriptor = {
70
+ dataType: 'float32',
71
+ dimensions: [size, size]
72
+ };
73
+ // WebNN might require specific buffer types, Float32Array is good.
74
+ const weightConstant = builder.constant(weightDesc, weights);
75
+
76
+ const biasDesc: MLOperandDescriptor = {
77
+ dataType: 'float32',
78
+ dimensions: [size] // 1D, will broadcast to [batch, size]
79
+ };
80
+ const biasConstant = builder.constant(biasDesc, biases);
81
+
82
+ // 3. Operations
83
+ // MatMul: [batch, size] * [size, size] -> [batch, size]
84
+ const matmul = builder.matmul(input, weightConstant);
85
+
86
+ // Add Bias (Broadcast)
87
+ const added = builder.add(matmul, biasConstant);
88
+
89
+ // Activation
90
+ const output = builder.tanh(added);
91
+
92
+ // 4. Build
93
+ this.graph = await builder.build({ 'output': output });
94
+ console.log("WebNN: Graph compiled successfully");
95
+
96
+ } catch (e) {
97
+ console.error("WebNN Build Error:", e);
98
+ }
99
+ }
100
+
101
+ async runTick(inputs: Float32Array): Promise<Float32Array> {
102
+ if (!this.context || !this.graph) {
103
+ throw new Error("WebNN not ready");
104
+ }
105
+ if (inputs.length !== this.networkSize * this.batchSize) {
106
+ throw new Error(`Input size mismatch. Expected ${this.networkSize * this.batchSize}, got ${inputs.length}`);
107
+ }
108
+
109
+ const outputs = new Float32Array(this.networkSize * this.batchSize);
110
+
111
+ const inputsMap = { 'input': inputs };
112
+ const outputsMap = { 'output': outputs };
113
+
114
+ await this.context.compute(this.graph, inputsMap, outputsMap);
115
+
116
+ return outputs;
117
+ }
118
+ }
package/src/index.ts CHANGED
@@ -2,24 +2,32 @@ import { dash } from "@buley/dash";
2
2
  import { initializeSchema } from "./db/schema";
3
3
  import { NeuronRepository, SynapseRepository } from "./db/repository";
4
4
  import { GPUEngine } from "./engine/gpu";
5
+ import { WebNNEngine } from "./engine/webnn";
5
6
  import { Translator } from "./engine/translator";
7
+ import { Neuron, Synapse } from "./types";
8
+
9
+ export type { Neuron, Synapse } from "./types";
6
10
 
7
11
  export class NeuralEngine {
8
12
  gpu: GPUEngine;
13
+ npu: WebNNEngine;
9
14
  neuronRepo: NeuronRepository;
10
15
  synapseRepo: SynapseRepository;
11
16
  private translator: Translator;
12
17
 
18
+ activeBackend: 'gpu' | 'npu' = 'gpu';
19
+
13
20
  constructor() {
14
21
  this.gpu = new GPUEngine();
22
+ this.npu = new WebNNEngine();
15
23
  this.neuronRepo = new NeuronRepository();
16
24
  this.synapseRepo = new SynapseRepository();
17
25
  this.translator = new Translator();
18
26
  }
19
27
 
20
28
  // Cache
21
- private neurons: any[] = [];
22
- private synapses: any[] = [];
29
+ private neurons: Neuron[] = [];
30
+ private synapses: Synapse[] = [];
23
31
 
24
32
  async init() {
25
33
  console.log("Neural 2.0 Engine Initializing...");
@@ -32,6 +40,13 @@ export class NeuralEngine {
32
40
  await this.gpu.init();
33
41
  this.gpu.batchSize = 2; // Default to mini-batch of 2 for demo
34
42
 
43
+ // Try NPU
44
+ await this.npu.init();
45
+ if (this.npu.isReady) {
46
+ console.log("Neural Engine: NPU Accelerated Backend Available.");
47
+ this.activeBackend = 'npu';
48
+ }
49
+
35
50
  // 3. Hydration
36
51
  this.neurons = await this.neuronRepo.getAll();
37
52
  this.synapses = await this.synapseRepo.getAll();
@@ -61,17 +76,27 @@ export class NeuralEngine {
61
76
  this.synapses = await this.synapseRepo.getAll();
62
77
  }
63
78
 
64
- // 4. Compile to GPU
79
+ // 4. Compile to Compute Backends
80
+ await this.compile();
81
+
82
+ console.log(`Engine Ready. Active Backend: ${this.activeBackend.toUpperCase()}`);
83
+ return this.getGraphData();
84
+ }
85
+
86
+ async compile() {
65
87
  console.log(`Compiling graph: ${this.neurons.length} neurons, ${this.synapses.length} synapses`);
66
88
  const data = this.translator.flatten(this.neurons, this.synapses);
67
89
 
90
+ // GPU
68
91
  this.gpu.prepareBuffers(data.size, data.weights, data.biases, this.gpu.batchSize);
69
- // Also prepare training buffers!
70
- // Init target buffer with zeros
71
92
  this.gpu.prepareTrainingBuffers(new Float32Array(data.size * this.gpu.batchSize), 0.1);
72
93
 
73
- console.log("Engine Ready.");
74
- return data;
94
+ // NPU
95
+ if (this.npu.isReady) {
96
+ await this.npu.prepareModel(data.size, data.weights, data.biases, 2);
97
+ }
98
+
99
+ return data; // Return data for init/others if needed
75
100
  }
76
101
 
77
102
  async deployToCloud() {
@@ -128,12 +153,7 @@ export class NeuralEngine {
128
153
  this.synapses = this.synapses.filter(s => s.id !== id);
129
154
 
130
155
  // Recompile (Heavy!)
131
- // In a real app we'd just zero the weight in buffer
132
- // But for "The Visible Brain" seeing it disappear is cooler.
133
- const data = this.translator.flatten(this.neurons, this.synapses);
134
- this.gpu.prepareBuffers(data.size, data.weights, data.biases, this.gpu.batchSize);
135
- // Reset training buffers too to be safe/simple
136
- this.gpu.prepareTrainingBuffers(new Float32Array(data.size * this.gpu.batchSize), 0.1);
156
+ await this.compile();
137
157
 
138
158
  return this.getGraphData();
139
159
  }
@@ -146,7 +166,7 @@ export class NeuralEngine {
146
166
  };
147
167
  }
148
168
 
149
- async importGraph(data: any) {
169
+ async importGraph(data: { neurons: Neuron[], synapses: Synapse[] }) {
150
170
  if (!data.neurons || !data.synapses) throw new Error("Invalid graph data");
151
171
 
152
172
  console.log("Importing graph...");
@@ -167,18 +187,25 @@ export class NeuralEngine {
167
187
  this.synapses = await this.synapseRepo.getAll();
168
188
 
169
189
  console.log(`Compiling imported graph: ${this.neurons.length} neurons, ${this.synapses.length} synapses`);
170
- const graph = this.translator.flatten(this.neurons, this.synapses);
171
- this.gpu.prepareBuffers(graph.size, graph.weights, graph.biases, this.gpu.batchSize);
172
- this.gpu.prepareTrainingBuffers(new Float32Array(graph.size * this.gpu.batchSize), 0.1);
190
+ await this.compile();
173
191
 
174
192
  return this.getGraphData();
175
193
  }
176
194
 
177
195
  async injectInput(data: Float32Array) {
178
- // Map data to input neurons
179
- // In simulation, we assume first N neurons are inputs.
180
- // Or we just overwrite the first N values of the input buffer.
181
- await this.gpu.injectInput(data);
196
+ if (this.activeBackend === 'npu' && this.npu.isReady) {
197
+ // WebNN takes input at runTick
198
+ } else {
199
+ await this.gpu.injectInput(data);
200
+ }
201
+ }
202
+
203
+ async runTick(inputs: Float32Array): Promise<Float32Array> {
204
+ if (this.activeBackend === 'npu' && this.npu.isReady) {
205
+ return this.npu.runTick(inputs);
206
+ } else {
207
+ return this.gpu.runTick(inputs);
208
+ }
182
209
  }
183
210
  }
184
211
 
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export interface Neuron {
2
2
  id: string;
3
- type: 'input' | 'hidden' | 'output';
3
+ type: 'input' | 'hidden' | 'output' | 'cloud';
4
4
  bias: number;
5
5
  activation: string;
6
6
  }
@@ -0,0 +1,43 @@
1
+
2
+ // Minimal WebNN Type Definitions for TypeScript
3
+ // Based on W3C Web Neural Network API Draft
4
+
5
+ interface MLContext {
6
+ compute(graph: MLGraph, inputs: Record<string, ArrayBufferView>, outputs: Record<string, ArrayBufferView>): Promise<MLComputeResult>;
7
+ }
8
+
9
+ interface MLComputeResult {
10
+ inputs: Record<string, ArrayBufferView>;
11
+ outputs: Record<string, ArrayBufferView>;
12
+ }
13
+
14
+ interface MLGraphBuilder {
15
+ input(name: string, descriptor: MLOperandDescriptor): MLOperand;
16
+ constant(descriptor: MLOperandDescriptor, buffer: ArrayBufferView): MLOperand;
17
+ matmul(a: MLOperand, b: MLOperand): MLOperand;
18
+ add(a: MLOperand, b: MLOperand): MLOperand;
19
+ tanh(x: MLOperand): MLOperand;
20
+ build(outputs: Record<string, MLOperand>): Promise<MLGraph>;
21
+ }
22
+
23
+ interface MLGraph {}
24
+
25
+ interface MLOperand {}
26
+
27
+ interface MLOperandDescriptor {
28
+ dataType: 'float32' | 'float16' | 'int32' | 'uint32' | 'int8' | 'uint8';
29
+ dimensions: number[];
30
+ }
31
+
32
+ interface MLContextOptions {
33
+ deviceType?: 'cpu' | 'gpu' | 'npu';
34
+ powerPreference?: 'default' | 'high-performance' | 'low-power';
35
+ }
36
+
37
+ interface NavigatorML {
38
+ createContext(options?: MLContextOptions): Promise<MLContext>;
39
+ }
40
+
41
+ interface Navigator {
42
+ ml?: NavigatorML;
43
+ }