@lucern/confidence 1.0.29 → 1.0.30
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/dist/index.d.ts +7 -7
- package/dist/index.js +905 -770
- package/dist/index.js.map +1 -1
- package/dist/proof-attestation.json +1 -1
- package/dist/v1/codec.js.map +1 -1
- package/dist/v1/index.d.ts +7 -7
- package/dist/v1/index.js +905 -770
- package/dist/v1/index.js.map +1 -1
- package/dist/v1/interfaces.d.ts +5 -5
- package/dist/v1/operations/approximation.d.ts +1 -1
- package/dist/v1/operations/approximation.js +8 -7
- package/dist/v1/operations/approximation.js.map +1 -1
- package/dist/v1/operations/bridge/index.js +7 -6
- package/dist/v1/operations/bridge/index.js.map +1 -1
- package/dist/v1/operations/canonical.d.ts +1 -1
- package/dist/v1/operations/canonical.js +106 -57
- package/dist/v1/operations/canonical.js.map +1 -1
- package/dist/v1/operations/contracts/epistemicContract.d.ts +1 -1
- package/dist/v1/operations/contracts/epistemicContract.js +9 -3
- package/dist/v1/operations/contracts/epistemicContract.js.map +1 -1
- package/dist/v1/operations/contradiction/detectTupleContradiction.js.map +1 -1
- package/dist/v1/operations/contradiction/index.js.map +1 -1
- package/dist/v1/operations/dynamics/cascade.js +1 -1
- package/dist/v1/operations/dynamics/cascade.js.map +1 -1
- package/dist/v1/operations/dynamics/decay.js +105 -41
- package/dist/v1/operations/dynamics/decay.js.map +1 -1
- package/dist/v1/operations/dynamics/defeat.js.map +1 -1
- package/dist/v1/operations/dynamics/propagation.d.ts +3 -3
- package/dist/v1/operations/dynamics/propagation.js +101 -57
- package/dist/v1/operations/dynamics/propagation.js.map +1 -1
- package/dist/v1/operations/dynamics/revision.js +7 -6
- package/dist/v1/operations/dynamics/revision.js.map +1 -1
- package/dist/v1/operations/index.js +8 -3
- package/dist/v1/operations/index.js.map +1 -1
- package/dist/v1/operations/lucern.d.ts +7 -7
- package/dist/v1/operations/lucern.js +934 -807
- package/dist/v1/operations/lucern.js.map +1 -1
- package/dist/v1/operations/operatorTaxonomy.d.ts +10 -10
- package/dist/v1/operations/operatorTaxonomy.js.map +1 -1
- package/dist/v1/operations/scoring.d.ts +2 -1
- package/dist/v1/operations/scoring.js +20 -5
- package/dist/v1/operations/scoring.js.map +1 -1
- package/dist/v1/operations/subjectiveLogic/index.js +91 -54
- package/dist/v1/operations/subjectiveLogic/index.js.map +1 -1
- package/dist/v1/operations/temporalDecay.d.ts +3 -3
- package/dist/v1/operations/temporalDecay.js +8 -3
- package/dist/v1/operations/temporalDecay.js.map +1 -1
- package/dist/v1/types.d.ts +101 -101
- package/package.json +1 -1
|
@@ -131,6 +131,102 @@ function lifecycleMultiplier(status) {
|
|
|
131
131
|
}
|
|
132
132
|
return 1;
|
|
133
133
|
}
|
|
134
|
+
function resolveLifecycleRescore(context) {
|
|
135
|
+
if (context.isOverdue && context.lifecycleStatus === "assumption") {
|
|
136
|
+
return {
|
|
137
|
+
priority: "critical",
|
|
138
|
+
reason: `Untested assumption is stale (${Math.round(context.decayState.ageDays)}d) \u2014 validate or supersede`
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (context.isOverdue && context.lifecycleStatus === "hypothesis" || context.lifecycleStatus === "hypothesis" && context.daysUntilRescore < 7) {
|
|
142
|
+
return {
|
|
143
|
+
priority: "high",
|
|
144
|
+
reason: `Hypothesis aging without validation (${Math.round(context.decayState.ageDays)}d) \u2014 run/finish sprint testing`
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function resolveDeadlineRescore(context) {
|
|
149
|
+
const urgency = context.decayState.urgency;
|
|
150
|
+
if (urgency?.isOverdue) {
|
|
151
|
+
return {
|
|
152
|
+
priority: "critical",
|
|
153
|
+
reason: `Prediction deadline passed ${Math.abs(urgency.daysToDeadline)}d ago \u2014 validate or archive`
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (urgency?.urgencyTier.label === "critical") {
|
|
157
|
+
return {
|
|
158
|
+
priority: "critical",
|
|
159
|
+
reason: `Deadline in ${urgency.daysToDeadline}d \u2014 needs immediate rescoring`
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (urgency?.urgencyTier.label === "imminent") {
|
|
163
|
+
return {
|
|
164
|
+
priority: "high",
|
|
165
|
+
reason: `Deadline in ${urgency.daysToDeadline}d \u2014 weekly rescoring required`
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function resolveStalenessRescore(context) {
|
|
170
|
+
if (context.isOverdue && context.decayState.baseTier.label === "expired") {
|
|
171
|
+
return {
|
|
172
|
+
priority: "critical",
|
|
173
|
+
reason: `Not scored in ${Math.round(context.decayState.ageDays)}d \u2014 confidence severely degraded`
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
if (context.isOverdue && context.decayState.baseTier.label === "stale") {
|
|
177
|
+
return {
|
|
178
|
+
priority: "high",
|
|
179
|
+
reason: `Stale (${Math.round(context.decayState.ageDays)}d since scoring) \u2014 evidence update required`
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (context.isOverdue) {
|
|
183
|
+
return {
|
|
184
|
+
priority: "high",
|
|
185
|
+
reason: `Rescore overdue by ${Math.abs(Math.round(context.daysUntilRescore))}d`
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function resolveRoutineRescore(context) {
|
|
190
|
+
if (context.temporalNature === "forecast" && context.daysUntilRescore < 7) {
|
|
191
|
+
return {
|
|
192
|
+
priority: "medium",
|
|
193
|
+
reason: `Forecast belief \u2014 next rescore due in ${Math.round(context.daysUntilRescore)}d`
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (context.daysUntilRescore < 14) {
|
|
197
|
+
return {
|
|
198
|
+
priority: "medium",
|
|
199
|
+
reason: `Rescore due in ${Math.round(context.daysUntilRescore)}d`
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
priority: "low",
|
|
204
|
+
reason: `On schedule \u2014 next rescore in ${Math.round(context.daysUntilRescore)}d`
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function escalateHighConfidenceRescore(result, context) {
|
|
208
|
+
if ((context.confidence ?? 0) >= 0.8 && context.decayState.baseTier.label !== "fresh" && result.priority === "medium") {
|
|
209
|
+
return {
|
|
210
|
+
priority: "high",
|
|
211
|
+
reason: `High-confidence belief (${((context.confidence ?? 0) * 100).toFixed(0)}%) aging without rescore \u2014 ${result.reason}`
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
var RESCORING_RULES = [
|
|
217
|
+
resolveLifecycleRescore,
|
|
218
|
+
resolveDeadlineRescore,
|
|
219
|
+
resolveStalenessRescore
|
|
220
|
+
];
|
|
221
|
+
function resolveRescoringPriority(context) {
|
|
222
|
+
for (const rule of RESCORING_RULES) {
|
|
223
|
+
const result = rule(context);
|
|
224
|
+
if (result) {
|
|
225
|
+
return escalateHighConfidenceRescore(result, context);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return escalateHighConfidenceRescore(resolveRoutineRescore(context), context);
|
|
229
|
+
}
|
|
134
230
|
function computeBaseDecay(lastScoredAt) {
|
|
135
231
|
const ageDays = (Date.now() - lastScoredAt) / (1e3 * 60 * 60 * 24);
|
|
136
232
|
let tier = DECAY_TIERS.EXPIRED;
|
|
@@ -202,46 +298,14 @@ function getRescoringSchedule(opts) {
|
|
|
202
298
|
);
|
|
203
299
|
const daysUntilRescore = (decayState.rescoreByDate - Date.now()) / (1e3 * 60 * 60 * 24);
|
|
204
300
|
const isOverdue = daysUntilRescore < 0;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
} else if (decayState.urgency?.isOverdue) {
|
|
214
|
-
priority = "critical";
|
|
215
|
-
reason = `Prediction deadline passed ${Math.abs(decayState.urgency.daysToDeadline)}d ago \u2014 validate or archive`;
|
|
216
|
-
} else if (isOverdue && decayState.baseTier.label === "expired") {
|
|
217
|
-
priority = "critical";
|
|
218
|
-
reason = `Not scored in ${Math.round(decayState.ageDays)}d \u2014 confidence severely degraded`;
|
|
219
|
-
} else if (isOverdue && decayState.baseTier.label === "stale") {
|
|
220
|
-
priority = "high";
|
|
221
|
-
reason = `Stale (${Math.round(decayState.ageDays)}d since scoring) \u2014 evidence update required`;
|
|
222
|
-
} else if (decayState.urgency && decayState.urgency.urgencyTier.label === "critical") {
|
|
223
|
-
priority = "critical";
|
|
224
|
-
reason = `Deadline in ${decayState.urgency.daysToDeadline}d \u2014 needs immediate rescoring`;
|
|
225
|
-
} else if (decayState.urgency && decayState.urgency.urgencyTier.label === "imminent") {
|
|
226
|
-
priority = "high";
|
|
227
|
-
reason = `Deadline in ${decayState.urgency.daysToDeadline}d \u2014 weekly rescoring required`;
|
|
228
|
-
} else if (isOverdue) {
|
|
229
|
-
priority = "high";
|
|
230
|
-
reason = `Rescore overdue by ${Math.abs(Math.round(daysUntilRescore))}d`;
|
|
231
|
-
} else if (opts.temporalNature === "forecast" && daysUntilRescore < 7) {
|
|
232
|
-
priority = "medium";
|
|
233
|
-
reason = `Forecast belief \u2014 next rescore due in ${Math.round(daysUntilRescore)}d`;
|
|
234
|
-
} else if (daysUntilRescore < 14) {
|
|
235
|
-
priority = "medium";
|
|
236
|
-
reason = `Rescore due in ${Math.round(daysUntilRescore)}d`;
|
|
237
|
-
} else {
|
|
238
|
-
priority = "low";
|
|
239
|
-
reason = `On schedule \u2014 next rescore in ${Math.round(daysUntilRescore)}d`;
|
|
240
|
-
}
|
|
241
|
-
if ((opts.confidence ?? 0) >= 0.8 && decayState.baseTier.label !== "fresh" && priority === "medium") {
|
|
242
|
-
priority = "high";
|
|
243
|
-
reason = `High-confidence belief (${((opts.confidence ?? 0) * 100).toFixed(0)}%) aging without rescore \u2014 ${reason}`;
|
|
244
|
-
}
|
|
301
|
+
const { priority, reason } = resolveRescoringPriority({
|
|
302
|
+
confidence: opts.confidence,
|
|
303
|
+
daysUntilRescore,
|
|
304
|
+
decayState,
|
|
305
|
+
isOverdue,
|
|
306
|
+
lifecycleStatus,
|
|
307
|
+
temporalNature: opts.temporalNature
|
|
308
|
+
});
|
|
245
309
|
return {
|
|
246
310
|
nextRescoreBy: decayState.rescoreByDate,
|
|
247
311
|
daysUntilRescore: Math.round(daysUntilRescore),
|
|
@@ -255,7 +319,7 @@ function decay(current, daysSinceLastUpdate, halfLifeDays = 90) {
|
|
|
255
319
|
if (daysSinceLastUpdate <= 0) {
|
|
256
320
|
return current;
|
|
257
321
|
}
|
|
258
|
-
const decayFactor =
|
|
322
|
+
const decayFactor = 0.5 ** (daysSinceLastUpdate / halfLifeDays);
|
|
259
323
|
const certainty = current.b + current.d;
|
|
260
324
|
const lostCertainty = certainty - certainty * decayFactor;
|
|
261
325
|
return opinion(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/v1/operations/subjectiveLogic/index.ts","../../../../src/v1/operations/dynamics/decay.ts"],"names":[],"mappings":"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","file":"decay.js","sourcesContent":["import type { SLOpinion } from \"../../types\";\n\nexport function opinion(\n belief: number,\n disbelief: number,\n uncertainty: number,\n baseRate: number\n): SLOpinion {\n const b = Number.isFinite(belief) ? Math.max(0, belief) : 0;\n const d = Number.isFinite(disbelief) ? Math.max(0, disbelief) : 0;\n const u = Number.isFinite(uncertainty) ? Math.max(0, uncertainty) : 0;\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 } as SLOpinion;\n }\n\n return {\n b: b / sum,\n d: d / sum,\n u: u / sum,\n a,\n } as SLOpinion;\n}\n\nexport function mkOpinion(\n belief: number,\n disbelief: number,\n uncertainty: number,\n baseRate: number\n): SLOpinion {\n return opinion(belief, disbelief, uncertainty, baseRate);\n}\n\nexport function vacuous(baseRate: number): SLOpinion {\n return mkOpinion(0, 0, 1, baseRate);\n}\n\nexport function dogmatic(probability: number, baseRate: number): SLOpinion {\n const p = Math.max(0, Math.min(1, probability));\n return mkOpinion(p, 1 - p, 0, baseRate);\n}\n\nexport function project(o: SLOpinion): number {\n return o.b + o.a * o.u;\n}\n\nexport function confidenceLevel(o: SLOpinion): \"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\nconst VACUOUS_IDENTITY_EPSILON = 1e-12;\n\n/**\n * Associative, vacuous-identity-preserving base rate for a fused opinion.\n *\n * The legacy `(left.a + right.a) / 2` arithmetic mean was the shared root of\n * RR.7 F1 (projected-confidence non-associativity) and F2 (a vacuous opinion of\n * a different base rate was not a fusion identity): averaging always pulled the\n * fused base rate toward the other operand even when that operand carried no\n * evidence. Subjective Logic requires every opinion over the same frame to share\n * one canonical base rate, so a correct fusion never *invents* a new base rate\n * from the operands' priors. We therefore:\n *\n * - return the informative operand's base rate when exactly one operand is\n * vacuous (the vacuous operand contributes no prior), making `vacuous(a)` a\n * true identity for any `a`; and\n * - require equal base rates otherwise (the canonical-frame contract). When\n * they coincide the result is that shared value — trivially associative.\n *\n * If two informative opinions carry genuinely different base rates they are not\n * opinions over the same frame and must be reconciled before fusion; we fall\n * back to the arithmetic mean ONLY in that ill-posed case to stay total, and the\n * property suite documents that this path is outside the order-independence\n * guarantee.\n */\nfunction fusedBaseRate(left: SLOpinion, right: SLOpinion): number {\n const leftVacuous = 1 - left.u <= VACUOUS_IDENTITY_EPSILON;\n const rightVacuous = 1 - right.u <= VACUOUS_IDENTITY_EPSILON;\n if (leftVacuous && !rightVacuous) {\n return right.a;\n }\n if (rightVacuous && !leftVacuous) {\n return left.a;\n }\n if (Math.abs(left.a - right.a) <= VACUOUS_IDENTITY_EPSILON) {\n return left.a;\n }\n return (left.a + right.a) / 2;\n}\n\nexport function cumulativeFusion(left: SLOpinion, right: SLOpinion): SLOpinion {\n const a = fusedBaseRate(left, right);\n\n // F2: a vacuous operand is the identity element of cumulative fusion. Return\n // the other operand verbatim (including its base rate) so fusion with the\n // vacuous opinion is a true no-op regardless of differing priors.\n if (1 - left.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(right.b, right.d, right.u, a);\n }\n if (1 - right.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(left.b, left.d, left.u, a);\n }\n\n // F4: dogmatic pair handled via the ε-limit of the cumulative rule rather than\n // the standalone `(b+d)/2` shortcut. As both uncertainties shrink at the same\n // rate, the cumulative formula's `(b_L·u_R + b_R·u_L)/k` term converges to the\n // arithmetic mean of the masses; deriving it from the same limit the interior\n // formula uses keeps mass-associativity intact (no special-case discontinuity).\n if (left.u <= VACUOUS_IDENTITY_EPSILON && right.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion((left.b + right.b) / 2, (left.d + right.d) / 2, 0, a);\n }\n\n const k = left.u + right.u - left.u * right.u;\n if (k === 0) {\n return vacuous(a);\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 a\n );\n}\n\nexport function averagingFusion(left: SLOpinion, right: SLOpinion): SLOpinion {\n const a = fusedBaseRate(left, right);\n\n // F2: vacuous identity, mirroring cumulativeFusion.\n if (1 - left.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(right.b, right.d, right.u, a);\n }\n if (1 - right.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(left.b, left.d, left.u, a);\n }\n\n // F4: dogmatic pair via the ε-limit of the averaging rule.\n if (left.u <= VACUOUS_IDENTITY_EPSILON && right.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion((left.b + right.b) / 2, (left.d + right.d) / 2, 0, a);\n }\n\n const k = left.u + right.u;\n if (k === 0) {\n return vacuous(a);\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 a\n );\n}\n\nexport function trustDiscount(sourceOpinion: SLOpinion, trust: number): SLOpinion {\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: SLOpinion,\n ifFalse: SLOpinion,\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: SLOpinion,\n ifTrue: SLOpinion,\n ifFalse: SLOpinion,\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: SLOpinion,\n ifTrue: SLOpinion,\n ifFalse: SLOpinion,\n fallbackBaseRate?: number\n): SLOpinion {\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: SLOpinion,\n ifTrue: SLOpinion,\n ifFalse: SLOpinion,\n baseRateX: number\n): SLOpinion {\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: SLOpinion): SLOpinion {\n return mkOpinion(o.d, o.b, o.u, 1 - o.a);\n}\n\nexport function constraintFusion(\n left: SLOpinion,\n right: SLOpinion,\n mode: \"pressure\" | \"redistribute\" = \"pressure\"\n): { o1: SLOpinion; o2: SLOpinion } {\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: SLOpinion): 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: SLOpinion, right: SLOpinion): 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: SLOpinion): 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 {\n BeliefLifecycleBucket,\n DeadlineUrgency,\n DeadlineUrgencyTier,\n DecayTier,\n EffectiveDecay,\n Opinion,\n RescoringSchedule,\n} from \"../../types\";\nimport { opinion } from \"../subjectiveLogic\";\n\nexport const DECAY_TIERS: Record<string, DecayTier> = {\n FRESH: {\n maxAgeDays: 30,\n weight: 1,\n label: \"fresh\",\n action: \"Full confidence — recently validated\",\n rescoreInDays: 30,\n },\n AGING: {\n maxAgeDays: 90,\n weight: 0.8,\n label: \"aging\",\n action: \"Evidence refresh recommended\",\n rescoreInDays: 14,\n },\n STALE: {\n maxAgeDays: 180,\n weight: 0.5,\n label: \"stale\",\n action: \"Evidence update required before trusting\",\n rescoreInDays: 7,\n },\n EXPIRED: {\n maxAgeDays: Number.POSITIVE_INFINITY,\n weight: 0.2,\n label: \"expired\",\n action: \"Full re-evaluation needed\",\n rescoreInDays: 0,\n },\n};\n\nexport const DEADLINE_URGENCY: Record<string, DeadlineUrgencyTier> = {\n DISTANT: {\n minDaysToDeadline: 365,\n urgencyMultiplier: 1,\n label: \"distant\",\n rescoreIntervalDays: 90,\n action: \"Quarterly confidence check\",\n },\n APPROACHING: {\n minDaysToDeadline: 180,\n urgencyMultiplier: 0.9,\n label: \"approaching\",\n rescoreIntervalDays: 30,\n action: \"Monthly confidence check — conditions may be shifting\",\n },\n NEAR: {\n minDaysToDeadline: 90,\n urgencyMultiplier: 0.75,\n label: \"near\",\n rescoreIntervalDays: 14,\n action: \"Biweekly rescore — deadline within 3 months\",\n },\n IMMINENT: {\n minDaysToDeadline: 30,\n urgencyMultiplier: 0.6,\n label: \"imminent\",\n rescoreIntervalDays: 7,\n action: \"Weekly rescore — deadline within 1 month\",\n },\n CRITICAL: {\n minDaysToDeadline: 7,\n urgencyMultiplier: 0.4,\n label: \"critical\",\n rescoreIntervalDays: 1,\n action: \"Daily rescore — deadline THIS WEEK\",\n },\n OVERDUE: {\n minDaysToDeadline: Number.NEGATIVE_INFINITY,\n urgencyMultiplier: 0.2,\n label: \"overdue\",\n rescoreIntervalDays: 0,\n action: \"OVERDUE — must validate or archive immediately\",\n },\n};\n\nfunction normalizeBeliefConfidence(confidence: unknown): number | null {\n if (typeof confidence !== \"number\" || !Number.isFinite(confidence)) {\n return null;\n }\n if (confidence >= 0 && confidence <= 1) {\n return confidence;\n }\n if (confidence > 1 && confidence <= 100) {\n return confidence / 100;\n }\n return null;\n}\n\nfunction hasResolvedPredictionOutcome(predictionMeta: unknown): boolean {\n if (!predictionMeta || typeof predictionMeta !== \"object\") {\n return false;\n }\n\n const outcome = (predictionMeta as { outcome?: unknown }).outcome;\n return (\n outcome === \"confirmed\" ||\n outcome === \"disconfirmed\" ||\n outcome === \"partial\" ||\n outcome === \"expired\"\n );\n}\n\nfunction resolveLifecycleBucket(args: {\n beliefStatus?: string | null;\n confidence?: unknown;\n predictionMeta?: unknown;\n}): BeliefLifecycleBucket {\n if (\n normalizeBeliefConfidence(args.confidence) === 0 ||\n normalizeBeliefConfidence(args.confidence) === 1 ||\n hasResolvedPredictionOutcome(args.predictionMeta)\n ) {\n return \"fact\";\n }\n\n if (\n args.beliefStatus === \"assumption\" ||\n args.beliefStatus === \"hypothesis\" ||\n args.beliefStatus === \"belief\" ||\n args.beliefStatus === \"fact\"\n ) {\n if (\n normalizeBeliefConfidence(args.confidence) !== null &&\n (args.beliefStatus === \"assumption\" || args.beliefStatus === \"hypothesis\")\n ) {\n return \"belief\";\n }\n return args.beliefStatus;\n }\n\n return \"assumption\";\n}\n\nfunction lifecycleMultiplier(status: BeliefLifecycleBucket | string | null | undefined) {\n if (status === \"assumption\") {\n return 0.65;\n }\n if (status === \"hypothesis\") {\n return 0.8;\n }\n return 1;\n}\n\nexport function computeBaseDecay(lastScoredAt: number): {\n ageDays: number;\n tier: DecayTier;\n weight: number;\n} {\n const ageDays = (Date.now() - lastScoredAt) / (1000 * 60 * 60 * 24);\n let tier = DECAY_TIERS.EXPIRED;\n\n if (ageDays <= DECAY_TIERS.FRESH.maxAgeDays) {\n tier = DECAY_TIERS.FRESH;\n } else if (ageDays <= DECAY_TIERS.AGING.maxAgeDays) {\n tier = DECAY_TIERS.AGING;\n } else if (ageDays <= DECAY_TIERS.STALE.maxAgeDays) {\n tier = DECAY_TIERS.STALE;\n }\n\n return { ageDays, tier, weight: tier.weight };\n}\n\nexport function computeDeadlineUrgency(\n expectedBy: number | null | undefined\n): DeadlineUrgency {\n if (!expectedBy) {\n return null;\n }\n\n const daysToDeadline = (expectedBy - Date.now()) / (1000 * 60 * 60 * 24);\n let urgencyTier = DEADLINE_URGENCY.DISTANT;\n\n if (daysToDeadline < 0) {\n urgencyTier = DEADLINE_URGENCY.OVERDUE;\n } else if (daysToDeadline <= 7) {\n urgencyTier = DEADLINE_URGENCY.CRITICAL;\n } else if (daysToDeadline <= 30) {\n urgencyTier = DEADLINE_URGENCY.IMMINENT;\n } else if (daysToDeadline <= 90) {\n urgencyTier = DEADLINE_URGENCY.NEAR;\n } else if (daysToDeadline <= 180) {\n urgencyTier = DEADLINE_URGENCY.APPROACHING;\n }\n\n return {\n daysToDeadline: Math.round(daysToDeadline),\n urgencyTier,\n urgencyMultiplier: urgencyTier.urgencyMultiplier,\n isOverdue: daysToDeadline < 0,\n };\n}\n\nexport function computeEffectiveDecay(\n lastScoredAt: number,\n expectedBy?: number | null,\n beliefStatus?: BeliefLifecycleBucket | string | null\n): EffectiveDecay {\n const base = computeBaseDecay(lastScoredAt);\n const urgency = computeDeadlineUrgency(expectedBy);\n const effectiveWeight =\n (urgency ? base.weight * urgency.urgencyMultiplier : base.weight) *\n lifecycleMultiplier(beliefStatus);\n\n const urgencyRescoreDays =\n urgency?.urgencyTier.rescoreIntervalDays ?? Number.POSITIVE_INFINITY;\n const rescoreInDays = Math.min(base.tier.rescoreInDays, urgencyRescoreDays);\n const rescoreByDate = lastScoredAt + rescoreInDays * 24 * 60 * 60 * 1000;\n\n let action = base.tier.action;\n if (urgency && urgency.urgencyTier.label !== \"distant\") {\n action = `${urgency.urgencyTier.action}. ${base.tier.action}`;\n }\n\n return {\n ageDays: base.ageDays,\n baseTier: base.tier,\n baseWeight: base.weight,\n urgency,\n effectiveWeight: Math.max(0, Math.min(1, effectiveWeight)),\n rescoreByDate,\n rescoreInDays,\n action,\n };\n}\n\nexport function getRescoringSchedule(opts: {\n lastScoredAt: number;\n temporalNature?: \"factual\" | \"forecast\" | \"unknown\" | null;\n expectedBy?: number | null;\n predictionMeta?: { expectedBy?: number; outcome?: unknown } | null;\n confidence?: number;\n beliefStatus?: string | null;\n}): RescoringSchedule {\n const lifecycleStatus = resolveLifecycleBucket({\n beliefStatus: opts.beliefStatus,\n confidence: opts.confidence,\n predictionMeta: opts.predictionMeta,\n });\n\n const decayState = computeEffectiveDecay(\n opts.lastScoredAt,\n opts.expectedBy,\n lifecycleStatus\n );\n const daysUntilRescore =\n (decayState.rescoreByDate - Date.now()) / (1000 * 60 * 60 * 24);\n const isOverdue = daysUntilRescore < 0;\n\n let priority: RescoringSchedule[\"priority\"];\n let reason: string;\n\n if (isOverdue && lifecycleStatus === \"assumption\") {\n priority = \"critical\";\n reason = `Untested assumption is stale (${Math.round(decayState.ageDays)}d) — validate or supersede`;\n } else if (\n (isOverdue && lifecycleStatus === \"hypothesis\") ||\n (lifecycleStatus === \"hypothesis\" && daysUntilRescore < 7)\n ) {\n priority = \"high\";\n reason = `Hypothesis aging without validation (${Math.round(decayState.ageDays)}d) — run/finish sprint testing`;\n } else if (decayState.urgency?.isOverdue) {\n priority = \"critical\";\n reason = `Prediction deadline passed ${Math.abs(decayState.urgency.daysToDeadline)}d ago — validate or archive`;\n } else if (isOverdue && decayState.baseTier.label === \"expired\") {\n priority = \"critical\";\n reason = `Not scored in ${Math.round(decayState.ageDays)}d — confidence severely degraded`;\n } else if (isOverdue && decayState.baseTier.label === \"stale\") {\n priority = \"high\";\n reason = `Stale (${Math.round(decayState.ageDays)}d since scoring) — evidence update required`;\n } else if (\n decayState.urgency &&\n decayState.urgency.urgencyTier.label === \"critical\"\n ) {\n priority = \"critical\";\n reason = `Deadline in ${decayState.urgency.daysToDeadline}d — needs immediate rescoring`;\n } else if (\n decayState.urgency &&\n decayState.urgency.urgencyTier.label === \"imminent\"\n ) {\n priority = \"high\";\n reason = `Deadline in ${decayState.urgency.daysToDeadline}d — weekly rescoring required`;\n } else if (isOverdue) {\n priority = \"high\";\n reason = `Rescore overdue by ${Math.abs(Math.round(daysUntilRescore))}d`;\n } else if (opts.temporalNature === \"forecast\" && daysUntilRescore < 7) {\n priority = \"medium\";\n reason = `Forecast belief — next rescore due in ${Math.round(daysUntilRescore)}d`;\n } else if (daysUntilRescore < 14) {\n priority = \"medium\";\n reason = `Rescore due in ${Math.round(daysUntilRescore)}d`;\n } else {\n priority = \"low\";\n reason = `On schedule — next rescore in ${Math.round(daysUntilRescore)}d`;\n }\n\n if (\n (opts.confidence ?? 0) >= 0.8 &&\n decayState.baseTier.label !== \"fresh\" &&\n priority === \"medium\"\n ) {\n priority = \"high\";\n reason = `High-confidence belief (${((opts.confidence ?? 0) * 100).toFixed(0)}%) aging without rescore — ${reason}`;\n }\n\n return {\n nextRescoreBy: decayState.rescoreByDate,\n daysUntilRescore: Math.round(daysUntilRescore),\n isOverdue,\n priority,\n reason,\n decay: decayState,\n };\n}\n\nexport function decay(\n current: Opinion,\n daysSinceLastUpdate: number,\n halfLifeDays: number = 90\n): Opinion {\n if (daysSinceLastUpdate <= 0) {\n return current;\n }\n\n const decayFactor = Math.pow(0.5, daysSinceLastUpdate / halfLifeDays);\n const certainty = current.b + current.d;\n const lostCertainty = certainty - certainty * decayFactor;\n\n return opinion(\n current.b * decayFactor,\n current.d * decayFactor,\n current.u + lostCertainty,\n current.a\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/v1/operations/subjectiveLogic/index.ts","../../../../src/v1/operations/dynamics/decay.ts"],"names":[],"mappings":"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","file":"decay.js","sourcesContent":["import type { SLOpinion } from \"../../types\";\n\nexport function opinion(\n belief: number,\n disbelief: number,\n uncertainty: number,\n baseRate: number\n): SLOpinion {\n const b = Number.isFinite(belief) ? Math.max(0, belief) : 0;\n const d = Number.isFinite(disbelief) ? Math.max(0, disbelief) : 0;\n const u = Number.isFinite(uncertainty) ? Math.max(0, uncertainty) : 0;\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 } as SLOpinion;\n }\n\n return {\n b: b / sum,\n d: d / sum,\n u: u / sum,\n a,\n } as SLOpinion;\n}\n\nexport function mkOpinion(\n belief: number,\n disbelief: number,\n uncertainty: number,\n baseRate: number\n): SLOpinion {\n return opinion(belief, disbelief, uncertainty, baseRate);\n}\n\nexport function vacuous(baseRate: number): SLOpinion {\n return mkOpinion(0, 0, 1, baseRate);\n}\n\nexport function dogmatic(probability: number, baseRate: number): SLOpinion {\n const p = Math.max(0, Math.min(1, probability));\n return mkOpinion(p, 1 - p, 0, baseRate);\n}\n\nexport function project(o: SLOpinion): number {\n return o.b + o.a * o.u;\n}\n\nexport function confidenceLevel(o: SLOpinion): \"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\nconst VACUOUS_IDENTITY_EPSILON = 1e-12;\n\n/**\n * Associative, vacuous-identity-preserving base rate for a fused opinion.\n *\n * The legacy `(left.a + right.a) / 2` arithmetic mean was the shared root of\n * RR.7 F1 (projected-confidence non-associativity) and F2 (a vacuous opinion of\n * a different base rate was not a fusion identity): averaging always pulled the\n * fused base rate toward the other operand even when that operand carried no\n * evidence. Subjective Logic requires every opinion over the same frame to share\n * one canonical base rate, so a correct fusion never *invents* a new base rate\n * from the operands' priors. We therefore:\n *\n * - return the informative operand's base rate when exactly one operand is\n * vacuous (the vacuous operand contributes no prior), making `vacuous(a)` a\n * true identity for any `a`; and\n * - require equal base rates otherwise (the canonical-frame contract). When\n * they coincide the result is that shared value — trivially associative.\n *\n * If two informative opinions carry genuinely different base rates they are not\n * opinions over the same frame and must be reconciled before fusion; we fall\n * back to the arithmetic mean ONLY in that ill-posed case to stay total, and the\n * property suite documents that this path is outside the order-independence\n * guarantee.\n */\nfunction fusedBaseRate(left: SLOpinion, right: SLOpinion): number {\n const leftVacuous = 1 - left.u <= VACUOUS_IDENTITY_EPSILON;\n const rightVacuous = 1 - right.u <= VACUOUS_IDENTITY_EPSILON;\n if (leftVacuous && !rightVacuous) {\n return right.a;\n }\n if (rightVacuous && !leftVacuous) {\n return left.a;\n }\n if (Math.abs(left.a - right.a) <= VACUOUS_IDENTITY_EPSILON) {\n return left.a;\n }\n return (left.a + right.a) / 2;\n}\n\nexport function cumulativeFusion(left: SLOpinion, right: SLOpinion): SLOpinion {\n const a = fusedBaseRate(left, right);\n\n // F2: a vacuous operand is the identity element of cumulative fusion. Return\n // the other operand verbatim (including its base rate) so fusion with the\n // vacuous opinion is a true no-op regardless of differing priors.\n if (1 - left.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(right.b, right.d, right.u, a);\n }\n if (1 - right.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(left.b, left.d, left.u, a);\n }\n\n // F4: dogmatic pair handled via the ε-limit of the cumulative rule rather than\n // the standalone `(b+d)/2` shortcut. As both uncertainties shrink at the same\n // rate, the cumulative formula's `(b_L·u_R + b_R·u_L)/k` term converges to the\n // arithmetic mean of the masses; deriving it from the same limit the interior\n // formula uses keeps mass-associativity intact (no special-case discontinuity).\n if (\n left.u <= VACUOUS_IDENTITY_EPSILON &&\n right.u <= VACUOUS_IDENTITY_EPSILON\n ) {\n return opinion((left.b + right.b) / 2, (left.d + right.d) / 2, 0, a);\n }\n\n const k = left.u + right.u - left.u * right.u;\n if (k === 0) {\n return vacuous(a);\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 a\n );\n}\n\nexport function averagingFusion(left: SLOpinion, right: SLOpinion): SLOpinion {\n const a = fusedBaseRate(left, right);\n\n // F2: vacuous identity, mirroring cumulativeFusion.\n if (1 - left.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(right.b, right.d, right.u, a);\n }\n if (1 - right.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(left.b, left.d, left.u, a);\n }\n\n // F4: dogmatic pair via the ε-limit of the averaging rule.\n if (\n left.u <= VACUOUS_IDENTITY_EPSILON &&\n right.u <= VACUOUS_IDENTITY_EPSILON\n ) {\n return opinion((left.b + right.b) / 2, (left.d + right.d) / 2, 0, a);\n }\n\n const k = left.u + right.u;\n if (k === 0) {\n return vacuous(a);\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 a\n );\n}\n\nexport function trustDiscount(\n sourceOpinion: SLOpinion,\n trust: number\n): SLOpinion {\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: SLOpinion,\n ifFalse: SLOpinion,\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: SLOpinion,\n ifTrue: SLOpinion,\n ifFalse: SLOpinion,\n fallbackBaseRate: number\n): number {\n const denominator = 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;\n }\n\n const value = numerator / denominator;\n if (!Number.isFinite(value)) {\n return;\n }\n\n return Math.max(0, value);\n}\n\ninterface ConditionalDeductionCorrectionContext {\n childBaseRate: number;\n ifFalse: SLOpinion;\n ifTrue: SLOpinion;\n intermediateBelief: number;\n intermediateDisbelief: number;\n opinionA: SLOpinion;\n projectedAntecedent: number;\n projectedAntecedentComplement: number;\n projectedConditionalA: number;\n projectedVacuousDeduction: number;\n}\n\nfunction correctionOrZero(numerator: number, denominator: number): number {\n return safeCorrectionTerm(numerator, denominator) ?? 0;\n}\n\nfunction hasNoConditionalDeductionCorrection(\n ifTrue: SLOpinion,\n ifFalse: SLOpinion\n): boolean {\n return (\n (ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d) ||\n (ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d)\n );\n}\n\nfunction usesLowerVacuousBranch(\n context: ConditionalDeductionCorrectionContext\n): boolean {\n return context.projectedVacuousDeduction <= context.projectedConditionalA;\n}\n\nfunction usesLowerAntecedentBranch(\n context: ConditionalDeductionCorrectionContext\n): boolean {\n return context.projectedAntecedent <= context.opinionA.a;\n}\n\nfunction computeTrueDominantDeductionCorrection(\n context: ConditionalDeductionCorrectionContext\n): number {\n const beliefGap = context.ifTrue.b - context.ifFalse.b;\n const disbeliefGap = context.ifFalse.d - context.ifTrue.d;\n const lowerVacuous = usesLowerVacuousBranch(context);\n const lowerAntecedent = usesLowerAntecedentBranch(context);\n\n if (lowerVacuous && lowerAntecedent) {\n return correctionOrZero(\n context.opinionA.a *\n context.opinionA.u *\n (context.intermediateBelief - context.ifTrue.b),\n context.projectedAntecedent * context.childBaseRate\n );\n }\n\n if (lowerVacuous) {\n return correctionOrZero(\n context.opinionA.a *\n context.opinionA.u *\n (context.intermediateDisbelief - context.ifTrue.d) *\n beliefGap,\n context.projectedAntecedentComplement *\n context.childBaseRate *\n disbeliefGap\n );\n }\n\n if (lowerAntecedent) {\n return correctionOrZero(\n (1 - context.opinionA.a) *\n context.opinionA.u *\n (context.intermediateBelief - context.ifTrue.b) *\n disbeliefGap,\n context.projectedAntecedent * (1 - context.childBaseRate) * beliefGap\n );\n }\n\n return correctionOrZero(\n (1 - context.opinionA.a) *\n context.opinionA.u *\n (context.intermediateDisbelief - context.ifTrue.d),\n context.projectedAntecedentComplement * (1 - context.childBaseRate)\n );\n}\n\nfunction computeFalseDominantDeductionCorrection(\n context: ConditionalDeductionCorrectionContext\n): number {\n const beliefGap = context.ifFalse.b - context.ifTrue.b;\n const disbeliefGap = context.ifTrue.d - context.ifFalse.d;\n const lowerVacuous = usesLowerVacuousBranch(context);\n const lowerAntecedent = usesLowerAntecedentBranch(context);\n\n if (lowerVacuous && lowerAntecedent) {\n return correctionOrZero(\n (1 - context.opinionA.a) *\n context.opinionA.u *\n (context.intermediateDisbelief - context.ifTrue.d) *\n beliefGap,\n context.projectedAntecedent * context.childBaseRate * disbeliefGap\n );\n }\n\n if (lowerVacuous) {\n return correctionOrZero(\n (1 - context.opinionA.a) *\n context.opinionA.u *\n (context.intermediateBelief - context.ifTrue.b),\n context.projectedAntecedentComplement * context.childBaseRate\n );\n }\n\n if (lowerAntecedent) {\n return correctionOrZero(\n context.opinionA.a *\n context.opinionA.u *\n (context.intermediateDisbelief - context.ifTrue.d),\n context.projectedAntecedent * (1 - context.childBaseRate)\n );\n }\n\n return correctionOrZero(\n context.opinionA.a *\n context.opinionA.u *\n (context.intermediateBelief - context.ifTrue.b) *\n disbeliefGap,\n context.projectedAntecedentComplement *\n (1 - context.childBaseRate) *\n beliefGap\n );\n}\n\nfunction computeConditionalDeductionCorrection(\n context: ConditionalDeductionCorrectionContext\n): number {\n if (hasNoConditionalDeductionCorrection(context.ifTrue, context.ifFalse)) {\n return 0;\n }\n\n if (\n context.ifTrue.b > context.ifFalse.b &&\n context.ifTrue.d <= context.ifFalse.d\n ) {\n return computeTrueDominantDeductionCorrection(context);\n }\n\n return computeFalseDominantDeductionCorrection(context);\n}\n\nexport function conditionalDeduction(\n opinionA: SLOpinion,\n ifTrue: SLOpinion,\n ifFalse: SLOpinion,\n fallbackBaseRate?: number\n): SLOpinion {\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 * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));\n const projectedConditionalA =\n ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);\n const correction = computeConditionalDeductionCorrection({\n childBaseRate,\n ifFalse,\n ifTrue,\n intermediateBelief,\n intermediateDisbelief,\n opinionA,\n projectedAntecedent,\n projectedAntecedentComplement,\n projectedConditionalA,\n projectedVacuousDeduction,\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: SLOpinion,\n ifTrue: SLOpinion,\n ifFalse: SLOpinion,\n baseRateX: number\n): SLOpinion {\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 + 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: SLOpinion): SLOpinion {\n return mkOpinion(o.d, o.b, o.u, 1 - o.a);\n}\n\nexport function constraintFusion(\n left: SLOpinion,\n right: SLOpinion,\n mode: \"pressure\" | \"redistribute\" = \"pressure\"\n): { o1: SLOpinion; o2: SLOpinion } {\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: SLOpinion): 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: SLOpinion, right: SLOpinion): 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: SLOpinion): 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 {\n BeliefLifecycleBucket,\n DeadlineUrgency,\n DeadlineUrgencyTier,\n DecayTier,\n EffectiveDecay,\n Opinion,\n RescoringSchedule,\n} from \"../../types\";\nimport { opinion } from \"../subjectiveLogic\";\n\ntype RescoringPriorityReason = Pick<RescoringSchedule, \"priority\" | \"reason\">;\n\ninterface RescoringPriorityContext {\n confidence?: number;\n daysUntilRescore: number;\n decayState: EffectiveDecay;\n isOverdue: boolean;\n lifecycleStatus: BeliefLifecycleBucket;\n temporalNature?: \"factual\" | \"forecast\" | \"unknown\" | null;\n}\n\ntype RescoringRule = (\n context: RescoringPriorityContext\n) => RescoringPriorityReason | undefined;\n\nexport const DECAY_TIERS: Record<string, DecayTier> = {\n FRESH: {\n maxAgeDays: 30,\n weight: 1,\n label: \"fresh\",\n action: \"Full confidence — recently validated\",\n rescoreInDays: 30,\n },\n AGING: {\n maxAgeDays: 90,\n weight: 0.8,\n label: \"aging\",\n action: \"Evidence refresh recommended\",\n rescoreInDays: 14,\n },\n STALE: {\n maxAgeDays: 180,\n weight: 0.5,\n label: \"stale\",\n action: \"Evidence update required before trusting\",\n rescoreInDays: 7,\n },\n EXPIRED: {\n maxAgeDays: Number.POSITIVE_INFINITY,\n weight: 0.2,\n label: \"expired\",\n action: \"Full re-evaluation needed\",\n rescoreInDays: 0,\n },\n};\n\nexport const DEADLINE_URGENCY: Record<string, DeadlineUrgencyTier> = {\n DISTANT: {\n minDaysToDeadline: 365,\n urgencyMultiplier: 1,\n label: \"distant\",\n rescoreIntervalDays: 90,\n action: \"Quarterly confidence check\",\n },\n APPROACHING: {\n minDaysToDeadline: 180,\n urgencyMultiplier: 0.9,\n label: \"approaching\",\n rescoreIntervalDays: 30,\n action: \"Monthly confidence check — conditions may be shifting\",\n },\n NEAR: {\n minDaysToDeadline: 90,\n urgencyMultiplier: 0.75,\n label: \"near\",\n rescoreIntervalDays: 14,\n action: \"Biweekly rescore — deadline within 3 months\",\n },\n IMMINENT: {\n minDaysToDeadline: 30,\n urgencyMultiplier: 0.6,\n label: \"imminent\",\n rescoreIntervalDays: 7,\n action: \"Weekly rescore — deadline within 1 month\",\n },\n CRITICAL: {\n minDaysToDeadline: 7,\n urgencyMultiplier: 0.4,\n label: \"critical\",\n rescoreIntervalDays: 1,\n action: \"Daily rescore — deadline THIS WEEK\",\n },\n OVERDUE: {\n minDaysToDeadline: Number.NEGATIVE_INFINITY,\n urgencyMultiplier: 0.2,\n label: \"overdue\",\n rescoreIntervalDays: 0,\n action: \"OVERDUE — must validate or archive immediately\",\n },\n};\n\nfunction normalizeBeliefConfidence(confidence: unknown): number | null {\n if (typeof confidence !== \"number\" || !Number.isFinite(confidence)) {\n return null;\n }\n if (confidence >= 0 && confidence <= 1) {\n return confidence;\n }\n if (confidence > 1 && confidence <= 100) {\n return confidence / 100;\n }\n return null;\n}\n\nfunction hasResolvedPredictionOutcome(predictionMeta: unknown): boolean {\n if (!predictionMeta || typeof predictionMeta !== \"object\") {\n return false;\n }\n\n const outcome = (predictionMeta as { outcome?: unknown }).outcome;\n return (\n outcome === \"confirmed\" ||\n outcome === \"disconfirmed\" ||\n outcome === \"partial\" ||\n outcome === \"expired\"\n );\n}\n\nfunction resolveLifecycleBucket(args: {\n beliefStatus?: string | null;\n confidence?: unknown;\n predictionMeta?: unknown;\n}): BeliefLifecycleBucket {\n if (\n normalizeBeliefConfidence(args.confidence) === 0 ||\n normalizeBeliefConfidence(args.confidence) === 1 ||\n hasResolvedPredictionOutcome(args.predictionMeta)\n ) {\n return \"fact\";\n }\n\n if (\n args.beliefStatus === \"assumption\" ||\n args.beliefStatus === \"hypothesis\" ||\n args.beliefStatus === \"belief\" ||\n args.beliefStatus === \"fact\"\n ) {\n if (\n normalizeBeliefConfidence(args.confidence) !== null &&\n (args.beliefStatus === \"assumption\" || args.beliefStatus === \"hypothesis\")\n ) {\n return \"belief\";\n }\n return args.beliefStatus;\n }\n\n return \"assumption\";\n}\n\nfunction lifecycleMultiplier(\n status: BeliefLifecycleBucket | string | null | undefined\n) {\n if (status === \"assumption\") {\n return 0.65;\n }\n if (status === \"hypothesis\") {\n return 0.8;\n }\n return 1;\n}\n\nfunction resolveLifecycleRescore(\n context: RescoringPriorityContext\n): RescoringPriorityReason | undefined {\n if (context.isOverdue && context.lifecycleStatus === \"assumption\") {\n return {\n priority: \"critical\",\n reason: `Untested assumption is stale (${Math.round(context.decayState.ageDays)}d) — validate or supersede`,\n };\n }\n\n if (\n (context.isOverdue && context.lifecycleStatus === \"hypothesis\") ||\n (context.lifecycleStatus === \"hypothesis\" && context.daysUntilRescore < 7)\n ) {\n return {\n priority: \"high\",\n reason: `Hypothesis aging without validation (${Math.round(context.decayState.ageDays)}d) — run/finish sprint testing`,\n };\n }\n}\n\nfunction resolveDeadlineRescore(\n context: RescoringPriorityContext\n): RescoringPriorityReason | undefined {\n const urgency = context.decayState.urgency;\n if (urgency?.isOverdue) {\n return {\n priority: \"critical\",\n reason: `Prediction deadline passed ${Math.abs(urgency.daysToDeadline)}d ago — validate or archive`,\n };\n }\n\n if (urgency?.urgencyTier.label === \"critical\") {\n return {\n priority: \"critical\",\n reason: `Deadline in ${urgency.daysToDeadline}d — needs immediate rescoring`,\n };\n }\n\n if (urgency?.urgencyTier.label === \"imminent\") {\n return {\n priority: \"high\",\n reason: `Deadline in ${urgency.daysToDeadline}d — weekly rescoring required`,\n };\n }\n}\n\nfunction resolveStalenessRescore(\n context: RescoringPriorityContext\n): RescoringPriorityReason | undefined {\n if (context.isOverdue && context.decayState.baseTier.label === \"expired\") {\n return {\n priority: \"critical\",\n reason: `Not scored in ${Math.round(context.decayState.ageDays)}d — confidence severely degraded`,\n };\n }\n\n if (context.isOverdue && context.decayState.baseTier.label === \"stale\") {\n return {\n priority: \"high\",\n reason: `Stale (${Math.round(context.decayState.ageDays)}d since scoring) — evidence update required`,\n };\n }\n\n if (context.isOverdue) {\n return {\n priority: \"high\",\n reason: `Rescore overdue by ${Math.abs(Math.round(context.daysUntilRescore))}d`,\n };\n }\n}\n\nfunction resolveRoutineRescore(\n context: RescoringPriorityContext\n): RescoringPriorityReason {\n if (context.temporalNature === \"forecast\" && context.daysUntilRescore < 7) {\n return {\n priority: \"medium\",\n reason: `Forecast belief — next rescore due in ${Math.round(context.daysUntilRescore)}d`,\n };\n }\n\n if (context.daysUntilRescore < 14) {\n return {\n priority: \"medium\",\n reason: `Rescore due in ${Math.round(context.daysUntilRescore)}d`,\n };\n }\n\n return {\n priority: \"low\",\n reason: `On schedule — next rescore in ${Math.round(context.daysUntilRescore)}d`,\n };\n}\n\nfunction escalateHighConfidenceRescore(\n result: RescoringPriorityReason,\n context: RescoringPriorityContext\n): RescoringPriorityReason {\n if (\n (context.confidence ?? 0) >= 0.8 &&\n context.decayState.baseTier.label !== \"fresh\" &&\n result.priority === \"medium\"\n ) {\n return {\n priority: \"high\",\n reason: `High-confidence belief (${((context.confidence ?? 0) * 100).toFixed(0)}%) aging without rescore — ${result.reason}`,\n };\n }\n\n return result;\n}\n\nconst RESCORING_RULES: readonly RescoringRule[] = [\n resolveLifecycleRescore,\n resolveDeadlineRescore,\n resolveStalenessRescore,\n];\n\nfunction resolveRescoringPriority(\n context: RescoringPriorityContext\n): RescoringPriorityReason {\n for (const rule of RESCORING_RULES) {\n const result = rule(context);\n if (result) {\n return escalateHighConfidenceRescore(result, context);\n }\n }\n\n return escalateHighConfidenceRescore(resolveRoutineRescore(context), context);\n}\n\nexport function computeBaseDecay(lastScoredAt: number): {\n ageDays: number;\n tier: DecayTier;\n weight: number;\n} {\n const ageDays = (Date.now() - lastScoredAt) / (1000 * 60 * 60 * 24);\n let tier = DECAY_TIERS.EXPIRED;\n\n if (ageDays <= DECAY_TIERS.FRESH.maxAgeDays) {\n tier = DECAY_TIERS.FRESH;\n } else if (ageDays <= DECAY_TIERS.AGING.maxAgeDays) {\n tier = DECAY_TIERS.AGING;\n } else if (ageDays <= DECAY_TIERS.STALE.maxAgeDays) {\n tier = DECAY_TIERS.STALE;\n }\n\n return { ageDays, tier, weight: tier.weight };\n}\n\nexport function computeDeadlineUrgency(\n expectedBy: number | null | undefined\n): DeadlineUrgency {\n if (!expectedBy) {\n return null;\n }\n\n const daysToDeadline = (expectedBy - Date.now()) / (1000 * 60 * 60 * 24);\n let urgencyTier = DEADLINE_URGENCY.DISTANT;\n\n if (daysToDeadline < 0) {\n urgencyTier = DEADLINE_URGENCY.OVERDUE;\n } else if (daysToDeadline <= 7) {\n urgencyTier = DEADLINE_URGENCY.CRITICAL;\n } else if (daysToDeadline <= 30) {\n urgencyTier = DEADLINE_URGENCY.IMMINENT;\n } else if (daysToDeadline <= 90) {\n urgencyTier = DEADLINE_URGENCY.NEAR;\n } else if (daysToDeadline <= 180) {\n urgencyTier = DEADLINE_URGENCY.APPROACHING;\n }\n\n return {\n daysToDeadline: Math.round(daysToDeadline),\n urgencyTier,\n urgencyMultiplier: urgencyTier.urgencyMultiplier,\n isOverdue: daysToDeadline < 0,\n };\n}\n\nexport function computeEffectiveDecay(\n lastScoredAt: number,\n expectedBy?: number | null,\n beliefStatus?: BeliefLifecycleBucket | string | null\n): EffectiveDecay {\n const base = computeBaseDecay(lastScoredAt);\n const urgency = computeDeadlineUrgency(expectedBy);\n const effectiveWeight =\n (urgency ? base.weight * urgency.urgencyMultiplier : base.weight) *\n lifecycleMultiplier(beliefStatus);\n\n const urgencyRescoreDays =\n urgency?.urgencyTier.rescoreIntervalDays ?? Number.POSITIVE_INFINITY;\n const rescoreInDays = Math.min(base.tier.rescoreInDays, urgencyRescoreDays);\n const rescoreByDate = lastScoredAt + rescoreInDays * 24 * 60 * 60 * 1000;\n\n let action = base.tier.action;\n if (urgency && urgency.urgencyTier.label !== \"distant\") {\n action = `${urgency.urgencyTier.action}. ${base.tier.action}`;\n }\n\n return {\n ageDays: base.ageDays,\n baseTier: base.tier,\n baseWeight: base.weight,\n urgency,\n effectiveWeight: Math.max(0, Math.min(1, effectiveWeight)),\n rescoreByDate,\n rescoreInDays,\n action,\n };\n}\n\nexport function getRescoringSchedule(opts: {\n lastScoredAt: number;\n temporalNature?: \"factual\" | \"forecast\" | \"unknown\" | null;\n expectedBy?: number | null;\n predictionMeta?: { expectedBy?: number; outcome?: unknown } | null;\n confidence?: number;\n beliefStatus?: string | null;\n}): RescoringSchedule {\n const lifecycleStatus = resolveLifecycleBucket({\n beliefStatus: opts.beliefStatus,\n confidence: opts.confidence,\n predictionMeta: opts.predictionMeta,\n });\n\n const decayState = computeEffectiveDecay(\n opts.lastScoredAt,\n opts.expectedBy,\n lifecycleStatus\n );\n const daysUntilRescore =\n (decayState.rescoreByDate - Date.now()) / (1000 * 60 * 60 * 24);\n const isOverdue = daysUntilRescore < 0;\n\n const { priority, reason } = resolveRescoringPriority({\n confidence: opts.confidence,\n daysUntilRescore,\n decayState,\n isOverdue,\n lifecycleStatus,\n temporalNature: opts.temporalNature,\n });\n\n return {\n nextRescoreBy: decayState.rescoreByDate,\n daysUntilRescore: Math.round(daysUntilRescore),\n isOverdue,\n priority,\n reason,\n decay: decayState,\n };\n}\n\nexport function decay(\n current: Opinion,\n daysSinceLastUpdate: number,\n halfLifeDays = 90\n): Opinion {\n if (daysSinceLastUpdate <= 0) {\n return current;\n }\n\n const decayFactor = 0.5 ** (daysSinceLastUpdate / halfLifeDays);\n const certainty = current.b + current.d;\n const lostCertainty = certainty - certainty * decayFactor;\n\n return opinion(\n current.b * decayFactor,\n current.d * decayFactor,\n current.u + lostCertainty,\n current.a\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/v1/operations/subjectiveLogic/index.ts","../../../../src/v1/operations/dynamics/defeat.ts"],"names":[],"mappings":"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","file":"defeat.js","sourcesContent":["import type { SLOpinion } from \"../../types\";\n\nexport function opinion(\n belief: number,\n disbelief: number,\n uncertainty: number,\n baseRate: number\n): SLOpinion {\n const b = Number.isFinite(belief) ? Math.max(0, belief) : 0;\n const d = Number.isFinite(disbelief) ? Math.max(0, disbelief) : 0;\n const u = Number.isFinite(uncertainty) ? Math.max(0, uncertainty) : 0;\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 } as SLOpinion;\n }\n\n return {\n b: b / sum,\n d: d / sum,\n u: u / sum,\n a,\n } as SLOpinion;\n}\n\nexport function mkOpinion(\n belief: number,\n disbelief: number,\n uncertainty: number,\n baseRate: number\n): SLOpinion {\n return opinion(belief, disbelief, uncertainty, baseRate);\n}\n\nexport function vacuous(baseRate: number): SLOpinion {\n return mkOpinion(0, 0, 1, baseRate);\n}\n\nexport function dogmatic(probability: number, baseRate: number): SLOpinion {\n const p = Math.max(0, Math.min(1, probability));\n return mkOpinion(p, 1 - p, 0, baseRate);\n}\n\nexport function project(o: SLOpinion): number {\n return o.b + o.a * o.u;\n}\n\nexport function confidenceLevel(o: SLOpinion): \"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\nconst VACUOUS_IDENTITY_EPSILON = 1e-12;\n\n/**\n * Associative, vacuous-identity-preserving base rate for a fused opinion.\n *\n * The legacy `(left.a + right.a) / 2` arithmetic mean was the shared root of\n * RR.7 F1 (projected-confidence non-associativity) and F2 (a vacuous opinion of\n * a different base rate was not a fusion identity): averaging always pulled the\n * fused base rate toward the other operand even when that operand carried no\n * evidence. Subjective Logic requires every opinion over the same frame to share\n * one canonical base rate, so a correct fusion never *invents* a new base rate\n * from the operands' priors. We therefore:\n *\n * - return the informative operand's base rate when exactly one operand is\n * vacuous (the vacuous operand contributes no prior), making `vacuous(a)` a\n * true identity for any `a`; and\n * - require equal base rates otherwise (the canonical-frame contract). When\n * they coincide the result is that shared value — trivially associative.\n *\n * If two informative opinions carry genuinely different base rates they are not\n * opinions over the same frame and must be reconciled before fusion; we fall\n * back to the arithmetic mean ONLY in that ill-posed case to stay total, and the\n * property suite documents that this path is outside the order-independence\n * guarantee.\n */\nfunction fusedBaseRate(left: SLOpinion, right: SLOpinion): number {\n const leftVacuous = 1 - left.u <= VACUOUS_IDENTITY_EPSILON;\n const rightVacuous = 1 - right.u <= VACUOUS_IDENTITY_EPSILON;\n if (leftVacuous && !rightVacuous) {\n return right.a;\n }\n if (rightVacuous && !leftVacuous) {\n return left.a;\n }\n if (Math.abs(left.a - right.a) <= VACUOUS_IDENTITY_EPSILON) {\n return left.a;\n }\n return (left.a + right.a) / 2;\n}\n\nexport function cumulativeFusion(left: SLOpinion, right: SLOpinion): SLOpinion {\n const a = fusedBaseRate(left, right);\n\n // F2: a vacuous operand is the identity element of cumulative fusion. Return\n // the other operand verbatim (including its base rate) so fusion with the\n // vacuous opinion is a true no-op regardless of differing priors.\n if (1 - left.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(right.b, right.d, right.u, a);\n }\n if (1 - right.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(left.b, left.d, left.u, a);\n }\n\n // F4: dogmatic pair handled via the ε-limit of the cumulative rule rather than\n // the standalone `(b+d)/2` shortcut. As both uncertainties shrink at the same\n // rate, the cumulative formula's `(b_L·u_R + b_R·u_L)/k` term converges to the\n // arithmetic mean of the masses; deriving it from the same limit the interior\n // formula uses keeps mass-associativity intact (no special-case discontinuity).\n if (left.u <= VACUOUS_IDENTITY_EPSILON && right.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion((left.b + right.b) / 2, (left.d + right.d) / 2, 0, a);\n }\n\n const k = left.u + right.u - left.u * right.u;\n if (k === 0) {\n return vacuous(a);\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 a\n );\n}\n\nexport function averagingFusion(left: SLOpinion, right: SLOpinion): SLOpinion {\n const a = fusedBaseRate(left, right);\n\n // F2: vacuous identity, mirroring cumulativeFusion.\n if (1 - left.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(right.b, right.d, right.u, a);\n }\n if (1 - right.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(left.b, left.d, left.u, a);\n }\n\n // F4: dogmatic pair via the ε-limit of the averaging rule.\n if (left.u <= VACUOUS_IDENTITY_EPSILON && right.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion((left.b + right.b) / 2, (left.d + right.d) / 2, 0, a);\n }\n\n const k = left.u + right.u;\n if (k === 0) {\n return vacuous(a);\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 a\n );\n}\n\nexport function trustDiscount(sourceOpinion: SLOpinion, trust: number): SLOpinion {\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: SLOpinion,\n ifFalse: SLOpinion,\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: SLOpinion,\n ifTrue: SLOpinion,\n ifFalse: SLOpinion,\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: SLOpinion,\n ifTrue: SLOpinion,\n ifFalse: SLOpinion,\n fallbackBaseRate?: number\n): SLOpinion {\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: SLOpinion,\n ifTrue: SLOpinion,\n ifFalse: SLOpinion,\n baseRateX: number\n): SLOpinion {\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: SLOpinion): SLOpinion {\n return mkOpinion(o.d, o.b, o.u, 1 - o.a);\n}\n\nexport function constraintFusion(\n left: SLOpinion,\n right: SLOpinion,\n mode: \"pressure\" | \"redistribute\" = \"pressure\"\n): { o1: SLOpinion; o2: SLOpinion } {\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: SLOpinion): 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: SLOpinion, right: SLOpinion): 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: SLOpinion): 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 { EdgeMetadata, Opinion, PropagationResult } from \"../../types\";\nimport {\n constraintFusion,\n cumulativeFusion,\n mkOpinion,\n negate,\n project,\n trustDiscount,\n} from \"../subjectiveLogic\";\n\nexport function applyNegativeSupport(\n source: Opinion,\n target: Opinion,\n weight: number,\n metadata: EdgeMetadata = {}\n): PropagationResult {\n const sourceOpinion = mkOpinion(source.b, source.d, source.u, source.a);\n const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);\n if (metadata.constraint === \"xor\") {\n const result = constraintFusion(\n sourceOpinion,\n targetOpinion,\n metadata.normalization ?? \"pressure\"\n );\n return {\n opinion: result.o2,\n operator: \"constraint_fusion\",\n rationale: `XOR constraint: source belief at ${project(\n sourceOpinion\n ).toFixed(2)} pressures target`,\n };\n }\n\n const discounted = trustDiscount(negate(sourceOpinion), Math.abs(weight));\n return {\n opinion: cumulativeFusion(targetOpinion, discounted),\n operator: \"cumulative_fusion\",\n rationale: `Contradicting evidence (weight=${weight.toFixed(\n 2\n )}) from source at ${project(sourceOpinion).toFixed(2)}`,\n };\n}\n\nexport function applyNegativeEvidence(\n source: Opinion,\n target: Opinion,\n weight: number\n): PropagationResult {\n const sourceOpinion = mkOpinion(source.b, source.d, source.u, source.a);\n const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);\n const discounted = trustDiscount(negate(sourceOpinion), Math.abs(weight));\n return {\n opinion: cumulativeFusion(targetOpinion, discounted),\n operator: \"cumulative_fusion\",\n rationale: `Contradicting evidence (weight=${weight.toFixed(2)})`,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/v1/operations/subjectiveLogic/index.ts","../../../../src/v1/operations/dynamics/defeat.ts"],"names":[],"mappings":"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","file":"defeat.js","sourcesContent":["import type { SLOpinion } from \"../../types\";\n\nexport function opinion(\n belief: number,\n disbelief: number,\n uncertainty: number,\n baseRate: number\n): SLOpinion {\n const b = Number.isFinite(belief) ? Math.max(0, belief) : 0;\n const d = Number.isFinite(disbelief) ? Math.max(0, disbelief) : 0;\n const u = Number.isFinite(uncertainty) ? Math.max(0, uncertainty) : 0;\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 } as SLOpinion;\n }\n\n return {\n b: b / sum,\n d: d / sum,\n u: u / sum,\n a,\n } as SLOpinion;\n}\n\nexport function mkOpinion(\n belief: number,\n disbelief: number,\n uncertainty: number,\n baseRate: number\n): SLOpinion {\n return opinion(belief, disbelief, uncertainty, baseRate);\n}\n\nexport function vacuous(baseRate: number): SLOpinion {\n return mkOpinion(0, 0, 1, baseRate);\n}\n\nexport function dogmatic(probability: number, baseRate: number): SLOpinion {\n const p = Math.max(0, Math.min(1, probability));\n return mkOpinion(p, 1 - p, 0, baseRate);\n}\n\nexport function project(o: SLOpinion): number {\n return o.b + o.a * o.u;\n}\n\nexport function confidenceLevel(o: SLOpinion): \"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\nconst VACUOUS_IDENTITY_EPSILON = 1e-12;\n\n/**\n * Associative, vacuous-identity-preserving base rate for a fused opinion.\n *\n * The legacy `(left.a + right.a) / 2` arithmetic mean was the shared root of\n * RR.7 F1 (projected-confidence non-associativity) and F2 (a vacuous opinion of\n * a different base rate was not a fusion identity): averaging always pulled the\n * fused base rate toward the other operand even when that operand carried no\n * evidence. Subjective Logic requires every opinion over the same frame to share\n * one canonical base rate, so a correct fusion never *invents* a new base rate\n * from the operands' priors. We therefore:\n *\n * - return the informative operand's base rate when exactly one operand is\n * vacuous (the vacuous operand contributes no prior), making `vacuous(a)` a\n * true identity for any `a`; and\n * - require equal base rates otherwise (the canonical-frame contract). When\n * they coincide the result is that shared value — trivially associative.\n *\n * If two informative opinions carry genuinely different base rates they are not\n * opinions over the same frame and must be reconciled before fusion; we fall\n * back to the arithmetic mean ONLY in that ill-posed case to stay total, and the\n * property suite documents that this path is outside the order-independence\n * guarantee.\n */\nfunction fusedBaseRate(left: SLOpinion, right: SLOpinion): number {\n const leftVacuous = 1 - left.u <= VACUOUS_IDENTITY_EPSILON;\n const rightVacuous = 1 - right.u <= VACUOUS_IDENTITY_EPSILON;\n if (leftVacuous && !rightVacuous) {\n return right.a;\n }\n if (rightVacuous && !leftVacuous) {\n return left.a;\n }\n if (Math.abs(left.a - right.a) <= VACUOUS_IDENTITY_EPSILON) {\n return left.a;\n }\n return (left.a + right.a) / 2;\n}\n\nexport function cumulativeFusion(left: SLOpinion, right: SLOpinion): SLOpinion {\n const a = fusedBaseRate(left, right);\n\n // F2: a vacuous operand is the identity element of cumulative fusion. Return\n // the other operand verbatim (including its base rate) so fusion with the\n // vacuous opinion is a true no-op regardless of differing priors.\n if (1 - left.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(right.b, right.d, right.u, a);\n }\n if (1 - right.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(left.b, left.d, left.u, a);\n }\n\n // F4: dogmatic pair handled via the ε-limit of the cumulative rule rather than\n // the standalone `(b+d)/2` shortcut. As both uncertainties shrink at the same\n // rate, the cumulative formula's `(b_L·u_R + b_R·u_L)/k` term converges to the\n // arithmetic mean of the masses; deriving it from the same limit the interior\n // formula uses keeps mass-associativity intact (no special-case discontinuity).\n if (\n left.u <= VACUOUS_IDENTITY_EPSILON &&\n right.u <= VACUOUS_IDENTITY_EPSILON\n ) {\n return opinion((left.b + right.b) / 2, (left.d + right.d) / 2, 0, a);\n }\n\n const k = left.u + right.u - left.u * right.u;\n if (k === 0) {\n return vacuous(a);\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 a\n );\n}\n\nexport function averagingFusion(left: SLOpinion, right: SLOpinion): SLOpinion {\n const a = fusedBaseRate(left, right);\n\n // F2: vacuous identity, mirroring cumulativeFusion.\n if (1 - left.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(right.b, right.d, right.u, a);\n }\n if (1 - right.u <= VACUOUS_IDENTITY_EPSILON) {\n return opinion(left.b, left.d, left.u, a);\n }\n\n // F4: dogmatic pair via the ε-limit of the averaging rule.\n if (\n left.u <= VACUOUS_IDENTITY_EPSILON &&\n right.u <= VACUOUS_IDENTITY_EPSILON\n ) {\n return opinion((left.b + right.b) / 2, (left.d + right.d) / 2, 0, a);\n }\n\n const k = left.u + right.u;\n if (k === 0) {\n return vacuous(a);\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 a\n );\n}\n\nexport function trustDiscount(\n sourceOpinion: SLOpinion,\n trust: number\n): SLOpinion {\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: SLOpinion,\n ifFalse: SLOpinion,\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: SLOpinion,\n ifTrue: SLOpinion,\n ifFalse: SLOpinion,\n fallbackBaseRate: number\n): number {\n const denominator = 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;\n }\n\n const value = numerator / denominator;\n if (!Number.isFinite(value)) {\n return;\n }\n\n return Math.max(0, value);\n}\n\ninterface ConditionalDeductionCorrectionContext {\n childBaseRate: number;\n ifFalse: SLOpinion;\n ifTrue: SLOpinion;\n intermediateBelief: number;\n intermediateDisbelief: number;\n opinionA: SLOpinion;\n projectedAntecedent: number;\n projectedAntecedentComplement: number;\n projectedConditionalA: number;\n projectedVacuousDeduction: number;\n}\n\nfunction correctionOrZero(numerator: number, denominator: number): number {\n return safeCorrectionTerm(numerator, denominator) ?? 0;\n}\n\nfunction hasNoConditionalDeductionCorrection(\n ifTrue: SLOpinion,\n ifFalse: SLOpinion\n): boolean {\n return (\n (ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d) ||\n (ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d)\n );\n}\n\nfunction usesLowerVacuousBranch(\n context: ConditionalDeductionCorrectionContext\n): boolean {\n return context.projectedVacuousDeduction <= context.projectedConditionalA;\n}\n\nfunction usesLowerAntecedentBranch(\n context: ConditionalDeductionCorrectionContext\n): boolean {\n return context.projectedAntecedent <= context.opinionA.a;\n}\n\nfunction computeTrueDominantDeductionCorrection(\n context: ConditionalDeductionCorrectionContext\n): number {\n const beliefGap = context.ifTrue.b - context.ifFalse.b;\n const disbeliefGap = context.ifFalse.d - context.ifTrue.d;\n const lowerVacuous = usesLowerVacuousBranch(context);\n const lowerAntecedent = usesLowerAntecedentBranch(context);\n\n if (lowerVacuous && lowerAntecedent) {\n return correctionOrZero(\n context.opinionA.a *\n context.opinionA.u *\n (context.intermediateBelief - context.ifTrue.b),\n context.projectedAntecedent * context.childBaseRate\n );\n }\n\n if (lowerVacuous) {\n return correctionOrZero(\n context.opinionA.a *\n context.opinionA.u *\n (context.intermediateDisbelief - context.ifTrue.d) *\n beliefGap,\n context.projectedAntecedentComplement *\n context.childBaseRate *\n disbeliefGap\n );\n }\n\n if (lowerAntecedent) {\n return correctionOrZero(\n (1 - context.opinionA.a) *\n context.opinionA.u *\n (context.intermediateBelief - context.ifTrue.b) *\n disbeliefGap,\n context.projectedAntecedent * (1 - context.childBaseRate) * beliefGap\n );\n }\n\n return correctionOrZero(\n (1 - context.opinionA.a) *\n context.opinionA.u *\n (context.intermediateDisbelief - context.ifTrue.d),\n context.projectedAntecedentComplement * (1 - context.childBaseRate)\n );\n}\n\nfunction computeFalseDominantDeductionCorrection(\n context: ConditionalDeductionCorrectionContext\n): number {\n const beliefGap = context.ifFalse.b - context.ifTrue.b;\n const disbeliefGap = context.ifTrue.d - context.ifFalse.d;\n const lowerVacuous = usesLowerVacuousBranch(context);\n const lowerAntecedent = usesLowerAntecedentBranch(context);\n\n if (lowerVacuous && lowerAntecedent) {\n return correctionOrZero(\n (1 - context.opinionA.a) *\n context.opinionA.u *\n (context.intermediateDisbelief - context.ifTrue.d) *\n beliefGap,\n context.projectedAntecedent * context.childBaseRate * disbeliefGap\n );\n }\n\n if (lowerVacuous) {\n return correctionOrZero(\n (1 - context.opinionA.a) *\n context.opinionA.u *\n (context.intermediateBelief - context.ifTrue.b),\n context.projectedAntecedentComplement * context.childBaseRate\n );\n }\n\n if (lowerAntecedent) {\n return correctionOrZero(\n context.opinionA.a *\n context.opinionA.u *\n (context.intermediateDisbelief - context.ifTrue.d),\n context.projectedAntecedent * (1 - context.childBaseRate)\n );\n }\n\n return correctionOrZero(\n context.opinionA.a *\n context.opinionA.u *\n (context.intermediateBelief - context.ifTrue.b) *\n disbeliefGap,\n context.projectedAntecedentComplement *\n (1 - context.childBaseRate) *\n beliefGap\n );\n}\n\nfunction computeConditionalDeductionCorrection(\n context: ConditionalDeductionCorrectionContext\n): number {\n if (hasNoConditionalDeductionCorrection(context.ifTrue, context.ifFalse)) {\n return 0;\n }\n\n if (\n context.ifTrue.b > context.ifFalse.b &&\n context.ifTrue.d <= context.ifFalse.d\n ) {\n return computeTrueDominantDeductionCorrection(context);\n }\n\n return computeFalseDominantDeductionCorrection(context);\n}\n\nexport function conditionalDeduction(\n opinionA: SLOpinion,\n ifTrue: SLOpinion,\n ifFalse: SLOpinion,\n fallbackBaseRate?: number\n): SLOpinion {\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 * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));\n const projectedConditionalA =\n ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);\n const correction = computeConditionalDeductionCorrection({\n childBaseRate,\n ifFalse,\n ifTrue,\n intermediateBelief,\n intermediateDisbelief,\n opinionA,\n projectedAntecedent,\n projectedAntecedentComplement,\n projectedConditionalA,\n projectedVacuousDeduction,\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: SLOpinion,\n ifTrue: SLOpinion,\n ifFalse: SLOpinion,\n baseRateX: number\n): SLOpinion {\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 + 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: SLOpinion): SLOpinion {\n return mkOpinion(o.d, o.b, o.u, 1 - o.a);\n}\n\nexport function constraintFusion(\n left: SLOpinion,\n right: SLOpinion,\n mode: \"pressure\" | \"redistribute\" = \"pressure\"\n): { o1: SLOpinion; o2: SLOpinion } {\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: SLOpinion): 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: SLOpinion, right: SLOpinion): 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: SLOpinion): 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 { EdgeMetadata, Opinion, PropagationResult } from \"../../types\";\nimport {\n constraintFusion,\n cumulativeFusion,\n mkOpinion,\n negate,\n project,\n trustDiscount,\n} from \"../subjectiveLogic\";\n\nexport function applyNegativeSupport(\n source: Opinion,\n target: Opinion,\n weight: number,\n metadata: EdgeMetadata = {}\n): PropagationResult {\n const sourceOpinion = mkOpinion(source.b, source.d, source.u, source.a);\n const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);\n if (metadata.constraint === \"xor\") {\n const result = constraintFusion(\n sourceOpinion,\n targetOpinion,\n metadata.normalization ?? \"pressure\"\n );\n return {\n opinion: result.o2,\n operator: \"constraint_fusion\",\n rationale: `XOR constraint: source belief at ${project(\n sourceOpinion\n ).toFixed(2)} pressures target`,\n };\n }\n\n const discounted = trustDiscount(negate(sourceOpinion), Math.abs(weight));\n return {\n opinion: cumulativeFusion(targetOpinion, discounted),\n operator: \"cumulative_fusion\",\n rationale: `Contradicting evidence (weight=${weight.toFixed(\n 2\n )}) from source at ${project(sourceOpinion).toFixed(2)}`,\n };\n}\n\nexport function applyNegativeEvidence(\n source: Opinion,\n target: Opinion,\n weight: number\n): PropagationResult {\n const sourceOpinion = mkOpinion(source.b, source.d, source.u, source.a);\n const targetOpinion = mkOpinion(target.b, target.d, target.u, target.a);\n const discounted = trustDiscount(negate(sourceOpinion), Math.abs(weight));\n return {\n opinion: cumulativeFusion(targetOpinion, discounted),\n operator: \"cumulative_fusion\",\n rationale: `Contradicting evidence (weight=${weight.toFixed(2)})`,\n };\n}\n"]}
|
|
@@ -40,10 +40,10 @@ declare const EDGE_PROPAGATION_RULES: {
|
|
|
40
40
|
};
|
|
41
41
|
};
|
|
42
42
|
type PropagationEdgeType = keyof typeof EDGE_PROPAGATION_RULES;
|
|
43
|
-
|
|
44
|
-
edgeType: PropagationEdgeType;
|
|
43
|
+
interface PropagationTraversalSpec {
|
|
45
44
|
direction: PropagationTraversalDirection;
|
|
46
|
-
|
|
45
|
+
edgeType: PropagationEdgeType;
|
|
46
|
+
}
|
|
47
47
|
declare const PROPAGATION_TRAVERSAL_SPECS: readonly PropagationTraversalSpec[];
|
|
48
48
|
declare function isPropagationEdgeType(edgeType: string): edgeType is PropagationEdgeType;
|
|
49
49
|
declare function getPropagationTraversalSpecs(): readonly PropagationTraversalSpec[];
|