@iyulab/u-numflow 0.6.3 → 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/README.md CHANGED
@@ -38,31 +38,34 @@ u-numflow = "0.2"
38
38
  ```
39
39
 
40
40
  ```rust
41
- use u_numflow::stats::OnlineStats;
42
- use u_numflow::distributions::{PertDistribution, Distribution};
43
- use u_numflow::random::Rng;
41
+ use u_numflow::distributions::Pert;
42
+ use u_numflow::random::{create_rng, shuffle};
43
+ use u_numflow::stats::WelfordAccumulator;
44
44
 
45
- // Online statistics with numerical stability
46
- let mut stats = OnlineStats::new();
45
+ // Online statistics with numerical stability (Welford)
46
+ let mut stats = WelfordAccumulator::new();
47
47
  for x in [1.0, 2.0, 3.0, 4.0, 5.0] {
48
- stats.push(x);
48
+ stats.update(x);
49
49
  }
50
- assert_eq!(stats.mean(), 3.0);
50
+ assert_eq!(stats.mean(), Some(3.0));
51
51
 
52
- // PERT distribution sampling
53
- let pert = PertDistribution::new(1.0, 4.0, 7.0);
54
- let mut rng = Rng::seed_from_u64(42);
55
- let sample = pert.sample(&mut rng);
52
+ // PERT distribution: moments and quantiles; sample by inverting a uniform draw
53
+ let pert = Pert::new(1.0, 4.0, 7.0).unwrap();
54
+ assert_eq!(pert.mean(), 4.0);
55
+ let p90 = pert.quantile(0.9).unwrap();
56
+ assert!(p90 > 4.0 && p90 < 7.0);
56
57
 
57
58
  // Seeded shuffling for reproducibility
59
+ let mut rng = create_rng(42);
58
60
  let mut items = vec![1, 2, 3, 4, 5];
59
- u_numflow::random::shuffle(&mut items, &mut rng);
61
+ shuffle(&mut items, &mut rng);
60
62
 
61
63
  // Box-Cox transformation (non-normal data normalization)
62
- use u_numflow::transforms::{estimate_lambda, box_cox};
64
+ use u_numflow::transforms::{box_cox, estimate_lambda};
63
65
  let data = [1.0, 2.0, 4.0, 8.0, 16.0];
64
- let lambda = estimate_lambda(&data, -2.0, 2.0).unwrap(); // MLE via golden-section
65
- let transformed = box_cox(&data, lambda).unwrap();
66
+ let fit = estimate_lambda(&data, -2.0, 2.0).unwrap(); // MLE via golden-section
67
+ let transformed = box_cox(&data, fit.lambda).unwrap();
68
+ assert_eq!(transformed.len(), data.len());
66
69
 
67
70
  // Discrete Fourier transform of any length
68
71
  use u_numflow::fourier::rfft;
@@ -100,12 +103,17 @@ The package resolves per environment via a conditional `exports` map:
100
103
  | Bundlers (webpack, Vite, …) | ESM + WebAssembly ESM-integration (`default` condition) |
101
104
  | Node.js — `require()`, ESM `import`, CJS TS runners (`tsx`, `ts-node`) | CJS glue loading the wasm from the filesystem (`node` condition) — no loader hooks or flags |
102
105
 
106
+ A browser **without** a bundler is not supported: the package loads its `.wasm`
107
+ file with an ES module import, which browsers refuse (`application/wasm` is not a
108
+ module script type), so `<script type="module">` from a CDN fails, and CDN
109
+ re-bundling services fail on the same import. Use a bundler or Node.
110
+
103
111
  Exported functions: `mean`, `std_dev`, `variance`, `normal_cdf`, `normal_sf` (upper tail `P(Z > x)`, computed directly so tail probabilities keep ~15 significant digits), `box_cox`, `estimate_lambda` (returns `{ lambda, at_bound }` — `at_bound` is `true` when the likelihood was still rising at an end of the search range, so `lambda` is that limit), and
104
112
  `rfft(data) -> Float64Array` — the DFT of a real sequence of any length, interleaved as
105
113
  `[re0, im0, re1, im1, …]` (bins `k` and `n − k` are conjugates, so `0..=n/2` describes the spectrum).
106
114
 
107
115
  Distribution functions for critical values and p-values — each returns a `number` and
108
- **throws** (a message naming the argument) when an argument is outside the domain,
116
+ **throws** (an `Error` naming the argument — see *Errors* below) when an argument is outside the domain,
109
117
  rather than returning `NaN`:
110
118
 
111
119
  | Function | Returns |
