@cavelang/fusion 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/README.md +50 -0
- package/dist/src/conditional.d.ts +34 -0
- package/dist/src/conditional.d.ts.map +1 -0
- package/dist/src/conditional.js +36 -0
- package/dist/src/conditional.js.map +1 -0
- package/dist/src/fuse.d.ts +47 -0
- package/dist/src/fuse.d.ts.map +1 -0
- package/dist/src/fuse.js +70 -0
- package/dist/src/fuse.js.map +1 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +11 -0
- package/dist/src/index.js.map +1 -0
- package/package.json +42 -0
- package/src/conditional.ts +42 -0
- package/src/fuse.ts +90 -0
- package/src/index.ts +12 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @cavelang/fusion
|
|
2
|
+
|
|
3
|
+
The CAVE probabilistic layer (spec §10) — pure functions over `@cavelang/core`
|
|
4
|
+
claims. The math is an implementation layer, not required syntax: CAVE
|
|
5
|
+
itself only stores claims and metadata.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { fuseClaims, noisyAndIndependent } from '@cavelang/fusion'
|
|
9
|
+
|
|
10
|
+
// revenue IS 18B USD/yr +/- 3B USD/yr @ 60% (analyst)
|
|
11
|
+
// revenue IS 20B USD/yr +/- 0.5B USD/yr @ 95% (filing)
|
|
12
|
+
fuseClaims([analyst, filing])
|
|
13
|
+
// → { mean: ≈19.97e9, sigma: ≈0.25e9 } — the filing dominates
|
|
14
|
+
|
|
15
|
+
noisyAndIndependent(0.8, [0.6]) // → 0.48
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Bayesian fusion (§10.1)
|
|
19
|
+
|
|
20
|
+
For normally distributed estimates with `+/- Δ` at kσ: σ = Δ/k, precision
|
|
21
|
+
= 1/σ², confidence acts as a weight multiplier:
|
|
22
|
+
|
|
23
|
+
- weighted precision wᵢ = pᵢ / σᵢ²
|
|
24
|
+
- posterior mean μ = Σ wᵢxᵢ / Σ wᵢ
|
|
25
|
+
- posterior σ = 1 / √(Σ wᵢ)
|
|
26
|
+
|
|
27
|
+
`estimateOf(claim)` extracts `{ mean, sigma, conf }` from any numeric claim
|
|
28
|
+
carrying `+/-` uncertainty (attribute or metric payload); claims without a
|
|
29
|
+
usable estimate are skipped, zero-confidence and zero-σ estimates
|
|
30
|
+
contribute nothing. The spec's worked example is a test case verbatim.
|
|
31
|
+
|
|
32
|
+
## Conditional confidence (§10.2)
|
|
33
|
+
|
|
34
|
+
`noisyAndIndependent(pClaim, pConditions)` multiplies through — named
|
|
35
|
+
loudly because the spec requires the independence assumption to be
|
|
36
|
+
explicit in the query engine, never silently assumed.
|
|
37
|
+
|
|
38
|
+
## Competing hypotheses (§10.3)
|
|
39
|
+
|
|
40
|
+
`normalizeHypotheses` rescales an exhaustive hypothesis set to sum to 1
|
|
41
|
+
(preserving proportions, as in the spec's post-evidence redistribution);
|
|
42
|
+
`hypothesisGap` measures how far a set is from exhaustive. Redistribution
|
|
43
|
+
itself stays manual — new confidences are appended as new claims, which is
|
|
44
|
+
the append-only §9 discipline, not a computation.
|
|
45
|
+
|
|
46
|
+
## Tests
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
pnpm --filter @cavelang/fusion test
|
|
50
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conditional confidence — noisy-AND (spec §10.2), and competing-hypothesis
|
|
3
|
+
* helpers (spec §10.3).
|
|
4
|
+
*
|
|
5
|
+
* Treating a claim's conditions as independent:
|
|
6
|
+
*
|
|
7
|
+
* p_effective = p_claim × Π p_conditionᵢ
|
|
8
|
+
*
|
|
9
|
+
* The independence assumption MUST be explicit in the query engine — never
|
|
10
|
+
* silently assumed for all domains (spec §10.2); hence the deliberately
|
|
11
|
+
* loud function name.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Noisy-AND under an explicit independence assumption:
|
|
15
|
+
* `noisyAndIndependent(0.8, [0.6])` → 0.48 (spec §10.2's example).
|
|
16
|
+
*/
|
|
17
|
+
export declare const noisyAndIndependent: (claimConf: number, conditionConfs: readonly number[]) => number;
|
|
18
|
+
/**
|
|
19
|
+
* Normalizes an exhaustive hypothesis set so confidences sum to 1
|
|
20
|
+
* (spec §10.3: "confidences of an exhaustive hypothesis set should sum to
|
|
21
|
+
* ~100%"). Proportions are preserved.
|
|
22
|
+
* @returns `undefined` when every confidence is 0.
|
|
23
|
+
*/
|
|
24
|
+
export declare const normalizeHypotheses: <T extends {
|
|
25
|
+
readonly conf: number;
|
|
26
|
+
}>(hypotheses: readonly T[]) => undefined | (T & {
|
|
27
|
+
readonly conf: number;
|
|
28
|
+
})[];
|
|
29
|
+
/**
|
|
30
|
+
* @returns how far a hypothesis set is from exhaustive (Σ conf − 1);
|
|
31
|
+
* ≈0 for a well-formed set, negative when probability mass is missing.
|
|
32
|
+
*/
|
|
33
|
+
export declare const hypothesisGap: (confs: readonly number[]) => number;
|
|
34
|
+
//# sourceMappingURL=conditional.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditional.d.ts","sourceRoot":"","sources":["../../src/conditional.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,WAAW,MAAM,EAAE,gBAAgB,SAAS,MAAM,EAAE,KAAG,MACtB,CAAA;AAErE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACrE,YAAY,SAAS,CAAC,EAAE,KACvB,SAAS,GAAG,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAM7C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,SAAS,MAAM,EAAE,KAAG,MACT,CAAA"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conditional confidence — noisy-AND (spec §10.2), and competing-hypothesis
|
|
3
|
+
* helpers (spec §10.3).
|
|
4
|
+
*
|
|
5
|
+
* Treating a claim's conditions as independent:
|
|
6
|
+
*
|
|
7
|
+
* p_effective = p_claim × Π p_conditionᵢ
|
|
8
|
+
*
|
|
9
|
+
* The independence assumption MUST be explicit in the query engine — never
|
|
10
|
+
* silently assumed for all domains (spec §10.2); hence the deliberately
|
|
11
|
+
* loud function name.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Noisy-AND under an explicit independence assumption:
|
|
15
|
+
* `noisyAndIndependent(0.8, [0.6])` → 0.48 (spec §10.2's example).
|
|
16
|
+
*/
|
|
17
|
+
export const noisyAndIndependent = (claimConf, conditionConfs) => conditionConfs.reduce((product, conf) => product * conf, claimConf);
|
|
18
|
+
/**
|
|
19
|
+
* Normalizes an exhaustive hypothesis set so confidences sum to 1
|
|
20
|
+
* (spec §10.3: "confidences of an exhaustive hypothesis set should sum to
|
|
21
|
+
* ~100%"). Proportions are preserved.
|
|
22
|
+
* @returns `undefined` when every confidence is 0.
|
|
23
|
+
*/
|
|
24
|
+
export const normalizeHypotheses = (hypotheses) => {
|
|
25
|
+
const total = hypotheses.reduce((sum, hypothesis) => sum + hypothesis.conf, 0);
|
|
26
|
+
if (!(total > 0)) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
return hypotheses.map(hypothesis => ({ ...hypothesis, conf: hypothesis.conf / total }));
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* @returns how far a hypothesis set is from exhaustive (Σ conf − 1);
|
|
33
|
+
* ≈0 for a well-formed set, negative when probability mass is missing.
|
|
34
|
+
*/
|
|
35
|
+
export const hypothesisGap = (confs) => confs.reduce((sum, conf) => sum + conf, 0) - 1;
|
|
36
|
+
//# sourceMappingURL=conditional.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditional.js","sourceRoot":"","sources":["../../src/conditional.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,SAAiB,EAAE,cAAiC,EAAU,EAAE,CAClG,cAAc,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,GAAG,IAAI,EAAE,SAAS,CAAC,CAAA;AAErE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,UAAwB,EACuB,EAAE;IACjD,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,CAAC,GAAG,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;IAC9E,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;QACjB,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAA;AACzF,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAwB,EAAU,EAAE,CAChE,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bayesian fusion of numeric estimates (spec §10.1).
|
|
3
|
+
*
|
|
4
|
+
* Given independent, normally distributed estimates of the same quantity —
|
|
5
|
+
* each with a mean, a σ, and an epistemic confidence acting as a weight
|
|
6
|
+
* multiplier — the posterior is precision-weighted:
|
|
7
|
+
*
|
|
8
|
+
* - weighted precision: wᵢ = pᵢ / σᵢ²
|
|
9
|
+
* - posterior mean: μ = Σ wᵢxᵢ / Σ wᵢ
|
|
10
|
+
* - posterior σ: 1 / √(Σ wᵢ)
|
|
11
|
+
*
|
|
12
|
+
* The math is an implementation layer, not required syntax (spec §10) —
|
|
13
|
+
* CAVE itself only stores claims and metadata.
|
|
14
|
+
*/
|
|
15
|
+
import { Claim } from '@cavelang/core';
|
|
16
|
+
/** One numeric estimate: mean, σ, and confidence weight p ∈ (0, 1]. */
|
|
17
|
+
export type Estimate = {
|
|
18
|
+
readonly mean: number;
|
|
19
|
+
readonly sigma: number;
|
|
20
|
+
/** Epistemic confidence used as a precision multiplier (default 1). */
|
|
21
|
+
readonly conf?: number;
|
|
22
|
+
};
|
|
23
|
+
export type Posterior = {
|
|
24
|
+
readonly mean: number;
|
|
25
|
+
readonly sigma: number;
|
|
26
|
+
/** Total weighted precision Σ wᵢ. */
|
|
27
|
+
readonly precision: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Fuses estimates into a posterior. Estimates with `conf` 0 or a
|
|
31
|
+
* non-positive σ contribute nothing and are skipped.
|
|
32
|
+
* @returns `undefined` when no estimate carries usable weight.
|
|
33
|
+
*/
|
|
34
|
+
export declare const fuse: (estimates: readonly Estimate[]) => undefined | Posterior;
|
|
35
|
+
/**
|
|
36
|
+
* @returns the estimate a claim carries, when it is numeric with `+/-`
|
|
37
|
+
* uncertainty: mean from the payload value, σ = Δ / k (spec §7.2),
|
|
38
|
+
* confidence from `@ N%`. `undefined` otherwise.
|
|
39
|
+
*/
|
|
40
|
+
export declare const estimateOf: (claim: Claim.t) => undefined | Estimate;
|
|
41
|
+
/**
|
|
42
|
+
* Fuses the numeric estimates found in `claims` (spec §10.1's worked
|
|
43
|
+
* example lives in the tests). Claims without usable numeric uncertainty
|
|
44
|
+
* are ignored.
|
|
45
|
+
*/
|
|
46
|
+
export declare const fuseClaims: (claims: readonly Claim.t[]) => undefined | Posterior;
|
|
47
|
+
//# sourceMappingURL=fuse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuse.d.ts","sourceRoot":"","sources":["../../src/fuse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAEtC,uEAAuE;AACvE,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,uEAAuE;IACvE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,qCAAqC;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,IAAI,GAAI,WAAW,SAAS,QAAQ,EAAE,KAAG,SAAS,GAAG,SAoBjE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,KAAK,CAAC,CAAC,KAAG,SAAS,GAAG,QAcvD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,QAAQ,SAAS,KAAK,CAAC,CAAC,EAAE,KAAG,SAAS,GAAG,SAI/D,CAAA"}
|
package/dist/src/fuse.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bayesian fusion of numeric estimates (spec §10.1).
|
|
3
|
+
*
|
|
4
|
+
* Given independent, normally distributed estimates of the same quantity —
|
|
5
|
+
* each with a mean, a σ, and an epistemic confidence acting as a weight
|
|
6
|
+
* multiplier — the posterior is precision-weighted:
|
|
7
|
+
*
|
|
8
|
+
* - weighted precision: wᵢ = pᵢ / σᵢ²
|
|
9
|
+
* - posterior mean: μ = Σ wᵢxᵢ / Σ wᵢ
|
|
10
|
+
* - posterior σ: 1 / √(Σ wᵢ)
|
|
11
|
+
*
|
|
12
|
+
* The math is an implementation layer, not required syntax (spec §10) —
|
|
13
|
+
* CAVE itself only stores claims and metadata.
|
|
14
|
+
*/
|
|
15
|
+
import { Claim } from '@cavelang/core';
|
|
16
|
+
/**
|
|
17
|
+
* Fuses estimates into a posterior. Estimates with `conf` 0 or a
|
|
18
|
+
* non-positive σ contribute nothing and are skipped.
|
|
19
|
+
* @returns `undefined` when no estimate carries usable weight.
|
|
20
|
+
*/
|
|
21
|
+
export const fuse = (estimates) => {
|
|
22
|
+
let totalWeight = 0;
|
|
23
|
+
let weightedSum = 0;
|
|
24
|
+
for (const estimate of estimates) {
|
|
25
|
+
const conf = estimate.conf ?? 1;
|
|
26
|
+
if (!(estimate.sigma > 0) || !(conf > 0)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const weight = conf / (estimate.sigma * estimate.sigma);
|
|
30
|
+
totalWeight += weight;
|
|
31
|
+
weightedSum += weight * estimate.mean;
|
|
32
|
+
}
|
|
33
|
+
if (totalWeight === 0) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
mean: weightedSum / totalWeight,
|
|
38
|
+
sigma: 1 / Math.sqrt(totalWeight),
|
|
39
|
+
precision: totalWeight
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* @returns the estimate a claim carries, when it is numeric with `+/-`
|
|
44
|
+
* uncertainty: mean from the payload value, σ = Δ / k (spec §7.2),
|
|
45
|
+
* confidence from `@ N%`. `undefined` otherwise.
|
|
46
|
+
*/
|
|
47
|
+
export const estimateOf = (claim) => {
|
|
48
|
+
const payload = claim.payload;
|
|
49
|
+
const value = payload.kind === 'attribute' ? payload.value :
|
|
50
|
+
payload.kind === 'metric' ? payload.value :
|
|
51
|
+
undefined;
|
|
52
|
+
if (value?.num === undefined) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const sigma = Claim.sigmaOf(claim);
|
|
56
|
+
if (sigma === undefined || !(sigma > 0)) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
return { mean: value.num, sigma, conf: claim.conf };
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Fuses the numeric estimates found in `claims` (spec §10.1's worked
|
|
63
|
+
* example lives in the tests). Claims without usable numeric uncertainty
|
|
64
|
+
* are ignored.
|
|
65
|
+
*/
|
|
66
|
+
export const fuseClaims = (claims) => fuse(claims.flatMap(claim => {
|
|
67
|
+
const estimate = estimateOf(claim);
|
|
68
|
+
return estimate === undefined ? [] : [estimate];
|
|
69
|
+
}));
|
|
70
|
+
//# sourceMappingURL=fuse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuse.js","sourceRoot":"","sources":["../../src/fuse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAiBtC;;;;GAIG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,SAA8B,EAAyB,EAAE;IAC5E,IAAI,WAAW,GAAG,CAAC,CAAA;IACnB,IAAI,WAAW,GAAG,CAAC,CAAA;IACnB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAA;QAC/B,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC;YACzC,SAAQ;QACV,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;QACvD,WAAW,IAAI,MAAM,CAAA;QACrB,WAAW,IAAI,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAA;IACvC,CAAC;IACD,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO;QACL,IAAI,EAAE,WAAW,GAAG,WAAW;QAC/B,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;QACjC,SAAS,EAAE,WAAW;KACvB,CAAA;AACH,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAc,EAAwB,EAAE;IACjE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAA;IAC7B,MAAM,KAAK,GACT,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9C,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC3C,SAAS,CAAA;IACX,IAAI,KAAK,EAAE,GAAG,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAClC,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;QACxC,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAA;AACrD,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,MAA0B,EAAyB,EAAE,CAC9E,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;IAC1B,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;IAClC,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;AACjD,CAAC,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cavelang/fusion` — the CAVE probabilistic layer (spec §10).
|
|
3
|
+
*
|
|
4
|
+
* Pure functions over `@cavelang/core` claims: Bayesian fusion of numeric
|
|
5
|
+
* estimates (§10.1), noisy-AND conditional confidence under an explicit
|
|
6
|
+
* independence assumption (§10.2), and competing-hypothesis helpers
|
|
7
|
+
* (§10.3). No storage, no I/O.
|
|
8
|
+
*/
|
|
9
|
+
export { fuse, fuseClaims, estimateOf } from './fuse.ts';
|
|
10
|
+
export type { Estimate, Posterior } from './fuse.ts';
|
|
11
|
+
export { noisyAndIndependent, normalizeHypotheses, hypothesisGap } from './conditional.ts';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACxD,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACpD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cavelang/fusion` — the CAVE probabilistic layer (spec §10).
|
|
3
|
+
*
|
|
4
|
+
* Pure functions over `@cavelang/core` claims: Bayesian fusion of numeric
|
|
5
|
+
* estimates (§10.1), noisy-AND conditional confidence under an explicit
|
|
6
|
+
* independence assumption (§10.2), and competing-hypothesis helpers
|
|
7
|
+
* (§10.3). No storage, no I/O.
|
|
8
|
+
*/
|
|
9
|
+
export { fuse, fuseClaims, estimateOf } from "./fuse.js";
|
|
10
|
+
export { noisyAndIndependent, normalizeHypotheses, hypothesisGap } from "./conditional.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAExD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cavelang/fusion",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "CAVE probabilistic layer — Bayesian fusion of numeric estimates, noisy-AND conditional confidence, hypothesis helpers.",
|
|
6
|
+
"license": "CC0-1.0",
|
|
7
|
+
"author": "Mirek Rusin (https://github.com/mirek)",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/mirek/cave.git",
|
|
11
|
+
"directory": "packages/fusion"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=22.18"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/src/index.d.ts",
|
|
19
|
+
"default": "./dist/src/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"!dist/test",
|
|
25
|
+
"!dist/tsconfig.tsbuildinfo",
|
|
26
|
+
"src",
|
|
27
|
+
"!src/**/*.test.ts",
|
|
28
|
+
"README.md",
|
|
29
|
+
"License.md"
|
|
30
|
+
],
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@cavelang/core": "0.1.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc -b",
|
|
39
|
+
"test": "node --disable-warning=ExperimentalWarning --test 'test/*.test.ts'",
|
|
40
|
+
"typecheck": "tsc -b"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conditional confidence — noisy-AND (spec §10.2), and competing-hypothesis
|
|
3
|
+
* helpers (spec §10.3).
|
|
4
|
+
*
|
|
5
|
+
* Treating a claim's conditions as independent:
|
|
6
|
+
*
|
|
7
|
+
* p_effective = p_claim × Π p_conditionᵢ
|
|
8
|
+
*
|
|
9
|
+
* The independence assumption MUST be explicit in the query engine — never
|
|
10
|
+
* silently assumed for all domains (spec §10.2); hence the deliberately
|
|
11
|
+
* loud function name.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Noisy-AND under an explicit independence assumption:
|
|
16
|
+
* `noisyAndIndependent(0.8, [0.6])` → 0.48 (spec §10.2's example).
|
|
17
|
+
*/
|
|
18
|
+
export const noisyAndIndependent = (claimConf: number, conditionConfs: readonly number[]): number =>
|
|
19
|
+
conditionConfs.reduce((product, conf) => product * conf, claimConf)
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Normalizes an exhaustive hypothesis set so confidences sum to 1
|
|
23
|
+
* (spec §10.3: "confidences of an exhaustive hypothesis set should sum to
|
|
24
|
+
* ~100%"). Proportions are preserved.
|
|
25
|
+
* @returns `undefined` when every confidence is 0.
|
|
26
|
+
*/
|
|
27
|
+
export const normalizeHypotheses = <T extends { readonly conf: number }>(
|
|
28
|
+
hypotheses: readonly T[]
|
|
29
|
+
): undefined | (T & { readonly conf: number })[] => {
|
|
30
|
+
const total = hypotheses.reduce((sum, hypothesis) => sum + hypothesis.conf, 0)
|
|
31
|
+
if (!(total > 0)) {
|
|
32
|
+
return undefined
|
|
33
|
+
}
|
|
34
|
+
return hypotheses.map(hypothesis => ({ ...hypothesis, conf: hypothesis.conf / total }))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @returns how far a hypothesis set is from exhaustive (Σ conf − 1);
|
|
39
|
+
* ≈0 for a well-formed set, negative when probability mass is missing.
|
|
40
|
+
*/
|
|
41
|
+
export const hypothesisGap = (confs: readonly number[]): number =>
|
|
42
|
+
confs.reduce((sum, conf) => sum + conf, 0) - 1
|
package/src/fuse.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bayesian fusion of numeric estimates (spec §10.1).
|
|
3
|
+
*
|
|
4
|
+
* Given independent, normally distributed estimates of the same quantity —
|
|
5
|
+
* each with a mean, a σ, and an epistemic confidence acting as a weight
|
|
6
|
+
* multiplier — the posterior is precision-weighted:
|
|
7
|
+
*
|
|
8
|
+
* - weighted precision: wᵢ = pᵢ / σᵢ²
|
|
9
|
+
* - posterior mean: μ = Σ wᵢxᵢ / Σ wᵢ
|
|
10
|
+
* - posterior σ: 1 / √(Σ wᵢ)
|
|
11
|
+
*
|
|
12
|
+
* The math is an implementation layer, not required syntax (spec §10) —
|
|
13
|
+
* CAVE itself only stores claims and metadata.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { Claim } from '@cavelang/core'
|
|
17
|
+
|
|
18
|
+
/** One numeric estimate: mean, σ, and confidence weight p ∈ (0, 1]. */
|
|
19
|
+
export type Estimate = {
|
|
20
|
+
readonly mean: number
|
|
21
|
+
readonly sigma: number
|
|
22
|
+
/** Epistemic confidence used as a precision multiplier (default 1). */
|
|
23
|
+
readonly conf?: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type Posterior = {
|
|
27
|
+
readonly mean: number
|
|
28
|
+
readonly sigma: number
|
|
29
|
+
/** Total weighted precision Σ wᵢ. */
|
|
30
|
+
readonly precision: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Fuses estimates into a posterior. Estimates with `conf` 0 or a
|
|
35
|
+
* non-positive σ contribute nothing and are skipped.
|
|
36
|
+
* @returns `undefined` when no estimate carries usable weight.
|
|
37
|
+
*/
|
|
38
|
+
export const fuse = (estimates: readonly Estimate[]): undefined | Posterior => {
|
|
39
|
+
let totalWeight = 0
|
|
40
|
+
let weightedSum = 0
|
|
41
|
+
for (const estimate of estimates) {
|
|
42
|
+
const conf = estimate.conf ?? 1
|
|
43
|
+
if (!(estimate.sigma > 0) || !(conf > 0)) {
|
|
44
|
+
continue
|
|
45
|
+
}
|
|
46
|
+
const weight = conf / (estimate.sigma * estimate.sigma)
|
|
47
|
+
totalWeight += weight
|
|
48
|
+
weightedSum += weight * estimate.mean
|
|
49
|
+
}
|
|
50
|
+
if (totalWeight === 0) {
|
|
51
|
+
return undefined
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
mean: weightedSum / totalWeight,
|
|
55
|
+
sigma: 1 / Math.sqrt(totalWeight),
|
|
56
|
+
precision: totalWeight
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @returns the estimate a claim carries, when it is numeric with `+/-`
|
|
62
|
+
* uncertainty: mean from the payload value, σ = Δ / k (spec §7.2),
|
|
63
|
+
* confidence from `@ N%`. `undefined` otherwise.
|
|
64
|
+
*/
|
|
65
|
+
export const estimateOf = (claim: Claim.t): undefined | Estimate => {
|
|
66
|
+
const payload = claim.payload
|
|
67
|
+
const value =
|
|
68
|
+
payload.kind === 'attribute' ? payload.value :
|
|
69
|
+
payload.kind === 'metric' ? payload.value :
|
|
70
|
+
undefined
|
|
71
|
+
if (value?.num === undefined) {
|
|
72
|
+
return undefined
|
|
73
|
+
}
|
|
74
|
+
const sigma = Claim.sigmaOf(claim)
|
|
75
|
+
if (sigma === undefined || !(sigma > 0)) {
|
|
76
|
+
return undefined
|
|
77
|
+
}
|
|
78
|
+
return { mean: value.num, sigma, conf: claim.conf }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Fuses the numeric estimates found in `claims` (spec §10.1's worked
|
|
83
|
+
* example lives in the tests). Claims without usable numeric uncertainty
|
|
84
|
+
* are ignored.
|
|
85
|
+
*/
|
|
86
|
+
export const fuseClaims = (claims: readonly Claim.t[]): undefined | Posterior =>
|
|
87
|
+
fuse(claims.flatMap(claim => {
|
|
88
|
+
const estimate = estimateOf(claim)
|
|
89
|
+
return estimate === undefined ? [] : [estimate]
|
|
90
|
+
}))
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cavelang/fusion` — the CAVE probabilistic layer (spec §10).
|
|
3
|
+
*
|
|
4
|
+
* Pure functions over `@cavelang/core` claims: Bayesian fusion of numeric
|
|
5
|
+
* estimates (§10.1), noisy-AND conditional confidence under an explicit
|
|
6
|
+
* independence assumption (§10.2), and competing-hypothesis helpers
|
|
7
|
+
* (§10.3). No storage, no I/O.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export { fuse, fuseClaims, estimateOf } from './fuse.ts'
|
|
11
|
+
export type { Estimate, Posterior } from './fuse.ts'
|
|
12
|
+
export { noisyAndIndependent, normalizeHypotheses, hypothesisGap } from './conditional.ts'
|