edgeflowjs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +473 -0
- package/dist/backends/index.d.ts +13 -0
- package/dist/backends/index.d.ts.map +1 -0
- package/dist/backends/index.js +32 -0
- package/dist/backends/index.js.map +1 -0
- package/dist/backends/onnx.d.ts +46 -0
- package/dist/backends/onnx.d.ts.map +1 -0
- package/dist/backends/onnx.js +249 -0
- package/dist/backends/onnx.js.map +1 -0
- package/dist/backends/wasm.d.ts +78 -0
- package/dist/backends/wasm.d.ts.map +1 -0
- package/dist/backends/wasm.js +358 -0
- package/dist/backends/wasm.js.map +1 -0
- package/dist/backends/webgpu.d.ts +143 -0
- package/dist/backends/webgpu.d.ts.map +1 -0
- package/dist/backends/webgpu.js +326 -0
- package/dist/backends/webgpu.js.map +1 -0
- package/dist/backends/webnn.d.ts +115 -0
- package/dist/backends/webnn.d.ts.map +1 -0
- package/dist/backends/webnn.js +202 -0
- package/dist/backends/webnn.js.map +1 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +14 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/memory.d.ts +234 -0
- package/dist/core/memory.d.ts.map +1 -0
- package/dist/core/memory.js +554 -0
- package/dist/core/memory.js.map +1 -0
- package/dist/core/runtime.d.ts +129 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +352 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/scheduler.d.ts +118 -0
- package/dist/core/scheduler.d.ts.map +1 -0
- package/dist/core/scheduler.js +600 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/tensor.d.ts +149 -0
- package/dist/core/tensor.d.ts.map +1 -0
- package/dist/core/tensor.js +719 -0
- package/dist/core/tensor.js.map +1 -0
- package/dist/core/types.d.ts +367 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +54 -0
- package/dist/core/types.js.map +1 -0
- package/dist/edgeflow.browser.js +5601 -0
- package/dist/edgeflow.browser.js.map +7 -0
- package/dist/edgeflow.browser.min.js +19 -0
- package/dist/edgeflow.browser.min.js.map +7 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +158 -0
- package/dist/index.js.map +1 -0
- package/dist/pipelines/base.d.ts +122 -0
- package/dist/pipelines/base.d.ts.map +1 -0
- package/dist/pipelines/base.js +155 -0
- package/dist/pipelines/base.js.map +1 -0
- package/dist/pipelines/feature-extraction.d.ts +68 -0
- package/dist/pipelines/feature-extraction.d.ts.map +1 -0
- package/dist/pipelines/feature-extraction.js +197 -0
- package/dist/pipelines/feature-extraction.js.map +1 -0
- package/dist/pipelines/image-classification.d.ts +61 -0
- package/dist/pipelines/image-classification.d.ts.map +1 -0
- package/dist/pipelines/image-classification.js +140 -0
- package/dist/pipelines/image-classification.js.map +1 -0
- package/dist/pipelines/index.d.ts +58 -0
- package/dist/pipelines/index.d.ts.map +1 -0
- package/dist/pipelines/index.js +72 -0
- package/dist/pipelines/index.js.map +1 -0
- package/dist/pipelines/text-classification.d.ts +71 -0
- package/dist/pipelines/text-classification.d.ts.map +1 -0
- package/dist/pipelines/text-classification.js +175 -0
- package/dist/pipelines/text-classification.js.map +1 -0
- package/dist/tools/index.d.ts +143 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +294 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/utils/cache.d.ts +162 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +443 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +12 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/model-loader.d.ts +107 -0
- package/dist/utils/model-loader.d.ts.map +1 -0
- package/dist/utils/model-loader.js +694 -0
- package/dist/utils/model-loader.js.map +1 -0
- package/dist/utils/preprocessor.d.ts +147 -0
- package/dist/utils/preprocessor.d.ts.map +1 -0
- package/dist/utils/preprocessor.js +423 -0
- package/dist/utils/preprocessor.js.map +1 -0
- package/dist/utils/tokenizer.d.ts +140 -0
- package/dist/utils/tokenizer.d.ts.map +1 -0
- package/dist/utils/tokenizer.js +397 -0
- package/dist/utils/tokenizer.js.map +1 -0
- package/package.json +87 -0
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* edgeFlow.js - Tensor Implementation
|
|
3
|
+
*
|
|
4
|
+
* Lightweight tensor implementation with efficient memory management.
|
|
5
|
+
*/
|
|
6
|
+
import { EdgeFlowError, ErrorCodes } from './types.js';
|
|
7
|
+
// Counter for generating unique tensor IDs
|
|
8
|
+
let tensorIdCounter = 0;
|
|
9
|
+
/**
|
|
10
|
+
* Generate a unique tensor ID
|
|
11
|
+
*/
|
|
12
|
+
function generateTensorId() {
|
|
13
|
+
return `tensor_${++tensorIdCounter}_${Date.now().toString(36)}`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get the typed array constructor for a data type
|
|
17
|
+
*/
|
|
18
|
+
function getTypedArrayConstructor(dtype) {
|
|
19
|
+
switch (dtype) {
|
|
20
|
+
case 'float32':
|
|
21
|
+
return Float32Array;
|
|
22
|
+
case 'float16':
|
|
23
|
+
// Float16 not natively supported, use Float32Array
|
|
24
|
+
return Float32Array;
|
|
25
|
+
case 'int32':
|
|
26
|
+
return Int32Array;
|
|
27
|
+
case 'int64':
|
|
28
|
+
return BigInt64Array;
|
|
29
|
+
case 'uint8':
|
|
30
|
+
case 'bool':
|
|
31
|
+
return Uint8Array;
|
|
32
|
+
case 'int8':
|
|
33
|
+
return Int8Array;
|
|
34
|
+
default:
|
|
35
|
+
throw new EdgeFlowError(`Unsupported data type: ${dtype}`, ErrorCodes.INVALID_ARGUMENT, { dtype });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Calculate the total number of elements from shape
|
|
40
|
+
*/
|
|
41
|
+
function calculateSize(shape) {
|
|
42
|
+
if (shape.length === 0)
|
|
43
|
+
return 1; // Scalar
|
|
44
|
+
return shape.reduce((acc, dim) => acc * dim, 1);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Validate tensor shape
|
|
48
|
+
*/
|
|
49
|
+
function validateShape(shape) {
|
|
50
|
+
for (let i = 0; i < shape.length; i++) {
|
|
51
|
+
const dim = shape[i];
|
|
52
|
+
if (dim === undefined || !Number.isInteger(dim) || dim < 0) {
|
|
53
|
+
throw new EdgeFlowError(`Invalid shape dimension at index ${i}: ${dim}`, ErrorCodes.INVALID_ARGUMENT, { shape, index: i, dimension: dim });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* EdgeFlowTensor - Core tensor implementation
|
|
59
|
+
*/
|
|
60
|
+
export class EdgeFlowTensor {
|
|
61
|
+
id;
|
|
62
|
+
dtype;
|
|
63
|
+
shape;
|
|
64
|
+
size;
|
|
65
|
+
_data;
|
|
66
|
+
_isDisposed = false;
|
|
67
|
+
constructor(data, shape, dtype = 'float32') {
|
|
68
|
+
validateShape(shape);
|
|
69
|
+
this.id = generateTensorId();
|
|
70
|
+
this.dtype = dtype;
|
|
71
|
+
this.shape = Object.freeze([...shape]);
|
|
72
|
+
this.size = calculateSize(this.shape);
|
|
73
|
+
// Validate data size matches shape
|
|
74
|
+
const expectedSize = this.size;
|
|
75
|
+
if (data.length !== expectedSize) {
|
|
76
|
+
throw new EdgeFlowError(`Data length (${data.length}) does not match shape ${JSON.stringify(shape)} (expected ${expectedSize})`, ErrorCodes.TENSOR_SHAPE_MISMATCH, { dataLength: data.length, expectedSize, shape });
|
|
77
|
+
}
|
|
78
|
+
// Convert to appropriate typed array
|
|
79
|
+
if (data instanceof Array) {
|
|
80
|
+
const TypedArrayCtor = getTypedArrayConstructor(dtype);
|
|
81
|
+
this._data = new TypedArrayCtor(data.length);
|
|
82
|
+
if (dtype === 'int64') {
|
|
83
|
+
// BigInt64Array requires BigInt values
|
|
84
|
+
const bigIntData = this._data;
|
|
85
|
+
for (let i = 0; i < data.length; i++) {
|
|
86
|
+
bigIntData[i] = BigInt(Math.round(data[i] ?? 0));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
for (let i = 0; i < data.length; i++) {
|
|
91
|
+
this._data[i] = data[i] ?? 0;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this._data = data;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
get data() {
|
|
100
|
+
this.checkDisposed();
|
|
101
|
+
return this._data;
|
|
102
|
+
}
|
|
103
|
+
get isDisposed() {
|
|
104
|
+
return this._isDisposed;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Check if tensor has been disposed
|
|
108
|
+
*/
|
|
109
|
+
checkDisposed() {
|
|
110
|
+
if (this._isDisposed) {
|
|
111
|
+
throw new EdgeFlowError('Cannot access disposed tensor', ErrorCodes.TENSOR_DISPOSED, { tensorId: this.id });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Convert to Float32Array
|
|
116
|
+
*/
|
|
117
|
+
toFloat32Array() {
|
|
118
|
+
this.checkDisposed();
|
|
119
|
+
if (this._data instanceof Float32Array) {
|
|
120
|
+
return this._data;
|
|
121
|
+
}
|
|
122
|
+
const result = new Float32Array(this.size);
|
|
123
|
+
for (let i = 0; i < this.size; i++) {
|
|
124
|
+
result[i] = Number(this._data[i] ?? 0);
|
|
125
|
+
}
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Convert to regular array
|
|
130
|
+
*/
|
|
131
|
+
toArray() {
|
|
132
|
+
this.checkDisposed();
|
|
133
|
+
if (this.dtype === 'int64') {
|
|
134
|
+
// BigInt64Array needs special handling
|
|
135
|
+
const bigIntData = this._data;
|
|
136
|
+
const result = [];
|
|
137
|
+
for (let i = 0; i < bigIntData.length; i++) {
|
|
138
|
+
result.push(Number(bigIntData[i]));
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
return Array.from(this._data);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Clone the tensor
|
|
146
|
+
*/
|
|
147
|
+
clone() {
|
|
148
|
+
this.checkDisposed();
|
|
149
|
+
const TypedArrayCtor = this._data.constructor;
|
|
150
|
+
const clonedData = new TypedArrayCtor(this._data);
|
|
151
|
+
return new EdgeFlowTensor(clonedData, this.shape, this.dtype);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Dispose the tensor and free memory
|
|
155
|
+
*/
|
|
156
|
+
dispose() {
|
|
157
|
+
if (!this._isDisposed) {
|
|
158
|
+
this._isDisposed = true;
|
|
159
|
+
// Help garbage collection - use Object.assign to avoid type issues
|
|
160
|
+
Object.assign(this, { _data: null });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get value at specific indices
|
|
165
|
+
*/
|
|
166
|
+
get(...indices) {
|
|
167
|
+
this.checkDisposed();
|
|
168
|
+
if (indices.length !== this.shape.length) {
|
|
169
|
+
throw new EdgeFlowError(`Expected ${this.shape.length} indices, got ${indices.length}`, ErrorCodes.INVALID_ARGUMENT, { expectedIndices: this.shape.length, gotIndices: indices.length });
|
|
170
|
+
}
|
|
171
|
+
let flatIndex = 0;
|
|
172
|
+
let stride = 1;
|
|
173
|
+
for (let i = this.shape.length - 1; i >= 0; i--) {
|
|
174
|
+
const idx = indices[i] ?? 0;
|
|
175
|
+
const dim = this.shape[i] ?? 1;
|
|
176
|
+
if (idx < 0 || idx >= dim) {
|
|
177
|
+
throw new EdgeFlowError(`Index ${idx} out of bounds for dimension ${i} with size ${dim}`, ErrorCodes.INVALID_ARGUMENT, { index: idx, dimension: i, size: dim });
|
|
178
|
+
}
|
|
179
|
+
flatIndex += idx * stride;
|
|
180
|
+
stride *= dim;
|
|
181
|
+
}
|
|
182
|
+
return Number(this._data[flatIndex] ?? 0);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Set value at specific indices
|
|
186
|
+
*/
|
|
187
|
+
set(value, ...indices) {
|
|
188
|
+
this.checkDisposed();
|
|
189
|
+
if (indices.length !== this.shape.length) {
|
|
190
|
+
throw new EdgeFlowError(`Expected ${this.shape.length} indices, got ${indices.length}`, ErrorCodes.INVALID_ARGUMENT, { expectedIndices: this.shape.length, gotIndices: indices.length });
|
|
191
|
+
}
|
|
192
|
+
let flatIndex = 0;
|
|
193
|
+
let stride = 1;
|
|
194
|
+
for (let i = this.shape.length - 1; i >= 0; i--) {
|
|
195
|
+
const idx = indices[i] ?? 0;
|
|
196
|
+
const dim = this.shape[i] ?? 1;
|
|
197
|
+
if (idx < 0 || idx >= dim) {
|
|
198
|
+
throw new EdgeFlowError(`Index ${idx} out of bounds for dimension ${i} with size ${dim}`, ErrorCodes.INVALID_ARGUMENT, { index: idx, dimension: i, size: dim });
|
|
199
|
+
}
|
|
200
|
+
flatIndex += idx * stride;
|
|
201
|
+
stride *= dim;
|
|
202
|
+
}
|
|
203
|
+
this._data[flatIndex] = value;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Reshape the tensor (returns new tensor)
|
|
207
|
+
*/
|
|
208
|
+
reshape(newShape) {
|
|
209
|
+
this.checkDisposed();
|
|
210
|
+
const newSize = calculateSize(newShape);
|
|
211
|
+
if (newSize !== this.size) {
|
|
212
|
+
throw new EdgeFlowError(`Cannot reshape tensor of size ${this.size} to shape ${JSON.stringify(newShape)} (size ${newSize})`, ErrorCodes.TENSOR_SHAPE_MISMATCH, { currentSize: this.size, newSize, newShape });
|
|
213
|
+
}
|
|
214
|
+
const TypedArrayCtor = this._data.constructor;
|
|
215
|
+
const clonedData = new TypedArrayCtor(this._data);
|
|
216
|
+
return new EdgeFlowTensor(clonedData, newShape, this.dtype);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Transpose the tensor (2D only for now)
|
|
220
|
+
*/
|
|
221
|
+
transpose() {
|
|
222
|
+
this.checkDisposed();
|
|
223
|
+
if (this.shape.length !== 2) {
|
|
224
|
+
throw new EdgeFlowError('Transpose is currently only supported for 2D tensors', ErrorCodes.NOT_IMPLEMENTED, { shape: this.shape });
|
|
225
|
+
}
|
|
226
|
+
const [rows, cols] = this.shape;
|
|
227
|
+
const result = new Float32Array(this.size);
|
|
228
|
+
for (let i = 0; i < rows; i++) {
|
|
229
|
+
for (let j = 0; j < cols; j++) {
|
|
230
|
+
result[j * rows + i] = Number(this._data[i * cols + j] ?? 0);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return new EdgeFlowTensor(result, [cols, rows], this.dtype);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Create string representation
|
|
237
|
+
*/
|
|
238
|
+
toString() {
|
|
239
|
+
return `Tensor(shape=[${this.shape.join(', ')}], dtype=${this.dtype})`;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// ============================================================================
|
|
243
|
+
// Tensor Factory Functions
|
|
244
|
+
// ============================================================================
|
|
245
|
+
/**
|
|
246
|
+
* Create a tensor from data
|
|
247
|
+
*/
|
|
248
|
+
export function tensor(data, shape, dtype = 'float32') {
|
|
249
|
+
// Handle nested arrays
|
|
250
|
+
if (Array.isArray(data) && data.length > 0 && Array.isArray(data[0])) {
|
|
251
|
+
const rows = data.length;
|
|
252
|
+
const cols = data[0].length;
|
|
253
|
+
const flatData = [];
|
|
254
|
+
for (const row of data) {
|
|
255
|
+
if (row.length !== cols) {
|
|
256
|
+
throw new EdgeFlowError('Nested arrays must have consistent dimensions', ErrorCodes.INVALID_ARGUMENT);
|
|
257
|
+
}
|
|
258
|
+
flatData.push(...row);
|
|
259
|
+
}
|
|
260
|
+
return new EdgeFlowTensor(flatData, shape ?? [rows, cols], dtype);
|
|
261
|
+
}
|
|
262
|
+
// Infer shape if not provided
|
|
263
|
+
const inferredShape = shape ?? [data.length];
|
|
264
|
+
return new EdgeFlowTensor(data, inferredShape, dtype);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Create a tensor filled with zeros
|
|
268
|
+
*/
|
|
269
|
+
export function zeros(shape, dtype = 'float32') {
|
|
270
|
+
const size = calculateSize(shape);
|
|
271
|
+
const TypedArrayCtor = getTypedArrayConstructor(dtype);
|
|
272
|
+
const data = new TypedArrayCtor(size);
|
|
273
|
+
return new EdgeFlowTensor(data, shape, dtype);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Create a tensor filled with ones
|
|
277
|
+
*/
|
|
278
|
+
export function ones(shape, dtype = 'float32') {
|
|
279
|
+
const size = calculateSize(shape);
|
|
280
|
+
const TypedArrayCtor = getTypedArrayConstructor(dtype);
|
|
281
|
+
const data = new TypedArrayCtor(size);
|
|
282
|
+
data.fill(1);
|
|
283
|
+
return new EdgeFlowTensor(data, shape, dtype);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Create a tensor filled with a specific value
|
|
287
|
+
*/
|
|
288
|
+
export function full(shape, value, dtype = 'float32') {
|
|
289
|
+
const size = calculateSize(shape);
|
|
290
|
+
const TypedArrayCtor = getTypedArrayConstructor(dtype);
|
|
291
|
+
const data = new TypedArrayCtor(size);
|
|
292
|
+
data.fill(value);
|
|
293
|
+
return new EdgeFlowTensor(data, shape, dtype);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Create a tensor with random values between 0 and 1
|
|
297
|
+
*/
|
|
298
|
+
export function random(shape, dtype = 'float32') {
|
|
299
|
+
const size = calculateSize(shape);
|
|
300
|
+
const data = new Float32Array(size);
|
|
301
|
+
for (let i = 0; i < size; i++) {
|
|
302
|
+
data[i] = Math.random();
|
|
303
|
+
}
|
|
304
|
+
return new EdgeFlowTensor(data, shape, dtype);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Create a tensor with random values from normal distribution
|
|
308
|
+
*/
|
|
309
|
+
export function randn(shape, dtype = 'float32') {
|
|
310
|
+
const size = calculateSize(shape);
|
|
311
|
+
const data = new Float32Array(size);
|
|
312
|
+
// Box-Muller transform for normal distribution
|
|
313
|
+
for (let i = 0; i < size; i += 2) {
|
|
314
|
+
const u1 = Math.random();
|
|
315
|
+
const u2 = Math.random();
|
|
316
|
+
const r = Math.sqrt(-2 * Math.log(u1));
|
|
317
|
+
const theta = 2 * Math.PI * u2;
|
|
318
|
+
data[i] = r * Math.cos(theta);
|
|
319
|
+
if (i + 1 < size) {
|
|
320
|
+
data[i + 1] = r * Math.sin(theta);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return new EdgeFlowTensor(data, shape, dtype);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Create a 1D tensor with evenly spaced values
|
|
327
|
+
*/
|
|
328
|
+
export function arange(start, stop, step = 1, dtype = 'float32') {
|
|
329
|
+
if (stop === undefined) {
|
|
330
|
+
stop = start;
|
|
331
|
+
start = 0;
|
|
332
|
+
}
|
|
333
|
+
const size = Math.ceil((stop - start) / step);
|
|
334
|
+
const data = new Float32Array(size);
|
|
335
|
+
for (let i = 0; i < size; i++) {
|
|
336
|
+
data[i] = start + i * step;
|
|
337
|
+
}
|
|
338
|
+
return new EdgeFlowTensor(data, [size], dtype);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Create a 1D tensor with evenly spaced values (specify number of points)
|
|
342
|
+
*/
|
|
343
|
+
export function linspace(start, stop, num = 50, dtype = 'float32') {
|
|
344
|
+
const data = new Float32Array(num);
|
|
345
|
+
const step = (stop - start) / (num - 1);
|
|
346
|
+
for (let i = 0; i < num; i++) {
|
|
347
|
+
data[i] = start + i * step;
|
|
348
|
+
}
|
|
349
|
+
return new EdgeFlowTensor(data, [num], dtype);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Create an identity matrix
|
|
353
|
+
*/
|
|
354
|
+
export function eye(n, dtype = 'float32') {
|
|
355
|
+
const data = new Float32Array(n * n);
|
|
356
|
+
for (let i = 0; i < n; i++) {
|
|
357
|
+
data[i * n + i] = 1;
|
|
358
|
+
}
|
|
359
|
+
return new EdgeFlowTensor(data, [n, n], dtype);
|
|
360
|
+
}
|
|
361
|
+
// ============================================================================
|
|
362
|
+
// Tensor Operations
|
|
363
|
+
// ============================================================================
|
|
364
|
+
/**
|
|
365
|
+
* Element-wise addition
|
|
366
|
+
*/
|
|
367
|
+
export function add(a, b) {
|
|
368
|
+
if (typeof b === 'number') {
|
|
369
|
+
const result = new Float32Array(a.size);
|
|
370
|
+
const aData = a.toFloat32Array();
|
|
371
|
+
for (let i = 0; i < a.size; i++) {
|
|
372
|
+
result[i] = (aData[i] ?? 0) + b;
|
|
373
|
+
}
|
|
374
|
+
return new EdgeFlowTensor(result, a.shape, a.dtype);
|
|
375
|
+
}
|
|
376
|
+
if (a.size !== b.size) {
|
|
377
|
+
throw new EdgeFlowError('Tensor sizes must match for element-wise operations', ErrorCodes.TENSOR_SHAPE_MISMATCH, { aShape: a.shape, bShape: b.shape });
|
|
378
|
+
}
|
|
379
|
+
const result = new Float32Array(a.size);
|
|
380
|
+
const aData = a.toFloat32Array();
|
|
381
|
+
const bData = b.toFloat32Array();
|
|
382
|
+
for (let i = 0; i < a.size; i++) {
|
|
383
|
+
result[i] = (aData[i] ?? 0) + (bData[i] ?? 0);
|
|
384
|
+
}
|
|
385
|
+
return new EdgeFlowTensor(result, a.shape, a.dtype);
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Element-wise subtraction
|
|
389
|
+
*/
|
|
390
|
+
export function sub(a, b) {
|
|
391
|
+
if (typeof b === 'number') {
|
|
392
|
+
const result = new Float32Array(a.size);
|
|
393
|
+
const aData = a.toFloat32Array();
|
|
394
|
+
for (let i = 0; i < a.size; i++) {
|
|
395
|
+
result[i] = (aData[i] ?? 0) - b;
|
|
396
|
+
}
|
|
397
|
+
return new EdgeFlowTensor(result, a.shape, a.dtype);
|
|
398
|
+
}
|
|
399
|
+
if (a.size !== b.size) {
|
|
400
|
+
throw new EdgeFlowError('Tensor sizes must match for element-wise operations', ErrorCodes.TENSOR_SHAPE_MISMATCH, { aShape: a.shape, bShape: b.shape });
|
|
401
|
+
}
|
|
402
|
+
const result = new Float32Array(a.size);
|
|
403
|
+
const aData = a.toFloat32Array();
|
|
404
|
+
const bData = b.toFloat32Array();
|
|
405
|
+
for (let i = 0; i < a.size; i++) {
|
|
406
|
+
result[i] = (aData[i] ?? 0) - (bData[i] ?? 0);
|
|
407
|
+
}
|
|
408
|
+
return new EdgeFlowTensor(result, a.shape, a.dtype);
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Element-wise multiplication
|
|
412
|
+
*/
|
|
413
|
+
export function mul(a, b) {
|
|
414
|
+
if (typeof b === 'number') {
|
|
415
|
+
const result = new Float32Array(a.size);
|
|
416
|
+
const aData = a.toFloat32Array();
|
|
417
|
+
for (let i = 0; i < a.size; i++) {
|
|
418
|
+
result[i] = (aData[i] ?? 0) * b;
|
|
419
|
+
}
|
|
420
|
+
return new EdgeFlowTensor(result, a.shape, a.dtype);
|
|
421
|
+
}
|
|
422
|
+
if (a.size !== b.size) {
|
|
423
|
+
throw new EdgeFlowError('Tensor sizes must match for element-wise operations', ErrorCodes.TENSOR_SHAPE_MISMATCH, { aShape: a.shape, bShape: b.shape });
|
|
424
|
+
}
|
|
425
|
+
const result = new Float32Array(a.size);
|
|
426
|
+
const aData = a.toFloat32Array();
|
|
427
|
+
const bData = b.toFloat32Array();
|
|
428
|
+
for (let i = 0; i < a.size; i++) {
|
|
429
|
+
result[i] = (aData[i] ?? 0) * (bData[i] ?? 0);
|
|
430
|
+
}
|
|
431
|
+
return new EdgeFlowTensor(result, a.shape, a.dtype);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Element-wise division
|
|
435
|
+
*/
|
|
436
|
+
export function div(a, b) {
|
|
437
|
+
if (typeof b === 'number') {
|
|
438
|
+
const result = new Float32Array(a.size);
|
|
439
|
+
const aData = a.toFloat32Array();
|
|
440
|
+
for (let i = 0; i < a.size; i++) {
|
|
441
|
+
result[i] = (aData[i] ?? 0) / b;
|
|
442
|
+
}
|
|
443
|
+
return new EdgeFlowTensor(result, a.shape, a.dtype);
|
|
444
|
+
}
|
|
445
|
+
if (a.size !== b.size) {
|
|
446
|
+
throw new EdgeFlowError('Tensor sizes must match for element-wise operations', ErrorCodes.TENSOR_SHAPE_MISMATCH, { aShape: a.shape, bShape: b.shape });
|
|
447
|
+
}
|
|
448
|
+
const result = new Float32Array(a.size);
|
|
449
|
+
const aData = a.toFloat32Array();
|
|
450
|
+
const bData = b.toFloat32Array();
|
|
451
|
+
for (let i = 0; i < a.size; i++) {
|
|
452
|
+
result[i] = (aData[i] ?? 0) / (bData[i] ?? 0);
|
|
453
|
+
}
|
|
454
|
+
return new EdgeFlowTensor(result, a.shape, a.dtype);
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Matrix multiplication (2D tensors)
|
|
458
|
+
*/
|
|
459
|
+
export function matmul(a, b) {
|
|
460
|
+
if (a.shape.length !== 2 || b.shape.length !== 2) {
|
|
461
|
+
throw new EdgeFlowError('matmul requires 2D tensors', ErrorCodes.INVALID_ARGUMENT, { aShape: a.shape, bShape: b.shape });
|
|
462
|
+
}
|
|
463
|
+
const [m, k1] = a.shape;
|
|
464
|
+
const [k2, n] = b.shape;
|
|
465
|
+
if (k1 !== k2) {
|
|
466
|
+
throw new EdgeFlowError(`Matrix dimensions incompatible for multiplication: (${m}x${k1}) @ (${k2}x${n})`, ErrorCodes.TENSOR_SHAPE_MISMATCH, { aShape: a.shape, bShape: b.shape });
|
|
467
|
+
}
|
|
468
|
+
const result = new Float32Array(m * n);
|
|
469
|
+
const aData = a.toFloat32Array();
|
|
470
|
+
const bData = b.toFloat32Array();
|
|
471
|
+
for (let i = 0; i < m; i++) {
|
|
472
|
+
for (let j = 0; j < n; j++) {
|
|
473
|
+
let sum = 0;
|
|
474
|
+
for (let k = 0; k < k1; k++) {
|
|
475
|
+
sum += (aData[i * k1 + k] ?? 0) * (bData[k * n + j] ?? 0);
|
|
476
|
+
}
|
|
477
|
+
result[i * n + j] = sum;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return new EdgeFlowTensor(result, [m, n], a.dtype);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Softmax activation
|
|
484
|
+
*/
|
|
485
|
+
export function softmax(t, axis = -1) {
|
|
486
|
+
const data = t.toFloat32Array();
|
|
487
|
+
const result = new Float32Array(t.size);
|
|
488
|
+
// Handle negative axis
|
|
489
|
+
const actualAxis = axis < 0 ? t.shape.length + axis : axis;
|
|
490
|
+
if (actualAxis < 0 || actualAxis >= t.shape.length) {
|
|
491
|
+
throw new EdgeFlowError(`Invalid axis ${axis} for tensor with ${t.shape.length} dimensions`, ErrorCodes.INVALID_ARGUMENT, { axis, shape: t.shape });
|
|
492
|
+
}
|
|
493
|
+
// For 1D tensors
|
|
494
|
+
if (t.shape.length === 1) {
|
|
495
|
+
let max = -Infinity;
|
|
496
|
+
for (let i = 0; i < t.size; i++) {
|
|
497
|
+
if ((data[i] ?? 0) > max)
|
|
498
|
+
max = data[i] ?? 0;
|
|
499
|
+
}
|
|
500
|
+
let sum = 0;
|
|
501
|
+
for (let i = 0; i < t.size; i++) {
|
|
502
|
+
result[i] = Math.exp((data[i] ?? 0) - max);
|
|
503
|
+
sum += result[i] ?? 0;
|
|
504
|
+
}
|
|
505
|
+
for (let i = 0; i < t.size; i++) {
|
|
506
|
+
result[i] = (result[i] ?? 0) / sum;
|
|
507
|
+
}
|
|
508
|
+
return new EdgeFlowTensor(result, t.shape, t.dtype);
|
|
509
|
+
}
|
|
510
|
+
// For 2D tensors along last axis
|
|
511
|
+
if (t.shape.length === 2 && actualAxis === 1) {
|
|
512
|
+
const [rows, cols] = t.shape;
|
|
513
|
+
for (let i = 0; i < rows; i++) {
|
|
514
|
+
let max = -Infinity;
|
|
515
|
+
for (let j = 0; j < cols; j++) {
|
|
516
|
+
if ((data[i * cols + j] ?? 0) > max)
|
|
517
|
+
max = data[i * cols + j] ?? 0;
|
|
518
|
+
}
|
|
519
|
+
let sum = 0;
|
|
520
|
+
for (let j = 0; j < cols; j++) {
|
|
521
|
+
result[i * cols + j] = Math.exp((data[i * cols + j] ?? 0) - max);
|
|
522
|
+
sum += result[i * cols + j] ?? 0;
|
|
523
|
+
}
|
|
524
|
+
for (let j = 0; j < cols; j++) {
|
|
525
|
+
result[i * cols + j] = (result[i * cols + j] ?? 0) / sum;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return new EdgeFlowTensor(result, t.shape, t.dtype);
|
|
529
|
+
}
|
|
530
|
+
throw new EdgeFlowError('Softmax currently only supports 1D tensors or 2D tensors along the last axis', ErrorCodes.NOT_IMPLEMENTED, { shape: t.shape, axis });
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* ReLU activation
|
|
534
|
+
*/
|
|
535
|
+
export function relu(t) {
|
|
536
|
+
const data = t.toFloat32Array();
|
|
537
|
+
const result = new Float32Array(t.size);
|
|
538
|
+
for (let i = 0; i < t.size; i++) {
|
|
539
|
+
result[i] = Math.max(0, data[i] ?? 0);
|
|
540
|
+
}
|
|
541
|
+
return new EdgeFlowTensor(result, t.shape, t.dtype);
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Sigmoid activation
|
|
545
|
+
*/
|
|
546
|
+
export function sigmoid(t) {
|
|
547
|
+
const data = t.toFloat32Array();
|
|
548
|
+
const result = new Float32Array(t.size);
|
|
549
|
+
for (let i = 0; i < t.size; i++) {
|
|
550
|
+
result[i] = 1 / (1 + Math.exp(-(data[i] ?? 0)));
|
|
551
|
+
}
|
|
552
|
+
return new EdgeFlowTensor(result, t.shape, t.dtype);
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Tanh activation
|
|
556
|
+
*/
|
|
557
|
+
export function tanh(t) {
|
|
558
|
+
const data = t.toFloat32Array();
|
|
559
|
+
const result = new Float32Array(t.size);
|
|
560
|
+
for (let i = 0; i < t.size; i++) {
|
|
561
|
+
result[i] = Math.tanh(data[i] ?? 0);
|
|
562
|
+
}
|
|
563
|
+
return new EdgeFlowTensor(result, t.shape, t.dtype);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Sum all elements or along an axis
|
|
567
|
+
*/
|
|
568
|
+
export function sum(t, axis) {
|
|
569
|
+
const data = t.toFloat32Array();
|
|
570
|
+
if (axis === undefined) {
|
|
571
|
+
let total = 0;
|
|
572
|
+
for (let i = 0; i < t.size; i++) {
|
|
573
|
+
total += data[i] ?? 0;
|
|
574
|
+
}
|
|
575
|
+
return total;
|
|
576
|
+
}
|
|
577
|
+
// Handle negative axis
|
|
578
|
+
const actualAxis = axis < 0 ? t.shape.length + axis : axis;
|
|
579
|
+
if (actualAxis < 0 || actualAxis >= t.shape.length) {
|
|
580
|
+
throw new EdgeFlowError(`Invalid axis ${axis} for tensor with ${t.shape.length} dimensions`, ErrorCodes.INVALID_ARGUMENT, { axis, shape: t.shape });
|
|
581
|
+
}
|
|
582
|
+
// Calculate new shape
|
|
583
|
+
const newShape = [...t.shape];
|
|
584
|
+
newShape.splice(actualAxis, 1);
|
|
585
|
+
if (newShape.length === 0) {
|
|
586
|
+
let total = 0;
|
|
587
|
+
for (let i = 0; i < t.size; i++) {
|
|
588
|
+
total += data[i] ?? 0;
|
|
589
|
+
}
|
|
590
|
+
return total;
|
|
591
|
+
}
|
|
592
|
+
// For 2D sum along axis
|
|
593
|
+
if (t.shape.length === 2) {
|
|
594
|
+
const [rows, cols] = t.shape;
|
|
595
|
+
if (actualAxis === 0) {
|
|
596
|
+
const result = new Float32Array(cols);
|
|
597
|
+
for (let j = 0; j < cols; j++) {
|
|
598
|
+
for (let i = 0; i < rows; i++) {
|
|
599
|
+
result[j] = (result[j] ?? 0) + (data[i * cols + j] ?? 0);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return new EdgeFlowTensor(result, [cols], t.dtype);
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
const result = new Float32Array(rows);
|
|
606
|
+
for (let i = 0; i < rows; i++) {
|
|
607
|
+
for (let j = 0; j < cols; j++) {
|
|
608
|
+
result[i] = (result[i] ?? 0) + (data[i * cols + j] ?? 0);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return new EdgeFlowTensor(result, [rows], t.dtype);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
throw new EdgeFlowError('Sum along axis currently only supports up to 2D tensors', ErrorCodes.NOT_IMPLEMENTED, { shape: t.shape, axis });
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Mean of all elements or along an axis
|
|
618
|
+
*/
|
|
619
|
+
export function mean(t, axis) {
|
|
620
|
+
if (axis === undefined) {
|
|
621
|
+
return sum(t) / t.size;
|
|
622
|
+
}
|
|
623
|
+
const result = sum(t, axis);
|
|
624
|
+
if (typeof result === 'number') {
|
|
625
|
+
return result / (t.shape[axis] ?? 1);
|
|
626
|
+
}
|
|
627
|
+
const axisSize = t.shape[axis] ?? 1;
|
|
628
|
+
return div(result, axisSize);
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Argmax - return index of maximum value
|
|
632
|
+
*/
|
|
633
|
+
export function argmax(t, axis) {
|
|
634
|
+
const data = t.toFloat32Array();
|
|
635
|
+
if (axis === undefined) {
|
|
636
|
+
let maxIdx = 0;
|
|
637
|
+
let maxVal = data[0] ?? -Infinity;
|
|
638
|
+
for (let i = 1; i < t.size; i++) {
|
|
639
|
+
if ((data[i] ?? -Infinity) > maxVal) {
|
|
640
|
+
maxVal = data[i] ?? -Infinity;
|
|
641
|
+
maxIdx = i;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return maxIdx;
|
|
645
|
+
}
|
|
646
|
+
// Handle negative axis
|
|
647
|
+
const actualAxis = axis < 0 ? t.shape.length + axis : axis;
|
|
648
|
+
// For 2D along last axis
|
|
649
|
+
if (t.shape.length === 2 && actualAxis === 1) {
|
|
650
|
+
const [rows, cols] = t.shape;
|
|
651
|
+
const result = new Float32Array(rows);
|
|
652
|
+
for (let i = 0; i < rows; i++) {
|
|
653
|
+
let maxIdx = 0;
|
|
654
|
+
let maxVal = data[i * cols] ?? -Infinity;
|
|
655
|
+
for (let j = 1; j < cols; j++) {
|
|
656
|
+
if ((data[i * cols + j] ?? -Infinity) > maxVal) {
|
|
657
|
+
maxVal = data[i * cols + j] ?? -Infinity;
|
|
658
|
+
maxIdx = j;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
result[i] = maxIdx;
|
|
662
|
+
}
|
|
663
|
+
return new EdgeFlowTensor(result, [rows], 'int32');
|
|
664
|
+
}
|
|
665
|
+
throw new EdgeFlowError('Argmax along axis currently only supports 2D tensors along the last axis', ErrorCodes.NOT_IMPLEMENTED, { shape: t.shape, axis });
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Concatenate tensors along an axis
|
|
669
|
+
*/
|
|
670
|
+
export function concat(tensors, axis = 0) {
|
|
671
|
+
if (tensors.length === 0) {
|
|
672
|
+
throw new EdgeFlowError('Cannot concatenate empty array of tensors', ErrorCodes.INVALID_ARGUMENT);
|
|
673
|
+
}
|
|
674
|
+
if (tensors.length === 1) {
|
|
675
|
+
return tensors[0]?.clone() ?? zeros([0]);
|
|
676
|
+
}
|
|
677
|
+
const first = tensors[0];
|
|
678
|
+
if (!first) {
|
|
679
|
+
throw new EdgeFlowError('First tensor is undefined', ErrorCodes.INVALID_ARGUMENT);
|
|
680
|
+
}
|
|
681
|
+
// Handle negative axis
|
|
682
|
+
const actualAxis = axis < 0 ? first.shape.length + axis : axis;
|
|
683
|
+
// Validate shapes
|
|
684
|
+
for (let i = 1; i < tensors.length; i++) {
|
|
685
|
+
const t = tensors[i];
|
|
686
|
+
if (!t)
|
|
687
|
+
continue;
|
|
688
|
+
if (t.shape.length !== first.shape.length) {
|
|
689
|
+
throw new EdgeFlowError('All tensors must have the same number of dimensions', ErrorCodes.TENSOR_SHAPE_MISMATCH);
|
|
690
|
+
}
|
|
691
|
+
for (let j = 0; j < first.shape.length; j++) {
|
|
692
|
+
if (j !== actualAxis && first.shape[j] !== t.shape[j]) {
|
|
693
|
+
throw new EdgeFlowError(`Shape mismatch at dimension ${j}`, ErrorCodes.TENSOR_SHAPE_MISMATCH);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
// Calculate new shape
|
|
698
|
+
const newShape = [...first.shape];
|
|
699
|
+
let totalAxisSize = 0;
|
|
700
|
+
for (const t of tensors) {
|
|
701
|
+
if (t)
|
|
702
|
+
totalAxisSize += t.shape[actualAxis] ?? 0;
|
|
703
|
+
}
|
|
704
|
+
newShape[actualAxis] = totalAxisSize;
|
|
705
|
+
// For 1D concatenation
|
|
706
|
+
if (first.shape.length === 1) {
|
|
707
|
+
const result = new Float32Array(totalAxisSize);
|
|
708
|
+
let offset = 0;
|
|
709
|
+
for (const t of tensors) {
|
|
710
|
+
if (!t)
|
|
711
|
+
continue;
|
|
712
|
+
result.set(t.toFloat32Array(), offset);
|
|
713
|
+
offset += t.size;
|
|
714
|
+
}
|
|
715
|
+
return new EdgeFlowTensor(result, newShape, first.dtype);
|
|
716
|
+
}
|
|
717
|
+
throw new EdgeFlowError('Concatenation currently only supports 1D tensors', ErrorCodes.NOT_IMPLEMENTED);
|
|
718
|
+
}
|
|
719
|
+
//# sourceMappingURL=tensor.js.map
|