@@ -123,22 +131,45 @@ const { t_distribution_quantile } = require("@iyulab/u-numflow");
123
131
  t_distribution_quantile(0.975, 10); // 2.2281…
124
132
  ```
125
133
 
134
+ **Errors.** A refusal throws an `Error` whose `message` is readable text and which
135
+ carries a `code` naming the reason, next to the values behind it:
136
+
137
+ ```js
138
+ const { t_distribution_quantile } = require("@iyulab/u-numflow");
139
+ try {
140
+ t_distribution_quantile(1.5, 10);
141
+ } catch (err) {
142
+ console.log(err.code, err.parameter, err.min, err.max, err.got); // parameter_out_of_range p 0 1 1.5
143
+ }
144
+ ```
145
+
146
+ | `code` | Fields | Meaning |
147
+ |---|---|---|
148
+ | `parameter_out_of_range` | `parameter`, `min`, `max` (or `null`), `got` | `p` not strictly inside (0, 1), or a degrees of freedom that is not a finite number `> 0` (both bounds excluded) |
149
+ | `value_not_finite` | `parameter` (for a scalar argument) | A NaN argument, or a NaN or infinity in `box_cox`/`estimate_lambda` data |
150
+ | `non_positive_data` | — | Box-Cox data with a value `≤ 0` |
151
+ | `insufficient_data` | — | Box-Cox data with fewer than 2 values |
152
+ | `invalid_transform` | — | A Box-Cox result that is not finite |
153
+ | `invalid_lambda_range` | — | `estimate_lambda` bounds that are not finite with `lambda_min < lambda_max` |
154
+
126
155
  ### TypeScript
127
156
 
128
- Every exported function declares its return type, and the declarations are
129
- generated from the same structs the binding serialises, so they cannot drift
130
- from what it actually returns:
157
+ Every exported function declares its parameter and return types, and the
158
+ declarations are generated from the same structs the binding reads and
159
+ serialises, so they cannot drift from what it actually accepts and returns:
131
160
 
132
161
  ```ts
