@iyulab/u-numflow 0.7.0 → 0.9.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 +98 -13
- package/node/u_numflow.cjs +500 -40
- package/node/u_numflow.d.cts +120 -9
- package/node/u_numflow_bg.wasm +0 -0
- package/package.json +1 -1
- package/u_numflow.d.ts +120 -9
- package/u_numflow.js +1 -1
- package/u_numflow_bg.js +490 -39
- package/u_numflow_bg.wasm +0 -0
package/README.md
CHANGED
|
@@ -16,13 +16,14 @@ u-numflow provides foundational mathematical, statistical, and probabilistic bui
|
|
|
16
16
|
| Module | Description |
|
|
17
17
|
|--------|-------------|
|
|
18
18
|
| `stats` | Descriptive statistics (mean, variance, skewness, kurtosis) with Welford's online algorithm and Neumaier summation |
|
|
19
|
-
| `distributions` | Probability distributions: Uniform, Triangular, PERT, Normal, LogNormal |
|
|
19
|
+
| `distributions` | Probability distributions: Uniform, Triangular, PERT, Normal, LogNormal, Weibull, Exponential, Gamma, Beta, χ² |
|
|
20
20
|
| `special` | Special functions: normal CDF and tail-precise survival function (and their inverses), t/F/chi² CDF and quantiles, the noncentral t CDF (Lenth AS 243 — the distribution power is computed from), regularized incomplete beta/gamma, erf |
|
|
21
21
|
| `transforms` | Data transformations: Box-Cox (λ via MLE golden-section search), inverse Box-Cox |
|
|
22
22
|
| `fourier` | Discrete Fourier transform of any length (radix-2 for powers of two, Bluestein otherwise): `fft`, `ifft`, `rfft`, `Complex` |
|
|
23
23
|
| `matrix` | Dense matrix operations: determinant, inverse, Cholesky decomposition, Jacobi eigenvalue decomposition |
|
|
24
24
|
| `random` | Seeded RNG, Fisher-Yates shuffle, weighted sampling, random subset selection |
|
|
25
|
-
| `
|
|
25
|
+
| `distributions::Sample` | Random variates from every continuous distribution — `sample(&mut rng)` and reproducible `sample_n(n, seed)`; inversion where the quantile is direct, Marsaglia–Tsang for the gamma family (Gamma, χ², Beta, PERT); uniforms from the open interval (0, 1), so no variate is infinite |
|
|
26
|
+
| `collections` | Specialized data structures: Union-Find with path compression and union-by-rank; `IntervalSet` — unions of half-open intervals with union, intersection, difference, clip and a measure that counts overlapping input once |
|
|
26
27
|
|
|
27
28
|
## Design Philosophy
|
|
28
29
|
|
|
@@ -34,7 +35,7 @@ u-numflow provides foundational mathematical, statistical, and probabilistic bui
|
|
|
34
35
|
|
|
35
36
|
```toml
|
|
36
37
|
[dependencies]
|
|
37
|
-
u-numflow = "0.
|
|
38
|
+
u-numflow = "0.9"
|
|
38
39
|
```
|
|
39
40
|
|
|
40
41
|
```rust
|
|
@@ -49,11 +50,14 @@ for x in [1.0, 2.0, 3.0, 4.0, 5.0] {
|
|
|
49
50
|
}
|
|
50
51
|
assert_eq!(stats.mean(), Some(3.0));
|
|
51
52
|
|
|
52
|
-
// PERT distribution: moments
|
|
53
|
+
// PERT distribution: moments, quantiles and reproducible samples
|
|
54
|
+
use u_numflow::distributions::Sample;
|
|
53
55
|
let pert = Pert::new(1.0, 4.0, 7.0).unwrap();
|
|
54
56
|
assert_eq!(pert.mean(), 4.0);
|
|
55
57
|
let p90 = pert.quantile(0.9).unwrap();
|
|
56
58
|
assert!(p90 > 4.0 && p90 < 7.0);
|
|
59
|
+
let draws = pert.sample_n(1000, 7); // same (n, seed), same draws
|
|
60
|
+
assert!(draws.iter().all(|&x| (1.0..=7.0).contains(&x)));
|
|
57
61
|
|
|
58
62
|
// Seeded shuffling for reproducibility
|
|
59
63
|
let mut rng = create_rng(42);
|
|
@@ -72,8 +76,22 @@ use u_numflow::fourier::rfft;
|
|
|
72
76
|
let signal: Vec<f64> = (0..30).map(|j| (2.0 * std::f64::consts::PI * 3.0 * j as f64 / 30.0).sin()).collect();
|
|
73
77
|
let spectrum = rfft(&signal); // 30 complex bins; bin 3 carries the energy
|
|
74
78
|
assert!(spectrum[3].norm() > 14.0);
|
|
79
|
+
|
|
80
|
+
// Interval sets: overlapping stops are down-time once, not twice
|
|
81
|
+
use u_numflow::collections::IntervalSet;
|
|
82
|
+
let planned = IntervalSet::from_intervals([(0.0, 24.0)]).unwrap();
|
|
83
|
+
let unplanned = IntervalSet::from_intervals([(10.0, 30.0)]).unwrap();
|
|
84
|
+
let week = IntervalSet::from_intervals([(0.0, 168.0)]).unwrap();
|
|
85
|
+
let up = week.difference(&planned.union(&unplanned));
|
|
86
|
+
assert_eq!(up.measure(), 138.0);
|
|
75
87
|
```
|
|
76
88
|
|
|
89
|
+
`IntervalSet<T>` works over `f32`, `f64` and every primitive integer type. Its
|
|
90
|
+
measure has the type of a distance — the unsigned type of the same width for
|
|
91
|
+
integers (as `i64::abs_diff` returns `u64`), so it cannot overflow. A reversed
|
|
92
|
+
interval (`start > end`) or a NaN/infinite bound is refused with its position,
|
|
93
|
+
never swapped or dropped.
|
|
94
|
+
|
|
77
95
|
## Build & Test
|
|
78
96
|
|
|
79
97
|
```bash
|
|
@@ -83,13 +101,24 @@ cargo test
|
|
|
83
101
|
|
|
84
102
|
## Dependencies
|
|
85
103
|
|
|
86
|
-
- `rand` 0.
|
|
104
|
+
- `rand` 0.10 — Random number generation
|
|
87
105
|
- `proptest` 1.4 — Property-based testing (dev only)
|
|
88
106
|
|
|
89
107
|
## License
|
|
90
108
|
|
|
91
109
|
MIT License — see [LICENSE](LICENSE).
|
|
92
110
|
|
|
111
|
+
## .NET (NuGet)
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
dotnet add package UNumflow
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The distributions (CDF, quantile, moments, seeded sampling) and interval sets,
|
|
118
|
+
backed by this crate through its C ABI (the `ffi` feature) — see
|
|
119
|
+
[bindings/csharp/README.md](bindings/csharp/README.md). Refusals carry the same
|
|
120
|
+
`code` and fields as the WebAssembly binding's.
|
|
121
|
+
|
|
93
122
|
## npm (WebAssembly)
|
|
94
123
|
|
|
95
124
|
```bash
|
|
@@ -112,6 +141,12 @@ Exported functions: `mean`, `std_dev`, `variance`, `normal_cdf`, `normal_sf` (up
|
|
|
112
141
|
`rfft(data) -> Float64Array` — the DFT of a real sequence of any length, interleaved as
|
|
113
142
|
`[re0, im0, re1, im1, …]` (bins `k` and `n − k` are conjugates, so `0..=n/2` describes the spectrum).
|
|
114
143
|
|
|
144
|
+
Every `data` argument is a `number[]` or a `Float64Array`, read exactly as sent: an
|
|
145
|
+
element that is not a number (`null`, `undefined`, a string) throws `malformed_input`
|
|
146
|
+
and a NaN or ±Infinity throws `value_not_finite`, each with the element's `index` —
|
|
147
|
+
`mean([1, null, 3])` throws rather than averaging the `null` as 0. `mean` of no values
|
|
148
|
+
throws `empty_input`; `std_dev` and `variance` of fewer than 2 throw `insufficient_data`.
|
|
149
|
+
|
|
115
150
|
Distribution functions for critical values and p-values — each returns a `number` and
|
|
116
151
|
**throws** (an `Error` naming the argument — see *Errors* below) when an argument is outside the domain,
|
|
117
152
|
rather than returning `NaN`:
|
|
@@ -131,6 +166,50 @@ const { t_distribution_quantile } = require("@iyulab/u-numflow");
|
|
|
131
166
|
t_distribution_quantile(0.975, 10); // 2.2281…
|
|
132
167
|
```
|
|
133
168
|
|
|
169
|
+
Any of the crate's distributions by specification — `distribution` is an object
|
|
170
|
+
`{ kind, ...parameters }`, declared as the union type `DistributionSpec`:
|
|
171
|
+
|
|
172
|
+
| `kind` | Parameters |
|
|
173
|
+
|---|---|
|
|
174
|
+
| `uniform` | `min`, `max` |
|
|
175
|
+
| `triangular` | `min`, `mode`, `max` |
|
|
176
|
+
| `pert` | `min`, `mode`, `max`, `lambda?` (default 4) |
|
|
177
|
+
| `normal` / `lognormal` | `mu`, `sigma` |
|
|
178
|
+
| `weibull` | `shape`, `scale` |
|
|
179
|
+
| `exponential` | `rate` |
|
|
180
|
+
| `gamma` | `shape`, `rate` |
|
|
181
|
+
| `beta` | `alpha`, `beta` |
|
|
182
|
+
| `chi_squared` | `k` |
|
|
183
|
+
|
|
184
|
+
| Function | Returns |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `distribution_cdf(distribution, x)` | `P(X ≤ x)` |
|
|
187
|
+
| `distribution_quantile(distribution, p)` | `x` with `P(X ≤ x) = p`, `p` strictly inside (0, 1) |
|
|
188
|
+
| `distribution_sample(distribution, n, seed)` | `Float64Array` of `n` variates — the same `(distribution, n, seed)` always gives the same values |
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
const { distribution_quantile, distribution_sample } = require("@iyulab/u-numflow");
|
|
192
|
+
distribution_quantile({ kind: "chi_squared", k: 4 }, 0.95); // 9.4877…
|
|
193
|
+
const lives = distribution_sample({ kind: "weibull", shape: 2, scale: 100 }, 1000, 42);
|
|
194
|
+
console.log(lives.length); // 1000
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Interval sets — each argument is a `[start, end][]` of half-open intervals, and every
|
|
198
|
+
result is in normal form (sorted, disjoint, touching pieces merged, empty ones dropped):
|
|
199
|
+
|
|
200
|
+
| Function | Returns |
|
|
201
|
+
|---|---|
|
|
202
|
+
| `interval_normalize(intervals)` | The union of `intervals`, as `[number, number][]` |
|
|
203
|
+
| `interval_measure(intervals)` | Its total length — each point counted once, however many rows cover it |
|
|
204
|
+
| `interval_union(a, b)` / `interval_intersection(a, b)` / `interval_difference(a, b)` | Set operations, as `[number, number][]`; clip to a window with `interval_intersection(a, [[from, to]])` |
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
const { interval_union, interval_difference, interval_measure } = require("@iyulab/u-numflow");
|
|
208
|
+
const down = interval_union([[0, 24]], [[10, 30]]); // [[0, 30]] — the overlap counts once
|
|
209
|
+
const up = interval_difference([[0, 168]], down); // [[30, 168]]
|
|
210
|
+
console.log(interval_measure(up)); // 138
|
|
211
|
+
```
|
|
212
|
+
|
|
134
213
|
**Errors.** A refusal throws an `Error` whose `message` is readable text and which
|
|
135
214
|
carries a `code` naming the reason, next to the values behind it:
|
|
136
215
|
|
|
@@ -145,10 +224,15 @@ try {
|
|
|
145
224
|
|
|
146
225
|
| `code` | Fields | Meaning |
|
|
147
226
|
|---|---|---|
|
|
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
|
-
| `
|
|
227
|
+
| `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); a distribution parameter that must be `> 0` (`parameter` is its path, e.g. `distribution.sigma`); `n` or `seed` not a whole number in range |
|
|
228
|
+
| `unknown_option` | `parameter`, `got`, `expected` | A `distribution.kind` the crate does not have |
|
|
229
|
+
| `invalid_option` | `parameter` | Distribution parameters out of order: `min < max`, `min ≤ mode ≤ max` |
|
|
230
|
+
| `malformed_input` | `parameter`, `index` (or absent) | A `data` argument that is not an array or `Float64Array`, or an element that is not a number; a `distribution` that is not an object with a string `kind`, lacks a parameter or has one its kind does not take; an interval row that is not two numbers (`parameter` is its path, e.g. `a[2]`) |
|
|
231
|
+
| `reversed_interval` | `parameter`, `index` | An interval row with start > end — refused, not swapped |
|
|
232
|
+
| `value_not_finite` | `parameter`, `index` for an array element | A NaN argument, or a NaN or infinity in any `data` array or interval row (`parameter` is the row's path) |
|
|
233
|
+
| `empty_input` | `parameter` | `mean` of no values |
|
|
150
234
|
| `non_positive_data` | — | Box-Cox data with a value `≤ 0` |
|
|
151
|
-
| `insufficient_data` | —
|
|
235
|
+
| `insufficient_data` | `parameter`, `min`, `got` for `std_dev`/`variance`; — for Box-Cox | Fewer values than the function needs (`std_dev`/`variance` 2, Box-Cox 2) |
|
|
152
236
|
| `invalid_transform` | — | A Box-Cox result that is not finite |
|
|
153
237
|
| `invalid_lambda_range` | — | `estimate_lambda` bounds that are not finite with `lambda_min < lambda_max` |
|
|
154
238
|
|
|
@@ -159,17 +243,18 @@ declarations are generated from the same structs the binding reads and
|
|
|
159
243
|
serialises, so they cannot drift from what it actually accepts and returns:
|
|
160
244
|
|
|
161
245
|
```ts
|
|
162
|
-
export function
|
|
246
|
+
export function mean(data: number[] | Float64Array): number;
|
|
247
|
+
export function estimate_lambda(data: number[] | Float64Array, lambda_min: number, lambda_max: number): LambdaEstimateDto;
|
|
163
248
|
```
|
|
164
249
|
|
|
165
250
|
An absent optional value is declared `T | undefined`, which is what the binding
|
|
166
251
|
sends. Nothing needs an `as` cast -- and a wrong assumption about a result's
|
|
167
252
|
shape is a compile error rather than something that fails at run time.
|
|
168
253
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
254
|
+
A `data` parameter is declared `number[] | Float64Array` and read by the
|
|
255
|
+
binding itself rather than copied into a typed array by the generated glue, so
|
|
256
|
+
a value the declaration does not allow is refused where it sits instead of
|
|
257
|
+
being converted. The publishing workflow keeps every declaration free of `any`.
|
|
173
258
|
|
|
174
259
|
## Related
|
|
175
260
|
|