flockml 1.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.
Files changed (45) hide show
  1. package/deep-profiling-report.html +119 -0
  2. package/dist/activations.d.ts +13 -0
  3. package/dist/activations.js +47 -0
  4. package/dist/break-test.d.ts +1 -0
  5. package/dist/break-test.js +249 -0
  6. package/dist/brutal-test.d.ts +1 -0
  7. package/dist/brutal-test.js +113 -0
  8. package/dist/client-node.d.ts +48 -0
  9. package/dist/client-node.js +174 -0
  10. package/dist/coordinator.d.ts +41 -0
  11. package/dist/coordinator.js +155 -0
  12. package/dist/index.d.ts +5 -0
  13. package/dist/index.js +13 -0
  14. package/dist/matrix.d.ts +67 -0
  15. package/dist/matrix.js +185 -0
  16. package/dist/micro-benchmark.d.ts +1 -0
  17. package/dist/micro-benchmark.js +215 -0
  18. package/dist/network.d.ts +32 -0
  19. package/dist/network.js +127 -0
  20. package/dist/privacy.d.ts +17 -0
  21. package/dist/privacy.js +70 -0
  22. package/dist/quantization.d.ts +33 -0
  23. package/dist/quantization.js +92 -0
  24. package/dist/test.d.ts +1 -0
  25. package/dist/test.js +58 -0
  26. package/dist/worker.d.ts +15 -0
  27. package/dist/worker.js +95 -0
  28. package/package.json +21 -0
  29. package/src/activations.ts +45 -0
  30. package/src/break-test.ts +234 -0
  31. package/src/brutal-test.ts +103 -0
  32. package/src/client-node.ts +154 -0
  33. package/src/coordinator.ts +137 -0
  34. package/src/index.ts +5 -0
  35. package/src/messages.d.ts +429 -0
  36. package/src/messages.js +1173 -0
  37. package/src/messages.proto +30 -0
  38. package/src/micro-benchmark.ts +200 -0
  39. package/src/network.ts +113 -0
  40. package/src/privacy.ts +39 -0
  41. package/src/quantization.ts +82 -0
  42. package/src/test.ts +72 -0
  43. package/src/worker.ts +95 -0
  44. package/stress-report.html +190 -0
  45. package/tsconfig.json +14 -0
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FlockNode = void 0;
37
+ const network_1 = require("./network");
38
+ const quantization_1 = require("./quantization");
39
+ const privacy_1 = require("./privacy");
40
+ const worker_1 = require("./worker");
41
+ const messages_1 = require("./messages");
42
+ const tf = __importStar(require("@tensorflow/tfjs"));
43
+ /**
44
+ * The FlockML Client Node.
45
+ *
46
+ * Powered by TFJS. It encapsulates the neural network, the privacy engine, and the quantization engine.
47
+ * In a full production build, this entire class would be serialized into a Blob
48
+ * and executed inside a background Web Worker to keep the UI at 60fps.
49
+ */
50
+ class FlockNode {
51
+ network;
52
+ isConnected = false;
53
+ isTraining = false;
54
+ privacyEpsilon = 0.5;
55
+ workerManager;
56
+ dynamicBatchSize = 32;
57
+ // Error Feedback Memory Caches
58
+ error_ih;
59
+ error_ho;
60
+ error_bh;
61
+ error_bo;
62
+ constructor(inputNodes = 2, hiddenNodes = 4, outputNodes = 1) {
63
+ this.network = new network_1.NeuralNetwork(inputNodes, hiddenNodes, outputNodes);
64
+ this.workerManager = new worker_1.WorkerManager();
65
+ }
66
+ /**
67
+ * Connects to the central FedAvg Coordinator.
68
+ */
69
+ connect(websocketUrl) {
70
+ // Mocking WebSocket connection for the MVP
71
+ console.log(`[FlockML] Connecting to ${websocketUrl}...`);
72
+ this.isConnected = true;
73
+ console.log(`[FlockML] Connected. Awaiting global weights.`);
74
+ // Wire up the dynamic device profiler to automatically scale the batch size
75
+ this.workerManager.onDeviceProfiled = (batchSize) => {
76
+ this.dynamicBatchSize = batchSize;
77
+ };
78
+ // Initialize background thread
79
+ this.workerManager.initializeWorker();
80
+ }
81
+ /**
82
+ * Receives the latest global model from the server.
83
+ */
84
+ syncGlobalWeights(qWeightsIH, qWeightsHO, qBiasH, qBiasO) {
85
+ tf.tidy(() => {
86
+ const ih = quantization_1.Quantizer.dequantize(qWeightsIH);
87
+ const ho = quantization_1.Quantizer.dequantize(qWeightsHO);
88
+ const bh = quantization_1.Quantizer.dequantize(qBiasH);
89
+ const bo = quantization_1.Quantizer.dequantize(qBiasO);
90
+ this.network.weights_ih.assign(ih);
91
+ this.network.weights_ho.assign(ho);
92
+ this.network.bias_h.assign(bh);
93
+ this.network.bias_o.assign(bo);
94
+ });
95
+ }
96
+ /**
97
+ * Performs one local training epoch on a batch of data.
98
+ */
99
+ trainLocalBatch(inputs, targets) {
100
+ if (!this.isConnected)
101
+ throw new Error("FlockNode is not connected to a coordinator.");
102
+ this.isTraining = true;
103
+ // Dispatch heavy math to background Web Worker thread
104
+ this.workerManager.dispatchTrainingJob(inputs, targets);
105
+ // Legacy fallback for tests (mocking actual training update)
106
+ for (let i = 0; i < inputs.length; i++) {
107
+ this.network.train(inputs[i], targets[i]);
108
+ }
109
+ }
110
+ /**
111
+ * Cleans up background threads.
112
+ */
113
+ destroy() {
114
+ this.workerManager.terminate();
115
+ this.network.dispose();
116
+ if (this.error_ih)
117
+ this.error_ih.dispose();
118
+ if (this.error_ho)
119
+ this.error_ho.dispose();
120
+ if (this.error_bh)
121
+ this.error_bh.dispose();
122
+ if (this.error_bo)
123
+ this.error_bo.dispose();
124
+ }
125
+ /**
126
+ * Helper function to map our payload into the protobuf format
127
+ */
128
+ mapToProto(payload) {
129
+ return {
130
+ data: new Uint8Array(payload.data),
131
+ min: payload.min,
132
+ max: payload.max,
133
+ rows: payload.rows,
134
+ cols: payload.cols
135
+ };
136
+ }
137
+ /**
138
+ * Secures, compresses, and binary-serializes the newly trained weights.
139
+ */
140
+ exportSecureGradients() {
141
+ // 2. Apply Differential Privacy (Laplacian Noise) to protect user data
142
+ privacy_1.DifferentialPrivacy.applyNoise(this.network.weights_ih, this.privacyEpsilon);
143
+ privacy_1.DifferentialPrivacy.applyNoise(this.network.weights_ho, this.privacyEpsilon);
144
+ privacy_1.DifferentialPrivacy.applyNoise(this.network.bias_h, this.privacyEpsilon);
145
+ privacy_1.DifferentialPrivacy.applyNoise(this.network.bias_o, this.privacyEpsilon);
146
+ // 3. Quantize matrices to 8-bit integers while preserving precision loss in Error Memory
147
+ const qIH = quantization_1.Quantizer.quantizeWithErrorFeedback(this.network.weights_ih, this.error_ih);
148
+ if (this.error_ih)
149
+ this.error_ih.dispose();
150
+ this.error_ih = qIH.newError;
151
+ const qHO = quantization_1.Quantizer.quantizeWithErrorFeedback(this.network.weights_ho, this.error_ho);
152
+ if (this.error_ho)
153
+ this.error_ho.dispose();
154
+ this.error_ho = qHO.newError;
155
+ const qBH = quantization_1.Quantizer.quantizeWithErrorFeedback(this.network.bias_h, this.error_bh);
156
+ if (this.error_bh)
157
+ this.error_bh.dispose();
158
+ this.error_bh = qBH.newError;
159
+ const qBO = quantization_1.Quantizer.quantizeWithErrorFeedback(this.network.bias_o, this.error_bo);
160
+ if (this.error_bo)
161
+ this.error_bo.dispose();
162
+ this.error_bo = qBO.newError;
163
+ // 4. Binary Serialization (Protocol Buffers) to cut network payload bloat
164
+ const message = messages_1.flockml.GradientUpdate.create({
165
+ weightsIh: this.mapToProto(qIH.payload),
166
+ weightsHo: this.mapToProto(qHO.payload),
167
+ biasH: this.mapToProto(qBH.payload),
168
+ biasO: this.mapToProto(qBO.payload),
169
+ batchSize: this.dynamicBatchSize
170
+ });
171
+ return messages_1.flockml.GradientUpdate.encode(message).finish();
172
+ }
173
+ }
174
+ exports.FlockNode = FlockNode;
@@ -0,0 +1,41 @@
1
+ import { NeuralNetwork } from './network';
2
+ import { QuantizedPayload } from './quantization';
3
+ import * as tf from '@tensorflow/tfjs';
4
+ /**
5
+ * The Central Brain: FedAvg Coordinator.
6
+ *
7
+ * Powered by TFJS. Averages massive incoming weight tensors continuously
8
+ * using hardware acceleration.
9
+ */
10
+ export declare class Coordinator {
11
+ globalModel: NeuralNetwork;
12
+ clientUpdates: {
13
+ weights_ih: tf.Tensor2D;
14
+ weights_ho: tf.Tensor2D;
15
+ bias_h: tf.Tensor2D;
16
+ bias_o: tf.Tensor2D;
17
+ }[];
18
+ constructor(inputNodes: number, hiddenNodes: number, outputNodes: number);
19
+ private mapFromProto;
20
+ /**
21
+ * Receives a binary protobuf payload from a browser client.
22
+ */
23
+ receiveUpdate(binaryPayload: Uint8Array): void;
24
+ /**
25
+ * The Federated Averaging (FedAvg) Algorithm with Anomaly Detection.
26
+ *
27
+ * Averages all the incoming matrices. Because of Differential Privacy,
28
+ * the Laplacian noise mathematically cancels out to 0 here, leaving only
29
+ * the pure, learned signal from the crowdsourced devices.
30
+ */
31
+ aggregate(): void;
32
+ /**
33
+ * Broadcasts the current global model weights to all new clients.
34
+ */
35
+ getGlobalWeightsForBroadcast(): {
36
+ weights_ih: QuantizedPayload;
37
+ weights_ho: QuantizedPayload;
38
+ bias_h: QuantizedPayload;
39
+ bias_o: QuantizedPayload;
40
+ };
41
+ }
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.Coordinator = void 0;
37
+ const network_1 = require("./network");
38
+ const quantization_1 = require("./quantization");
39
+ const messages_1 = require("./messages");
40
+ const tf = __importStar(require("@tensorflow/tfjs"));
41
+ /**
42
+ * The Central Brain: FedAvg Coordinator.
43
+ *
44
+ * Powered by TFJS. Averages massive incoming weight tensors continuously
45
+ * using hardware acceleration.
46
+ */
47
+ class Coordinator {
48
+ globalModel;
49
+ clientUpdates;
50
+ constructor(inputNodes, hiddenNodes, outputNodes) {
51
+ this.globalModel = new network_1.NeuralNetwork(inputNodes, hiddenNodes, outputNodes);
52
+ this.clientUpdates = [];
53
+ }
54
+ mapFromProto(protoPayload) {
55
+ if (!protoPayload)
56
+ throw new Error("Missing payload section");
57
+ return {
58
+ data: protoPayload.data || new Uint8Array(),
59
+ min: protoPayload.min || 0,
60
+ max: protoPayload.max || 0,
61
+ rows: protoPayload.rows || 0,
62
+ cols: protoPayload.cols || 0
63
+ };
64
+ }
65
+ /**
66
+ * Receives a binary protobuf payload from a browser client.
67
+ */
68
+ receiveUpdate(binaryPayload) {
69
+ const message = messages_1.flockml.GradientUpdate.decode(binaryPayload);
70
+ const weights_ih = quantization_1.Quantizer.dequantize(this.mapFromProto(message.weightsIh));
71
+ const weights_ho = quantization_1.Quantizer.dequantize(this.mapFromProto(message.weightsHo));
72
+ const bias_h = quantization_1.Quantizer.dequantize(this.mapFromProto(message.biasH));
73
+ const bias_o = quantization_1.Quantizer.dequantize(this.mapFromProto(message.biasO));
74
+ this.clientUpdates.push({
75
+ weights_ih,
76
+ weights_ho,
77
+ bias_h,
78
+ bias_o
79
+ });
80
+ }
81
+ /**
82
+ * The Federated Averaging (FedAvg) Algorithm with Anomaly Detection.
83
+ *
84
+ * Averages all the incoming matrices. Because of Differential Privacy,
85
+ * the Laplacian noise mathematically cancels out to 0 here, leaving only
86
+ * the pure, learned signal from the crowdsourced devices.
87
+ */
88
+ aggregate() {
89
+ if (this.clientUpdates.length === 0)
90
+ return;
91
+ // 1. Anomaly Detection (Anti-Sybil / Data Poisoning)
92
+ // Runs mathematically on the GPU without downloading large buffers to CPU
93
+ const safeUpdates = this.clientUpdates.filter(update => {
94
+ return tf.tidy(() => {
95
+ const checkTensor = (t) => {
96
+ const hasNaN = tf.sum(tf.cast(tf.isNaN(t), 'int32')).dataSync()[0] > 0;
97
+ const maxAbs = tf.max(tf.abs(t)).dataSync()[0];
98
+ return !hasNaN && maxAbs <= 1000;
99
+ };
100
+ return checkTensor(update.weights_ih) &&
101
+ checkTensor(update.weights_ho) &&
102
+ checkTensor(update.bias_h) &&
103
+ checkTensor(update.bias_o);
104
+ });
105
+ });
106
+ if (safeUpdates.length === 0) {
107
+ console.warn("[Coordinator] All received updates were flagged as anomalous and dropped.");
108
+ // Dispose the tensors since we are dropping them
109
+ for (const update of this.clientUpdates) {
110
+ update.weights_ih.dispose();
111
+ update.weights_ho.dispose();
112
+ update.bias_h.dispose();
113
+ update.bias_o.dispose();
114
+ }
115
+ this.clientUpdates = [];
116
+ return;
117
+ }
118
+ if (safeUpdates.length < this.clientUpdates.length) {
119
+ console.warn(`[Coordinator] Dropped ${this.clientUpdates.length - safeUpdates.length} poisoned payloads.`);
120
+ }
121
+ // 2. Federated Averaging
122
+ const averageTensor = (matrixKey, targetVar) => {
123
+ tf.tidy(() => {
124
+ const tensors = safeUpdates.map(u => u[matrixKey]);
125
+ const stacked = tf.stack(tensors);
126
+ const mean = tf.mean(stacked, 0);
127
+ targetVar.assign(mean);
128
+ });
129
+ };
130
+ averageTensor('weights_ih', this.globalModel.weights_ih);
131
+ averageTensor('weights_ho', this.globalModel.weights_ho);
132
+ averageTensor('bias_h', this.globalModel.bias_h);
133
+ averageTensor('bias_o', this.globalModel.bias_o);
134
+ // Dispose all client tensors after aggregation to prevent memory leaks
135
+ for (const update of this.clientUpdates) {
136
+ update.weights_ih.dispose();
137
+ update.weights_ho.dispose();
138
+ update.bias_h.dispose();
139
+ update.bias_o.dispose();
140
+ }
141
+ this.clientUpdates = [];
142
+ }
143
+ /**
144
+ * Broadcasts the current global model weights to all new clients.
145
+ */
146
+ getGlobalWeightsForBroadcast() {
147
+ return {
148
+ weights_ih: quantization_1.Quantizer.quantize(this.globalModel.weights_ih),
149
+ weights_ho: quantization_1.Quantizer.quantize(this.globalModel.weights_ho),
150
+ bias_h: quantization_1.Quantizer.quantize(this.globalModel.bias_h),
151
+ bias_o: quantization_1.Quantizer.quantize(this.globalModel.bias_o)
152
+ };
153
+ }
154
+ }
155
+ exports.Coordinator = Coordinator;
@@ -0,0 +1,5 @@
1
+ export { NeuralNetwork } from './network';
2
+ export { Quantizer, type QuantizedPayload } from './quantization';
3
+ export { DifferentialPrivacy } from './privacy';
4
+ export { Coordinator } from './coordinator';
5
+ export { FlockNode as FlockNode } from './client-node';
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FlockNode = exports.Coordinator = exports.DifferentialPrivacy = exports.Quantizer = exports.NeuralNetwork = void 0;
4
+ var network_1 = require("./network");
5
+ Object.defineProperty(exports, "NeuralNetwork", { enumerable: true, get: function () { return network_1.NeuralNetwork; } });
6
+ var quantization_1 = require("./quantization");
7
+ Object.defineProperty(exports, "Quantizer", { enumerable: true, get: function () { return quantization_1.Quantizer; } });
8
+ var privacy_1 = require("./privacy");
9
+ Object.defineProperty(exports, "DifferentialPrivacy", { enumerable: true, get: function () { return privacy_1.DifferentialPrivacy; } });
10
+ var coordinator_1 = require("./coordinator");
11
+ Object.defineProperty(exports, "Coordinator", { enumerable: true, get: function () { return coordinator_1.Coordinator; } });
12
+ var client_node_1 = require("./client-node");
13
+ Object.defineProperty(exports, "FlockNode", { enumerable: true, get: function () { return client_node_1.FlockNode; } });
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Core Linear Algebra Engine for Browser-Native Neural Networks.
3
+ *
4
+ * This class handles all matrix math required for Forward Passes and Backpropagation.
5
+ * Optimized for V8 JIT compilation using typed Float32Arrays where possible,
6
+ * though standard arrays are used here for structural simplicity in the MVP.
7
+ */
8
+ export declare class Matrix {
9
+ rows: number;
10
+ cols: number;
11
+ data: number[][];
12
+ constructor(rows: number, cols: number);
13
+ /**
14
+ * Creates a matrix from an existing 2D array.
15
+ */
16
+ static fromArray(arr: number[][]): Matrix;
17
+ /**
18
+ * Randomizes the matrix weights (typically for initialization).
19
+ * Uses a standard normal distribution approximation (Xavier initialization simplified).
20
+ */
21
+ randomize(): void;
22
+ /**
23
+ * Matrix Dot Product (Matrix Multiplication).
24
+ * Used heavily in the Forward Pass to calculate layer activations.
25
+ * O(n^3) complexity - prime candidate for WebGPU acceleration later.
26
+ */
27
+ static dot(a: Matrix, b: Matrix): Matrix;
28
+ /**
29
+ * Element-wise addition.
30
+ */
31
+ add(n: Matrix | number): void;
32
+ /**
33
+ * Element-wise subtraction (A - B). Returns a new Matrix.
34
+ * Crucial for calculating the Error (Target - Prediction).
35
+ */
36
+ static subtract(a: Matrix, b: Matrix): Matrix;
37
+ /**
38
+ * Element-wise multiplication (Hadamard product). Returns a new Matrix.
39
+ */
40
+ static multiply(a: Matrix, b: Matrix): Matrix;
41
+ /**
42
+ * Scalar multiplication.
43
+ */
44
+ multiply(n: number): void;
45
+ /**
46
+ * Applies a function to every element in the matrix.
47
+ * Used for Activation Functions (e.g., Sigmoid, ReLU) and their derivatives.
48
+ */
49
+ map(func: (val: number, i: number, j: number) => number): void;
50
+ /**
51
+ * Static version of map, returns a new Matrix.
52
+ */
53
+ static map(matrix: Matrix, func: (val: number, i: number, j: number) => number): Matrix;
54
+ /**
55
+ * Transposes the matrix (flips rows and columns).
56
+ * Crucial for calculating gradients during Backpropagation.
57
+ */
58
+ static transpose(matrix: Matrix): Matrix;
59
+ /**
60
+ * Converts a 1D array to a Matrix (Column vector).
61
+ */
62
+ static from1DArray(arr: number[]): Matrix;
63
+ /**
64
+ * Converts the Matrix back to a 1D array.
65
+ */
66
+ toArray(): number[];
67
+ }
package/dist/matrix.js ADDED
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ /**
3
+ * Core Linear Algebra Engine for Browser-Native Neural Networks.
4
+ *
5
+ * This class handles all matrix math required for Forward Passes and Backpropagation.
6
+ * Optimized for V8 JIT compilation using typed Float32Arrays where possible,
7
+ * though standard arrays are used here for structural simplicity in the MVP.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.Matrix = void 0;
11
+ class Matrix {
12
+ rows;
13
+ cols;
14
+ data;
15
+ constructor(rows, cols) {
16
+ this.rows = rows;
17
+ this.cols = cols;
18
+ this.data = Array(this.rows).fill(0).map(() => Array(this.cols).fill(0));
19
+ }
20
+ /**
21
+ * Creates a matrix from an existing 2D array.
22
+ */
23
+ static fromArray(arr) {
24
+ const m = new Matrix(arr.length, arr[0].length);
25
+ for (let i = 0; i < m.rows; i++) {
26
+ for (let j = 0; j < m.cols; j++) {
27
+ m.data[i][j] = arr[i][j];
28
+ }
29
+ }
30
+ return m;
31
+ }
32
+ /**
33
+ * Randomizes the matrix weights (typically for initialization).
34
+ * Uses a standard normal distribution approximation (Xavier initialization simplified).
35
+ */
36
+ randomize() {
37
+ for (let i = 0; i < this.rows; i++) {
38
+ for (let j = 0; j < this.cols; j++) {
39
+ // Random value between -1 and 1
40
+ this.data[i][j] = Math.random() * 2 - 1;
41
+ }
42
+ }
43
+ }
44
+ /**
45
+ * Matrix Dot Product (Matrix Multiplication).
46
+ * Used heavily in the Forward Pass to calculate layer activations.
47
+ * O(n^3) complexity - prime candidate for WebGPU acceleration later.
48
+ */
49
+ static dot(a, b) {
50
+ if (a.cols !== b.rows) {
51
+ throw new Error(`Incompatible matrices for dot product: ${a.cols} !== ${b.rows}`);
52
+ }
53
+ const result = new Matrix(a.rows, b.cols);
54
+ for (let i = 0; i < result.rows; i++) {
55
+ for (let j = 0; j < result.cols; j++) {
56
+ let sum = 0;
57
+ for (let k = 0; k < a.cols; k++) {
58
+ sum += a.data[i][k] * b.data[k][j];
59
+ }
60
+ result.data[i][j] = sum;
61
+ }
62
+ }
63
+ return result;
64
+ }
65
+ /**
66
+ * Element-wise addition.
67
+ */
68
+ add(n) {
69
+ if (n instanceof Matrix) {
70
+ if (this.rows !== n.rows || this.cols !== n.cols) {
71
+ throw new Error('Incompatible matrices for addition');
72
+ }
73
+ for (let i = 0; i < this.rows; i++) {
74
+ for (let j = 0; j < this.cols; j++) {
75
+ this.data[i][j] += n.data[i][j];
76
+ }
77
+ }
78
+ }
79
+ else {
80
+ for (let i = 0; i < this.rows; i++) {
81
+ for (let j = 0; j < this.cols; j++) {
82
+ this.data[i][j] += n;
83
+ }
84
+ }
85
+ }
86
+ }
87
+ /**
88
+ * Element-wise subtraction (A - B). Returns a new Matrix.
89
+ * Crucial for calculating the Error (Target - Prediction).
90
+ */
91
+ static subtract(a, b) {
92
+ if (a.rows !== b.rows || a.cols !== b.cols) {
93
+ throw new Error('Incompatible matrices for subtraction');
94
+ }
95
+ const result = new Matrix(a.rows, a.cols);
96
+ for (let i = 0; i < result.rows; i++) {
97
+ for (let j = 0; j < result.cols; j++) {
98
+ result.data[i][j] = a.data[i][j] - b.data[i][j];
99
+ }
100
+ }
101
+ return result;
102
+ }
103
+ /**
104
+ * Element-wise multiplication (Hadamard product). Returns a new Matrix.
105
+ */
106
+ static multiply(a, b) {
107
+ if (a.rows !== b.rows || a.cols !== b.cols) {
108
+ throw new Error('Incompatible matrices for element-wise multiplication');
109
+ }
110
+ const result = new Matrix(a.rows, a.cols);
111
+ for (let i = 0; i < result.rows; i++) {
112
+ for (let j = 0; j < result.cols; j++) {
113
+ result.data[i][j] = a.data[i][j] * b.data[i][j];
114
+ }
115
+ }
116
+ return result;
117
+ }
118
+ /**
119
+ * Scalar multiplication.
120
+ */
121
+ multiply(n) {
122
+ for (let i = 0; i < this.rows; i++) {
123
+ for (let j = 0; j < this.cols; j++) {
124
+ this.data[i][j] *= n;
125
+ }
126
+ }
127
+ }
128
+ /**
129
+ * Applies a function to every element in the matrix.
130
+ * Used for Activation Functions (e.g., Sigmoid, ReLU) and their derivatives.
131
+ */
132
+ map(func) {
133
+ for (let i = 0; i < this.rows; i++) {
134
+ for (let j = 0; j < this.cols; j++) {
135
+ const val = this.data[i][j];
136
+ this.data[i][j] = func(val, i, j);
137
+ }
138
+ }
139
+ }
140
+ /**
141
+ * Static version of map, returns a new Matrix.
142
+ */
143
+ static map(matrix, func) {
144
+ const result = new Matrix(matrix.rows, matrix.cols);
145
+ for (let i = 0; i < matrix.rows; i++) {
146
+ for (let j = 0; j < matrix.cols; j++) {
147
+ const val = matrix.data[i][j];
148
+ result.data[i][j] = func(val, i, j);
149
+ }
150
+ }
151
+ return result;
152
+ }
153
+ /**
154
+ * Transposes the matrix (flips rows and columns).
155
+ * Crucial for calculating gradients during Backpropagation.
156
+ */
157
+ static transpose(matrix) {
158
+ const result = new Matrix(matrix.cols, matrix.rows);
159
+ for (let i = 0; i < matrix.rows; i++) {
160
+ for (let j = 0; j < matrix.cols; j++) {
161
+ result.data[j][i] = matrix.data[i][j];
162
+ }
163
+ }
164
+ return result;
165
+ }
166
+ /**
167
+ * Converts a 1D array to a Matrix (Column vector).
168
+ */
169
+ static from1DArray(arr) {
170
+ return Matrix.fromArray(arr.map(x => [x]));
171
+ }
172
+ /**
173
+ * Converts the Matrix back to a 1D array.
174
+ */
175
+ toArray() {
176
+ const arr = [];
177
+ for (let i = 0; i < this.rows; i++) {
178
+ for (let j = 0; j < this.cols; j++) {
179
+ arr.push(this.data[i][j]);
180
+ }
181
+ }
182
+ return arr;
183
+ }
184
+ }
185
+ exports.Matrix = Matrix;
@@ -0,0 +1 @@
1
+ export {};