133
- export function estimate_lambda(data: any): LambdaEstimateDto;
162
+ export function estimate_lambda(data: Float64Array, lambda_min: number, lambda_max: number): LambdaEstimateDto;
134
163
  ```
135
164
 
136
165
  An absent optional value is declared `T | undefined`, which is what the binding
137
166
  sends. Nothing needs an `as` cast -- and a wrong assumption about a result's
138
167
  shape is a compile error rather than something that fails at run time.
139
168
 
140
- Inputs are still `any`: they are validated at the boundary, and a rejected one
141
- says what was wrong.
169
+ Every parameter is a number or a `Float64Array`, so there is nothing to
170
+ declare beyond what wasm-bindgen already does; the publishing workflow keeps it
171
+ that way. The binding still validates every input at the boundary, and a
172
+ rejected one says what was wrong.
142
173
 
143
174
  ## Related
144
175
 
@@ -6,8 +6,9 @@
6
6
  * All values in `data` must be strictly positive.
7
7
  *
8
8
  * # Errors
9
- * Returns a `JsValue` error string if data contains non-positive values or
10
- * has fewer than 2 elements.
9
+ * Throws `non_positive_data`, `value_not_finite` or `insufficient_data` when
10
+ * data contains a value ≤ 0, a NaN or infinity, or fewer than 2 elements, and
11
+ * `invalid_transform` when a result is not finite.
11
12
  * @param {Float64Array} data
12
13
  * @param {number} lambda
13
14
  * @returns {Float64Array}
@@ -72,9 +73,8 @@ exports.chi_squared_quantile = chi_squared_quantile;
72
73
  * interior estimate; widen the range to find the unconstrained optimum.
73
74
  *
74
75
  * # Errors
75
- * Returns a `JsValue` error string if data contains non-positive values,
76
- * has fewer than 2 elements, or the range is not finite with
77
- * `lambda_min < lambda_max`.
76
+ * Throws as [`box_cox`] does for the data, and `invalid_lambda_range` when the
77
+ * range is not finite with `lambda_min < lambda_max`.
78
78
  * @param {Float64Array} data
79
79
  * @param {number} lambda_min
80
80
  * @param {number} lambda_max
@@ -289,13 +289,21 @@ function __wbg_get_imports() {
289
289
  getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
290
290
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
291
291
  },
292
- __wbg___wbindgen_throw_5d9e815e6fdf150f: function(arg0, arg1) {
292
+ __wbg___wbindgen_throw_41e9ee4f547fc59a: function(arg0, arg1) {
293
293
  throw new Error(getStringFromWasm0(arg0, arg1));
294
294
  },
295
- __wbg_new_bebc3f4757acf305: function() {
295
+ __wbg_new_343a093a3c2ffb4e: function(arg0, arg1) {
296
+ const ret = new Error(getStringFromWasm0(arg0, arg1));
297
+ return ret;
298
+ },
299
+ __wbg_new_617a8cdb8bb1130e: function() {
296
300
  const ret = new Object();
297
301
  return ret;
298
302
  },
303
+ __wbg_set_145a351398b48c65: function() { return handleError(function (arg0, arg1, arg2) {
304
+ const ret = Reflect.set(arg0, arg1, arg2);
305
+ return ret;
306
+ }, arguments); },
299
307
  __wbg_set_6be42768c690e380: function(arg0, arg1, arg2) {
300
308
  arg0[arg1] = arg2;
301
309
  },
@@ -325,6 +333,12 @@ function __wbg_get_imports() {
325
333
  };
326
334
  }
327
335
 
336
+ function addToExternrefTable0(obj) {
337
+ const idx = wasm.__externref_table_alloc();
338
+ wasm.__wbindgen_externrefs.set(idx, obj);
339
+ return idx;
340
+ }
341
+
328
342
  function getArrayF64FromWasm0(ptr, len) {
329
343
  ptr = ptr >>> 0;
330
344
  return getFloat64ArrayMemory0().subarray(ptr / 8, ptr / 8 + len);
@@ -358,6 +372,15 @@ function getUint8ArrayMemory0() {
358
372
  return cachedUint8ArrayMemory0;
359
373
  }
360
374
 
375
+ function handleError(f, args) {
376
+ try {
377
+ return f.apply(this, args);
378
+ } catch (e) {
379
+ const idx = addToExternrefTable0(e);
380
+ wasm.__wbindgen_exn_store(idx);
381
+ }
382
+ }
383
+
361
384
  function passArrayF64ToWasm0(arg, malloc) {
362
385
  const ptr = malloc(arg.length * 8, 8) >>> 0;
363
386
  getFloat64ArrayMemory0().set(arg, ptr / 8);
@@ -15,8 +15,9 @@ export interface LambdaEstimateDto {
15
15
  * All values in `data` must be strictly positive.
16
16
  *
17
17
  * # Errors
18
- * Returns a `JsValue` error string if data contains non-positive values or
19
- * has fewer than 2 elements.
18
+ * Throws `non_positive_data`, `value_not_finite` or `insufficient_data` when
19
+ * data contains a value ≤ 0, a NaN or infinity, or fewer than 2 elements, and
20
+ * `invalid_transform` when a result is not finite.
20
21
  */
21
22
  export function box_cox(data: Float64Array, lambda: number): Float64Array;
22
23
 
@@ -47,9 +48,8 @@ export function chi_squared_quantile(p: number, k: number): number;
47
48
  * interior estimate; widen the range to find the unconstrained optimum.
48
49
  *
49
50
  * # Errors
50
- * Returns a `JsValue` error string if data contains non-positive values,
51
- * has fewer than 2 elements, or the range is not finite with
52
- * `lambda_min < lambda_max`.
51
+ * Throws as [`box_cox`] does for the data, and `invalid_lambda_range` when the
52
+ * range is not finite with `lambda_min < lambda_max`.
53
53
  */
54
54
  export function estimate_lambda(data: Float64Array, lambda_min: number, lambda_max: number): LambdaEstimateDto;
55
55
 
Binary file
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "iyulab"
6
6
  ],
7
7
  "description": "Foundational mathematical primitives: statistics, probability distributions, random sampling, and collections.",
8
- "version": "0.6.3",
8
+ "version": "0.7.0",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",
package/u_numflow.d.ts CHANGED
@@ -15,8 +15,9 @@ export interface LambdaEstimateDto {
15
15
  * All values in `data` must be strictly positive.
16
16
  *
17
17
  * # Errors
18
- * Returns a `JsValue` error string if data contains non-positive values or
19
- * has fewer than 2 elements.
18
+ * Throws `non_positive_data`, `value_not_finite` or `insufficient_data` when
19
+ * data contains a value ≤ 0, a NaN or infinity, or fewer than 2 elements, and
20
+ * `invalid_transform` when a result is not finite.
20
21
  */
21
22
  export function box_cox(data: Float64Array, lambda: number): Float64Array;
22
23
 
@@ -47,9 +48,8 @@ export function chi_squared_quantile(p: number, k: number): number;
47
48
  * interior estimate; widen the range to find the unconstrained optimum.
48
49
  *
49
50
  * # Errors
50
- * Returns a `JsValue` error string if data contains non-positive values,
51
- * has fewer than 2 elements, or the range is not finite with
52
- * `lambda_min < lambda_max`.
51
+ * Throws as [`box_cox`] does for the data, and `invalid_lambda_range` when the
52
+ * range is not finite with `lambda_min < lambda_max`.
53
53
  */
54
54
  export function estimate_lambda(data: Float64Array, lambda_min: number, lambda_max: number): LambdaEstimateDto;
55
55
 
package/u_numflow_bg.js CHANGED
@@ -4,8 +4,9 @@
4
4
  * All values in `data` must be strictly positive.
5
5
  *
6
6
  * # Errors
7
- * Returns a `JsValue` error string if data contains non-positive values or
8
- * has fewer than 2 elements.
7
+ * Throws `non_positive_data`, `value_not_finite` or `insufficient_data` when
8
+ * data contains a value ≤ 0, a NaN or infinity, or fewer than 2 elements, and
9
+ * `invalid_transform` when a result is not finite.
9
10
  * @param {Float64Array} data
10
11
  * @param {number} lambda
11
12
  * @returns {Float64Array}
@@ -67,9 +68,8 @@ export function chi_squared_quantile(p, k) {
67
68
  * interior estimate; widen the range to find the unconstrained optimum.
68
69
  *
69
70
  * # Errors
70
- * Returns a `JsValue` error string if data contains non-positive values,
71
- * has fewer than 2 elements, or the range is not finite with
72
- * `lambda_min < lambda_max`.
71
+ * Throws as [`box_cox`] does for the data, and `invalid_lambda_range` when the
72
+ * range is not finite with `lambda_min < lambda_max`.
73
73
  * @param {Float64Array} data
74
74
  * @param {number} lambda_min
75
75
  * @param {number} lambda_max
@@ -269,13 +269,21 @@ export function __wbg_String_8564e559799eccda(arg0, arg1) {
269
269
  getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
270
270
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
271
271
  }
272
- export function __wbg___wbindgen_throw_5d9e815e6fdf150f(arg0, arg1) {
272
+ export function __wbg___wbindgen_throw_41e9ee4f547fc59a(arg0, arg1) {
273
273
  throw new Error(getStringFromWasm0(arg0, arg1));
274
274
  }
275
- export function __wbg_new_bebc3f4757acf305() {
275
+ export function __wbg_new_343a093a3c2ffb4e(arg0, arg1) {
276
+ const ret = new Error(getStringFromWasm0(arg0, arg1));
277
+ return ret;
278
+ }
279
+ export function __wbg_new_617a8cdb8bb1130e() {
276
280
  const ret = new Object();
277
281
  return ret;
278
282
  }
283
+ export function __wbg_set_145a351398b48c65() { return handleError(function (arg0, arg1, arg2) {
284
+ const ret = Reflect.set(arg0, arg1, arg2);
285
+ return ret;
286
+ }, arguments); }
279
287
  export function __wbg_set_6be42768c690e380(arg0, arg1, arg2) {
280
288
  arg0[arg1] = arg2;
281
289
  }
@@ -298,6 +306,12 @@ export function __wbindgen_init_externref_table() {
298
306
  table.set(offset + 2, true);
299
307
  table.set(offset + 3, false);
300
308
  }
309
+ function addToExternrefTable0(obj) {
310
+ const idx = wasm.__externref_table_alloc();
311
+ wasm.__wbindgen_externrefs.set(idx, obj);
312
+ return idx;
313
+ }
314
+
301
315
  function getArrayF64FromWasm0(ptr, len) {
302
316
  ptr = ptr >>> 0;
303
317
  return getFloat64ArrayMemory0().subarray(ptr / 8, ptr / 8 + len);
@@ -331,6 +345,15 @@ function getUint8ArrayMemory0() {
331
345
  return cachedUint8ArrayMemory0;
332
346
  }
333
347
 
348
+ function handleError(f, args) {
349
+ try {
350
+ return f.apply(this, args);
351
+ } catch (e) {
352
+ const idx = addToExternrefTable0(e);
353
+ wasm.__wbindgen_exn_store(idx);
354
+ }
355
+ }
356
+
334
357
  function passArrayF64ToWasm0(arg, malloc) {
335
358
  const ptr = malloc(arg.length * 8, 8) >>> 0;
336
359
  getFloat64ArrayMemory0().set(arg, ptr / 8);
package/u_numflow_bg.wasm CHANGED
Binary file