@danielsimonjr/mathts-core 0.6.0 → 0.7.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/dist/index.d.ts CHANGED
@@ -2445,6 +2445,75 @@ declare const math: {
2445
2445
  configure: (config: Partial<MathTSConfig>) => void;
2446
2446
  };
2447
2447
 
2448
+ /**
2449
+ * Numerically stable reduction primitives.
2450
+ *
2451
+ * Floating-point addition is not associative, so *how* you accumulate decides how much error you
2452
+ * carry. These are the algorithms the established numerical stack uses, and the reason it is
2453
+ * trusted.
2454
+ *
2455
+ * Measured on this repo's own reductions before these existed — 1e6 copies of `0.1`, exact answer
2456
+ * 100000:
2457
+ *
2458
+ * | accumulation | relative error |
2459
+ * | ------------------------------- | -------------- |
2460
+ * | naive `s += x` | **1.3e-11** |
2461
+ * | pairwise (this, = NumPy's algo) | **2.9e-16** |
2462
+ * | Neumaier compensated (`fsum`) | **0** (exact) |
2463
+ *
2464
+ * NumPy 2.3.4 reports 2.9e-16 on the same input — we are now at parity. Before this, we were
2465
+ * ~46,000× worse than NumPy on a bog-standard `sum`, and `mean`, `std`, `var` and every statistic
2466
+ * inherit that error.
2467
+ *
2468
+ * @packageDocumentation
2469
+ */
2470
+ /**
2471
+ * Sum with **pairwise (cascade) summation** — the algorithm behind `np.sum`.
2472
+ *
2473
+ * Naive accumulation lets the running total grow large while the addends stay small, so each
2474
+ * addition rounds off a little more of the total: error grows as **O(n)·ε**. Pairwise summation
2475
+ * adds numbers of *comparable magnitude* by recursively halving the range, so error grows as
2476
+ * **O(log n)·ε** — for n = 10⁶ that is the difference between ~1e-11 and ~1e-16.
2477
+ *
2478
+ * It costs the same number of additions as the naive loop. There is no speed/accuracy trade here;
2479
+ * the naive version is simply worse.
2480
+ *
2481
+ * Not exact: for that, use {@link neumaierSum}. Pairwise is the right default because it is free.
2482
+ */
2483
+ declare function pairwiseSum(xs: ArrayLike<number>, start?: number, end?: number): number;
2484
+ /**
2485
+ * Sum with **Neumaier compensation** — correctly rounded for practical purposes, the equivalent
2486
+ * of Python's `math.fsum`.
2487
+ *
2488
+ * Tracks the low-order bits that each addition throws away and folds them back in at the end. It
2489
+ * recovers information that pairwise summation cannot: `[1e16, 1, -1e16]` sums to **1** here,
2490
+ * while both naive and pairwise (and `np.sum`) give **0**, because the `1` is annihilated the
2491
+ * moment it meets `1e16`.
2492
+ *
2493
+ * ~2-4× slower than {@link pairwiseSum}, so it is opt-in rather than the default. Reach for it
2494
+ * when catastrophic cancellation is possible: near-zero results from large terms, long-running
2495
+ * accumulators, conservation checks.
2496
+ *
2497
+ * (Neumaier's variant, not classic Kahan: Kahan loses the compensation when the *addend* is
2498
+ * larger than the running sum, which is precisely the `1e16` case.)
2499
+ */
2500
+ declare function neumaierSum(xs: ArrayLike<number>): number;
2501
+ /**
2502
+ * Euclidean (2-)norm that does **not** overflow or underflow — BLAS's `dnrm2` scaling.
2503
+ *
2504
+ * The obvious `sqrt(Σxᵢ²)` squares before it adds, so it dies well inside the representable
2505
+ * range: `‖[1e200, 1e200, 1e200, 1e200]‖` overflows to `Infinity` (the true answer, 2e200, is
2506
+ * perfectly representable), and `‖[1e-200] × 4‖` **flushes to 0** — a silent wrong answer, which
2507
+ * is worse.
2508
+ *
2509
+ * NumPy has this bug too: `np.linalg.norm([1e200]*4)` returns `inf` with an overflow warning.
2510
+ * LAPACK does not, and neither do we.
2511
+ *
2512
+ * The fix is to carry a running scale: keep the largest magnitude seen out of the sum of squares,
2513
+ * so the accumulator only ever holds ratios in [0, 1].
2514
+ */
2515
+ declare function norm2(xs: ArrayLike<number>): number;
2516
+
2448
2517
  /**
2449
2518
  * @danielsimonjr/mathts-core - Core types and utilities for MathTS
2450
2519
  * @packageDocumentation
@@ -2452,4 +2521,4 @@ declare const math: {
2452
2521
 
2453
2522
  declare const VERSION = "0.1.0";
2454
2523
 
2455
- export { ALL_UNITS, BASE_UNITS, BEST_PREFIXES, BIGNUMBER_E, BIGNUMBER_LN10, BIGNUMBER_LN2, BIGNUMBER_NEG_ONE, BIGNUMBER_ONE, BIGNUMBER_PI, BIGNUMBER_TEN, BIGNUMBER_ZERO, type BackendType, BigNumber, type BigNumberConfig, COMPLEX_NEG_ONE, COMPLEX_ONE, COMPLEX_ZERO, Complex, type ConversionDef, DEFAULT_CONFIG, DERIVED_UNITS, DIMENSIONLESS, DUAL_UNARY_RULES, DimensionMismatchError, type Dimensions, Dual, type DualUnaryRule, type DualUnaryRuleName, E, FRACTION_HALF, FRACTION_NEG_ONE, FRACTION_ONE, FRACTION_QUARTER, FRACTION_THIRD, FRACTION_ZERO, type FactoryDependencies, type FactoryFunction, type FactoryImport, Fraction, FunctionRegistry, I, type IBigNumber, type IComplex, type IFraction, type IMatrix, LN10, LN2, LOG10E, LOG2E, MATHTS_CONVERSIONS, MATHTS_TYPES, type MathTSConfig, type MathTSValue, type MatrixBackend, type MatrixDimensions, type NumericScalar, type NumericType, PHI, PI, Range, type RangeForEachCallback, type RangeFormatOptions, type RangeJSON, type RangeMapCallback, type RoundingMode, SI_PREFIXES, SQRT1_2, SQRT2, type Scalar, TAU, type TypeDef, TypeRegistry, UNIT_ALIASES, type UnitDef, UnitParseError, VERSION, abs, addScalar, createFactory, createMathTSTyped, createRangeClass, createTypedFunction, dim, divideScalar, equal, fix, getPrefix, getUnitDef, initTypedWasm, isArray, isBigInt, isBigNumber, isBoolean, isComplex, isDenseMatrix, isDual, isFraction, isFunction, isMatrix, isNull, isNumber, isNumeric, isObject, isSparseMatrix, isString, isTypedWasmAvailable, isUndefined, isUnit, math, mathTyped, multiplyScalar, number, pow, registerNativeTypes, registry, round, subtractScalar };
2524
+ export { ALL_UNITS, BASE_UNITS, BEST_PREFIXES, BIGNUMBER_E, BIGNUMBER_LN10, BIGNUMBER_LN2, BIGNUMBER_NEG_ONE, BIGNUMBER_ONE, BIGNUMBER_PI, BIGNUMBER_TEN, BIGNUMBER_ZERO, type BackendType, BigNumber, type BigNumberConfig, COMPLEX_NEG_ONE, COMPLEX_ONE, COMPLEX_ZERO, Complex, type ConversionDef, DEFAULT_CONFIG, DERIVED_UNITS, DIMENSIONLESS, DUAL_UNARY_RULES, DimensionMismatchError, type Dimensions, Dual, type DualUnaryRule, type DualUnaryRuleName, E, FRACTION_HALF, FRACTION_NEG_ONE, FRACTION_ONE, FRACTION_QUARTER, FRACTION_THIRD, FRACTION_ZERO, type FactoryDependencies, type FactoryFunction, type FactoryImport, Fraction, FunctionRegistry, I, type IBigNumber, type IComplex, type IFraction, type IMatrix, LN10, LN2, LOG10E, LOG2E, MATHTS_CONVERSIONS, MATHTS_TYPES, type MathTSConfig, type MathTSValue, type MatrixBackend, type MatrixDimensions, type NumericScalar, type NumericType, PHI, PI, Range, type RangeForEachCallback, type RangeFormatOptions, type RangeJSON, type RangeMapCallback, type RoundingMode, SI_PREFIXES, SQRT1_2, SQRT2, type Scalar, TAU, type TypeDef, TypeRegistry, UNIT_ALIASES, type UnitDef, UnitParseError, VERSION, abs, addScalar, createFactory, createMathTSTyped, createRangeClass, createTypedFunction, dim, divideScalar, equal, fix, getPrefix, getUnitDef, initTypedWasm, isArray, isBigInt, isBigNumber, isBoolean, isComplex, isDenseMatrix, isDual, isFraction, isFunction, isMatrix, isNull, isNumber, isNumeric, isObject, isSparseMatrix, isString, isTypedWasmAvailable, isUndefined, isUnit, math, mathTyped, multiplyScalar, neumaierSum, norm2, number, pairwiseSum, pow, registerNativeTypes, registry, round, subtractScalar };
package/dist/index.js CHANGED
@@ -1186,6 +1186,69 @@ var math = {
1186
1186
  configure: (config) => registry.updateConfig(config)
1187
1187
  };
1188
1188
 
1189
+ // src/numeric/stable.ts
1190
+ var PAIRWISE_BLOCK = 128;
1191
+ function pairwiseSum(xs, start = 0, end = xs.length) {
1192
+ const n = end - start;
1193
+ if (n <= PAIRWISE_BLOCK) {
1194
+ let s0 = 0;
1195
+ let s1 = 0;
1196
+ let s2 = 0;
1197
+ let s3 = 0;
1198
+ let s4 = 0;
1199
+ let s5 = 0;
1200
+ let s6 = 0;
1201
+ let s7 = 0;
1202
+ let i = start;
1203
+ const limit = start + (n - n % 8);
1204
+ for (; i < limit; i += 8) {
1205
+ s0 += xs[i];
1206
+ s1 += xs[i + 1];
1207
+ s2 += xs[i + 2];
1208
+ s3 += xs[i + 3];
1209
+ s4 += xs[i + 4];
1210
+ s5 += xs[i + 5];
1211
+ s6 += xs[i + 6];
1212
+ s7 += xs[i + 7];
1213
+ }
1214
+ let tail = 0;
1215
+ for (; i < end; i++) tail += xs[i];
1216
+ return s0 + s1 + (s2 + s3) + (s4 + s5 + (s6 + s7)) + tail;
1217
+ }
1218
+ const half = (n >> 1) - (n >> 1) % 8;
1219
+ return pairwiseSum(xs, start, start + half) + pairwiseSum(xs, start + half, end);
1220
+ }
1221
+ function neumaierSum(xs) {
1222
+ let sum = 0;
1223
+ let c = 0;
1224
+ for (let i = 0; i < xs.length; i++) {
1225
+ const x = xs[i];
1226
+ const t = sum + x;
1227
+ c += Math.abs(sum) >= Math.abs(x) ? sum - t + x : x - t + sum;
1228
+ sum = t;
1229
+ }
1230
+ return sum + c;
1231
+ }
1232
+ function norm2(xs) {
1233
+ let scale = 0;
1234
+ let ssq = 1;
1235
+ for (let i = 0; i < xs.length; i++) {
1236
+ const x = xs[i];
1237
+ if (x === 0) continue;
1238
+ if (Number.isNaN(x)) return NaN;
1239
+ const ax = Math.abs(x);
1240
+ if (scale < ax) {
1241
+ const r = scale / ax;
1242
+ ssq = 1 + ssq * r * r;
1243
+ scale = ax;
1244
+ } else {
1245
+ const r = ax / scale;
1246
+ ssq += r * r;
1247
+ }
1248
+ }
1249
+ return scale === 0 ? 0 : scale * Math.sqrt(ssq);
1250
+ }
1251
+
1189
1252
  // src/index.ts
1190
1253
  var VERSION = "0.1.0";
1191
1254
  export {
@@ -1276,7 +1339,10 @@ export {
1276
1339
  math,
1277
1340
  mathTyped,
1278
1341
  multiplyScalar,
1342
+ neumaierSum,
1343
+ norm2,
1279
1344
  number,
1345
+ pairwiseSum,
1280
1346
  pow,
1281
1347
  registerNativeTypes,
1282
1348
  registry,
package/package.json CHANGED
@@ -1,59 +1,59 @@
1
- {
2
- "name": "@danielsimonjr/mathts-core",
3
- "version": "0.6.0",
4
- "description": "Core types, utilities, and configuration for MathTS",
5
- "author": "Daniel Simon Jr.",
6
- "license": "MIT",
7
- "type": "module",
8
- "main": "./dist/index.js",
9
- "module": "./dist/index.js",
10
- "types": "./dist/index.d.ts",
11
- "exports": {
12
- ".": {
13
- "import": "./dist/index.js",
14
- "types": "./dist/index.d.ts"
15
- },
16
- "./internal": {
17
- "import": "./dist/internal.js",
18
- "types": "./dist/internal.d.ts"
19
- }
20
- },
21
- "files": [
22
- "dist",
23
- "README.md"
24
- ],
25
- "scripts": {
26
- "build": "tsup src/index.ts src/internal.ts --format esm --dts --clean",
27
- "dev": "tsup src/index.ts src/internal.ts --format esm --dts --watch",
28
- "test": "vitest run",
29
- "test:watch": "vitest",
30
- "test:coverage": "vitest run --coverage",
31
- "typecheck": "tsc --noEmit",
32
- "lint": "eslint src --ext .ts",
33
- "lint:fix": "eslint src --ext .ts --fix",
34
- "clean": "rm -rf dist",
35
- "build:prod": "tsup src/index.ts --format esm --dts --clean --minify --treeshake"
36
- },
37
- "dependencies": {
38
- "typed-function": "github:danielsimonjr/typed-function"
39
- },
40
- "devDependencies": {
41
- "@types/node": "^25.5.2",
42
- "tsup": "^8.0.0",
43
- "typescript": "^5.3.0",
44
- "vitest": "^4.1.5"
45
- },
46
- "publishConfig": {
47
- "access": "public"
48
- },
49
- "repository": {
50
- "type": "git",
51
- "url": "https://github.com/danielsimonjr/mathts",
52
- "directory": "core"
53
- },
54
- "keywords": [
55
- "math",
56
- "typescript",
57
- "core"
58
- ]
59
- }
1
+ {
2
+ "name": "@danielsimonjr/mathts-core",
3
+ "version": "0.7.0",
4
+ "description": "Core types, utilities, and configuration for MathTS",
5
+ "author": "Daniel Simon Jr.",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ },
16
+ "./internal": {
17
+ "import": "./dist/internal.js",
18
+ "types": "./dist/internal.d.ts"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsup src/index.ts src/internal.ts --format esm --dts --clean",
27
+ "dev": "tsup src/index.ts src/internal.ts --format esm --dts --watch",
28
+ "test": "vitest run",
29
+ "test:watch": "vitest",
30
+ "test:coverage": "vitest run --coverage",
31
+ "typecheck": "tsc --noEmit",
32
+ "lint": "eslint src --ext .ts",
33
+ "lint:fix": "eslint src --ext .ts --fix",
34
+ "clean": "rm -rf dist",
35
+ "build:prod": "tsup src/index.ts --format esm --dts --clean --minify --treeshake"
36
+ },
37
+ "dependencies": {
38
+ "typed-function": "github:danielsimonjr/typed-function"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^25.5.2",
42
+ "tsup": "^8.0.0",
43
+ "typescript": "^5.3.0",
44
+ "vitest": "^4.1.5"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://github.com/danielsimonjr/mathts",
52
+ "directory": "core"
53
+ },
54
+ "keywords": [
55
+ "math",
56
+ "typescript",
57
+ "core"
58
+ ]
59
+ }