@danielsimonjr/mathts-tensor 0.1.0 → 0.1.2

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/dist/index.d.ts CHANGED
@@ -1,3 +1,81 @@
1
+ import { DenseMatrix } from '@danielsimonjr/mathts-matrix';
2
+
3
+ /**
4
+ * Index — an immutable value type carrying a unique identity, a dimension,
5
+ * optional tags, and a prime level. Two indices *match* (and will be
6
+ * contracted by `Tensor.contract`) iff their `id` and `primeLevel` agree.
7
+ * Dimension agreement is validated at contraction time, not at matching time.
8
+ *
9
+ * Inspired by ITensors.jl's `Index` primitive.
10
+ */
11
+ interface IndexOpts {
12
+ /** Human-readable name; not used for matching but useful in errors / debug. */
13
+ name?: string;
14
+ /**
15
+ * Free-form tag list. Two indices with the SAME id but DIFFERENT tags still
16
+ * match by id.
17
+ */
18
+ tags?: ReadonlyArray<string>;
19
+ /**
20
+ * Prime level (ITensor's "prime"). Two indices with the same id but different
21
+ * prime levels do NOT match.
22
+ */
23
+ primeLevel?: number;
24
+ }
25
+ /**
26
+ * A first-class index value. Each `new Index(...)` call mints a fresh `id`
27
+ * (Symbol) that can never collide with another. Methods that would mutate
28
+ * state instead return a new `Index` sharing the same `id`.
29
+ */
30
+ declare class Index {
31
+ readonly id: symbol;
32
+ readonly dim: number;
33
+ readonly name?: string;
34
+ readonly tags: ReadonlyArray<string>;
35
+ readonly primeLevel: number;
36
+ constructor(dim: number, opts?: IndexOpts);
37
+ /**
38
+ * Internal constructor used by mutating-style methods that need to share the
39
+ * same `id` with a different primeLevel / tags. Not exposed publicly.
40
+ */
41
+ private static _from;
42
+ /**
43
+ * Return a new Index with `primeLevel` incremented by `by` (default 1).
44
+ */
45
+ prime(by?: number): Index;
46
+ /**
47
+ * Return a new Index with `primeLevel` reset to 0.
48
+ */
49
+ noprime(): Index;
50
+ /**
51
+ * Return a new Index with `tag` added to its tag list (no-op if already present).
52
+ */
53
+ addTag(tag: string): Index;
54
+ /**
55
+ * Return a new Index with `tag` removed from its tag list (no-op if absent).
56
+ */
57
+ removeTag(tag: string): Index;
58
+ /**
59
+ * True if `tag` is in this index's tag list.
60
+ */
61
+ hasTag(tag: string): boolean;
62
+ /**
63
+ * Two indices match iff they share the same `id` AND the same `primeLevel`.
64
+ * Dimension is NOT used for matching — it is validated at contraction time.
65
+ */
66
+ matches(other: Index): boolean;
67
+ toString(): string;
68
+ }
69
+ /**
70
+ * Convenience factory: `const i = idx(3, 'i')`.
71
+ *
72
+ * ```ts
73
+ * const i = idx(3, 'i'); // dim=3, name='i', primeLevel=0
74
+ * const j = idx(4, 'j', { tags: ['site'] });
75
+ * ```
76
+ */
77
+ declare function idx(dim: number, name?: string, opts?: Omit<IndexOpts, 'name'>): Index;
78
+
1
79
  /**
2
80
  * Tensor — rank-N, Float64Array-backed, row-major dense tensor. The
3
81
  * compute primitive behind @danielsimonjr/mathts-tensor. Construction +
@@ -9,6 +87,7 @@
9
87
  * arbitrary rank, filling the `Matrix<T>` base class's documented
10
88
  * "generic template for future rank-N support".
11
89
  */
90
+
12
91
  type NestedArray = number | NestedArray[];
