@lucern/confidence 0.1.0-alpha.2
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 +34 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +1833 -0
- package/dist/index.js.map +1 -0
- package/dist/v1/index.d.ts +17 -0
- package/dist/v1/index.js +1833 -0
- package/dist/v1/index.js.map +1 -0
- package/dist/v1/interfaces.d.ts +28 -0
- package/dist/v1/interfaces.js +3 -0
- package/dist/v1/interfaces.js.map +1 -0
- package/dist/v1/operations/approximation.d.ts +8 -0
- package/dist/v1/operations/approximation.js +219 -0
- package/dist/v1/operations/approximation.js.map +1 -0
- package/dist/v1/operations/bridge/index.d.ts +12 -0
- package/dist/v1/operations/bridge/index.js +89 -0
- package/dist/v1/operations/bridge/index.js.map +1 -0
- package/dist/v1/operations/canonical.d.ts +7 -0
- package/dist/v1/operations/canonical.js +199 -0
- package/dist/v1/operations/canonical.js.map +1 -0
- package/dist/v1/operations/contracts/epistemicContract.d.ts +51 -0
- package/dist/v1/operations/contracts/epistemicContract.js +320 -0
- package/dist/v1/operations/contracts/epistemicContract.js.map +1 -0
- package/dist/v1/operations/contradiction/detectTupleContradiction.d.ts +13 -0
- package/dist/v1/operations/contradiction/detectTupleContradiction.js +40 -0
- package/dist/v1/operations/contradiction/detectTupleContradiction.js.map +1 -0
- package/dist/v1/operations/contradiction/index.d.ts +2 -0
- package/dist/v1/operations/contradiction/index.js +40 -0
- package/dist/v1/operations/contradiction/index.js.map +1 -0
- package/dist/v1/operations/dynamics/cascade.d.ts +6 -0
- package/dist/v1/operations/dynamics/cascade.js +56 -0
- package/dist/v1/operations/dynamics/cascade.js.map +1 -0
- package/dist/v1/operations/dynamics/decay.d.ts +25 -0
- package/dist/v1/operations/dynamics/decay.js +271 -0
- package/dist/v1/operations/dynamics/decay.js.map +1 -0
- package/dist/v1/operations/dynamics/defeat.d.ts +6 -0
- package/dist/v1/operations/dynamics/defeat.js +134 -0
- package/dist/v1/operations/dynamics/defeat.js.map +1 -0
- package/dist/v1/operations/dynamics/propagation.d.ts +58 -0
- package/dist/v1/operations/dynamics/propagation.js +399 -0
- package/dist/v1/operations/dynamics/propagation.js.map +1 -0
- package/dist/v1/operations/dynamics/revision.d.ts +24 -0
- package/dist/v1/operations/dynamics/revision.js +111 -0
- package/dist/v1/operations/dynamics/revision.js.map +1 -0
- package/dist/v1/operations/index.d.ts +2 -0
- package/dist/v1/operations/index.js +66 -0
- package/dist/v1/operations/index.js.map +1 -0
- package/dist/v1/operations/lucern.d.ts +14 -0
- package/dist/v1/operations/lucern.js +1280 -0
- package/dist/v1/operations/lucern.js.map +1 -0
- package/dist/v1/operations/operatorTaxonomy.d.ts +366 -0
- package/dist/v1/operations/operatorTaxonomy.js +508 -0
- package/dist/v1/operations/operatorTaxonomy.js.map +1 -0
- package/dist/v1/operations/scoring.d.ts +28 -0
- package/dist/v1/operations/scoring.js +107 -0
- package/dist/v1/operations/scoring.js.map +1 -0
- package/dist/v1/operations/subjectiveLogic/index.d.ts +26 -0
- package/dist/v1/operations/subjectiveLogic/index.js +285 -0
- package/dist/v1/operations/subjectiveLogic/index.js.map +1 -0
- package/dist/v1/operations/temporalDecay.d.ts +24 -0
- package/dist/v1/operations/temporalDecay.js +66 -0
- package/dist/v1/operations/temporalDecay.js.map +1 -0
- package/dist/v1/types.d.ts +208 -0
- package/dist/v1/types.js +3 -0
- package/dist/v1/types.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/v1/operations/subjectiveLogic/index.ts
|
|
2
|
+
function vacuous(baseRate = 0.5) {
|
|
3
|
+
return { b: 0, d: 0, u: 1, a: baseRate };
|
|
4
|
+
}
|
|
5
|
+
function dogmatic(probability, baseRate = 0.5) {
|
|
6
|
+
const p = Math.max(0, Math.min(1, probability));
|
|
7
|
+
return { b: p, d: 1 - p, u: 0, a: baseRate };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/v1/operations/scoring.ts
|
|
11
|
+
function finiteNumber(value) {
|
|
12
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
13
|
+
}
|
|
14
|
+
function clamp01(value) {
|
|
15
|
+
return Math.max(0, Math.min(1, value));
|
|
16
|
+
}
|
|
17
|
+
function confidenceFromOpinion(opinion) {
|
|
18
|
+
return clamp01(opinion.b + opinion.a * opinion.u);
|
|
19
|
+
}
|
|
20
|
+
function confidenceFromSL(belief, _disbelief, uncertainty, baseRate = 0.5) {
|
|
21
|
+
return confidenceFromOpinion({
|
|
22
|
+
b: belief,
|
|
23
|
+
u: uncertainty,
|
|
24
|
+
a: baseRate
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function toStoredOpinionFields(opinion) {
|
|
28
|
+
return {
|
|
29
|
+
belief: opinion.b,
|
|
30
|
+
disbelief: opinion.d,
|
|
31
|
+
uncertainty: opinion.u,
|
|
32
|
+
baseRate: opinion.a
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function readOpinionFromRecord(source, fallback = {}) {
|
|
36
|
+
const record = source && typeof source === "object" ? source : {};
|
|
37
|
+
return {
|
|
38
|
+
b: finiteNumber(record.b) ?? finiteNumber(record.belief) ?? finiteNumber(record.slBelief) ?? finiteNumber(record.opinion_b) ?? fallback.b ?? 0,
|
|
39
|
+
d: finiteNumber(record.d) ?? finiteNumber(record.disbelief) ?? finiteNumber(record.slDisbelief) ?? finiteNumber(record.opinion_d) ?? fallback.d ?? 0,
|
|
40
|
+
u: finiteNumber(record.u) ?? finiteNumber(record.uncertainty) ?? finiteNumber(record.slUncertainty) ?? finiteNumber(record.opinion_u) ?? fallback.u ?? 1,
|
|
41
|
+
a: finiteNumber(record.a) ?? finiteNumber(record.baseRate) ?? finiteNumber(record.slBaseRate) ?? finiteNumber(record.opinion_a) ?? fallback.a ?? 0.5
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function opinionFromScalar(value, mode, options) {
|
|
45
|
+
const clampedValue = clamp01(value);
|
|
46
|
+
const baseRate = clamp01(options?.baseRate ?? 0.5);
|
|
47
|
+
switch (mode) {
|
|
48
|
+
case "base_rate":
|
|
49
|
+
return {
|
|
50
|
+
b: 0,
|
|
51
|
+
d: 0,
|
|
52
|
+
u: 1,
|
|
53
|
+
a: clampedValue
|
|
54
|
+
};
|
|
55
|
+
case "dogmatic":
|
|
56
|
+
return {
|
|
57
|
+
b: clampedValue,
|
|
58
|
+
d: 1 - clampedValue,
|
|
59
|
+
u: 0,
|
|
60
|
+
a: baseRate
|
|
61
|
+
};
|
|
62
|
+
case "projected_with_u": {
|
|
63
|
+
const uncertainty = options?.uncertainty;
|
|
64
|
+
if (uncertainty === void 0) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'opinionFromScalar(value, "projected_with_u") requires options.uncertainty.'
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
const clampedUncertainty = clamp01(uncertainty);
|
|
70
|
+
const evidenceWeight = 1 - clampedUncertainty;
|
|
71
|
+
return {
|
|
72
|
+
b: clampedValue * evidenceWeight,
|
|
73
|
+
d: (1 - clampedValue) * evidenceWeight,
|
|
74
|
+
u: clampedUncertainty,
|
|
75
|
+
a: baseRate
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
throw new Error(`Unsupported opinionFromScalar mode: ${mode}`);
|
|
80
|
+
}
|
|
81
|
+
function toDogmaticOpinion(confidence, baseRate = 0.5) {
|
|
82
|
+
return opinionFromScalar(confidence, "dogmatic", { baseRate });
|
|
83
|
+
}
|
|
84
|
+
function opinionFromBaseRate(probability) {
|
|
85
|
+
return vacuous(clamp01(probability));
|
|
86
|
+
}
|
|
87
|
+
function opinionFromDogmatic(probability, baseRate = 0.5) {
|
|
88
|
+
return dogmatic(clamp01(probability), clamp01(baseRate));
|
|
89
|
+
}
|
|
90
|
+
function opinionFromProjected(probability, uncertainty, baseRate = 0.5) {
|
|
91
|
+
const p = clamp01(probability);
|
|
92
|
+
const u = clamp01(uncertainty);
|
|
93
|
+
const remainingMass = 1 - u;
|
|
94
|
+
return {
|
|
95
|
+
b: p * remainingMass,
|
|
96
|
+
d: (1 - p) * remainingMass,
|
|
97
|
+
u,
|
|
98
|
+
a: clamp01(baseRate)
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function hasProjectedOpinionChanged(current, next, tolerance = 0.01) {
|
|
102
|
+
return Math.abs(confidenceFromOpinion(next) - confidenceFromOpinion(current)) >= tolerance;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { clamp01, confidenceFromOpinion, confidenceFromSL, hasProjectedOpinionChanged, opinionFromBaseRate, opinionFromDogmatic, opinionFromProjected, opinionFromScalar, readOpinionFromRecord, toDogmaticOpinion, toStoredOpinionFields };
|
|
106
|
+
//# sourceMappingURL=scoring.js.map
|
|
107
|
+
//# sourceMappingURL=scoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/v1/operations/subjectiveLogic/index.ts","../../../src/v1/operations/scoring.ts"],"names":[],"mappings":"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","file":"scoring.js","sourcesContent":["import type { Opinion } from \"../../types\";\n\nexport function opinion(\n belief: number,\n disbelief: number,\n uncertainty: number,\n baseRate: number = 0.5\n): Opinion {\n const b = Math.max(0, Math.min(1, belief));\n const d = Math.max(0, Math.min(1, disbelief));\n const u = Math.max(0, Math.min(1, uncertainty));\n const a = Math.max(0, Math.min(1, baseRate));\n const sum = b + d + u;\n\n if (sum === 0) {\n return { b: 0, d: 0, u: 1, a };\n }\n\n return {\n b: b / sum,\n d: d / sum,\n u: u / sum,\n a,\n };\n}\n\nexport function vacuous(baseRate: number = 0.5): Opinion {\n return { b: 0, d: 0, u: 1, a: baseRate };\n}\n\nexport function dogmatic(probability: number, baseRate: number = 0.5): Opinion {\n const p = Math.max(0, Math.min(1, probability));\n return { b: p, d: 1 - p, u: 0, a: baseRate };\n}\n\nexport function project(o: Opinion): number {\n return o.b + o.a * o.u;\n}\n\nexport function confidenceLevel(o: Opinion): \"high\" | \"medium\" | \"low\" {\n const projected = project(o);\n if (o.u > 0.5) {\n return \"low\";\n }\n if (projected >= 0.8 && o.u < 0.2) {\n return \"high\";\n }\n if (projected >= 0.6) {\n return \"medium\";\n }\n return \"low\";\n}\n\nexport function cumulativeFusion(left: Opinion, right: Opinion): Opinion {\n if (left.u === 0 && right.u === 0) {\n return opinion(\n (left.b + right.b) / 2,\n (left.d + right.d) / 2,\n 0,\n (left.a + right.a) / 2\n );\n }\n\n const k = left.u + right.u - left.u * right.u;\n if (k === 0) {\n return vacuous((left.a + right.a) / 2);\n }\n\n return opinion(\n (left.b * right.u + right.b * left.u) / k,\n (left.d * right.u + right.d * left.u) / k,\n (left.u * right.u) / k,\n (left.a + right.a) / 2\n );\n}\n\nexport function averagingFusion(left: Opinion, right: Opinion): Opinion {\n if (left.u === 0 && right.u === 0) {\n return opinion(\n (left.b + right.b) / 2,\n (left.d + right.d) / 2,\n 0,\n (left.a + right.a) / 2\n );\n }\n\n const k = left.u + right.u;\n if (k === 0) {\n return vacuous((left.a + right.a) / 2);\n }\n\n return opinion(\n (left.b * right.u + right.b * left.u) / k,\n (left.d * right.u + right.d * left.u) / k,\n (2 * left.u * right.u) / k,\n (left.a + right.a) / 2\n );\n}\n\nexport function trustDiscount(sourceOpinion: Opinion, trust: number): Opinion {\n const weight = Math.max(0, Math.min(1, Math.abs(trust)));\n return opinion(\n weight * sourceOpinion.b,\n weight * sourceOpinion.d,\n 1 - weight * (sourceOpinion.b + sourceOpinion.d),\n sourceOpinion.a\n );\n}\n\nconst EPSILON = 1e-9;\n\nfunction childBaseRateFallback(\n ifTrue: Opinion,\n ifFalse: Opinion,\n fallbackBaseRate: number | undefined\n): number {\n if (fallbackBaseRate !== undefined) {\n return Math.max(0, Math.min(1, fallbackBaseRate));\n }\n\n if (Math.abs(ifTrue.a - ifFalse.a) <= EPSILON) {\n return ifTrue.a;\n }\n\n return (ifTrue.a + ifFalse.a) / 2;\n}\n\nfunction computeConditionalDeductionBaseRate(\n opinionA: Opinion,\n ifTrue: Opinion,\n ifFalse: Opinion,\n fallbackBaseRate: number\n): number {\n const denominator =\n 1 - opinionA.a * ifTrue.u - (1 - opinionA.a) * ifFalse.u;\n\n if (ifTrue.u + ifFalse.u < 2 - EPSILON && Math.abs(denominator) > EPSILON) {\n const baseRate =\n (opinionA.a * ifTrue.b + (1 - opinionA.a) * ifFalse.b) / denominator;\n if (baseRate >= -EPSILON && baseRate <= 1 + EPSILON) {\n return Math.max(0, Math.min(1, baseRate));\n }\n }\n\n return fallbackBaseRate;\n}\n\nfunction safeCorrectionTerm(\n numerator: number,\n denominator: number\n): number | undefined {\n if (Math.abs(denominator) <= EPSILON) {\n return undefined;\n }\n\n const value = numerator / denominator;\n if (!Number.isFinite(value)) {\n return undefined;\n }\n\n return Math.max(0, value);\n}\n\nexport function conditionalDeduction(\n opinionA: Opinion,\n ifTrue: Opinion,\n ifFalse: Opinion,\n fallbackBaseRate?: number\n): Opinion {\n const fallbackChildBaseRate = childBaseRateFallback(\n ifTrue,\n ifFalse,\n fallbackBaseRate\n );\n const childBaseRate = computeConditionalDeductionBaseRate(\n opinionA,\n ifTrue,\n ifFalse,\n fallbackChildBaseRate\n );\n const projectedAntecedent = project(opinionA);\n const projectedAntecedentComplement = 1 - projectedAntecedent;\n const intermediateBelief =\n opinionA.b * ifTrue.b +\n opinionA.d * ifFalse.b +\n opinionA.u * (ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a));\n const intermediateDisbelief =\n opinionA.b * ifTrue.d +\n opinionA.d * ifFalse.d +\n opinionA.u * (ifTrue.d * opinionA.a + ifFalse.d * (1 - opinionA.a));\n const intermediateUncertainty =\n opinionA.b * ifTrue.u +\n opinionA.d * ifFalse.u +\n opinionA.u * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));\n const projectedVacuousDeduction =\n ifTrue.b * opinionA.a +\n ifFalse.b * (1 - opinionA.a) +\n childBaseRate *\n (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));\n const projectedConditionalA =\n ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);\n let correction = 0;\n\n if (\n (ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d) ||\n (ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d)\n ) {\n correction = 0;\n } else if (ifTrue.b > ifFalse.b && ifTrue.d <= ifFalse.d) {\n const beliefGap = ifTrue.b - ifFalse.b;\n const disbeliefGap = ifFalse.d - ifTrue.d;\n\n if (\n projectedVacuousDeduction <= projectedConditionalA &&\n projectedAntecedent <= opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b),\n projectedAntecedent * childBaseRate\n ) ?? 0;\n } else if (\n projectedVacuousDeduction <= projectedConditionalA &&\n projectedAntecedent > opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,\n projectedAntecedentComplement * childBaseRate * disbeliefGap\n ) ?? 0;\n } else if (\n projectedVacuousDeduction > projectedConditionalA &&\n projectedAntecedent <= opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n (1 - opinionA.a) *\n opinionA.u *\n (intermediateBelief - ifTrue.b) *\n disbeliefGap,\n projectedAntecedent * (1 - childBaseRate) * beliefGap\n ) ?? 0;\n } else {\n correction =\n safeCorrectionTerm(\n (1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d),\n projectedAntecedentComplement * (1 - childBaseRate)\n ) ?? 0;\n }\n } else {\n const beliefGap = ifFalse.b - ifTrue.b;\n const disbeliefGap = ifTrue.d - ifFalse.d;\n\n if (\n projectedVacuousDeduction <= projectedConditionalA &&\n projectedAntecedent <= opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n (1 - opinionA.a) *\n opinionA.u *\n (intermediateDisbelief - ifTrue.d) *\n beliefGap,\n projectedAntecedent * childBaseRate * disbeliefGap\n ) ?? 0;\n } else if (\n projectedVacuousDeduction <= projectedConditionalA &&\n projectedAntecedent > opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n (1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b),\n projectedAntecedentComplement * childBaseRate\n ) ?? 0;\n } else if (\n projectedVacuousDeduction > projectedConditionalA &&\n projectedAntecedent <= opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d),\n projectedAntecedent * (1 - childBaseRate)\n ) ?? 0;\n } else {\n correction =\n safeCorrectionTerm(\n opinionA.a *\n opinionA.u *\n (intermediateBelief - ifTrue.b) *\n disbeliefGap,\n projectedAntecedentComplement * (1 - childBaseRate) * beliefGap\n ) ?? 0;\n }\n }\n\n return opinion(\n intermediateBelief - childBaseRate * correction,\n intermediateDisbelief - (1 - childBaseRate) * correction,\n intermediateUncertainty + correction,\n childBaseRate\n );\n}\n\n/**\n * Abductive inference over a conditional.\n * Given an opinion on Y and conditionals P(Y|X), P(Y|~X), infer an opinion on X.\n */\nexport function conditionalAbduction(\n opinionY: Opinion,\n ifTrue: Opinion,\n ifFalse: Opinion,\n baseRateX: number\n): Opinion {\n const priorX = Math.max(0, Math.min(1, baseRateX));\n const probabilityY = project(opinionY);\n const probabilityGivenTrue = project(ifTrue);\n const probabilityGivenFalse = project(ifFalse);\n\n const posteriorIfYDenominator =\n probabilityGivenTrue * priorX +\n probabilityGivenFalse * (1 - priorX);\n const posteriorIfY =\n posteriorIfYDenominator === 0\n ? priorX\n : (probabilityGivenTrue * priorX) / posteriorIfYDenominator;\n\n const posteriorIfNotYDenominator =\n (1 - probabilityGivenTrue) * priorX +\n (1 - probabilityGivenFalse) * (1 - priorX);\n const posteriorIfNotY =\n posteriorIfNotYDenominator === 0\n ? priorX\n : ((1 - probabilityGivenTrue) * priorX) / posteriorIfNotYDenominator;\n\n const projectedX =\n probabilityY * posteriorIfY + (1 - probabilityY) * posteriorIfNotY;\n const uncertainty = Math.max(\n opinionY.u * 0.5,\n probabilityY * ifTrue.u + (1 - probabilityY) * ifFalse.u\n );\n\n return opinion(\n projectedX * (1 - uncertainty),\n (1 - projectedX) * (1 - uncertainty),\n uncertainty,\n priorX\n );\n}\n\nexport function negate(o: Opinion): Opinion {\n return { b: o.d, d: o.b, u: o.u, a: 1 - o.a };\n}\n\nexport function constraintFusion(\n left: Opinion,\n right: Opinion,\n mode: \"pressure\" | \"redistribute\" = \"pressure\"\n): { o1: Opinion; o2: Opinion } {\n if (mode === \"redistribute\") {\n const leftProjected = project(left);\n const rightProjected = project(right);\n const total = leftProjected + rightProjected;\n\n if (total <= 1) {\n return { o1: left, o2: right };\n }\n\n const scale = 1 / total;\n return {\n o1: opinion(\n left.b * scale,\n left.d + left.b * (1 - scale),\n left.u,\n left.a\n ),\n o2: opinion(\n right.b * scale,\n right.d + right.b * (1 - scale),\n right.u,\n right.a\n ),\n };\n }\n\n const pressureLeft = right.b * 0.5;\n const pressureRight = left.b * 0.5;\n\n return {\n o1: opinion(\n left.b - pressureLeft * 0.3,\n left.d + pressureLeft * 0.3,\n left.u,\n left.a\n ),\n o2: opinion(\n right.b - pressureRight * 0.3,\n right.d + pressureRight * 0.3,\n right.u,\n right.a\n ),\n };\n}\n\nexport function evidenceBalance(o: Opinion): number {\n const total = o.b + o.d;\n if (total === 0) {\n return 0;\n }\n return (o.b - o.d) / total;\n}\n\nexport function areTensioned(left: Opinion, right: Opinion): boolean {\n return (\n project(left) > 0.5 &&\n project(right) > 0.5 &&\n (left.d > 0.2 || right.d > 0.2)\n );\n}\n\nexport function informationGain(o: Opinion): number {\n if (o.u === 0) {\n return 0;\n }\n if (o.u === 1) {\n return 1;\n }\n return o.u * (1 - Math.abs(o.b - o.d));\n}\n","import type { Opinion, StoredOpinionFields } from \"../types\";\nimport { dogmatic, vacuous } from \"./subjectiveLogic\";\n\nfunction finiteNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nexport function clamp01(value: number): number {\n return Math.max(0, Math.min(1, value));\n}\n\nexport function confidenceFromOpinion(opinion: Opinion): number {\n return clamp01(opinion.b + opinion.a * opinion.u);\n}\n\nexport function confidenceFromSL(\n belief: number,\n _disbelief: number,\n uncertainty: number,\n baseRate: number = 0.5\n): number {\n return confidenceFromOpinion({\n b: belief,\n d: _disbelief,\n u: uncertainty,\n a: baseRate,\n });\n}\n\nexport function toStoredOpinionFields(opinion: Opinion): StoredOpinionFields {\n return {\n belief: opinion.b,\n disbelief: opinion.d,\n uncertainty: opinion.u,\n baseRate: opinion.a,\n };\n}\n\nexport function readOpinionFromRecord(\n source: unknown,\n fallback: Partial<Opinion> = {}\n): Opinion {\n const record =\n source && typeof source === \"object\"\n ? (source as Record<string, unknown>)\n : {};\n\n return {\n b:\n finiteNumber(record.b) ??\n finiteNumber(record.belief) ??\n finiteNumber(record.slBelief) ??\n finiteNumber(record.opinion_b) ??\n fallback.b ??\n 0,\n d:\n finiteNumber(record.d) ??\n finiteNumber(record.disbelief) ??\n finiteNumber(record.slDisbelief) ??\n finiteNumber(record.opinion_d) ??\n fallback.d ??\n 0,\n u:\n finiteNumber(record.u) ??\n finiteNumber(record.uncertainty) ??\n finiteNumber(record.slUncertainty) ??\n finiteNumber(record.opinion_u) ??\n fallback.u ??\n 1,\n a:\n finiteNumber(record.a) ??\n finiteNumber(record.baseRate) ??\n finiteNumber(record.slBaseRate) ??\n finiteNumber(record.opinion_a) ??\n fallback.a ??\n 0.5,\n };\n}\n\nexport type OpinionFromScalarMode =\n | \"base_rate\"\n | \"dogmatic\"\n | \"projected_with_u\";\n\nexport function opinionFromScalar(\n value: number,\n mode: OpinionFromScalarMode,\n options?: { baseRate?: number; uncertainty?: number }\n): Opinion {\n const clampedValue = clamp01(value);\n const baseRate = clamp01(options?.baseRate ?? 0.5);\n\n switch (mode) {\n case \"base_rate\":\n return {\n b: 0,\n d: 0,\n u: 1,\n a: clampedValue,\n };\n case \"dogmatic\":\n return {\n b: clampedValue,\n d: 1 - clampedValue,\n u: 0,\n a: baseRate,\n };\n case \"projected_with_u\": {\n const uncertainty = options?.uncertainty;\n if (uncertainty === undefined) {\n throw new Error(\n \"opinionFromScalar(value, \\\"projected_with_u\\\") requires options.uncertainty.\"\n );\n }\n const clampedUncertainty = clamp01(uncertainty);\n const evidenceWeight = 1 - clampedUncertainty;\n return {\n b: clampedValue * evidenceWeight,\n d: (1 - clampedValue) * evidenceWeight,\n u: clampedUncertainty,\n a: baseRate,\n };\n }\n }\n\n throw new Error(`Unsupported opinionFromScalar mode: ${mode}`);\n}\n\n/**\n * @deprecated Use opinionFromScalar(value, \"dogmatic\", { baseRate }) instead.\n */\nexport function toDogmaticOpinion(\n confidence: number,\n baseRate: number = 0.5\n): Opinion {\n return opinionFromScalar(confidence, \"dogmatic\", { baseRate });\n}\n\n/** Interpret p as a prior with no observed evidence. */\nexport function opinionFromBaseRate(probability: number): Opinion {\n return vacuous(clamp01(probability));\n}\n\n/** Interpret p as a certainty-equivalent projected probability. */\nexport function opinionFromDogmatic(\n probability: number,\n baseRate: number = 0.5\n): Opinion {\n return dogmatic(clamp01(probability), clamp01(baseRate));\n}\n\n/**\n * Interpret p as a projected probability with an explicit uncertainty bucket.\n * The resulting opinion always projects back to p.\n */\nexport function opinionFromProjected(\n probability: number,\n uncertainty: number,\n baseRate: number = 0.5\n): Opinion {\n const p = clamp01(probability);\n const u = clamp01(uncertainty);\n const remainingMass = 1 - u;\n return {\n b: p * remainingMass,\n d: (1 - p) * remainingMass,\n u,\n a: clamp01(baseRate),\n };\n}\n\nexport function hasProjectedOpinionChanged(\n current: Opinion,\n next: Opinion,\n tolerance: number = 0.01\n): boolean {\n return Math.abs(confidenceFromOpinion(next) - confidenceFromOpinion(current)) >=\n tolerance;\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Opinion } from '../../types.js';
|
|
2
|
+
|
|
3
|
+
declare function opinion(belief: number, disbelief: number, uncertainty: number, baseRate?: number): Opinion;
|
|
4
|
+
declare function vacuous(baseRate?: number): Opinion;
|
|
5
|
+
declare function dogmatic(probability: number, baseRate?: number): Opinion;
|
|
6
|
+
declare function project(o: Opinion): number;
|
|
7
|
+
declare function confidenceLevel(o: Opinion): "high" | "medium" | "low";
|
|
8
|
+
declare function cumulativeFusion(left: Opinion, right: Opinion): Opinion;
|
|
9
|
+
declare function averagingFusion(left: Opinion, right: Opinion): Opinion;
|
|
10
|
+
declare function trustDiscount(sourceOpinion: Opinion, trust: number): Opinion;
|
|
11
|
+
declare function conditionalDeduction(opinionA: Opinion, ifTrue: Opinion, ifFalse: Opinion, fallbackBaseRate?: number): Opinion;
|
|
12
|
+
/**
|
|
13
|
+
* Abductive inference over a conditional.
|
|
14
|
+
* Given an opinion on Y and conditionals P(Y|X), P(Y|~X), infer an opinion on X.
|
|
15
|
+
*/
|
|
16
|
+
declare function conditionalAbduction(opinionY: Opinion, ifTrue: Opinion, ifFalse: Opinion, baseRateX: number): Opinion;
|
|
17
|
+
declare function negate(o: Opinion): Opinion;
|
|
18
|
+
declare function constraintFusion(left: Opinion, right: Opinion, mode?: "pressure" | "redistribute"): {
|
|
19
|
+
o1: Opinion;
|
|
20
|
+
o2: Opinion;
|
|
21
|
+
};
|
|
22
|
+
declare function evidenceBalance(o: Opinion): number;
|
|
23
|
+
declare function areTensioned(left: Opinion, right: Opinion): boolean;
|
|
24
|
+
declare function informationGain(o: Opinion): number;
|
|
25
|
+
|
|
26
|
+
export { areTensioned, averagingFusion, conditionalAbduction, conditionalDeduction, confidenceLevel, constraintFusion, cumulativeFusion, dogmatic, evidenceBalance, informationGain, negate, opinion, project, trustDiscount, vacuous };
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
// src/v1/operations/subjectiveLogic/index.ts
|
|
2
|
+
function opinion(belief, disbelief, uncertainty, baseRate = 0.5) {
|
|
3
|
+
const b = Math.max(0, Math.min(1, belief));
|
|
4
|
+
const d = Math.max(0, Math.min(1, disbelief));
|
|
5
|
+
const u = Math.max(0, Math.min(1, uncertainty));
|
|
6
|
+
const a = Math.max(0, Math.min(1, baseRate));
|
|
7
|
+
const sum = b + d + u;
|
|
8
|
+
if (sum === 0) {
|
|
9
|
+
return { b: 0, d: 0, u: 1, a };
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
b: b / sum,
|
|
13
|
+
d: d / sum,
|
|
14
|
+
u: u / sum,
|
|
15
|
+
a
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function vacuous(baseRate = 0.5) {
|
|
19
|
+
return { b: 0, d: 0, u: 1, a: baseRate };
|
|
20
|
+
}
|
|
21
|
+
function dogmatic(probability, baseRate = 0.5) {
|
|
22
|
+
const p = Math.max(0, Math.min(1, probability));
|
|
23
|
+
return { b: p, d: 1 - p, u: 0, a: baseRate };
|
|
24
|
+
}
|
|
25
|
+
function project(o) {
|
|
26
|
+
return o.b + o.a * o.u;
|
|
27
|
+
}
|
|
28
|
+
function confidenceLevel(o) {
|
|
29
|
+
const projected = project(o);
|
|
30
|
+
if (o.u > 0.5) {
|
|
31
|
+
return "low";
|
|
32
|
+
}
|
|
33
|
+
if (projected >= 0.8 && o.u < 0.2) {
|
|
34
|
+
return "high";
|
|
35
|
+
}
|
|
36
|
+
if (projected >= 0.6) {
|
|
37
|
+
return "medium";
|
|
38
|
+
}
|
|
39
|
+
return "low";
|
|
40
|
+
}
|
|
41
|
+
function cumulativeFusion(left, right) {
|
|
42
|
+
if (left.u === 0 && right.u === 0) {
|
|
43
|
+
return opinion(
|
|
44
|
+
(left.b + right.b) / 2,
|
|
45
|
+
(left.d + right.d) / 2,
|
|
46
|
+
0,
|
|
47
|
+
(left.a + right.a) / 2
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const k = left.u + right.u - left.u * right.u;
|
|
51
|
+
if (k === 0) {
|
|
52
|
+
return vacuous((left.a + right.a) / 2);
|
|
53
|
+
}
|
|
54
|
+
return opinion(
|
|
55
|
+
(left.b * right.u + right.b * left.u) / k,
|
|
56
|
+
(left.d * right.u + right.d * left.u) / k,
|
|
57
|
+
left.u * right.u / k,
|
|
58
|
+
(left.a + right.a) / 2
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
function averagingFusion(left, right) {
|
|
62
|
+
if (left.u === 0 && right.u === 0) {
|
|
63
|
+
return opinion(
|
|
64
|
+
(left.b + right.b) / 2,
|
|
65
|
+
(left.d + right.d) / 2,
|
|
66
|
+
0,
|
|
67
|
+
(left.a + right.a) / 2
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const k = left.u + right.u;
|
|
71
|
+
if (k === 0) {
|
|
72
|
+
return vacuous((left.a + right.a) / 2);
|
|
73
|
+
}
|
|
74
|
+
return opinion(
|
|
75
|
+
(left.b * right.u + right.b * left.u) / k,
|
|
76
|
+
(left.d * right.u + right.d * left.u) / k,
|
|
77
|
+
2 * left.u * right.u / k,
|
|
78
|
+
(left.a + right.a) / 2
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
function trustDiscount(sourceOpinion, trust) {
|
|
82
|
+
const weight = Math.max(0, Math.min(1, Math.abs(trust)));
|
|
83
|
+
return opinion(
|
|
84
|
+
weight * sourceOpinion.b,
|
|
85
|
+
weight * sourceOpinion.d,
|
|
86
|
+
1 - weight * (sourceOpinion.b + sourceOpinion.d),
|
|
87
|
+
sourceOpinion.a
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
var EPSILON = 1e-9;
|
|
91
|
+
function childBaseRateFallback(ifTrue, ifFalse, fallbackBaseRate) {
|
|
92
|
+
if (fallbackBaseRate !== void 0) {
|
|
93
|
+
return Math.max(0, Math.min(1, fallbackBaseRate));
|
|
94
|
+
}
|
|
95
|
+
if (Math.abs(ifTrue.a - ifFalse.a) <= EPSILON) {
|
|
96
|
+
return ifTrue.a;
|
|
97
|
+
}
|
|
98
|
+
return (ifTrue.a + ifFalse.a) / 2;
|
|
99
|
+
}
|
|
100
|
+
function computeConditionalDeductionBaseRate(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
|
|
101
|
+
const denominator = 1 - opinionA.a * ifTrue.u - (1 - opinionA.a) * ifFalse.u;
|
|
102
|
+
if (ifTrue.u + ifFalse.u < 2 - EPSILON && Math.abs(denominator) > EPSILON) {
|
|
103
|
+
const baseRate = (opinionA.a * ifTrue.b + (1 - opinionA.a) * ifFalse.b) / denominator;
|
|
104
|
+
if (baseRate >= -EPSILON && baseRate <= 1 + EPSILON) {
|
|
105
|
+
return Math.max(0, Math.min(1, baseRate));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return fallbackBaseRate;
|
|
109
|
+
}
|
|
110
|
+
function safeCorrectionTerm(numerator, denominator) {
|
|
111
|
+
if (Math.abs(denominator) <= EPSILON) {
|
|
112
|
+
return void 0;
|
|
113
|
+
}
|
|
114
|
+
const value = numerator / denominator;
|
|
115
|
+
if (!Number.isFinite(value)) {
|
|
116
|
+
return void 0;
|
|
117
|
+
}
|
|
118
|
+
return Math.max(0, value);
|
|
119
|
+
}
|
|
120
|
+
function conditionalDeduction(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
|
|
121
|
+
const fallbackChildBaseRate = childBaseRateFallback(
|
|
122
|
+
ifTrue,
|
|
123
|
+
ifFalse,
|
|
124
|
+
fallbackBaseRate
|
|
125
|
+
);
|
|
126
|
+
const childBaseRate = computeConditionalDeductionBaseRate(
|
|
127
|
+
opinionA,
|
|
128
|
+
ifTrue,
|
|
129
|
+
ifFalse,
|
|
130
|
+
fallbackChildBaseRate
|
|
131
|
+
);
|
|
132
|
+
const projectedAntecedent = project(opinionA);
|
|
133
|
+
const projectedAntecedentComplement = 1 - projectedAntecedent;
|
|
134
|
+
const intermediateBelief = opinionA.b * ifTrue.b + opinionA.d * ifFalse.b + opinionA.u * (ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a));
|
|
135
|
+
const intermediateDisbelief = opinionA.b * ifTrue.d + opinionA.d * ifFalse.d + opinionA.u * (ifTrue.d * opinionA.a + ifFalse.d * (1 - opinionA.a));
|
|
136
|
+
const intermediateUncertainty = opinionA.b * ifTrue.u + opinionA.d * ifFalse.u + opinionA.u * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
|
|
137
|
+
const projectedVacuousDeduction = ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a) + childBaseRate * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
|
|
138
|
+
const projectedConditionalA = ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);
|
|
139
|
+
let correction = 0;
|
|
140
|
+
if (ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d || ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d) {
|
|
141
|
+
correction = 0;
|
|
142
|
+
} else if (ifTrue.b > ifFalse.b && ifTrue.d <= ifFalse.d) {
|
|
143
|
+
const beliefGap = ifTrue.b - ifFalse.b;
|
|
144
|
+
const disbeliefGap = ifFalse.d - ifTrue.d;
|
|
145
|
+
if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
146
|
+
correction = safeCorrectionTerm(
|
|
147
|
+
opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b),
|
|
148
|
+
projectedAntecedent * childBaseRate
|
|
149
|
+
) ?? 0;
|
|
150
|
+
} else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
|
|
151
|
+
correction = safeCorrectionTerm(
|
|
152
|
+
opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
|
|
153
|
+
projectedAntecedentComplement * childBaseRate * disbeliefGap
|
|
154
|
+
) ?? 0;
|
|
155
|
+
} else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
156
|
+
correction = safeCorrectionTerm(
|
|
157
|
+
(1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
|
|
158
|
+
projectedAntecedent * (1 - childBaseRate) * beliefGap
|
|
159
|
+
) ?? 0;
|
|
160
|
+
} else {
|
|
161
|
+
correction = safeCorrectionTerm(
|
|
162
|
+
(1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d),
|
|
163
|
+
projectedAntecedentComplement * (1 - childBaseRate)
|
|
164
|
+
) ?? 0;
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
const beliefGap = ifFalse.b - ifTrue.b;
|
|
168
|
+
const disbeliefGap = ifTrue.d - ifFalse.d;
|
|
169
|
+
if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
170
|
+
correction = safeCorrectionTerm(
|
|
171
|
+
(1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
|
|
172
|
+
projectedAntecedent * childBaseRate * disbeliefGap
|
|
173
|
+
) ?? 0;
|
|
174
|
+
} else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
|
|
175
|
+
correction = safeCorrectionTerm(
|
|
176
|
+
(1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b),
|
|
177
|
+
projectedAntecedentComplement * childBaseRate
|
|
178
|
+
) ?? 0;
|
|
179
|
+
} else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
180
|
+
correction = safeCorrectionTerm(
|
|
181
|
+
opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d),
|
|
182
|
+
projectedAntecedent * (1 - childBaseRate)
|
|
183
|
+
) ?? 0;
|
|
184
|
+
} else {
|
|
185
|
+
correction = safeCorrectionTerm(
|
|
186
|
+
opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
|
|
187
|
+
projectedAntecedentComplement * (1 - childBaseRate) * beliefGap
|
|
188
|
+
) ?? 0;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return opinion(
|
|
192
|
+
intermediateBelief - childBaseRate * correction,
|
|
193
|
+
intermediateDisbelief - (1 - childBaseRate) * correction,
|
|
194
|
+
intermediateUncertainty + correction,
|
|
195
|
+
childBaseRate
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
function conditionalAbduction(opinionY, ifTrue, ifFalse, baseRateX) {
|
|
199
|
+
const priorX = Math.max(0, Math.min(1, baseRateX));
|
|
200
|
+
const probabilityY = project(opinionY);
|
|
201
|
+
const probabilityGivenTrue = project(ifTrue);
|
|
202
|
+
const probabilityGivenFalse = project(ifFalse);
|
|
203
|
+
const posteriorIfYDenominator = probabilityGivenTrue * priorX + probabilityGivenFalse * (1 - priorX);
|
|
204
|
+
const posteriorIfY = posteriorIfYDenominator === 0 ? priorX : probabilityGivenTrue * priorX / posteriorIfYDenominator;
|
|
205
|
+
const posteriorIfNotYDenominator = (1 - probabilityGivenTrue) * priorX + (1 - probabilityGivenFalse) * (1 - priorX);
|
|
206
|
+
const posteriorIfNotY = posteriorIfNotYDenominator === 0 ? priorX : (1 - probabilityGivenTrue) * priorX / posteriorIfNotYDenominator;
|
|
207
|
+
const projectedX = probabilityY * posteriorIfY + (1 - probabilityY) * posteriorIfNotY;
|
|
208
|
+
const uncertainty = Math.max(
|
|
209
|
+
opinionY.u * 0.5,
|
|
210
|
+
probabilityY * ifTrue.u + (1 - probabilityY) * ifFalse.u
|
|
211
|
+
);
|
|
212
|
+
return opinion(
|
|
213
|
+
projectedX * (1 - uncertainty),
|
|
214
|
+
(1 - projectedX) * (1 - uncertainty),
|
|
215
|
+
uncertainty,
|
|
216
|
+
priorX
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
function negate(o) {
|
|
220
|
+
return { b: o.d, d: o.b, u: o.u, a: 1 - o.a };
|
|
221
|
+
}
|
|
222
|
+
function constraintFusion(left, right, mode = "pressure") {
|
|
223
|
+
if (mode === "redistribute") {
|
|
224
|
+
const leftProjected = project(left);
|
|
225
|
+
const rightProjected = project(right);
|
|
226
|
+
const total = leftProjected + rightProjected;
|
|
227
|
+
if (total <= 1) {
|
|
228
|
+
return { o1: left, o2: right };
|
|
229
|
+
}
|
|
230
|
+
const scale = 1 / total;
|
|
231
|
+
return {
|
|
232
|
+
o1: opinion(
|
|
233
|
+
left.b * scale,
|
|
234
|
+
left.d + left.b * (1 - scale),
|
|
235
|
+
left.u,
|
|
236
|
+
left.a
|
|
237
|
+
),
|
|
238
|
+
o2: opinion(
|
|
239
|
+
right.b * scale,
|
|
240
|
+
right.d + right.b * (1 - scale),
|
|
241
|
+
right.u,
|
|
242
|
+
right.a
|
|
243
|
+
)
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const pressureLeft = right.b * 0.5;
|
|
247
|
+
const pressureRight = left.b * 0.5;
|
|
248
|
+
return {
|
|
249
|
+
o1: opinion(
|
|
250
|
+
left.b - pressureLeft * 0.3,
|
|
251
|
+
left.d + pressureLeft * 0.3,
|
|
252
|
+
left.u,
|
|
253
|
+
left.a
|
|
254
|
+
),
|
|
255
|
+
o2: opinion(
|
|
256
|
+
right.b - pressureRight * 0.3,
|
|
257
|
+
right.d + pressureRight * 0.3,
|
|
258
|
+
right.u,
|
|
259
|
+
right.a
|
|
260
|
+
)
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function evidenceBalance(o) {
|
|
264
|
+
const total = o.b + o.d;
|
|
265
|
+
if (total === 0) {
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
|
+
return (o.b - o.d) / total;
|
|
269
|
+
}
|
|
270
|
+
function areTensioned(left, right) {
|
|
271
|
+
return project(left) > 0.5 && project(right) > 0.5 && (left.d > 0.2 || right.d > 0.2);
|
|
272
|
+
}
|
|
273
|
+
function informationGain(o) {
|
|
274
|
+
if (o.u === 0) {
|
|
275
|
+
return 0;
|
|
276
|
+
}
|
|
277
|
+
if (o.u === 1) {
|
|
278
|
+
return 1;
|
|
279
|
+
}
|
|
280
|
+
return o.u * (1 - Math.abs(o.b - o.d));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export { areTensioned, averagingFusion, conditionalAbduction, conditionalDeduction, confidenceLevel, constraintFusion, cumulativeFusion, dogmatic, evidenceBalance, informationGain, negate, opinion, project, trustDiscount, vacuous };
|
|
284
|
+
//# sourceMappingURL=index.js.map
|
|
285
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/v1/operations/subjectiveLogic/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Opinion } from \"../../types\";\n\nexport function opinion(\n belief: number,\n disbelief: number,\n uncertainty: number,\n baseRate: number = 0.5\n): Opinion {\n const b = Math.max(0, Math.min(1, belief));\n const d = Math.max(0, Math.min(1, disbelief));\n const u = Math.max(0, Math.min(1, uncertainty));\n const a = Math.max(0, Math.min(1, baseRate));\n const sum = b + d + u;\n\n if (sum === 0) {\n return { b: 0, d: 0, u: 1, a };\n }\n\n return {\n b: b / sum,\n d: d / sum,\n u: u / sum,\n a,\n };\n}\n\nexport function vacuous(baseRate: number = 0.5): Opinion {\n return { b: 0, d: 0, u: 1, a: baseRate };\n}\n\nexport function dogmatic(probability: number, baseRate: number = 0.5): Opinion {\n const p = Math.max(0, Math.min(1, probability));\n return { b: p, d: 1 - p, u: 0, a: baseRate };\n}\n\nexport function project(o: Opinion): number {\n return o.b + o.a * o.u;\n}\n\nexport function confidenceLevel(o: Opinion): \"high\" | \"medium\" | \"low\" {\n const projected = project(o);\n if (o.u > 0.5) {\n return \"low\";\n }\n if (projected >= 0.8 && o.u < 0.2) {\n return \"high\";\n }\n if (projected >= 0.6) {\n return \"medium\";\n }\n return \"low\";\n}\n\nexport function cumulativeFusion(left: Opinion, right: Opinion): Opinion {\n if (left.u === 0 && right.u === 0) {\n return opinion(\n (left.b + right.b) / 2,\n (left.d + right.d) / 2,\n 0,\n (left.a + right.a) / 2\n );\n }\n\n const k = left.u + right.u - left.u * right.u;\n if (k === 0) {\n return vacuous((left.a + right.a) / 2);\n }\n\n return opinion(\n (left.b * right.u + right.b * left.u) / k,\n (left.d * right.u + right.d * left.u) / k,\n (left.u * right.u) / k,\n (left.a + right.a) / 2\n );\n}\n\nexport function averagingFusion(left: Opinion, right: Opinion): Opinion {\n if (left.u === 0 && right.u === 0) {\n return opinion(\n (left.b + right.b) / 2,\n (left.d + right.d) / 2,\n 0,\n (left.a + right.a) / 2\n );\n }\n\n const k = left.u + right.u;\n if (k === 0) {\n return vacuous((left.a + right.a) / 2);\n }\n\n return opinion(\n (left.b * right.u + right.b * left.u) / k,\n (left.d * right.u + right.d * left.u) / k,\n (2 * left.u * right.u) / k,\n (left.a + right.a) / 2\n );\n}\n\nexport function trustDiscount(sourceOpinion: Opinion, trust: number): Opinion {\n const weight = Math.max(0, Math.min(1, Math.abs(trust)));\n return opinion(\n weight * sourceOpinion.b,\n weight * sourceOpinion.d,\n 1 - weight * (sourceOpinion.b + sourceOpinion.d),\n sourceOpinion.a\n );\n}\n\nconst EPSILON = 1e-9;\n\nfunction childBaseRateFallback(\n ifTrue: Opinion,\n ifFalse: Opinion,\n fallbackBaseRate: number | undefined\n): number {\n if (fallbackBaseRate !== undefined) {\n return Math.max(0, Math.min(1, fallbackBaseRate));\n }\n\n if (Math.abs(ifTrue.a - ifFalse.a) <= EPSILON) {\n return ifTrue.a;\n }\n\n return (ifTrue.a + ifFalse.a) / 2;\n}\n\nfunction computeConditionalDeductionBaseRate(\n opinionA: Opinion,\n ifTrue: Opinion,\n ifFalse: Opinion,\n fallbackBaseRate: number\n): number {\n const denominator =\n 1 - opinionA.a * ifTrue.u - (1 - opinionA.a) * ifFalse.u;\n\n if (ifTrue.u + ifFalse.u < 2 - EPSILON && Math.abs(denominator) > EPSILON) {\n const baseRate =\n (opinionA.a * ifTrue.b + (1 - opinionA.a) * ifFalse.b) / denominator;\n if (baseRate >= -EPSILON && baseRate <= 1 + EPSILON) {\n return Math.max(0, Math.min(1, baseRate));\n }\n }\n\n return fallbackBaseRate;\n}\n\nfunction safeCorrectionTerm(\n numerator: number,\n denominator: number\n): number | undefined {\n if (Math.abs(denominator) <= EPSILON) {\n return undefined;\n }\n\n const value = numerator / denominator;\n if (!Number.isFinite(value)) {\n return undefined;\n }\n\n return Math.max(0, value);\n}\n\nexport function conditionalDeduction(\n opinionA: Opinion,\n ifTrue: Opinion,\n ifFalse: Opinion,\n fallbackBaseRate?: number\n): Opinion {\n const fallbackChildBaseRate = childBaseRateFallback(\n ifTrue,\n ifFalse,\n fallbackBaseRate\n );\n const childBaseRate = computeConditionalDeductionBaseRate(\n opinionA,\n ifTrue,\n ifFalse,\n fallbackChildBaseRate\n );\n const projectedAntecedent = project(opinionA);\n const projectedAntecedentComplement = 1 - projectedAntecedent;\n const intermediateBelief =\n opinionA.b * ifTrue.b +\n opinionA.d * ifFalse.b +\n opinionA.u * (ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a));\n const intermediateDisbelief =\n opinionA.b * ifTrue.d +\n opinionA.d * ifFalse.d +\n opinionA.u * (ifTrue.d * opinionA.a + ifFalse.d * (1 - opinionA.a));\n const intermediateUncertainty =\n opinionA.b * ifTrue.u +\n opinionA.d * ifFalse.u +\n opinionA.u * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));\n const projectedVacuousDeduction =\n ifTrue.b * opinionA.a +\n ifFalse.b * (1 - opinionA.a) +\n childBaseRate *\n (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));\n const projectedConditionalA =\n ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);\n let correction = 0;\n\n if (\n (ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d) ||\n (ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d)\n ) {\n correction = 0;\n } else if (ifTrue.b > ifFalse.b && ifTrue.d <= ifFalse.d) {\n const beliefGap = ifTrue.b - ifFalse.b;\n const disbeliefGap = ifFalse.d - ifTrue.d;\n\n if (\n projectedVacuousDeduction <= projectedConditionalA &&\n projectedAntecedent <= opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b),\n projectedAntecedent * childBaseRate\n ) ?? 0;\n } else if (\n projectedVacuousDeduction <= projectedConditionalA &&\n projectedAntecedent > opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,\n projectedAntecedentComplement * childBaseRate * disbeliefGap\n ) ?? 0;\n } else if (\n projectedVacuousDeduction > projectedConditionalA &&\n projectedAntecedent <= opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n (1 - opinionA.a) *\n opinionA.u *\n (intermediateBelief - ifTrue.b) *\n disbeliefGap,\n projectedAntecedent * (1 - childBaseRate) * beliefGap\n ) ?? 0;\n } else {\n correction =\n safeCorrectionTerm(\n (1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d),\n projectedAntecedentComplement * (1 - childBaseRate)\n ) ?? 0;\n }\n } else {\n const beliefGap = ifFalse.b - ifTrue.b;\n const disbeliefGap = ifTrue.d - ifFalse.d;\n\n if (\n projectedVacuousDeduction <= projectedConditionalA &&\n projectedAntecedent <= opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n (1 - opinionA.a) *\n opinionA.u *\n (intermediateDisbelief - ifTrue.d) *\n beliefGap,\n projectedAntecedent * childBaseRate * disbeliefGap\n ) ?? 0;\n } else if (\n projectedVacuousDeduction <= projectedConditionalA &&\n projectedAntecedent > opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n (1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b),\n projectedAntecedentComplement * childBaseRate\n ) ?? 0;\n } else if (\n projectedVacuousDeduction > projectedConditionalA &&\n projectedAntecedent <= opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d),\n projectedAntecedent * (1 - childBaseRate)\n ) ?? 0;\n } else {\n correction =\n safeCorrectionTerm(\n opinionA.a *\n opinionA.u *\n (intermediateBelief - ifTrue.b) *\n disbeliefGap,\n projectedAntecedentComplement * (1 - childBaseRate) * beliefGap\n ) ?? 0;\n }\n }\n\n return opinion(\n intermediateBelief - childBaseRate * correction,\n intermediateDisbelief - (1 - childBaseRate) * correction,\n intermediateUncertainty + correction,\n childBaseRate\n );\n}\n\n/**\n * Abductive inference over a conditional.\n * Given an opinion on Y and conditionals P(Y|X), P(Y|~X), infer an opinion on X.\n */\nexport function conditionalAbduction(\n opinionY: Opinion,\n ifTrue: Opinion,\n ifFalse: Opinion,\n baseRateX: number\n): Opinion {\n const priorX = Math.max(0, Math.min(1, baseRateX));\n const probabilityY = project(opinionY);\n const probabilityGivenTrue = project(ifTrue);\n const probabilityGivenFalse = project(ifFalse);\n\n const posteriorIfYDenominator =\n probabilityGivenTrue * priorX +\n probabilityGivenFalse * (1 - priorX);\n const posteriorIfY =\n posteriorIfYDenominator === 0\n ? priorX\n : (probabilityGivenTrue * priorX) / posteriorIfYDenominator;\n\n const posteriorIfNotYDenominator =\n (1 - probabilityGivenTrue) * priorX +\n (1 - probabilityGivenFalse) * (1 - priorX);\n const posteriorIfNotY =\n posteriorIfNotYDenominator === 0\n ? priorX\n : ((1 - probabilityGivenTrue) * priorX) / posteriorIfNotYDenominator;\n\n const projectedX =\n probabilityY * posteriorIfY + (1 - probabilityY) * posteriorIfNotY;\n const uncertainty = Math.max(\n opinionY.u * 0.5,\n probabilityY * ifTrue.u + (1 - probabilityY) * ifFalse.u\n );\n\n return opinion(\n projectedX * (1 - uncertainty),\n (1 - projectedX) * (1 - uncertainty),\n uncertainty,\n priorX\n );\n}\n\nexport function negate(o: Opinion): Opinion {\n return { b: o.d, d: o.b, u: o.u, a: 1 - o.a };\n}\n\nexport function constraintFusion(\n left: Opinion,\n right: Opinion,\n mode: \"pressure\" | \"redistribute\" = \"pressure\"\n): { o1: Opinion; o2: Opinion } {\n if (mode === \"redistribute\") {\n const leftProjected = project(left);\n const rightProjected = project(right);\n const total = leftProjected + rightProjected;\n\n if (total <= 1) {\n return { o1: left, o2: right };\n }\n\n const scale = 1 / total;\n return {\n o1: opinion(\n left.b * scale,\n left.d + left.b * (1 - scale),\n left.u,\n left.a\n ),\n o2: opinion(\n right.b * scale,\n right.d + right.b * (1 - scale),\n right.u,\n right.a\n ),\n };\n }\n\n const pressureLeft = right.b * 0.5;\n const pressureRight = left.b * 0.5;\n\n return {\n o1: opinion(\n left.b - pressureLeft * 0.3,\n left.d + pressureLeft * 0.3,\n left.u,\n left.a\n ),\n o2: opinion(\n right.b - pressureRight * 0.3,\n right.d + pressureRight * 0.3,\n right.u,\n right.a\n ),\n };\n}\n\nexport function evidenceBalance(o: Opinion): number {\n const total = o.b + o.d;\n if (total === 0) {\n return 0;\n }\n return (o.b - o.d) / total;\n}\n\nexport function areTensioned(left: Opinion, right: Opinion): boolean {\n return (\n project(left) > 0.5 &&\n project(right) > 0.5 &&\n (left.d > 0.2 || right.d > 0.2)\n );\n}\n\nexport function informationGain(o: Opinion): number {\n if (o.u === 0) {\n return 0;\n }\n if (o.u === 1) {\n return 1;\n }\n return o.u * (1 - Math.abs(o.b - o.d));\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Opinion } from '../types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LKC-3 decision note:
|
|
5
|
+
* 1. Lucern already persists posterior opinions in `beliefConfidence`.
|
|
6
|
+
* 2. It does not persist a full per-observation Beta event stream on every path.
|
|
7
|
+
* 3. Reweighting raw observations would therefore force new schema + replay logic.
|
|
8
|
+
* 4. This operator decays the formed opinion instead of reconstructing old evidence.
|
|
9
|
+
* 5. The decay is exponential over epistemic age with a configurable half-life.
|
|
10
|
+
* 6. Belief/disbelief mass shrinks over time and returns to uncertainty.
|
|
11
|
+
* 7. Base rate `a` stays fixed, so the projection moves back toward the prior.
|
|
12
|
+
* 8. The reference clock is "when this posterior became knowable" (`assessedAt`).
|
|
13
|
+
* 9. That makes Invariant 9 operational with the smallest implementation surface.
|
|
14
|
+
* 10. Per-observation decay can be layered later without breaking this API.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
declare const DEFAULT_TEMPORAL_DECAY_HALF_LIFE_MS: number;
|
|
18
|
+
type TemporalDecayParams = {
|
|
19
|
+
referenceTime: Date | number;
|
|
20
|
+
halfLifeMs?: number;
|
|
21
|
+
};
|
|
22
|
+
declare function temporalDecay(sourceOpinion: Opinion, now: Date | number, decayParams: TemporalDecayParams): Opinion;
|
|
23
|
+
|
|
24
|
+
export { DEFAULT_TEMPORAL_DECAY_HALF_LIFE_MS, type TemporalDecayParams, temporalDecay };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/v1/operations/subjectiveLogic/index.ts
|
|
2
|
+
function opinion(belief, disbelief, uncertainty, baseRate = 0.5) {
|
|
3
|
+
const b = Math.max(0, Math.min(1, belief));
|
|
4
|
+
const d = Math.max(0, Math.min(1, disbelief));
|
|
5
|
+
const u = Math.max(0, Math.min(1, uncertainty));
|
|
6
|
+
const a = Math.max(0, Math.min(1, baseRate));
|
|
7
|
+
const sum = b + d + u;
|
|
8
|
+
if (sum === 0) {
|
|
9
|
+
return { b: 0, d: 0, u: 1, a };
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
b: b / sum,
|
|
13
|
+
d: d / sum,
|
|
14
|
+
u: u / sum,
|
|
15
|
+
a
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function trustDiscount(sourceOpinion, trust) {
|
|
19
|
+
const weight = Math.max(0, Math.min(1, Math.abs(trust)));
|
|
20
|
+
return opinion(
|
|
21
|
+
weight * sourceOpinion.b,
|
|
22
|
+
weight * sourceOpinion.d,
|
|
23
|
+
1 - weight * (sourceOpinion.b + sourceOpinion.d),
|
|
24
|
+
sourceOpinion.a
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/v1/operations/temporalDecay.ts
|
|
29
|
+
var DEFAULT_TEMPORAL_DECAY_HALF_LIFE_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
30
|
+
function toEpochMs(value) {
|
|
31
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
if (value instanceof Date) {
|
|
35
|
+
const epochMs = value.getTime();
|
|
36
|
+
return Number.isFinite(epochMs) ? epochMs : void 0;
|
|
37
|
+
}
|
|
38
|
+
return void 0;
|
|
39
|
+
}
|
|
40
|
+
function normalizeHalfLifeMs(halfLifeMs) {
|
|
41
|
+
if (halfLifeMs === void 0 || !Number.isFinite(halfLifeMs)) {
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
return Math.max(0, halfLifeMs);
|
|
45
|
+
}
|
|
46
|
+
function temporalDecay(sourceOpinion, now, decayParams) {
|
|
47
|
+
const halfLifeMs = normalizeHalfLifeMs(decayParams.halfLifeMs);
|
|
48
|
+
if (halfLifeMs === 0) {
|
|
49
|
+
return { ...sourceOpinion };
|
|
50
|
+
}
|
|
51
|
+
const nowMs = toEpochMs(now);
|
|
52
|
+
const referenceTimeMs = toEpochMs(decayParams.referenceTime);
|
|
53
|
+
if (nowMs === void 0 || referenceTimeMs === void 0) {
|
|
54
|
+
return { ...sourceOpinion };
|
|
55
|
+
}
|
|
56
|
+
const ageMs = Math.max(0, nowMs - referenceTimeMs);
|
|
57
|
+
if (ageMs === 0) {
|
|
58
|
+
return { ...sourceOpinion };
|
|
59
|
+
}
|
|
60
|
+
const retainedEvidenceWeight = Math.pow(0.5, ageMs / halfLifeMs);
|
|
61
|
+
return trustDiscount(sourceOpinion, retainedEvidenceWeight);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { DEFAULT_TEMPORAL_DECAY_HALF_LIFE_MS, temporalDecay };
|
|
65
|
+
//# sourceMappingURL=temporalDecay.js.map
|
|
66
|
+
//# sourceMappingURL=temporalDecay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/v1/operations/subjectiveLogic/index.ts","../../../src/v1/operations/temporalDecay.ts"],"names":[],"mappings":"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","file":"temporalDecay.js","sourcesContent":["import type { Opinion } from \"../../types\";\n\nexport function opinion(\n belief: number,\n disbelief: number,\n uncertainty: number,\n baseRate: number = 0.5\n): Opinion {\n const b = Math.max(0, Math.min(1, belief));\n const d = Math.max(0, Math.min(1, disbelief));\n const u = Math.max(0, Math.min(1, uncertainty));\n const a = Math.max(0, Math.min(1, baseRate));\n const sum = b + d + u;\n\n if (sum === 0) {\n return { b: 0, d: 0, u: 1, a };\n }\n\n return {\n b: b / sum,\n d: d / sum,\n u: u / sum,\n a,\n };\n}\n\nexport function vacuous(baseRate: number = 0.5): Opinion {\n return { b: 0, d: 0, u: 1, a: baseRate };\n}\n\nexport function dogmatic(probability: number, baseRate: number = 0.5): Opinion {\n const p = Math.max(0, Math.min(1, probability));\n return { b: p, d: 1 - p, u: 0, a: baseRate };\n}\n\nexport function project(o: Opinion): number {\n return o.b + o.a * o.u;\n}\n\nexport function confidenceLevel(o: Opinion): \"high\" | \"medium\" | \"low\" {\n const projected = project(o);\n if (o.u > 0.5) {\n return \"low\";\n }\n if (projected >= 0.8 && o.u < 0.2) {\n return \"high\";\n }\n if (projected >= 0.6) {\n return \"medium\";\n }\n return \"low\";\n}\n\nexport function cumulativeFusion(left: Opinion, right: Opinion): Opinion {\n if (left.u === 0 && right.u === 0) {\n return opinion(\n (left.b + right.b) / 2,\n (left.d + right.d) / 2,\n 0,\n (left.a + right.a) / 2\n );\n }\n\n const k = left.u + right.u - left.u * right.u;\n if (k === 0) {\n return vacuous((left.a + right.a) / 2);\n }\n\n return opinion(\n (left.b * right.u + right.b * left.u) / k,\n (left.d * right.u + right.d * left.u) / k,\n (left.u * right.u) / k,\n (left.a + right.a) / 2\n );\n}\n\nexport function averagingFusion(left: Opinion, right: Opinion): Opinion {\n if (left.u === 0 && right.u === 0) {\n return opinion(\n (left.b + right.b) / 2,\n (left.d + right.d) / 2,\n 0,\n (left.a + right.a) / 2\n );\n }\n\n const k = left.u + right.u;\n if (k === 0) {\n return vacuous((left.a + right.a) / 2);\n }\n\n return opinion(\n (left.b * right.u + right.b * left.u) / k,\n (left.d * right.u + right.d * left.u) / k,\n (2 * left.u * right.u) / k,\n (left.a + right.a) / 2\n );\n}\n\nexport function trustDiscount(sourceOpinion: Opinion, trust: number): Opinion {\n const weight = Math.max(0, Math.min(1, Math.abs(trust)));\n return opinion(\n weight * sourceOpinion.b,\n weight * sourceOpinion.d,\n 1 - weight * (sourceOpinion.b + sourceOpinion.d),\n sourceOpinion.a\n );\n}\n\nconst EPSILON = 1e-9;\n\nfunction childBaseRateFallback(\n ifTrue: Opinion,\n ifFalse: Opinion,\n fallbackBaseRate: number | undefined\n): number {\n if (fallbackBaseRate !== undefined) {\n return Math.max(0, Math.min(1, fallbackBaseRate));\n }\n\n if (Math.abs(ifTrue.a - ifFalse.a) <= EPSILON) {\n return ifTrue.a;\n }\n\n return (ifTrue.a + ifFalse.a) / 2;\n}\n\nfunction computeConditionalDeductionBaseRate(\n opinionA: Opinion,\n ifTrue: Opinion,\n ifFalse: Opinion,\n fallbackBaseRate: number\n): number {\n const denominator =\n 1 - opinionA.a * ifTrue.u - (1 - opinionA.a) * ifFalse.u;\n\n if (ifTrue.u + ifFalse.u < 2 - EPSILON && Math.abs(denominator) > EPSILON) {\n const baseRate =\n (opinionA.a * ifTrue.b + (1 - opinionA.a) * ifFalse.b) / denominator;\n if (baseRate >= -EPSILON && baseRate <= 1 + EPSILON) {\n return Math.max(0, Math.min(1, baseRate));\n }\n }\n\n return fallbackBaseRate;\n}\n\nfunction safeCorrectionTerm(\n numerator: number,\n denominator: number\n): number | undefined {\n if (Math.abs(denominator) <= EPSILON) {\n return undefined;\n }\n\n const value = numerator / denominator;\n if (!Number.isFinite(value)) {\n return undefined;\n }\n\n return Math.max(0, value);\n}\n\nexport function conditionalDeduction(\n opinionA: Opinion,\n ifTrue: Opinion,\n ifFalse: Opinion,\n fallbackBaseRate?: number\n): Opinion {\n const fallbackChildBaseRate = childBaseRateFallback(\n ifTrue,\n ifFalse,\n fallbackBaseRate\n );\n const childBaseRate = computeConditionalDeductionBaseRate(\n opinionA,\n ifTrue,\n ifFalse,\n fallbackChildBaseRate\n );\n const projectedAntecedent = project(opinionA);\n const projectedAntecedentComplement = 1 - projectedAntecedent;\n const intermediateBelief =\n opinionA.b * ifTrue.b +\n opinionA.d * ifFalse.b +\n opinionA.u * (ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a));\n const intermediateDisbelief =\n opinionA.b * ifTrue.d +\n opinionA.d * ifFalse.d +\n opinionA.u * (ifTrue.d * opinionA.a + ifFalse.d * (1 - opinionA.a));\n const intermediateUncertainty =\n opinionA.b * ifTrue.u +\n opinionA.d * ifFalse.u +\n opinionA.u * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));\n const projectedVacuousDeduction =\n ifTrue.b * opinionA.a +\n ifFalse.b * (1 - opinionA.a) +\n childBaseRate *\n (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));\n const projectedConditionalA =\n ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);\n let correction = 0;\n\n if (\n (ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d) ||\n (ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d)\n ) {\n correction = 0;\n } else if (ifTrue.b > ifFalse.b && ifTrue.d <= ifFalse.d) {\n const beliefGap = ifTrue.b - ifFalse.b;\n const disbeliefGap = ifFalse.d - ifTrue.d;\n\n if (\n projectedVacuousDeduction <= projectedConditionalA &&\n projectedAntecedent <= opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b),\n projectedAntecedent * childBaseRate\n ) ?? 0;\n } else if (\n projectedVacuousDeduction <= projectedConditionalA &&\n projectedAntecedent > opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,\n projectedAntecedentComplement * childBaseRate * disbeliefGap\n ) ?? 0;\n } else if (\n projectedVacuousDeduction > projectedConditionalA &&\n projectedAntecedent <= opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n (1 - opinionA.a) *\n opinionA.u *\n (intermediateBelief - ifTrue.b) *\n disbeliefGap,\n projectedAntecedent * (1 - childBaseRate) * beliefGap\n ) ?? 0;\n } else {\n correction =\n safeCorrectionTerm(\n (1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d),\n projectedAntecedentComplement * (1 - childBaseRate)\n ) ?? 0;\n }\n } else {\n const beliefGap = ifFalse.b - ifTrue.b;\n const disbeliefGap = ifTrue.d - ifFalse.d;\n\n if (\n projectedVacuousDeduction <= projectedConditionalA &&\n projectedAntecedent <= opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n (1 - opinionA.a) *\n opinionA.u *\n (intermediateDisbelief - ifTrue.d) *\n beliefGap,\n projectedAntecedent * childBaseRate * disbeliefGap\n ) ?? 0;\n } else if (\n projectedVacuousDeduction <= projectedConditionalA &&\n projectedAntecedent > opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n (1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b),\n projectedAntecedentComplement * childBaseRate\n ) ?? 0;\n } else if (\n projectedVacuousDeduction > projectedConditionalA &&\n projectedAntecedent <= opinionA.a\n ) {\n correction =\n safeCorrectionTerm(\n opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d),\n projectedAntecedent * (1 - childBaseRate)\n ) ?? 0;\n } else {\n correction =\n safeCorrectionTerm(\n opinionA.a *\n opinionA.u *\n (intermediateBelief - ifTrue.b) *\n disbeliefGap,\n projectedAntecedentComplement * (1 - childBaseRate) * beliefGap\n ) ?? 0;\n }\n }\n\n return opinion(\n intermediateBelief - childBaseRate * correction,\n intermediateDisbelief - (1 - childBaseRate) * correction,\n intermediateUncertainty + correction,\n childBaseRate\n );\n}\n\n/**\n * Abductive inference over a conditional.\n * Given an opinion on Y and conditionals P(Y|X), P(Y|~X), infer an opinion on X.\n */\nexport function conditionalAbduction(\n opinionY: Opinion,\n ifTrue: Opinion,\n ifFalse: Opinion,\n baseRateX: number\n): Opinion {\n const priorX = Math.max(0, Math.min(1, baseRateX));\n const probabilityY = project(opinionY);\n const probabilityGivenTrue = project(ifTrue);\n const probabilityGivenFalse = project(ifFalse);\n\n const posteriorIfYDenominator =\n probabilityGivenTrue * priorX +\n probabilityGivenFalse * (1 - priorX);\n const posteriorIfY =\n posteriorIfYDenominator === 0\n ? priorX\n : (probabilityGivenTrue * priorX) / posteriorIfYDenominator;\n\n const posteriorIfNotYDenominator =\n (1 - probabilityGivenTrue) * priorX +\n (1 - probabilityGivenFalse) * (1 - priorX);\n const posteriorIfNotY =\n posteriorIfNotYDenominator === 0\n ? priorX\n : ((1 - probabilityGivenTrue) * priorX) / posteriorIfNotYDenominator;\n\n const projectedX =\n probabilityY * posteriorIfY + (1 - probabilityY) * posteriorIfNotY;\n const uncertainty = Math.max(\n opinionY.u * 0.5,\n probabilityY * ifTrue.u + (1 - probabilityY) * ifFalse.u\n );\n\n return opinion(\n projectedX * (1 - uncertainty),\n (1 - projectedX) * (1 - uncertainty),\n uncertainty,\n priorX\n );\n}\n\nexport function negate(o: Opinion): Opinion {\n return { b: o.d, d: o.b, u: o.u, a: 1 - o.a };\n}\n\nexport function constraintFusion(\n left: Opinion,\n right: Opinion,\n mode: \"pressure\" | \"redistribute\" = \"pressure\"\n): { o1: Opinion; o2: Opinion } {\n if (mode === \"redistribute\") {\n const leftProjected = project(left);\n const rightProjected = project(right);\n const total = leftProjected + rightProjected;\n\n if (total <= 1) {\n return { o1: left, o2: right };\n }\n\n const scale = 1 / total;\n return {\n o1: opinion(\n left.b * scale,\n left.d + left.b * (1 - scale),\n left.u,\n left.a\n ),\n o2: opinion(\n right.b * scale,\n right.d + right.b * (1 - scale),\n right.u,\n right.a\n ),\n };\n }\n\n const pressureLeft = right.b * 0.5;\n const pressureRight = left.b * 0.5;\n\n return {\n o1: opinion(\n left.b - pressureLeft * 0.3,\n left.d + pressureLeft * 0.3,\n left.u,\n left.a\n ),\n o2: opinion(\n right.b - pressureRight * 0.3,\n right.d + pressureRight * 0.3,\n right.u,\n right.a\n ),\n };\n}\n\nexport function evidenceBalance(o: Opinion): number {\n const total = o.b + o.d;\n if (total === 0) {\n return 0;\n }\n return (o.b - o.d) / total;\n}\n\nexport function areTensioned(left: Opinion, right: Opinion): boolean {\n return (\n project(left) > 0.5 &&\n project(right) > 0.5 &&\n (left.d > 0.2 || right.d > 0.2)\n );\n}\n\nexport function informationGain(o: Opinion): number {\n if (o.u === 0) {\n return 0;\n }\n if (o.u === 1) {\n return 1;\n }\n return o.u * (1 - Math.abs(o.b - o.d));\n}\n","/**\n * LKC-3 decision note:\n * 1. Lucern already persists posterior opinions in `beliefConfidence`.\n * 2. It does not persist a full per-observation Beta event stream on every path.\n * 3. Reweighting raw observations would therefore force new schema + replay logic.\n * 4. This operator decays the formed opinion instead of reconstructing old evidence.\n * 5. The decay is exponential over epistemic age with a configurable half-life.\n * 6. Belief/disbelief mass shrinks over time and returns to uncertainty.\n * 7. Base rate `a` stays fixed, so the projection moves back toward the prior.\n * 8. The reference clock is \"when this posterior became knowable\" (`assessedAt`).\n * 9. That makes Invariant 9 operational with the smallest implementation surface.\n * 10. Per-observation decay can be layered later without breaking this API.\n */\n\nimport type { Opinion } from \"../types\";\nimport { trustDiscount } from \"./subjectiveLogic\";\n\nexport const DEFAULT_TEMPORAL_DECAY_HALF_LIFE_MS =\n 90 * 24 * 60 * 60 * 1000;\n\nexport type TemporalDecayParams = {\n referenceTime: Date | number;\n halfLifeMs?: number;\n};\n\nfunction toEpochMs(value: Date | number): number | undefined {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return value;\n }\n\n if (value instanceof Date) {\n const epochMs = value.getTime();\n return Number.isFinite(epochMs) ? epochMs : undefined;\n }\n\n return undefined;\n}\n\nfunction normalizeHalfLifeMs(halfLifeMs?: number): number {\n if (halfLifeMs === undefined || !Number.isFinite(halfLifeMs)) {\n return 0;\n }\n\n return Math.max(0, halfLifeMs);\n}\n\nexport function temporalDecay(\n sourceOpinion: Opinion,\n now: Date | number,\n decayParams: TemporalDecayParams\n): Opinion {\n const halfLifeMs = normalizeHalfLifeMs(decayParams.halfLifeMs);\n if (halfLifeMs === 0) {\n return { ...sourceOpinion };\n }\n\n const nowMs = toEpochMs(now);\n const referenceTimeMs = toEpochMs(decayParams.referenceTime);\n if (nowMs === undefined || referenceTimeMs === undefined) {\n return { ...sourceOpinion };\n }\n\n const ageMs = Math.max(0, nowMs - referenceTimeMs);\n if (ageMs === 0) {\n return { ...sourceOpinion };\n }\n\n const retainedEvidenceWeight = Math.pow(0.5, ageMs / halfLifeMs);\n return trustDiscount(sourceOpinion, retainedEvidenceWeight);\n}\n"]}
|