@mindpeeker/negentropy 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +11 -0
- package/dist/errors.js.map +1 -0
- package/dist/estimators/entropy.d.ts +22 -0
- package/dist/estimators/entropy.d.ts.map +1 -0
- package/dist/estimators/entropy.js +78 -0
- package/dist/estimators/entropy.js.map +1 -0
- package/dist/estimators/frequency.d.ts +18 -0
- package/dist/estimators/frequency.d.ts.map +1 -0
- package/dist/estimators/frequency.js +57 -0
- package/dist/estimators/frequency.js.map +1 -0
- package/dist/estimators/negentropy.d.ts +62 -0
- package/dist/estimators/negentropy.d.ts.map +1 -0
- package/dist/estimators/negentropy.js +104 -0
- package/dist/estimators/negentropy.js.map +1 -0
- package/dist/estimators/pipeline.d.ts +25 -0
- package/dist/estimators/pipeline.d.ts.map +1 -0
- package/dist/estimators/pipeline.js +30 -0
- package/dist/estimators/pipeline.js.map +1 -0
- package/dist/estimators/vasicek.d.ts +18 -0
- package/dist/estimators/vasicek.d.ts.map +1 -0
- package/dist/estimators/vasicek.js +57 -0
- package/dist/estimators/vasicek.js.map +1 -0
- package/dist/estimators/windowed.d.ts +27 -0
- package/dist/estimators/windowed.d.ts.map +1 -0
- package/dist/estimators/windowed.js +66 -0
- package/dist/estimators/windowed.js.map +1 -0
- package/dist/experiment/batch.d.ts +15 -0
- package/dist/experiment/batch.d.ts.map +1 -0
- package/dist/experiment/batch.js +179 -0
- package/dist/experiment/batch.js.map +1 -0
- package/dist/experiment/composite.d.ts +12 -0
- package/dist/experiment/composite.d.ts.map +1 -0
- package/dist/experiment/composite.js +26 -0
- package/dist/experiment/composite.js.map +1 -0
- package/dist/experiment/registration.d.ts +20 -0
- package/dist/experiment/registration.d.ts.map +1 -0
- package/dist/experiment/registration.js +54 -0
- package/dist/experiment/registration.js.map +1 -0
- package/dist/experiment/session.d.ts +47 -0
- package/dist/experiment/session.d.ts.map +1 -0
- package/dist/experiment/session.js +245 -0
- package/dist/experiment/session.js.map +1 -0
- package/dist/experiment/types.d.ts +68 -0
- package/dist/experiment/types.d.ts.map +1 -0
- package/dist/experiment/types.js +2 -0
- package/dist/experiment/types.js.map +1 -0
- package/dist/extract/accounting.d.ts +52 -0
- package/dist/extract/accounting.d.ts.map +1 -0
- package/dist/extract/accounting.js +130 -0
- package/dist/extract/accounting.js.map +1 -0
- package/dist/extract/condition.d.ts +23 -0
- package/dist/extract/condition.d.ts.map +1 -0
- package/dist/extract/condition.js +50 -0
- package/dist/extract/condition.js.map +1 -0
- package/dist/extract/debias.d.ts +25 -0
- package/dist/extract/debias.d.ts.map +1 -0
- package/dist/extract/debias.js +80 -0
- package/dist/extract/debias.js.map +1 -0
- package/dist/extract/health.d.ts +44 -0
- package/dist/extract/health.d.ts.map +1 -0
- package/dist/extract/health.js +115 -0
- package/dist/extract/health.js.map +1 -0
- package/dist/extract/toeplitz.d.ts +19 -0
- package/dist/extract/toeplitz.d.ts.map +1 -0
- package/dist/extract/toeplitz.js +83 -0
- package/dist/extract/toeplitz.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/bytes.d.ts +6 -0
- package/dist/internal/bytes.d.ts.map +1 -0
- package/dist/internal/bytes.js +36 -0
- package/dist/internal/bytes.js.map +1 -0
- package/dist/internal/kahan.d.ts +7 -0
- package/dist/internal/kahan.d.ts.map +1 -0
- package/dist/internal/kahan.js +15 -0
- package/dist/internal/kahan.js.map +1 -0
- package/dist/internal/prng.d.ts +7 -0
- package/dist/internal/prng.d.ts.map +1 -0
- package/dist/internal/prng.js +16 -0
- package/dist/internal/prng.js.map +1 -0
- package/dist/internal/special.d.ts +36 -0
- package/dist/internal/special.d.ts.map +1 -0
- package/dist/internal/special.js +258 -0
- package/dist/internal/special.js.map +1 -0
- package/dist/internal/welford.d.ts +13 -0
- package/dist/internal/welford.d.ts.map +1 -0
- package/dist/internal/welford.js +30 -0
- package/dist/internal/welford.js.map +1 -0
- package/dist/numerics.d.ts +32 -0
- package/dist/numerics.d.ts.map +1 -0
- package/dist/numerics.js +32 -0
- package/dist/numerics.js.map +1 -0
- package/dist/stats/calibration.d.ts +14 -0
- package/dist/stats/calibration.d.ts.map +1 -0
- package/dist/stats/calibration.js +41 -0
- package/dist/stats/calibration.js.map +1 -0
- package/dist/stats/cumdev.d.ts +15 -0
- package/dist/stats/cumdev.d.ts.map +1 -0
- package/dist/stats/cumdev.js +38 -0
- package/dist/stats/cumdev.js.map +1 -0
- package/dist/stats/network.d.ts +29 -0
- package/dist/stats/network.d.ts.map +1 -0
- package/dist/stats/network.js +119 -0
- package/dist/stats/network.js.map +1 -0
- package/dist/stats/pvalues.d.ts +11 -0
- package/dist/stats/pvalues.d.ts.map +1 -0
- package/dist/stats/pvalues.js +18 -0
- package/dist/stats/pvalues.js.map +1 -0
- package/dist/stats/trials.d.ts +34 -0
- package/dist/stats/trials.d.ts.map +1 -0
- package/dist/stats/trials.js +149 -0
- package/dist/stats/trials.js.map +1 -0
- package/dist/stats/zscores.d.ts +10 -0
- package/dist/stats/zscores.d.ts.map +1 -0
- package/dist/stats/zscores.js +26 -0
- package/dist/stats/zscores.js.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { uniformStream } from '../internal/prng.js';
|
|
2
|
+
import { normPpf } from '../internal/special.js';
|
|
3
|
+
import { zScores } from '../stats/zscores.js';
|
|
4
|
+
/**
|
|
5
|
+
* Trial sums → z-scores + uniform dither over one lattice cell. The dithered
|
|
6
|
+
* variable is continuous, and its differential entropy equals the discrete
|
|
7
|
+
* entropy of the lattice variable (in lattice-spacing log units) — which is
|
|
8
|
+
* what makes Vasicek meaningful on trial data. Under H0 the result is
|
|
9
|
+
* approximately N(0, 1 + 1/(12·k/4)) with excess kurtosis −2/k + O(1/k²).
|
|
10
|
+
*/
|
|
11
|
+
export function ditheredTrialZ(series, cal, opts = {}) {
|
|
12
|
+
const zs = zScores(series, cal);
|
|
13
|
+
const next = uniformStream(opts.seed);
|
|
14
|
+
for (let i = 0; i < zs.length; i++)
|
|
15
|
+
zs[i] = zs[i] + (next() - 0.5) / cal.sd;
|
|
16
|
+
return zs;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Bytes → exactly standard normal samples under H0: u = (b + U(0,1))/256 is
|
|
20
|
+
* uniform when bytes are, and Φ⁻¹(u) is then exactly N(0,1). The exact-null
|
|
21
|
+
* mode — ideal for calibrating estimators and byte-level (non-trial) analysis.
|
|
22
|
+
*/
|
|
23
|
+
export function probitBytes(bytes, opts = {}) {
|
|
24
|
+
const next = uniformStream(opts.seed);
|
|
25
|
+
const out = new Float64Array(bytes.length);
|
|
26
|
+
for (let i = 0; i < bytes.length; i++)
|
|
27
|
+
out[i] = normPpf((bytes[i] + next()) / 256);
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/estimators/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAa7C;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAmB,EACnB,GAAgB,EAChB,OAAsB,EAAE;IAExB,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC/B,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,EAAE,CAAC,CAAC,CAAC,GAAI,EAAE,CAAC,CAAC,CAAY,GAAG,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,CAAA;IACvF,OAAO,EAAE,CAAA;AACX,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,KAAiB,EAAE,OAAsB,EAAE;IACrE,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACrC,MAAM,GAAG,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAE,KAAK,CAAC,CAAC,CAAY,GAAG,IAAI,EAAE,CAAC,GAAG,GAAG,CAAC,CAAA;IAC9F,OAAO,GAAG,CAAA;AACZ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vasicek m-spacings differential entropy estimate (nats):
|
|
3
|
+
* H = (1/n)Σ ln((n/(2m))·(x₍ᵢ₊ₘ₎ − x₍ᵢ₋ₘ₎)) with boundary clamping, matching
|
|
4
|
+
* scipy's `differential_entropy(method='vasicek')`. Default window
|
|
5
|
+
* m = ⌊√n + ½⌋. Negatively biased at small n; scale-equivariant:
|
|
6
|
+
* H(a·x) = H(x) + ln|a|.
|
|
7
|
+
*
|
|
8
|
+
* Lattice-valued data (bytes, trial sums) has ties → zero spacings → −∞;
|
|
9
|
+
* dither it first (see `ditheredTrialZ`/`probitBytes`).
|
|
10
|
+
*/
|
|
11
|
+
export declare function vasicekEntropy(x: ArrayLike<number>, m?: number): number;
|
|
12
|
+
/**
|
|
13
|
+
* Negentropy against the maximum-entropy Gaussian of equal (population)
|
|
14
|
+
* variance: J = ½ln(2πeσ̂²) − Ĥ_vasicek(x). Scale-invariant; can dip slightly
|
|
15
|
+
* negative from estimator bias — not clamped, by honesty.
|
|
16
|
+
*/
|
|
17
|
+
export declare function negentropyVasicek(x: ArrayLike<number>, m?: number): number;
|
|
18
|
+
//# sourceMappingURL=vasicek.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vasicek.d.ts","sourceRoot":"","sources":["../../src/estimators/vasicek.ts"],"names":[],"mappings":"AAMA;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CA6BvE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAU1E"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { NegentropyError } from '../errors.js';
|
|
2
|
+
import { KahanSum } from '../internal/kahan.js';
|
|
3
|
+
/** ½·ln(2πe) — the differential entropy of a unit-variance Gaussian, in nats. */
|
|
4
|
+
const HALF_LN_2PIE = 1.4189385332046727;
|
|
5
|
+
/**
|
|
6
|
+
* Vasicek m-spacings differential entropy estimate (nats):
|
|
7
|
+
* H = (1/n)Σ ln((n/(2m))·(x₍ᵢ₊ₘ₎ − x₍ᵢ₋ₘ₎)) with boundary clamping, matching
|
|
8
|
+
* scipy's `differential_entropy(method='vasicek')`. Default window
|
|
9
|
+
* m = ⌊√n + ½⌋. Negatively biased at small n; scale-equivariant:
|
|
10
|
+
* H(a·x) = H(x) + ln|a|.
|
|
11
|
+
*
|
|
12
|
+
* Lattice-valued data (bytes, trial sums) has ties → zero spacings → −∞;
|
|
13
|
+
* dither it first (see `ditheredTrialZ`/`probitBytes`).
|
|
14
|
+
*/
|
|
15
|
+
export function vasicekEntropy(x, m) {
|
|
16
|
+
const n = x.length;
|
|
17
|
+
if (n < 4) {
|
|
18
|
+
throw new NegentropyError('insufficient_data', `vasicekEntropy needs ≥ 4 samples, got ${n}`);
|
|
19
|
+
}
|
|
20
|
+
const window = m ?? Math.floor(Math.sqrt(n) + 0.5);
|
|
21
|
+
if (!Number.isInteger(window) || window < 1 || window >= n / 2) {
|
|
22
|
+
throw new NegentropyError('invalid_config', `m-spacings window must be an integer in [1, n/2), got ${window} for n=${n}`);
|
|
23
|
+
}
|
|
24
|
+
const sorted = Float64Array.from(x);
|
|
25
|
+
sorted.sort();
|
|
26
|
+
const acc = new KahanSum();
|
|
27
|
+
const scale = n / (2 * window);
|
|
28
|
+
for (let i = 0; i < n; i++) {
|
|
29
|
+
const lo = sorted[Math.max(0, i - window)];
|
|
30
|
+
const hi = sorted[Math.min(n - 1, i + window)];
|
|
31
|
+
const spacing = hi - lo;
|
|
32
|
+
if (!(spacing > 0)) {
|
|
33
|
+
throw new NegentropyError('insufficient_data', 'zero m-spacing (tied samples) — lattice-valued input must be dithered first');
|
|
34
|
+
}
|
|
35
|
+
acc.add(Math.log(scale * spacing));
|
|
36
|
+
}
|
|
37
|
+
return acc.value / n;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Negentropy against the maximum-entropy Gaussian of equal (population)
|
|
41
|
+
* variance: J = ½ln(2πeσ̂²) − Ĥ_vasicek(x). Scale-invariant; can dip slightly
|
|
42
|
+
* negative from estimator bias — not clamped, by honesty.
|
|
43
|
+
*/
|
|
44
|
+
export function negentropyVasicek(x, m) {
|
|
45
|
+
const h = vasicekEntropy(x, m);
|
|
46
|
+
const n = x.length;
|
|
47
|
+
let sum = 0;
|
|
48
|
+
for (let i = 0; i < n; i++)
|
|
49
|
+
sum += x[i];
|
|
50
|
+
const mean = sum / n;
|
|
51
|
+
let m2 = 0;
|
|
52
|
+
for (let i = 0; i < n; i++)
|
|
53
|
+
m2 += (x[i] - mean) ** 2;
|
|
54
|
+
const variance = m2 / n;
|
|
55
|
+
return HALF_LN_2PIE + 0.5 * Math.log(variance) - h;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=vasicek.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vasicek.js","sourceRoot":"","sources":["../../src/estimators/vasicek.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type WindowedEstimator = 'kurtosis' | 'logcosh' | 'exp' | 'vasicek';
|
|
2
|
+
export interface WindowedNegentropyOptions {
|
|
3
|
+
/** Samples per window (≥ 8). */
|
|
4
|
+
windowSize: number;
|
|
5
|
+
/** Samples between emissions. Default windowSize (non-overlapping). */
|
|
6
|
+
hopSize?: number;
|
|
7
|
+
/** Default 'logcosh'. 'vasicek' needs continuous input — dither lattice data first. */
|
|
8
|
+
estimator?: WindowedEstimator;
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
}
|
|
11
|
+
export interface WindowedNegentropyPoint {
|
|
12
|
+
/** Emission counter, from 0. */
|
|
13
|
+
index: number;
|
|
14
|
+
/** Input-sample index at which this window starts. */
|
|
15
|
+
startSample: number;
|
|
16
|
+
/** The window's negentropy. NaN for a degenerate (constant) window. */
|
|
17
|
+
j: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Rolling negentropy over a sample stream — the "when did order appear?"
|
|
21
|
+
* view. Lazy and pull-based; every emission recomputes the batch estimator
|
|
22
|
+
* on the current window, so streamed values are EXACTLY the batch values on
|
|
23
|
+
* the corresponding slices. Accepts numbers or byte chunks (bytes enter as
|
|
24
|
+
* their numeric values — estimators standardize internally).
|
|
25
|
+
*/
|
|
26
|
+
export declare function windowedNegentropy(input: AsyncIterable<number | Uint8Array> | Iterable<number | Uint8Array>, opts: WindowedNegentropyOptions): AsyncGenerator<WindowedNegentropyPoint>;
|
|
27
|
+
//# sourceMappingURL=windowed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windowed.d.ts","sourceRoot":"","sources":["../../src/estimators/windowed.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,SAAS,GAAG,KAAK,GAAG,SAAS,CAAA;AAE1E,MAAM,WAAW,yBAAyB;IACxC,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uFAAuF;IACvF,SAAS,CAAC,EAAE,iBAAiB,CAAA;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAA;IACnB,uEAAuE;IACvE,CAAC,EAAE,MAAM,CAAA;CACV;AAED;;;;;;GAMG;AACH,wBAAuB,kBAAkB,CACvC,KAAK,EAAE,aAAa,CAAC,MAAM,GAAG,UAAU,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,UAAU,CAAC,EACzE,IAAI,EAAE,yBAAyB,GAC9B,cAAc,CAAC,uBAAuB,CAAC,CAwDzC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { NegentropyError } from '../errors.js';
|
|
2
|
+
import { negentropyExp, negentropyKurtosis, negentropyLogcosh } from './negentropy.js';
|
|
3
|
+
import { negentropyVasicek } from './vasicek.js';
|
|
4
|
+
/**
|
|
5
|
+
* Rolling negentropy over a sample stream — the "when did order appear?"
|
|
6
|
+
* view. Lazy and pull-based; every emission recomputes the batch estimator
|
|
7
|
+
* on the current window, so streamed values are EXACTLY the batch values on
|
|
8
|
+
* the corresponding slices. Accepts numbers or byte chunks (bytes enter as
|
|
9
|
+
* their numeric values — estimators standardize internally).
|
|
10
|
+
*/
|
|
11
|
+
export async function* windowedNegentropy(input, opts) {
|
|
12
|
+
const { windowSize } = opts;
|
|
13
|
+
if (!Number.isInteger(windowSize) || windowSize < 8) {
|
|
14
|
+
throw new NegentropyError('invalid_config', `windowSize must be an integer ≥ 8, got ${windowSize}`);
|
|
15
|
+
}
|
|
16
|
+
const hop = opts.hopSize ?? windowSize;
|
|
17
|
+
if (!Number.isInteger(hop) || hop < 1) {
|
|
18
|
+
throw new NegentropyError('invalid_config', `hopSize must be a positive integer, got ${hop}`);
|
|
19
|
+
}
|
|
20
|
+
const estimator = opts.estimator ?? 'logcosh';
|
|
21
|
+
const estimate = (window) => {
|
|
22
|
+
switch (estimator) {
|
|
23
|
+
case 'kurtosis':
|
|
24
|
+
return negentropyKurtosis(window).j;
|
|
25
|
+
case 'logcosh':
|
|
26
|
+
return negentropyLogcosh(window).j;
|
|
27
|
+
case 'exp':
|
|
28
|
+
return negentropyExp(window).j;
|
|
29
|
+
case 'vasicek':
|
|
30
|
+
return negentropyVasicek(window);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const ring = new Float64Array(windowSize);
|
|
34
|
+
let pushed = 0;
|
|
35
|
+
let nextEmitAt = windowSize;
|
|
36
|
+
let index = 0;
|
|
37
|
+
function* push(value) {
|
|
38
|
+
ring[pushed % windowSize] = value;
|
|
39
|
+
pushed++;
|
|
40
|
+
if (pushed >= nextEmitAt) {
|
|
41
|
+
// unroll the ring into window order (oldest first)
|
|
42
|
+
const window = new Float64Array(windowSize);
|
|
43
|
+
const head = pushed % windowSize;
|
|
44
|
+
for (let i = 0; i < windowSize; i++)
|
|
45
|
+
window[i] = ring[(head + i) % windowSize];
|
|
46
|
+
yield { index: index++, startSample: pushed - windowSize, j: estimate(window) };
|
|
47
|
+
nextEmitAt += hop;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (opts.signal?.aborted) {
|
|
51
|
+
throw new NegentropyError('aborted', 'windowed negentropy aborted before start');
|
|
52
|
+
}
|
|
53
|
+
for await (const item of input) {
|
|
54
|
+
if (opts.signal?.aborted) {
|
|
55
|
+
throw new NegentropyError('aborted', 'windowed negentropy aborted');
|
|
56
|
+
}
|
|
57
|
+
if (typeof item === 'number') {
|
|
58
|
+
yield* push(item);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
for (let i = 0; i < item.length; i++)
|
|
62
|
+
yield* push(item[i]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=windowed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windowed.js","sourceRoot":"","sources":["../../src/estimators/windowed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TrialSeries } from '../types.js';
|
|
2
|
+
import type { RegisteredExperiment } from './registration.js';
|
|
3
|
+
import type { ExperimentConfig, ExperimentResult } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* The pure analysis core: recorded trial series in, ExperimentResult out.
|
|
6
|
+
* Live sessions delegate here on stop(), so a re-analysis of the archived
|
|
7
|
+
* series reproduces the live result exactly.
|
|
8
|
+
*/
|
|
9
|
+
export declare function analyzeTrials(series: readonly TrialSeries[], configOrRegistration: ExperimentConfig | RegisteredExperiment): ExperimentResult;
|
|
10
|
+
/** Convenience wrapper: recorded raw bytes per source → trials → analyzeTrials. */
|
|
11
|
+
export declare function analyzeBytes(recordings: ReadonlyArray<{
|
|
12
|
+
source: string;
|
|
13
|
+
bytes: Uint8Array;
|
|
14
|
+
}>, configOrRegistration: ExperimentConfig | RegisteredExperiment): ExperimentResult;
|
|
15
|
+
//# sourceMappingURL=batch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../../src/experiment/batch.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAA2B,WAAW,EAAE,MAAM,aAAa,CAAA;AAEvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,KAAK,EAIV,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,YAAY,CAAA;AAgFnB;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,oBAAoB,EAAE,gBAAgB,GAAG,oBAAoB,GAC5D,gBAAgB,CAkIlB;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAC1B,UAAU,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,CAAC,EAChE,oBAAoB,EAAE,gBAAgB,GAAG,oBAAoB,GAC5D,gBAAgB,CAIlB"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { NegentropyError } from '../errors.js';
|
|
2
|
+
import { normPpf } from '../internal/special.js';
|
|
3
|
+
import { calibrate, theoreticalCalibration } from '../stats/calibration.js';
|
|
4
|
+
import { cumulativeDeviation } from '../stats/cumdev.js';
|
|
5
|
+
import { devvar, interSourceCorrelation, netvar } from '../stats/network.js';
|
|
6
|
+
import { P_FLOOR } from '../stats/pvalues.js';
|
|
7
|
+
import { trialsFromBytes } from '../stats/trials.js';
|
|
8
|
+
import { stoufferZ, zScores } from '../stats/zscores.js';
|
|
9
|
+
import { compositeZ } from './composite.js';
|
|
10
|
+
function resolveConfig(config) {
|
|
11
|
+
if ('hash' in config && 'config' in config) {
|
|
12
|
+
return { config: config.config, registration: config.hash };
|
|
13
|
+
}
|
|
14
|
+
return { config: config };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Normal-equivalent z of a one-sided p, via normPpf(1−p) = −normPpf(p) —
|
|
18
|
+
* the lower tail keeps full precision where 1−p would round to exactly 1.
|
|
19
|
+
*/
|
|
20
|
+
function pToZ(p) {
|
|
21
|
+
return -normPpf(Math.min(Math.max(p, P_FLOOR), 1 - 1e-16));
|
|
22
|
+
}
|
|
23
|
+
function resolveWindow(spec, steps, timestamps) {
|
|
24
|
+
let start;
|
|
25
|
+
let end;
|
|
26
|
+
if (spec.start instanceof Date || spec.end instanceof Date) {
|
|
27
|
+
if (!timestamps) {
|
|
28
|
+
throw new NegentropyError('invalid_window', `event ${spec.id} uses Date bounds but the trial data has no timestamps`);
|
|
29
|
+
}
|
|
30
|
+
const startMs = spec.start instanceof Date ? spec.start.getTime() : Number.NaN;
|
|
31
|
+
const endMs = spec.end instanceof Date ? spec.end.getTime() : Number.NaN;
|
|
32
|
+
if (!(spec.start instanceof Date) || !(spec.end instanceof Date)) {
|
|
33
|
+
throw new NegentropyError('invalid_window', `event ${spec.id} mixes Date and step-index bounds`);
|
|
34
|
+
}
|
|
35
|
+
start = timestamps.findIndex((t) => t >= startMs);
|
|
36
|
+
if (start === -1)
|
|
37
|
+
start = steps;
|
|
38
|
+
end = timestamps.findIndex((t) => t >= endMs);
|
|
39
|
+
if (end === -1)
|
|
40
|
+
end = steps;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
start = spec.start;
|
|
44
|
+
end = spec.end;
|
|
45
|
+
}
|
|
46
|
+
if (!Number.isInteger(start) ||
|
|
47
|
+
!Number.isInteger(end) ||
|
|
48
|
+
start < 0 ||
|
|
49
|
+
end > steps ||
|
|
50
|
+
start >= end) {
|
|
51
|
+
throw new NegentropyError('invalid_window', `event ${spec.id}: window [${String(spec.start)}, ${String(spec.end)}) resolves to [${start}, ${end}) outside the ${steps} available steps`);
|
|
52
|
+
}
|
|
53
|
+
return { start, end };
|
|
54
|
+
}
|
|
55
|
+
function eventStatistic(statistic, zSlices, sources) {
|
|
56
|
+
switch (statistic) {
|
|
57
|
+
case 'netvar':
|
|
58
|
+
return netvar(zSlices, sources);
|
|
59
|
+
case 'devvar':
|
|
60
|
+
return devvar(zSlices, sources);
|
|
61
|
+
case 'correlation':
|
|
62
|
+
return interSourceCorrelation(zSlices, sources);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The pure analysis core: recorded trial series in, ExperimentResult out.
|
|
67
|
+
* Live sessions delegate here on stop(), so a re-analysis of the archived
|
|
68
|
+
* series reproduces the live result exactly.
|
|
69
|
+
*/
|
|
70
|
+
export function analyzeTrials(series, configOrRegistration) {
|
|
71
|
+
const { config, registration } = resolveConfig(configOrRegistration);
|
|
72
|
+
if (series.length === 0) {
|
|
73
|
+
throw new NegentropyError('invalid_config', 'analyzeTrials needs at least one series');
|
|
74
|
+
}
|
|
75
|
+
const bitsPerTrial = series[0].bitsPerTrial;
|
|
76
|
+
for (const s of series) {
|
|
77
|
+
if (s.bitsPerTrial !== bitsPerTrial) {
|
|
78
|
+
throw new NegentropyError('invalid_config', `mixed bitsPerTrial: ${s.source} has ${s.bitsPerTrial}, ${series[0].source} has ${bitsPerTrial}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const missing = config.missing ?? 'error';
|
|
82
|
+
// resolve calibration (possibly burning a leading window off each series)
|
|
83
|
+
const calibrationSpec = config.calibration ?? 'theoretical';
|
|
84
|
+
let analysisSeries;
|
|
85
|
+
let calibrations;
|
|
86
|
+
if (calibrationSpec === 'theoretical') {
|
|
87
|
+
analysisSeries = [...series];
|
|
88
|
+
calibrations = series.map((s) => theoreticalCalibration(s.source, bitsPerTrial));
|
|
89
|
+
}
|
|
90
|
+
else if (Array.isArray(calibrationSpec)) {
|
|
91
|
+
analysisSeries = [...series];
|
|
92
|
+
calibrations = series.map((s) => {
|
|
93
|
+
const cal = calibrationSpec.find((c) => c.source === s.source);
|
|
94
|
+
if (!cal) {
|
|
95
|
+
throw new NegentropyError('calibration_required', `no calibration provided for ${s.source}`, {
|
|
96
|
+
source: s.source,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return cal;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
const burn = calibrationSpec.trials;
|
|
104
|
+
if (!Number.isInteger(burn) || burn < 2) {
|
|
105
|
+
throw new NegentropyError('invalid_config', `calibration.trials must be an integer ≥ 2, got ${burn}`);
|
|
106
|
+
}
|
|
107
|
+
calibrations = [];
|
|
108
|
+
analysisSeries = series.map((s) => {
|
|
109
|
+
if (s.sums.length <= burn) {
|
|
110
|
+
throw new NegentropyError('insufficient_data', `${s.source} has ${s.sums.length} trials — not enough to burn ${burn} for calibration`, { source: s.source });
|
|
111
|
+
}
|
|
112
|
+
calibrations.push(calibrate({ source: s.source, bitsPerTrial, sums: s.sums.slice(0, burn) }, { minTrials: burn }));
|
|
113
|
+
return {
|
|
114
|
+
source: s.source,
|
|
115
|
+
bitsPerTrial,
|
|
116
|
+
sums: s.sums.slice(burn),
|
|
117
|
+
timestamps: s.timestamps?.slice(burn),
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// align lengths
|
|
122
|
+
const lengths = analysisSeries.map((s) => s.sums.length);
|
|
123
|
+
const steps = Math.min(...lengths);
|
|
124
|
+
if (missing === 'error' && new Set(lengths).size > 1) {
|
|
125
|
+
throw new NegentropyError('invalid_config', `series lengths differ (${lengths.join(', ')}) — align them or use missing: 'skip' to truncate to ${steps}`);
|
|
126
|
+
}
|
|
127
|
+
const aligned = analysisSeries.map((s) => s.sums.length === steps
|
|
128
|
+
? s
|
|
129
|
+
: {
|
|
130
|
+
source: s.source,
|
|
131
|
+
bitsPerTrial,
|
|
132
|
+
sums: s.sums.slice(0, steps),
|
|
133
|
+
timestamps: s.timestamps?.slice(0, steps),
|
|
134
|
+
});
|
|
135
|
+
const sources = aligned.map((s) => s.source);
|
|
136
|
+
const zBySource = aligned.map((s, i) => zScores(s, calibrations[i]));
|
|
137
|
+
const timeline = aligned[0].timestamps;
|
|
138
|
+
const events = [];
|
|
139
|
+
for (const spec of config.events ?? []) {
|
|
140
|
+
const { start, end } = resolveWindow(spec, steps, timeline);
|
|
141
|
+
const slices = zBySource.map((zs) => zs.slice(start, end));
|
|
142
|
+
const stat = eventStatistic(spec.statistic, slices, sources);
|
|
143
|
+
const windowSteps = end - start;
|
|
144
|
+
const stouffers = new Float64Array(windowSteps);
|
|
145
|
+
const column = new Float64Array(slices.length);
|
|
146
|
+
for (let t = 0; t < windowSteps; t++) {
|
|
147
|
+
for (let i = 0; i < slices.length; i++)
|
|
148
|
+
column[i] = slices[i][t];
|
|
149
|
+
stouffers[t] = stoufferZ(column);
|
|
150
|
+
}
|
|
151
|
+
events.push({
|
|
152
|
+
id: spec.id,
|
|
153
|
+
...(spec.label !== undefined && { label: spec.label }),
|
|
154
|
+
statistic: spec.statistic,
|
|
155
|
+
value: stat.statistic,
|
|
156
|
+
df: stat.df,
|
|
157
|
+
pValue: stat.pValue,
|
|
158
|
+
z: pToZ(stat.pValue),
|
|
159
|
+
steps: windowSteps,
|
|
160
|
+
cumulative: cumulativeDeviation(stouffers),
|
|
161
|
+
sources,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
const composite = events.length > 0 ? compositeZ(events) : { z: Number.NaN, pValue: Number.NaN, events: 0 };
|
|
165
|
+
return {
|
|
166
|
+
events,
|
|
167
|
+
composite,
|
|
168
|
+
calibration: calibrations,
|
|
169
|
+
series: aligned,
|
|
170
|
+
...(registration !== undefined && { registration }),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/** Convenience wrapper: recorded raw bytes per source → trials → analyzeTrials. */
|
|
174
|
+
export function analyzeBytes(recordings, configOrRegistration) {
|
|
175
|
+
const { config } = resolveConfig(configOrRegistration);
|
|
176
|
+
const series = recordings.map((r) => trialsFromBytes(r.bytes, r.source, config.trial));
|
|
177
|
+
return analyzeTrials(series, configOrRegistration);
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=batch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.js","sourceRoot":"","sources":["../../src/experiment/batch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EventResult, ExperimentComposite } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Stouffer composite across pre-registered events: Z = Σzₑ/√E, one-sided.
|
|
4
|
+
* Valid when events are independent (disjoint windows). This — not
|
|
5
|
+
* per-event significance hunting — is the recommended primary statistic for
|
|
6
|
+
* a multi-event experiment; for individual-event claims at level α over E
|
|
7
|
+
* events, Bonferroni α/E applies.
|
|
8
|
+
*/
|
|
9
|
+
export declare function compositeZ(events: readonly EventResult[]): ExperimentComposite;
|
|
10
|
+
/** Bonferroni-corrected significance threshold for individual-event claims. */
|
|
11
|
+
export declare function bonferroni(alpha: number, events: number): number;
|
|
12
|
+
//# sourceMappingURL=composite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composite.d.ts","sourceRoot":"","sources":["../../src/experiment/composite.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAElE;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,mBAAmB,CAM9E;AAED,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQhE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { NegentropyError } from '../errors.js';
|
|
2
|
+
import { normSf } from '../internal/special.js';
|
|
3
|
+
import { P_FLOOR } from '../stats/pvalues.js';
|
|
4
|
+
import { stoufferZ } from '../stats/zscores.js';
|
|
5
|
+
/**
|
|
6
|
+
* Stouffer composite across pre-registered events: Z = Σzₑ/√E, one-sided.
|
|
7
|
+
* Valid when events are independent (disjoint windows). This — not
|
|
8
|
+
* per-event significance hunting — is the recommended primary statistic for
|
|
9
|
+
* a multi-event experiment; for individual-event claims at level α over E
|
|
10
|
+
* events, Bonferroni α/E applies.
|
|
11
|
+
*/
|
|
12
|
+
export function compositeZ(events) {
|
|
13
|
+
if (events.length === 0) {
|
|
14
|
+
throw new NegentropyError('insufficient_data', 'composite needs at least one event');
|
|
15
|
+
}
|
|
16
|
+
const z = stoufferZ(events.map((event) => event.z));
|
|
17
|
+
return { z, pValue: Math.max(normSf(z), P_FLOOR), events: events.length };
|
|
18
|
+
}
|
|
19
|
+
/** Bonferroni-corrected significance threshold for individual-event claims. */
|
|
20
|
+
export function bonferroni(alpha, events) {
|
|
21
|
+
if (!(alpha > 0 && alpha < 1) || !Number.isInteger(events) || events < 1) {
|
|
22
|
+
throw new NegentropyError('invalid_config', `bad bonferroni inputs: alpha=${alpha}, events=${events}`);
|
|
23
|
+
}
|
|
24
|
+
return alpha / events;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=composite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composite.js","sourceRoot":"","sources":["../../src/experiment/composite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAG/C;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,MAA8B;IACvD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,eAAe,CAAC,mBAAmB,EAAE,oCAAoC,CAAC,CAAA;IACtF,CAAC;IACD,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IACnD,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAA;AAC3E,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,UAAU,CAAC,KAAa,EAAE,MAAc;IACtD,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACzE,MAAM,IAAI,eAAe,CACvB,gBAAgB,EAChB,gCAAgC,KAAK,YAAY,MAAM,EAAE,CAC1D,CAAA;IACH,CAAC;IACD,OAAO,KAAK,GAAG,MAAM,CAAA;AACvB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ExperimentConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Canonical JSON: recursively key-sorted, Dates as ISO strings, no
|
|
4
|
+
* insignificant whitespace — byte-stable across key insertion orders, so the
|
|
5
|
+
* hash commits to the CONTENT of a registration.
|
|
6
|
+
*/
|
|
7
|
+
export declare function canonicalJson(value: unknown): string;
|
|
8
|
+
export interface RegisteredExperiment {
|
|
9
|
+
readonly config: ExperimentConfig;
|
|
10
|
+
/** Hex SHA-256 of the canonical JSON — cite this alongside results. */
|
|
11
|
+
readonly hash: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Freeze and hash an experiment configuration BEFORE data ingestion. The
|
|
15
|
+
* returned config is deeply frozen (mutation throws), and passing the
|
|
16
|
+
* registration to `analyzeTrials`/`analyzeBytes`/`session` embeds its hash
|
|
17
|
+
* in the result — the paper trail against garden-of-forking-paths analysis.
|
|
18
|
+
*/
|
|
19
|
+
export declare function registerExperiment(config: ExperimentConfig): Promise<RegisteredExperiment>;
|
|
20
|
+
//# sourceMappingURL=registration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/experiment/registration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAyBpD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAA;IACjC,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAYD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAKhG"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { NegentropyError } from '../errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Canonical JSON: recursively key-sorted, Dates as ISO strings, no
|
|
4
|
+
* insignificant whitespace — byte-stable across key insertion orders, so the
|
|
5
|
+
* hash commits to the CONTENT of a registration.
|
|
6
|
+
*/
|
|
7
|
+
export function canonicalJson(value) {
|
|
8
|
+
if (value === null)
|
|
9
|
+
return 'null';
|
|
10
|
+
if (value instanceof Date)
|
|
11
|
+
return JSON.stringify(value.toISOString());
|
|
12
|
+
switch (typeof value) {
|
|
13
|
+
case 'string':
|
|
14
|
+
case 'boolean':
|
|
15
|
+
return JSON.stringify(value);
|
|
16
|
+
case 'number':
|
|
17
|
+
if (!Number.isFinite(value)) {
|
|
18
|
+
throw new NegentropyError('invalid_config', `non-finite number in registration: ${value}`);
|
|
19
|
+
}
|
|
20
|
+
return JSON.stringify(value);
|
|
21
|
+
case 'object': {
|
|
22
|
+
if (Array.isArray(value))
|
|
23
|
+
return `[${value.map((item) => canonicalJson(item)).join(',')}]`;
|
|
24
|
+
const entries = Object.entries(value)
|
|
25
|
+
.filter(([, v]) => v !== undefined)
|
|
26
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
27
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`;
|
|
28
|
+
}
|
|
29
|
+
default:
|
|
30
|
+
throw new NegentropyError('invalid_config', `unserializable value in registration: ${typeof value}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function deepFreeze(value) {
|
|
34
|
+
if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) {
|
|
35
|
+
Object.freeze(value);
|
|
36
|
+
for (const key of Object.getOwnPropertyNames(value)) {
|
|
37
|
+
deepFreeze(value[key]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Freeze and hash an experiment configuration BEFORE data ingestion. The
|
|
44
|
+
* returned config is deeply frozen (mutation throws), and passing the
|
|
45
|
+
* registration to `analyzeTrials`/`analyzeBytes`/`session` embeds its hash
|
|
46
|
+
* in the result — the paper trail against garden-of-forking-paths analysis.
|
|
47
|
+
*/
|
|
48
|
+
export async function registerExperiment(config) {
|
|
49
|
+
const canonical = canonicalJson(config);
|
|
50
|
+
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(canonical));
|
|
51
|
+
const hash = [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
52
|
+
return { config: deepFreeze(structuredClone(config)), hash };
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=registration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration.js","sourceRoot":"","sources":["../../src/experiment/registration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { TrialSource } from '../types.js';
|
|
2
|
+
import type { RegisteredExperiment } from './registration.js';
|
|
3
|
+
import type { ExperimentConfig, ExperimentResult } from './types.js';
|
|
4
|
+
export interface SessionOptions extends ExperimentConfig {
|
|
5
|
+
sources: readonly TrialSource[];
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
/** Max wait per lock-step round before 'timeout' (or a skip, under missing:'skip'). Default 30_000. */
|
|
8
|
+
stepTimeoutMs?: number;
|
|
9
|
+
/** Clock override for deterministic tests. */
|
|
10
|
+
now?: () => number;
|
|
11
|
+
/** Pre-registered config: overrides the inline trial/calibration/events/missing and embeds its hash. */
|
|
12
|
+
registration?: RegisteredExperiment;
|
|
13
|
+
}
|
|
14
|
+
export interface SessionTick {
|
|
15
|
+
step: number;
|
|
16
|
+
at: number;
|
|
17
|
+
/** Aligned to `sources`; NaN for a source that missed this round. */
|
|
18
|
+
zBySource: Float64Array;
|
|
19
|
+
/** Names of the sources that contributed this round. */
|
|
20
|
+
present: readonly string[];
|
|
21
|
+
/** Stouffer Z over the present sources. */
|
|
22
|
+
stouffer: number;
|
|
23
|
+
/** Running Σ Z² — live dashboard feed. */
|
|
24
|
+
netvar: number;
|
|
25
|
+
/** Running Σ (Z² − 1) — the live cumulative-deviation value. */
|
|
26
|
+
cumdev: number;
|
|
27
|
+
/** Events whose window contains this step (index windows) or this instant (Date windows). */
|
|
28
|
+
activeEvents: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
export interface Session extends AsyncIterable<SessionTick> {
|
|
31
|
+
/**
|
|
32
|
+
* End the run and analyze everything recorded so far via the batch core —
|
|
33
|
+
* a later re-analysis of `result.series` reproduces this result exactly.
|
|
34
|
+
*/
|
|
35
|
+
stop(): ExperimentResult;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Live experiment over N sources in lock-step rounds: each tick awaits one
|
|
39
|
+
* trial from every (still-active) source, so z vectors are step-aligned with
|
|
40
|
+
* bounded memory — one trial per source in flight. Lazy: no source I/O until
|
|
41
|
+
* the first tick is pulled. A stalled source stalls the round (that is the
|
|
42
|
+
* honest behavior for netvar, which needs simultaneity); under
|
|
43
|
+
* missing:'skip' the round proceeds with whoever answered and a source that
|
|
44
|
+
* ends is dropped from the roster.
|
|
45
|
+
*/
|
|
46
|
+
export declare function session(opts: SessionOptions): Session;
|
|
47
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/experiment/session.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,aAAa,CAAA;AAElE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,KAAK,EAAa,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE/E,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD,OAAO,EAAE,SAAS,WAAW,EAAE,CAAA;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,uGAAuG;IACvG,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,wGAAwG;IACxG,YAAY,CAAC,EAAE,oBAAoB,CAAA;CACpC;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,qEAAqE;IACrE,SAAS,EAAE,YAAY,CAAA;IACvB,wDAAwD;IACxD,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAA;IAChB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAA;IACd,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAA;IACd,6FAA6F;IAC7F,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;CAChC;AAED,MAAM,WAAW,OAAQ,SAAQ,aAAa,CAAC,WAAW,CAAC;IACzD;;;OAGG;IACH,IAAI,IAAI,gBAAgB,CAAA;CACzB;AAYD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAyQrD"}
|