13
92
  interface EinsumSpec {
14
93
  readonly contractions: ReadonlyArray<{
@@ -22,25 +101,1022 @@ interface EinsumSpec {
22
101
  declare class Tensor {
23
102
  readonly shape: ReadonlyArray<number>;
24
103
  readonly data: Float64Array;
25
- constructor(shape: ReadonlyArray<number>, data: Float64Array);
104
+ /** Optional per-axis Index labels. When set, enables `contract`, `replaceIndex`, `axisOf`. */
105
+ readonly axisLabels?: ReadonlyArray<Index>;
106
+ constructor(shape: ReadonlyArray<number>, data: Float64Array, axisLabels?: ReadonlyArray<Index>);
26
107
  static sizeOf(shape: ReadonlyArray<number>): number;
27
108
  static rowMajorStrides(shape: ReadonlyArray<number>): number[];
28
109
  static fromNested(data: NestedArray, shape: ReadonlyArray<number>): Tensor;
29
110
  static identity(n: number): Tensor;
30
111
  toNested(): NestedArray;
112
+ /**
113
+ * Build a rank-2 Tensor from a native DenseMatrix. Bridges the tensor and
114
+ * matrix packages so matrix data can flow into rank-N / autodiff code.
115
+ */
116
+ static fromDenseMatrix(m: DenseMatrix): Tensor;
117
+ /**
118
+ * Convert a rank-2 Tensor to a native DenseMatrix. Throws for any other rank.
119
+ */
120
+ toDenseMatrix(): DenseMatrix;
31
121
  private sameShape;
32
122
  private elementwise;
33
- add(other: Tensor): Tensor;
34
- sub(other: Tensor): Tensor;
35
- mul(other: Tensor): Tensor;
123
+ add(other: Tensor | number): Tensor;
124
+ sub(other: Tensor | number): Tensor;
125
+ mul(other: Tensor | number): Tensor;
36
126
  scale(k: number): Tensor;
37
127
  normInf(): number;
128
+ /**
129
+ * Compute the broadcast output shape of two shapes following NumPy rules:
130
+ * right-align the two shapes; a dimension of 1 broadcasts against any size;
131
+ * missing leading axes are treated as length-1.
132
+ * Throws with message including both shape arrays on incompatibility.
133
+ */
134
+ static broadcastShape(a: ReadonlyArray<number>, b: ReadonlyArray<number>): number[];
135
+ /**
136
+ * Apply a binary elementwise op with NumPy-style broadcasting.
137
+ * Both tensors are read via virtual broadcast indices — no data is copied
138
+ * until the output is written.
139
+ */
140
+ private static broadcastOp;
141
+ /**
142
+ * Generic strided reduction over a set of axes.
143
+ *
144
+ * `init` is the neutral element; `combine` folds one new value into the
145
+ * accumulator; `finalise` (optional) post-processes the accumulator once
146
+ * all elements have been visited.
147
+ */
148
+ private reduceAxes;
149
+ /**
150
+ * Sum elements along the given axis/axes (or all axes if omitted).
151
+ * `keepDims: true` preserves reduced axes as length-1.
152
+ */
153
+ sum(axis?: number | ReadonlyArray<number>, opts?: {
154
+ keepDims?: boolean;
155
+ }): Tensor;
156
+ /**
157
+ * Arithmetic mean along the given axis/axes (or all axes if omitted).
158
+ */
159
+ mean(axis?: number | ReadonlyArray<number>, opts?: {
160
+ keepDims?: boolean;
161
+ }): Tensor;
162
+ /**
163
+ * Element-wise maximum along the given axis/axes (or all axes if omitted).
164
+ * NaN propagates (matches NumPy default behaviour).
165
+ */
166
+ max(axis?: number | ReadonlyArray<number>, opts?: {
167
+ keepDims?: boolean;
168
+ }): Tensor;
169
+ /**
170
+ * Element-wise minimum along the given axis/axes (or all axes if omitted).
171
+ * NaN propagates (matches NumPy default behaviour).
172
+ */
173
+ min(axis?: number | ReadonlyArray<number>, opts?: {
174
+ keepDims?: boolean;
175
+ }): Tensor;
176
+ /**
177
+ * Product of elements along the given axis/axes (or all axes if omitted).
178
+ */
179
+ prod(axis?: number | ReadonlyArray<number>, opts?: {
180
+ keepDims?: boolean;
181
+ }): Tensor;
182
+ /**
183
+ * p-norm of the tensor.
184
+ *
185
+ * - `p = 2` (default): Euclidean / Frobenius norm — `sqrt(sum x_i^2)`.
186
+ * - `p = 'fro'`: alias for p=2 (Frobenius).
187
+ * - `p = 'inf'`: max of absolute values.
188
+ * - `p = '-inf'`: min of absolute values.
189
+ * - numeric p: `(sum |x_i|^p)^(1/p)`.
190
+ *
191
+ * When `axis` is given, the norm is computed along that single axis and the
192
+ * result has rank reduced by 1 (or stays the same with `keepDims`).
193
+ * When `axis` is omitted, the norm is computed over all elements.
194
+ */
195
+ norm(opts?: {
196
+ p?: number | 'inf' | '-inf' | 'fro';
197
+ axis?: number;
198
+ keepDims?: boolean;
199
+ }): Tensor;
200
+ /**
201
+ * Generalised dot product (NumPy `tensordot`) over explicitly specified axis pairs.
202
+ *
203
+ * `axes[i] = [a, b]` contracts axis `a` of `this` with axis `b` of `other`.
204
+ * The result shape is `this`'s non-contracted axes followed by `other`'s
205
+ * non-contracted axes (NumPy convention).
206
+ *
207
+ * Delegates to `Tensor.einsum` under the hood.
208
+ */
209
+ tensordot(other: Tensor, axes: ReadonlyArray<readonly [number, number]>): Tensor;
210
+ /**
211
+ * Normalise an `axis` argument into a sorted list of axis indices.
212
+ * `undefined` ⇒ all axes (reduce everything to a rank-0 scalar).
213
+ */
214
+ private resolveAxes;
38
215
  private static forEachIndex;
39
216
  private static flatIndex;
40
217
  reshape(shape: ReadonlyArray<number>): Tensor;
41
218
  transpose(perm?: ReadonlyArray<number>): Tensor;
42
219
  matMul(other: Tensor): Tensor;
43
220
  static einsum(spec: EinsumSpec, ...operands: Tensor[]): Tensor;
221
+ /**
222
+ * Return the axis position (0-based) of a labelled index.
223
+ * Throws if the index is not found in `axisLabels`.
224
+ */
225
+ axisOf(index: Index): number;
226
+ /**
227
+ * Return a new Tensor with the same data and shape but with `oldIndex`
228
+ * swapped for `newIndex` in `axisLabels`.
229
+ * Throws if `oldIndex` is not found.
230
+ */
231
+ replaceIndex(oldIndex: Index, newIndex: Index): Tensor;
232
+ /**
233
+ * Contract this tensor with `other` over every shared index (matched by
234
+ * `Index.matches`). Both operands must carry `axisLabels`.
235
+ *
236
+ * The result tensor carries the non-shared axes from `this` followed by
237
+ * the non-shared axes from `other` as its `axisLabels`.
238
+ *
239
+ * Throws:
240
+ * - If either operand lacks `axisLabels`.
241
+ * - If there are no shared indices (use `einsum` or an explicit outer product).
242
+ * - If a shared index has mismatched dimensions on the two operands.
243
+ */
244
+ contract(other: Tensor): Tensor;
245
+ }
246
+
247
+ /**
248
+ * tensorSvd — truncated SVD for rank-N tensors.
249
+ *
250
+ * Partitions the axes of `t` into "row" axes (joining with U) and "col" axes
251
+ * (the remaining axes, joining with V), permutes + reshapes `t` into a 2-D
252
+ * matrix, calls the existing DenseMatrix SVD primitive, optionally truncates
253
+ * the singular-value spectrum, and reshapes U / S / V back into Tensors.
254
+ *
255
+ * Phase-2 note: `rowAxes` is positional-integer-only. The named-Index overload
256
+ * (accepting `ReadonlyArray<Index>`) is reserved for the Phase-1-enabled
257
+ * follow-up once `Index` is part of the Tensor surface.
258
+ *
259
+ * The `joiningIndexName` option is accepted for forward-compatibility with the
260
+ * labelled-tensor extension but is unused in Phase 2 — the output Tensors carry
261
+ * no `axisLabels`.
262
+ */
263
+
264
+ interface TensorSvdOpts {
265
+ /** If set, keep at most `maxdim` singular values. */
266
+ maxdim?: number;
267
+ /**
268
+ * If set, drop singular values whose absolute value is strictly less than
269
+ * `cutoff`. Default is 0 (no cutoff). The test is `s >= cutoff`.
270
+ */
271
+ cutoff?: number;
272
+ /**
273
+ * Accepted for forward-compatibility with the labelled-tensor extension
274
+ * (Phase-2-extension follow-up). Currently unused — output Tensors carry
275
+ * no `axisLabels` in Phase 2.
276
+ */
277
+ joiningIndexName?: string;
278
+ }
279
+ interface TensorSvdResult {
280
+ /** U factor reshaped to [...rowDims, k]. */
281
+ U: Tensor;
282
+ /** Singular values as a rank-1 Tensor of shape [k]. */
283
+ S: Tensor;
284
+ /** V factor reshaped to [k, ...colDims]. */
285
+ V: Tensor;
286
+ /** Number of singular values retained after truncation. */
287
+ truncatedDim: number;
288
+ /** Sum of squares of the dropped singular values (Frobenius-norm squared of the discarded part). */
289
+ truncationError: number;
290
+ }
291
+ /**
292
+ * Compute the (optionally truncated) SVD of tensor `t` by partitioning its
293
+ * axes into "row" and "col" groups.
294
+ *
295
+ * @param t - Input tensor of any rank ≥ 1.
296
+ * @param rowAxes - Axis indices (integers) whose dimensions form the row
297
+ * dimension of the effective 2-D matrix. The remaining axes
298
+ * form the column dimension. The order of `rowAxes` controls
299
+ * the layout of the U output tensor.
300
+ * @param opts - Optional truncation / naming settings.
301
+ */
302
+ declare function tensorSvd(t: Tensor, rowAxes: ReadonlyArray<number>, opts?: TensorSvdOpts): TensorSvdResult;
303
+
304
+ /**
305
+ * randomTensor — construct a Tensor filled with pseudo-random values.
306
+ *
307
+ * Supported distributions:
308
+ * 'uniform' — U(0, 1) via the seeded PRNG or Math.random().
309
+ * 'normal' — N(0, 1) via Box–Muller transform on the underlying PRNG.
310
+ * 'orthogonal' — Uniform on the orthogonal group O(n) via QR decomposition
311
+ * of a normal random matrix. Rank-2 shapes only.
312
+ *
313
+ * Seeded RNG: Mulberry32 — a small, fast, high-quality 32-bit PRNG.
314
+ * Reference: Tommy Ettinger, https://gist.github.com/tommyettinger/46a874533244883189143505d203312c
315
+ *
316
+ * WARNING: The seeded path is NOT cryptographically secure. Do not use for
317
+ * security-sensitive purposes (key generation, nonces, etc.).
318
+ */
319
+
320
+ interface RandomTensorOpts {
321
+ /**
322
+ * Distribution to sample from.
323
+ *
324
+ * - `'uniform'` — U(0, 1).
325
+ * - `'normal'` — N(0, 1) via Box–Muller transform.
326
+ * - `'orthogonal'` — uniform on the orthogonal group; rank-2 shape only.
327
+ *
328
+ * @default 'uniform'
329
+ */
330
+ distribution?: 'uniform' | 'normal' | 'orthogonal';
331
+ /**
332
+ * Integer seed for the Mulberry32 PRNG. When omitted, Math.random() is
333
+ * used (non-reproducible). NOT cryptographically secure.
334
+ */
335
+ seed?: number;
336
+ /**
337
+ * Optional axis labels forwarded into the new Tensor's constructor.
338
+ * Requires Phase-1 `axisLabels` support in the Tensor constructor
339
+ * (constructor signature: `Tensor(shape, data, axisLabels?)`).
340
+ */
341
+ axisLabels?: ReadonlyArray<Index>;
342
+ }
343
+ /**
344
+ * Construct a Tensor of the given `shape` filled with pseudo-random values.
345
+ *
346
+ * @param shape - Array of positive integers defining the tensor dimensions.
347
+ * @param opts - Optional configuration (distribution, seed, axisLabels).
348
+ * @returns A new Tensor.
349
+ *
350
+ * @example
351
+ * ```ts
352
+ * // 3x4 uniform random tensor
353
+ * const t = randomTensor([3, 4]);
354
+ *
355
+ * // Seeded normal random vector (reproducible)
356
+ * const v = randomTensor([100], { distribution: 'normal', seed: 42 });
357
+ *
358
+ * // 8x8 random orthogonal matrix
359
+ * const O = randomTensor([8, 8], { distribution: 'orthogonal', seed: 7 });
360
+ * ```
361
+ */
362
+ declare function randomTensor(shape: ReadonlyArray<number>, opts?: RandomTensorOpts): Tensor;
363
+
364
+ /**
365
+ * contractNetwork — given an ordered list of Tensors (each carrying
366
+ * `axisLabels`), find the pairwise-contraction order minimising the total
367
+ * FLOP count and execute it. ITensor's `optimal_contraction_sequence`
368
+ * equivalent.
369
+ *
370
+ * Two algorithms are provided:
371
+ *
372
+ * - 'exact' — bitmask dynamic programming. For every non-empty subset
373
+ * S ⊂ {0..N-1} of input tensors, we compute `bestCost[S]` and the
374
+ * partition (S₁, S₂) achieving it (S = S₁ ⊔ S₂, both non-empty).
375
+ * Transition:
376
+ *
377
+ * bestCost[S] = min over all proper subset pairs (S₁, S₂) of
378
+ * bestCost[S₁] + bestCost[S₂] + pairwiseCost(net(S₁), net(S₂))
379
+ *
380
+ * Base: bestCost[{i}] = 0. Subset enumeration via the standard
381
+ * submask trick `for (sub = (mask-1) & mask; sub > 0; sub = (sub-1) & mask)`
382
+ * visits every non-empty proper subset in O(3^N) total.
383
+ *
384
+ * Determinism: ties broken by preferring the lexicographically smaller
385
+ * (subsetMask₁, subsetMask₂) where subsetMask₁ ≤ subsetMask₂.
386
+ *
387
+ * Bitmask encoding: bit `i` of a 32-bit integer is set iff input tensor
388
+ * index `i` is part of the subset. So `0b1011 = {0, 1, 3}`. We use
389
+ * ordinary JavaScript numbers (safe up to 2^32 for bitwise ops; capped
390
+ * at N ≤ 30 for headroom).
391
+ *
392
+ * - 'greedy' — Hendrickson–Sundaram. Maintain a working list of
393
+ * intermediate tensor shapes. At each step, choose the pair (i, j) with
394
+ * the cheapest pairwise contraction cost and replace them with their
395
+ * contraction. Repeat until one remains. O(N³) per step × O(N) steps.
396
+ *
397
+ * Cost model: for a pairwise contraction of two intermediate tensors with
398
+ * shared-axis dimension product `D_shared` and free-axis dimension products
399
+ * `D_free_left`, `D_free_right`, the contraction FLOP cost is
400
+ *
401
+ * cost = D_shared * D_free_left * D_free_right
402
+ *
403
+ * which is the volume of the OUTPUT tensor times the contraction depth. If
404
+ * `D_shared = 1` (no shared axes — outer product) we use `D_free_left *
405
+ * D_free_right`; mathematically that is the same formula. We follow this
406
+ * convention but it never fires in practice because we drive the cost
407
+ * through pairs with at least one shared index when possible.
408
+ *
409
+ * Output-tensor size for `intermediateSizes`: `D_free_left * D_free_right`
410
+ * (the contracted-axis dimensions are summed away, not multiplied into the
411
+ * result).
412
+ *
413
+ * `contractionOrder` indexing convention: each pair `[i, j]` is in
414
+ * "virtual-index" space. Input tensors occupy slots 0..N-1; the first
415
+ * pairwise contraction produces virtual slot N, the second slot N+1, and
416
+ * so on. So a length-N input network has a length-(N-1) order array.
417
+ */
418
+
419
+ interface ContractNetworkOpts {
420
+ /** Cap intermediate-tensor element count to avoid blowups. Default: 2^31. */
421
+ maxIntermediateSize?: number;
422
+ /**
423
+ * 'exact' = DP optimal (≤ ~16 tensors, slow beyond that).
424
+ * 'greedy' = Hendrickson–Sundaram heuristic (any N).
425
+ * 'auto' = 'exact' when N ≤ 16, otherwise 'greedy'.
426
+ * Default: 'auto'.
427
+ */
428
+ algorithm?: 'exact' | 'greedy' | 'auto';
429
+ }
430
+ interface ContractNetworkResult {
431
+ result: Tensor;
432
+ /** Pairwise contractions in (virtual) input-index space; see file header. */
433
+ contractionOrder: ReadonlyArray<readonly [number, number]>;
434
+ /** Sum of pairwise FLOP costs along the chosen order. */
435
+ totalFlops: number;
436
+ /** Element count of each intermediate, in creation order. */
437
+ intermediateSizes: ReadonlyArray<number>;
438
+ }
439
+ /**
440
+ * Contract a network of named-index tensors in the optimal pairwise order.
441
+ *
442
+ * Returns:
443
+ * - `result` — the fully contracted final Tensor.
444
+ * - `contractionOrder` — the chosen sequence of pairwise contractions in
445
+ * virtual-slot space (slots 0..N-1 = inputs; each produced intermediate
446
+ * takes the next free slot).
447
+ * - `totalFlops` — sum of per-step FLOP costs along the chosen order.
448
+ * - `intermediateSizes` — element count of each produced intermediate.
449
+ *
450
+ * Throws:
451
+ * - on empty input.
452
+ * - if any input lacks `axisLabels`.
453
+ * - if `maxIntermediateSize` is so tight no valid order exists.
454
+ */
455
+ declare function contractNetwork(tensors: ReadonlyArray<Tensor>, opts?: ContractNetworkOpts): ContractNetworkResult;
456
+
457
+ /**
458
+ * tensorQr — QR decomposition for rank-N tensors.
459
+ *
460
+ * Mirrors the `tensorSvd` pattern: partitions the axes of `t` into "row" axes
461
+ * (joining with Q) and "col" axes (joining with R), permutes + reshapes `t`
462
+ * into a 2-D matrix, calls the DenseMatrix `qr` primitive, then reshapes Q
463
+ * and R back into Tensors.
464
+ *
465
+ * In 'reduced' mode (default, thin QR):
466
+ * Q has shape [...rowDims, k] where k = min(numRows, numCols)
467
+ * R has shape [k, ...colDims]
468
+ *
469
+ * In 'full' mode:
470
+ * Q has shape [...rowDims, numRows] (square in the row direction)
471
+ * R has shape [numRows, ...colDims]
472
+ *
473
+ * `axisLabels` propagation: when the input carries labels, the row-axis
474
+ * labels are forwarded to Q's leading dims and the col-axis labels to R's
475
+ * trailing dims. A fresh "joining" Index of dimension `k` (or `numRows` in
476
+ * full mode) is inserted between them.
477
+ */
478
+
479
+ interface TensorQrOpts {
480
+ /** 'reduced' (default, thin QR) or 'full'. */
481
+ mode?: 'reduced' | 'full';
482
+ /**
483
+ * Optional name for the joining index between Q and R. Used only when
484
+ * input axisLabels are present to label the new joining axis. Defaults
485
+ * to "qr".
486
+ */
487
+ joiningIndexName?: string;
488
+ }
489
+ interface TensorQrResult {
490
+ /** Q factor with orthonormal columns reshaped to [...rowDims, k]. */
491
+ Q: Tensor;
492
+ /** R factor (upper-triangular) reshaped to [k, ...colDims]. */
493
+ R: Tensor;
494
+ }
495
+ /**
496
+ * Compute the QR decomposition of tensor `t` by partitioning its axes into
497
+ * "row" and "col" groups.
498
+ *
499
+ * @param t - Input tensor of any rank ≥ 1.
500
+ * @param rowAxes - Axis indices forming the row dimension of the effective
501
+ * 2-D matrix. The remaining axes form the column dimension.
502
+ * @param opts - Optional mode + naming settings.
503
+ */
504
+ declare function tensorQr(t: Tensor, rowAxes: ReadonlyArray<number>, opts?: TensorQrOpts): TensorQrResult;
505
+
506
+ /**
507
+ * tensorLU — LU decomposition with partial pivoting for rank-N tensors.
508
+ *
509
+ * Mirrors the `tensorSvd` pattern: partitions the axes of `t` into "row"
510
+ * axes and "col" axes, reshapes `t` into a 2-D matrix, performs LU with
511
+ * partial pivoting (Doolittle's algorithm via the matrix primitive), and
512
+ * returns the factors as Tensors.
513
+ *
514
+ * The 2-D-reshaped input must be square; otherwise an error is thrown.
515
+ *
516
+ * Decomposition: P · A = L · U, where L is unit lower-triangular and U is
517
+ * upper-triangular. P is returned as the row-permutation array (P[i] is
518
+ * the row index of the original A that was moved to position i).
519
+ *
520
+ * `axisLabels` propagation: L receives the rowAxes labels on its leading
521
+ * dims and a fresh joining Index on its trailing axis; U receives the
522
+ * joining Index on its leading axis and the colAxes labels on its trailing
523
+ * dims.
524
+ */
525
+
526
+ interface TensorLUResult {
527
+ /** Unit lower-triangular L. Shape [...rowDims, n]. */
528
+ L: Tensor;
529
+ /** Upper-triangular U. Shape [n, ...colDims]. */
530
+ U: Tensor;
531
+ /** Row permutation array of length n. P[i] is the original-row index now at position i. */
532
+ P: Int32Array;
533
+ /** Sign of the permutation (+1 for even, -1 for odd) — useful for det(). */
534
+ parity: 1 | -1;
535
+ }
536
+ interface TensorLUOpts {
537
+ /** Optional name for the joining index between L and U. Defaults to "lu". */
538
+ joiningIndexName?: string;
539
+ }
540
+ /**
541
+ * Compute the LU decomposition of tensor `t` by partitioning its axes into
542
+ * "row" and "col" groups. The reshaped 2-D matrix must be square.
543
+ *
544
+ * Delegates the core Doolittle algorithm to `@danielsimonjr/mathts-matrix`'s
545
+ * `lu` primitive.
546
+ *
547
+ * @param t - Input tensor of any rank ≥ 1.
548
+ * @param rowAxes - Axis indices forming the row dimension of the effective
549
+ * 2-D matrix. The remaining axes form the column dimension.
550
+ * @param opts - Optional joining-index name.
551
+ */
552
+ declare function tensorLU(t: Tensor, rowAxes: ReadonlyArray<number>, opts?: TensorLUOpts): TensorLUResult;
553
+
554
+ /**
555
+ * tensorCholesky — Cholesky decomposition for symmetric positive-definite
556
+ * rank-N tensors.
557
+ *
558
+ * Mirrors the `tensorSvd` pattern: partitions the axes of `t` into "row"
559
+ * axes and "col" axes, reshapes `t` into a 2-D matrix, performs the
560
+ * Cholesky decomposition via the matrix primitive (right-looking algorithm),
561
+ * and returns the factor as a Tensor.
562
+ *
563
+ * The 2-D reshaped input must be square and symmetric positive-definite;
564
+ * otherwise an error is thrown.
565
+ *
566
+ * Decomposition modes:
567
+ * - default (`lower: true`): returns L such that A = L · Lᵀ.
568
+ * - `lower: false`: returns U such that A = Uᵀ · U.
569
+ *
570
+ * `axisLabels` propagation: the returned factor receives the rowAxes labels
571
+ * on its leading dims and a fresh joining Index on its trailing axis
572
+ * (for L), or the joining Index on the leading axis and colAxes labels on
573
+ * the trailing dims (for U).
574
+ */
575
+
576
+ interface TensorCholeskyOpts {
577
+ /** When true (default), return L such that A = L·Lᵀ. When false, return U such that A = Uᵀ·U. */
578
+ lower?: boolean;
579
+ /** Optional name for the joining index on the returned factor. Defaults to "cholesky". */
580
+ joiningIndexName?: string;
581
+ }
582
+ interface TensorCholeskyResult {
583
+ /** Cholesky factor (lower or upper depending on `opts.lower`). */
584
+ L: Tensor;
585
+ }
586
+ /**
587
+ * Compute the Cholesky decomposition of tensor `t` by partitioning its
588
+ * axes into "row" and "col" groups. The reshaped 2-D matrix must be
589
+ * square and symmetric positive-definite.
590
+ *
591
+ * Delegates the core right-looking Cholesky algorithm to
592
+ * `@danielsimonjr/mathts-matrix`'s `cholesky` primitive.
593
+ *
594
+ * @param t - Input tensor of any rank ≥ 1.
595
+ * @param rowAxes - Axis indices forming the row dimension of the effective
596
+ * 2-D matrix. The remaining axes form the column dimension.
597
+ * @param opts - Optional triangle selector + naming.
598
+ */
599
+ declare function tensorCholesky(t: Tensor, rowAxes: ReadonlyArray<number>, opts?: TensorCholeskyOpts): TensorCholeskyResult;
600
+
601
+ /**
602
+ * tensorEig — eigenvalue / eigenvector decomposition for rank-N tensors.
603
+ *
604
+ * Mirrors the `tensorSvd` pattern: partitions the axes of `t` into "row"
605
+ * axes and "col" axes, reshapes `t` into a 2-D matrix, and delegates to
606
+ * the DenseMatrix `eig` primitive.
607
+ *
608
+ * The 2-D reshaped input must be square.
609
+ *
610
+ * Routing:
611
+ * - `symmetric: true` — eigenvalues are real and eigenvectors are
612
+ * orthogonal (matrix routes the symmetric path
613
+ * internally).
614
+ * - `symmetric: false` (default) — general eigensolver. If any eigenvalue
615
+ * has a non-zero imaginary part, the result
616
+ * includes an `eigenvaluesImaginary` Tensor;
617
+ * otherwise the field is undefined.
618
+ * - `computeVectors: false` — only eigenvalues are returned (faster).
619
+ */
620
+
621
+ interface TensorEigOpts {
622
+ /** Hint that the input is symmetric; routes through a stable real-eigenvalue path. */
623
+ symmetric?: boolean;
624
+ /** When false, only eigenvalues are computed. Default true. */
625
+ computeVectors?: boolean;
626
+ }
627
+ interface TensorEigResult {
628
+ /** Real parts of the eigenvalues. 1-D Tensor of length n. */
629
+ eigenvalues: Tensor;
630
+ /** Eigenvectors as columns; shape [n, n]. Present only when computeVectors !== false. */
631
+ eigenvectors?: Tensor;
632
+ /**
633
+ * Imaginary parts of the eigenvalues. 1-D Tensor of length n.
634
+ * Only present when any eigenvalue has nonzero imaginary part (general
635
+ * non-symmetric case). Undefined when all eigenvalues are real.
636
+ */
637
+ eigenvaluesImaginary?: Tensor;
638
+ }
639
+ /**
640
+ * Compute the eigendecomposition of tensor `t` by partitioning its axes
641
+ * into "row" and "col" groups. The reshaped 2-D matrix must be square.
642
+ *
643
+ * @param t - Input tensor of any rank ≥ 1.
644
+ * @param rowAxes - Axis indices forming the row dimension of the effective
645
+ * 2-D matrix. The remaining axes form the column dimension.
646
+ * @param opts - Optional symmetric hint + computeVectors flag.
647
+ */
648
+ declare function tensorEig(t: Tensor, rowAxes: ReadonlyArray<number>, opts?: TensorEigOpts): TensorEigResult;
649
+
650
+ /**
651
+ * tensorPinv — Moore-Penrose pseudoinverse for rank-N tensors.
652
+ *
653
+ * Partitions the axes of `t` into "row" axes (the axes that form the
654
+ * numerically "tall" side of the matrix) and the remaining "col" axes.
655
+ * Then:
656
+ * 1. Permutes + reshapes `t` to a 2-D matrix M (numRows × numCols).
657
+ * 2. Computes the full SVD: M = U · diag(S) · V^T.
658
+ * 3. Thresholds small singular values using `rcond`: keep s_i if
659
+ * s_i > rcond · max(S), else treat as zero.
660
+ * 4. Computes pinv(M) = V · diag(S^+) · U^T (shape: numCols × numRows).
661
+ * 5. Reshapes the result back into a Tensor with swapped row/col dims.
662
+ *
663
+ * Axis labels: if the input carries `axisLabels`, the output tensor's
664
+ * leading axes get the original col-axis labels and the trailing axes get
665
+ * the original row-axis labels (because the pseudoinverse swaps rows ↔ cols).
666
+ */
667
+
668
+ interface TensorPinvOpts {
669
+ /**
670
+ * Relative condition threshold for singular-value truncation.
671
+ * A singular value s_i is treated as zero when s_i ≤ rcond · max(S).
672
+ * Default: 1e-10.
673
+ */
674
+ rcond?: number;
675
+ }
676
+ /**
677
+ * Compute the Moore-Penrose pseudoinverse of tensor `t` by partitioning its
678
+ * axes into "row" and "col" groups.
679
+ *
680
+ * @param t - Input tensor of any rank ≥ 1.
681
+ * @param rowAxes - Axis indices (integers) or Index objects whose dimensions
682
+ * form the row dimension of the effective 2-D matrix. The
683
+ * remaining axes form the column dimension.
684
+ * @param opts - Optional rcond threshold.
685
+ */
686
+ declare function tensorPinv(t: Tensor, rowAxes: ReadonlyArray<number | Index>, opts?: TensorPinvOpts): Tensor;
687
+
688
+ /**
689
+ * tensorSolve — named-index linear solver for rank-N tensors.
690
+ *
691
+ * Reshapes tensor A into a square matrix and tensor b into a column vector (or
692
+ * multi-column RHS matrix), solves A·x = b via LU decomposition with partial
693
+ * pivoting, and reshapes the solution back.
694
+ *
695
+ * Axis matching:
696
+ * - When A has `axisLabels` and `b` has `axisLabels`, the "row" axes of A
697
+ * are matched to axes of `b` by Index identity (ignoring prime level).
698
+ * - Otherwise `rowAxesA` / `rowAxesB` (integer indices) must be provided,
699
+ * or the first axes of each tensor are used by default.
700
+ *
701
+ * Constraints:
702
+ * - The reshaped A must be square (numRows_A × numRows_A).
703
+ * - A must be non-singular (LU decomposition throws if pivot is zero).
704
+ * - For multi-RHS, the RHS col dimension must match A's row dimension.
705
+ *
706
+ * Result shape:
707
+ * - `x` has shape [...colDimsA, ...colDimsB] where colDimsA are the axes
708
+ * of A not used as row axes and colDimsB are the axes of b not used as
709
+ * row (contracting) axes.
710
+ * - When A is square as a whole tensor (rank 2), the result has the same
711
+ * shape as b.
712
+ *
713
+ * Axis labels: propagated from A's col axes and b's col axes.
714
+ */
715
+
716
+ interface TensorSolveOpts {
717
+ /**
718
+ * Axis indices (integers) or Index objects of A that form the "contracting"
719
+ * (row) side of the square matrix. Default: first `ceil(rank/2)` axes.
720
+ */
721
+ rowAxesA?: ReadonlyArray<number | Index>;
722
+ /**
723
+ * Axis indices (integers) or Index objects of b that form the "contracting"
724
+ * (row) side matched to A's row dimension. Default: first axis of b.
725
+ */
726
+ rowAxesB?: ReadonlyArray<number | Index>;
727
+ }
728
+ interface TensorSolveResult {
729
+ /** Solution tensor x satisfying A·x ≈ b. */
730
+ x: Tensor;
731
+ }
732
+ /**
733
+ * Solve A·x = b for x using LU decomposition.
734
+ *
735
+ * @param A - Coefficient tensor (rank ≥ 2 when reshaped to square).
736
+ * @param b - Right-hand side tensor.
737
+ * @param opts - Optional axis-selection overrides.
738
+ */
739
+ declare function tensorSolve(A: Tensor, b: Tensor, opts?: TensorSolveOpts): TensorSolveResult;
740
+
741
+ /**
742
+ * tensorKron — Kronecker product for rank-N tensors.
743
+ *
744
+ * Computes the Kronecker product of tensors `a` and `b`. For rank-2 matrices
745
+ * this is the classical Kronecker product:
746
+ *
747
+ * kron(A, B)[i0, j0] = A[i0 / B.rows, j0 / B.cols] * B[i0 % B.rows, j0 % B.cols]
748
+ *
749
+ * For higher-rank tensors, the same rule is applied axis-by-axis:
750
+ *
751
+ * result.shape[k] = a.shape[k] * b.shape[k]
752
+ * result[...i] = a[i[0]/b.shape[0], ..., i[r-1]/b.shape[r-1]]
753
+ * * b[i[0]%b.shape[0], ..., i[r-1]%b.shape[r-1]]
754
+ *
755
+ * Rank alignment: if the tensors have different ranks, the smaller one is
756
+ * broadcast-aligned to the right by prepending size-1 dimensions (numpy-style).
757
+ *
758
+ * Axis labels: when both tensors carry `axisLabels`, the output axis labels
759
+ * are formed by concatenating each pair as `"<aName>_X_<bName>"` to avoid
760
+ * collisions. When only one tensor has labels, the output carries no labels.
761
+ */
762
+
763
+ interface TensorKronOpts {
764
+ /**
765
+ * Separator string inserted between a's and b's axis-label names when
766
+ * building the output axisLabels. Default: "_X_".
767
+ */
768
+ separator?: string;
769
+ }
770
+ /**
771
+ * Compute the Kronecker product of tensors `a` and `b`.
772
+ *
773
+ * @param a - Left operand tensor.
774
+ * @param b - Right operand tensor.
775
+ * @param opts - Optional separator for axis-label naming.
776
+ */
777
+ declare function tensorKron(a: Tensor, b: Tensor, opts?: TensorKronOpts): Tensor;
778
+
779
+ /**
780
+ * slice — extract a sub-tensor by per-axis [start, stop, step] ranges.
781
+ *
782
+ * Mirrors NumPy / JAX basic slicing semantics:
783
+ * - Negative indices wrap around: -1 refers to the last element.
784
+ * - `null` range for an axis means "take the entire axis".
785
+ * - `step` must be >= 1 (reverse-step slicing is deferred).
786
+ * - Output shape for axis i: Math.ceil((stop - start) / step).
787
+ *
788
+ * Axis-label semantics: labels from the input axes are preserved in the
789
+ * output. A slice is still the same logical axis — just smaller — so the
790
+ * label name and tags are carried forward (dimension is updated to match the
791
+ * new axis size; the id is re-minted since the Index type is immutable and
792
+ * dimension is part of the index identity).
793
+ */
794
+
795
+ interface SliceRange {
796
+ /** Default 0. Supports negative indexing: -1 means shape[axis]-1. */
797
+ start?: number;
798
+ /** Default = shape[axis]. Supports negative indexing. */
799
+ stop?: number;
800
+ /** Default 1. Must be >= 1 (no reverse step). */
801
+ step?: number;
44
802
  }
803
+ /**
804
+ * Extract a sub-tensor by per-axis `SliceRange` triples.
805
+ *
806
+ * @param t - Input tensor of any rank.
807
+ * @param ranges - Array of length `t.shape.length`. Each element is either a
808
+ * `SliceRange` or `null` (which means "take the entire axis").
809
+ * Must have the same length as `t.shape`.
810
+ * @returns A new `Tensor` whose shape[i] = ceil((stop_i - start_i) / step_i),
811
+ * carrying preserved axis labels (same name/tags, updated dim).
812
+ *
813
+ * @throws {RangeError} If `step` is not a positive integer.
814
+ * @throws {Error} If `ranges.length !== t.shape.length`.
815
+ *
816
+ * @example
817
+ * // Extract rows 1–3 (exclusive), all columns from a 5×4 tensor:
818
+ * const sub = slice(t, [{ start: 1, stop: 3 }, null]);
819
+ */
820
+ declare function slice(t: Tensor, ranges: (SliceRange | null)[]): Tensor;
821
+
822
+ /**
823
+ * gather — pull elements along one axis (NumPy `take` / JAX `gather`).
824
+ *
825
+ * For each index in `indices`, the output contains the corresponding "slice"
826
+ * of the input tensor along `axis`. The output has the same rank as the input;
827
+ * only `shape[axis]` changes to `indices.length`.
828
+ *
829
+ * Axis-label semantics: the gathered axis's label is **primed** (primeLevel
830
+ * incremented by 1 via `Index.prime()`) so it does not auto-match the original
831
+ * axis in `contract`. All other axis labels are preserved unchanged.
832
+ *
833
+ * The priming approach mirrors ITensors.jl's convention: after gathering, the
834
+ * index is the "selected" variant of the original, not the original itself.
835
+ */
836
+
837
+ /**
838
+ * Gather elements along `axis` using `indices`.
839
+ *
840
+ * @param t - Input tensor of any rank.
841
+ * @param axis - The axis along which to gather. Supports negative indexing.
842
+ * @param indices - The indices to gather. Duplicates are allowed.
843
+ * @returns A new `Tensor` with `shape[axis] === indices.length` and all other
844
+ * dimensions unchanged. The gathered-axis label is primed.
845
+ *
846
+ * @throws {RangeError} If any index in `indices` is outside [0, shape[axis]).
847
+ * @throws {Error} If `axis` is out of range.
848
+ *
849
+ * @example
850
+ * // Select rows 0 and 2 from a 3×4 matrix:
851
+ * const rows = gather(matrix, 0, [0, 2]); // shape [2, 4]
852
+ */
853
+ declare function gather(t: Tensor, axis: number, indices: readonly number[]): Tensor;
854
+
855
+ /**
856
+ * stack — stack same-shape tensors along a new axis (NumPy `stack`).
857
+ *
858
+ * All input tensors must have identical shapes. A new axis is inserted at
859
+ * position `axis` in the output, making the output rank = input rank + 1.
860
+ * `stack([a, b], 0)` is equivalent to NumPy's `np.stack([a, b], axis=0)`.
861
+ *
862
+ * Axis-label semantics:
863
+ * - The new axis gets `undefined` label by default (it has no correspondence
864
+ * to any input axis).
865
+ * - Pass `opts.newAxisLabel` (an `Index`) to label the new axis explicitly.
866
+ * - The remaining axes inherit labels from `tensors[0]`. If labels disagree
867
+ * across inputs for a given axis, `tensors[0]`'s label is used silently
868
+ * (no error, no warning — document this choice here).
869
+ * - If `tensors[0]` has no `axisLabels`, the output also carries none.
870
+ */
871
+
872
+ interface StackOpts {
873
+ /**
874
+ * Optional label for the newly created axis. If omitted, the new axis
875
+ * carries no label (`axisLabels[axis] === undefined` in the output, or more
876
+ * precisely: the output `axisLabels` array contains `undefined` at that
877
+ * position — encoded as `null` in the array but TypeScript sees it as the
878
+ * slot being absent when axisLabels is stripped to only defined entries).
879
+ *
880
+ * In practice: if `tensors[0].axisLabels` is defined, an `Index` for the
881
+ * new axis is created either from this option or as a placeholder.
882
+ */
883
+ newAxisLabel?: Index;
884
+ }
885
+ /**
886
+ * Stack same-shape tensors along a new axis.
887
+ *
888
+ * @param tensors - Array of tensors, all with the same shape. Must be non-empty.
889
+ * @param axis - Position of the new axis in the output. Supports negative
890
+ * indexing relative to the output rank. E.g. `axis=0` inserts
891
+ * a new leading axis; `axis=-1` appends a new trailing axis.
892
+ * @param opts - Optional `newAxisLabel` for the inserted axis.
893
+ * @returns A tensor of rank `inputRank + 1` with `shape[axis] === tensors.length`.
894
+ *
895
+ * @throws {Error} If `tensors` is empty, or if any tensor's shape differs from
896
+ * `tensors[0]`.
897
+ *
898
+ * @example
899
+ * // Stack two row-vectors [a, b, c] and [d, e, f] → 2×3 matrix:
900
+ * const matrix = stack([v1, v2], 0);
901
+ */
902
+ declare function stack(tensors: Tensor[], axis: number, opts?: StackOpts): Tensor;
903
+
904
+ /**
905
+ * concatenate — join tensors along an existing axis (NumPy `concatenate`).
906
+ *
907
+ * All tensors must agree on every axis except the join axis. The output rank
908
+ * equals the input rank; only `shape[axis]` grows (sum of all input shapes
909
+ * along that axis).
910
+ *
911
+ * Axis-label semantics:
912
+ * - All axis labels are taken from `tensors[0]`.
913
+ * - If any `tensors[i].axisLabels[axis]` disagrees with `tensors[0]`'s label
914
+ * on the join axis, `tensors[0]`'s label is used silently (matching the
915
+ * principle that the first tensor is authoritative for label semantics).
916
+ * - If `tensors[0]` carries no `axisLabels`, the output also carries none.
917
+ */
918
+
919
+ /**
920
+ * Concatenate tensors along an existing `axis`.
921
+ *
922
+ * @param tensors - Array of tensors. Must be non-empty. All must agree on every
923
+ * axis except `axis`.
924
+ * @param axis - The axis along which to concatenate. Supports negative indexing.
925
+ * @returns A tensor with the same rank and with `shape[axis]` equal to the sum
926
+ * of `tensors[i].shape[axis]`.
927
+ *
928
+ * @throws {Error} If `tensors` is empty, if any tensor has a different rank, or
929
+ * if shapes disagree on any non-join axis.
930
+ *
931
+ * @example
932
+ * // Vertically stack two 2×3 matrices → 4×3 matrix:
933
+ * const tall = concatenate([a, b], 0);
934
+ * // Horizontally stack two 2×3 matrices → 2×6 matrix:
935
+ * const wide = concatenate([a, b], 1);
936
+ */
937
+ declare function concatenate(tensors: Tensor[], axis: number): Tensor;
938
+
939
+ /**
940
+ * scatter — inverse of gather. Writes `updates` into a copy of `t` at the
941
+ * given `indices` along `axis` (NumPy `np.put_along_axis` / JAX `scatter`).
942
+ *
943
+ * For each position `i` in `indices`, the corresponding "slice" of `updates`
944
+ * along `axis` is written into the matching "slice" of the output at position
945
+ * `indices[i]`. All positions of `t` not referenced by `indices` are copied
946
+ * unchanged.
947
+ *
948
+ * Duplicate-index semantics: **last-wins** — when the same index appears more
949
+ * than once in `indices`, the last occurrence overwrites earlier ones. This
950
+ * mirrors NumPy's default scatter behaviour. Document-level note: if
951
+ * deterministic accumulation (e.g. `add`) is needed, use `ScatterOpts.reduce`.
952
+ *
953
+ * Axis-label semantics: the output has exactly the same shape as `t`
954
+ * (scatter never changes the size of any axis), so all axis labels from `t`
955
+ * are preserved unchanged.
956
+ */
957
+
958
+ interface ScatterOpts {
959
+ /**
960
+ * How to combine `updates` with existing values at the target positions.
961
+ * - `'overwrite'` (default) — last-wins; target is replaced.
962
+ * - `'add'` — target is incremented by the update value.
963
+ */
964
+ reduce?: 'overwrite' | 'add';
965
+ }
966
+ /**
967
+ * Scatter `updates` into a copy of `t` at `indices` along `axis`.
968
+ *
969
+ * @param t - Destination tensor (not mutated). Any rank.
970
+ * @param axis - The axis along which to scatter. Supports negative indexing.
971
+ * @param indices - Target positions along `axis`. Must all be in
972
+ * `[0, t.shape[axis])`. Duplicates are allowed (last-wins
973
+ * by default, or add-accumulate when `opts.reduce === 'add'`).
974
+ * @param updates - Source tensor. Must have the same rank as `t` with
975
+ * `updates.shape[axis] === indices.length` and all other
976
+ * dimensions equal to `t.shape`.
977
+ * @param opts - Optional scatter options.
978
+ * @returns A new `Tensor` with the same shape as `t`, with axis labels
979
+ * preserved from `t`.
980
+ *
981
+ * @throws {Error} If `axis` is out of range, `updates` rank mismatches,
982
+ * or `updates` shape is incompatible.
983
+ * @throws {RangeError} If any index in `indices` is outside `[0, shape[axis])`.
984
+ *
985
+ * @example
986
+ * // Round-trip with gather: scatter zeros back to a masked copy
987
+ * const zeros = new Tensor([3, 4], new Float64Array(12));
988
+ * const result = scatter(zeros, 0, [0, 2], gather(t, 0, [0, 2]));
989
+ * // result[0, :] === t[0, :], result[1, :] === 0, result[2, :] === t[2, :]
990
+ */
991
+ declare function scatter(t: Tensor, axis: number, indices: readonly number[], updates: Tensor, opts?: ScatterOpts): Tensor;
992
+
993
+ /**
994
+ * pad — pad each axis by [before, after] amounts.
995
+ *
996
+ * Three modes are supported, mirroring NumPy's `np.pad`:
997
+ *
998
+ * - `'constant'` (default): Fill padded cells with `constantValue` (default 0).
999
+ * - `'edge'`: Repeat the boundary element along each padded slice.
1000
+ * - `'reflect'`: Mirror the data across the boundary, excluding the boundary
1001
+ * element itself. Requires that each padding amount is strictly
1002
+ * less than the corresponding axis size.
1003
+ *
1004
+ * Axis-label semantics: output rank equals input rank and axis identities do
1005
+ * not change (padding is just extension, not transformation). All axis labels
1006
+ * from the input are preserved unchanged in the output.
1007
+ */
1008
+
1009
+ interface PadOptions {
1010
+ /**
1011
+ * Padding mode.
1012
+ * - `'constant'` (default): fill with `constantValue`.
1013
+ * - `'edge'`: repeat boundary element.
1014
+ * - `'reflect'`: mirror across boundary, excluding boundary itself.
1015
+ */
1016
+ mode?: 'constant' | 'edge' | 'reflect';
1017
+ /** Value used when `mode === 'constant'`. Default `0`. */
1018
+ constantValue?: number;
1019
+ }
1020
+ /**
1021
+ * Pad a tensor along each axis by specified widths.
1022
+ *
1023
+ * @param t - Input tensor of any rank.
1024
+ * @param padWidths - Array of `[before, after]` pairs, one per axis. Length
1025
+ * must equal `t.shape.length`. Each value must be >= 0.
1026
+ * @param opts - Optional padding options (mode and constantValue).
1027
+ * @returns A new `Tensor` with `shape[k] = t.shape[k] + before_k + after_k`
1028
+ * and axis labels preserved from `t`.
1029
+ *
1030
+ * @throws {Error} If `padWidths.length !== t.shape.length`, any pad
1031
+ * value is negative, or `mode === 'reflect'` is used
1032
+ * with a pad amount >= the corresponding axis size.
1033
+ *
1034
+ * @example
1035
+ * // Constant-pad a 3×4 tensor with 1 cell of zeros on each side:
1036
+ * const padded = pad(t, [[1, 1], [1, 1]]); // shape [5, 6]
1037
+ *
1038
+ * // Edge-pad with asymmetric widths:
1039
+ * const ep = pad(t, [[2, 0], [0, 1]], { mode: 'edge' });
1040
+ */
1041
+ declare function pad(t: Tensor, padWidths: readonly (readonly [number, number])[], opts?: PadOptions): Tensor;
1042
+
1043
+ /**
1044
+ * roll — cyclic shift along given axes.
1045
+ *
1046
+ * Each element is moved `shifts[k]` positions forward along `axes[k]`
1047
+ * (wrapping around at the axis boundaries). Negative shifts move backward.
1048
+ *
1049
+ * When `axes` is omitted, `shifts` must be a single number and the tensor is
1050
+ * treated as a flattened 1-D array that is shifted by that amount, then
1051
+ * reshaped back to the original shape — exactly matching NumPy `np.roll(t, n)`.
1052
+ *
1053
+ * Implementation: for each output flat index, the corresponding input flat
1054
+ * index is computed by decoding the multi-index, un-shifting each specified
1055
+ * axis (with modulo wrap), then re-encoding to a flat input index.
1056
+ *
1057
+ * Axis-label semantics: cyclic shifting does not change the semantic identity
1058
+ * of any axis (it is the same axis with reordered elements), so all axis
1059
+ * labels from the input are preserved unchanged in the output.
1060
+ */
1061
+
1062
+ /**
1063
+ * Cyclically shift a tensor along given axes.
1064
+ *
1065
+ * @param t - Input tensor of any rank.
1066
+ * @param shifts - Number of positions to shift. A single number when `axes` is
1067
+ * omitted (flat shift); an array of the same length as `axes`
1068
+ * when `axes` is provided.
1069
+ * @param axes - Axes to shift along. Supports negative indexing. When
1070
+ * omitted, the tensor is treated as flattened and `shifts` must
1071
+ * be a single number.
1072
+ * @returns A new `Tensor` with the same shape as `t`, with axis labels
1073
+ * preserved from `t`.
1074
+ *
1075
+ * @throws {Error} If `axes` is provided and `shifts` array length !== `axes`
1076
+ * length, or if any axis value is out of range.
1077
+ *
1078
+ * @example
1079
+ * // Shift a rank-1 tensor [1,2,3,4,5] by 2: → [4,5,1,2,3]
1080
+ * const r = roll(t, 2, [0]);
1081
+ *
1082
+ * // Flat shift (no axes):
1083
+ * const flat = roll(matrix, 3);
1084
+ */
1085
+ declare function roll(t: Tensor, shifts: number | readonly number[], axes?: readonly number[]): Tensor;
1086
+
1087
+ /**
1088
+ * flip — reverse element order along given axes.
1089
+ *
1090
+ * For each axis in `axes`, the index along that axis is reversed:
1091
+ *
1092
+ * out[..., i, ...] = in[..., dim - 1 - i, ...]
1093
+ *
1094
+ * Multiple axes can be flipped in a single pass. An empty `axes` array is
1095
+ * a no-op (returns a copy of the input).
1096
+ *
1097
+ * Axis-label semantics: reversing the iteration order of an axis does not
1098
+ * change its semantic identity — it is still the same logical axis. All axis
1099
+ * labels from the input are therefore preserved unchanged in the output.
1100
+ */
1101
+
1102
+ /**
1103
+ * Reverse element order along the specified axes.
1104
+ *
1105
+ * @param t - Input tensor of any rank.
1106
+ * @param axes - The axes to flip. Supports negative indexing. Duplicates are
1107
+ * ignored. An empty array returns a copy of `t`.
1108
+ * @returns A new `Tensor` with the same shape as `t`, with axis labels
1109
+ * preserved from `t`.
1110
+ *
1111
+ * @throws {Error} If any axis value is out of range.
1112
+ *
1113
+ * @example
1114
+ * // Flip rows (axis 0) of a 3×4 matrix:
1115
+ * const flipped = flip(matrix, [0]);
1116
+ *
1117
+ * // Flip both axes simultaneously:
1118
+ * const rotated180 = flip(matrix, [0, 1]);
1119
+ */
1120
+ declare function flip(t: Tensor, axes: readonly number[]): Tensor;
45
1121
 
46
- export { type EinsumSpec, type NestedArray, Tensor };
1122
+ export { type ContractNetworkOpts, type ContractNetworkResult, type EinsumSpec, Index, type IndexOpts, type NestedArray, type PadOptions, type RandomTensorOpts, type ScatterOpts, type SliceRange, type StackOpts, Tensor, type TensorCholeskyOpts, type TensorCholeskyResult, type TensorEigOpts, type TensorEigResult, type TensorKronOpts, type TensorLUOpts, type TensorLUResult, type TensorPinvOpts, type TensorQrOpts, type TensorQrResult, type TensorSolveOpts, type TensorSolveResult, type TensorSvdOpts, type TensorSvdResult, concatenate, contractNetwork, flip, gather, idx, pad, randomTensor, roll, scatter, slice, stack, tensorCholesky, tensorEig, tensorKron, tensorLU, tensorPinv, tensorQr, tensorSolve, tensorSvd };