@lucern/confidence 1.0.28 → 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.
Files changed (49) hide show
  1. package/dist/index.d.ts +7 -7
  2. package/dist/index.js +905 -770
  3. package/dist/index.js.map +1 -1
  4. package/dist/proof-attestation.json +1 -1
  5. package/dist/v1/codec.js.map +1 -1
  6. package/dist/v1/index.d.ts +7 -7
  7. package/dist/v1/index.js +905 -770
  8. package/dist/v1/index.js.map +1 -1
  9. package/dist/v1/interfaces.d.ts +5 -5
  10. package/dist/v1/operations/approximation.d.ts +1 -1
  11. package/dist/v1/operations/approximation.js +8 -7
  12. package/dist/v1/operations/approximation.js.map +1 -1
  13. package/dist/v1/operations/bridge/index.js +7 -6
  14. package/dist/v1/operations/bridge/index.js.map +1 -1
  15. package/dist/v1/operations/canonical.d.ts +1 -1
  16. package/dist/v1/operations/canonical.js +106 -57
  17. package/dist/v1/operations/canonical.js.map +1 -1
  18. package/dist/v1/operations/contracts/epistemicContract.d.ts +1 -1
  19. package/dist/v1/operations/contracts/epistemicContract.js +9 -3
  20. package/dist/v1/operations/contracts/epistemicContract.js.map +1 -1
  21. package/dist/v1/operations/contradiction/detectTupleContradiction.js.map +1 -1
  22. package/dist/v1/operations/contradiction/index.js.map +1 -1
  23. package/dist/v1/operations/dynamics/cascade.js +1 -1
  24. package/dist/v1/operations/dynamics/cascade.js.map +1 -1
  25. package/dist/v1/operations/dynamics/decay.js +105 -41
  26. package/dist/v1/operations/dynamics/decay.js.map +1 -1
  27. package/dist/v1/operations/dynamics/defeat.js.map +1 -1
  28. package/dist/v1/operations/dynamics/propagation.d.ts +3 -3
  29. package/dist/v1/operations/dynamics/propagation.js +101 -57
  30. package/dist/v1/operations/dynamics/propagation.js.map +1 -1
  31. package/dist/v1/operations/dynamics/revision.js +7 -6
  32. package/dist/v1/operations/dynamics/revision.js.map +1 -1
  33. package/dist/v1/operations/index.js +8 -3
  34. package/dist/v1/operations/index.js.map +1 -1
  35. package/dist/v1/operations/lucern.d.ts +7 -7
  36. package/dist/v1/operations/lucern.js +934 -807
  37. package/dist/v1/operations/lucern.js.map +1 -1
  38. package/dist/v1/operations/operatorTaxonomy.d.ts +10 -10
  39. package/dist/v1/operations/operatorTaxonomy.js.map +1 -1
  40. package/dist/v1/operations/scoring.d.ts +2 -1
  41. package/dist/v1/operations/scoring.js +20 -5
  42. package/dist/v1/operations/scoring.js.map +1 -1
  43. package/dist/v1/operations/subjectiveLogic/index.js +91 -54
  44. package/dist/v1/operations/subjectiveLogic/index.js.map +1 -1
  45. package/dist/v1/operations/temporalDecay.d.ts +3 -3
  46. package/dist/v1/operations/temporalDecay.js +8 -3
  47. package/dist/v1/operations/temporalDecay.js.map +1 -1
  48. package/dist/v1/types.d.ts +101 -101
  49. package/package.json +1 -1
@@ -1,23 +1,23 @@
1
- import { Opinion, EdgeMetadata, PropagationResult, EpistemicContractEvaluationResult, EpistemicContractStatus, EpistemicContractModulation, ContractModulationPlan, EpistemicContractRecord } from './types.js';
1
+ import { Opinion, EdgeMetadata, PropagationResult, EpistemicContractModulation, EpistemicContractEvaluationResult, ContractModulationPlan, EpistemicContractStatus, EpistemicContractRecord } from './types.js';
2
2
 
3
3
  interface ConfidenceEngine {
4
- scoreOpinion(opinion: Opinion): number;
5
4
  propagateEdge(source: Opinion, target: Opinion, edgeType: string, weight?: number, metadata?: EdgeMetadata): PropagationResult;
5
+ scoreOpinion(opinion: Opinion): number;
6
6
  }
7
7
  interface SubjectiveLogicOperatorPort {
8
- project(opinion: Opinion): number;
9
8
  cumulativeFusion(left: Opinion, right: Opinion): Opinion;
9
+ project(opinion: Opinion): number;
10
10
  trustDiscount(source: Opinion, trust: number): Opinion;
11
11
  }
12
12
  interface ContractEvaluatorPort {
13
- deriveStatus(result: EpistemicContractEvaluationResult, currentStatus: EpistemicContractStatus): EpistemicContractStatus;
14
13
  deriveModulationPlan(args: {
15
14
  currentConfidence: number;
16
15
  modulation: EpistemicContractModulation;
17
16
  result: EpistemicContractEvaluationResult;
18
17
  resultConfidence?: number;
19
18
  }): ContractModulationPlan | null;
20
- inheritContract<TBeliefId = string, TTopicId = string | undefined>(contract: EpistemicContractRecord<any, any>, args: {
19
+ deriveStatus(result: EpistemicContractEvaluationResult, currentStatus: EpistemicContractStatus): EpistemicContractStatus;
20
+ inheritContract<TBeliefId = string, TTopicId = string | undefined>(contract: EpistemicContractRecord<unknown, unknown>, args: {
21
21
  beliefNodeId: TBeliefId;
22
22
  topicId?: TTopicId;
23
23
  createdBy: string;
@@ -1,6 +1,6 @@
1
- export { averagingFusion, conditionalAbduction, cumulativeFusion } from './subjectiveLogic/index.js';
2
1
  export { betaFromOpinion, opinionFromBeta, opinionFromDirichlet, projectDirichletOpinion } from './bridge/index.js';
3
2
  export { dampedDependencyCascade, dampedDependencyOpinion } from './dynamics/cascade.js';
3
+ export { averagingFusion, conditionalAbduction, cumulativeFusion } from './subjectiveLogic/index.js';
4
4
  import '../types.js';
5
5
 
6
6
  declare const SL_APPROXIMATION_OPERATOR_NAMES: readonly ["cumulativeFusion", "averagingFusion", "conditionalAbduction", "opinionFromDirichlet", "projectDirichletOpinion", "opinionFromBeta", "betaFromOpinion", "dampedDependencyOpinion", "dampedDependencyCascade"];
@@ -131,7 +131,9 @@ function normalizeBaseRateVector(baseRate, size) {
131
131
  const normalized = baseRate.map((value) => clampNonNegative(value));
132
132
  const total = normalized.reduce((sum, value) => sum + value, 0);
133
133
  if (total === 0) {
134
- throw new Error("Base-rate vector must contain at least one positive value.");
134
+ throw new Error(
135
+ "Base-rate vector must contain at least one positive value."
136
+ );
135
137
  }
136
138
  return normalized.map((value) => value / total);
137
139
  }
@@ -160,11 +162,10 @@ function projectDirichletOpinion(opinion2) {
160
162
  );
161
163
  }
162
164
  function opinionFromBeta(alpha, beta, nonInformativeWeight, baseRate) {
163
- const dirichlet = opinionFromDirichlet(
164
- [alpha, beta],
165
- nonInformativeWeight,
166
- [clamp01(baseRate), 1 - clamp01(baseRate)]
167
- );
165
+ const dirichlet = opinionFromDirichlet([alpha, beta], nonInformativeWeight, [
166
+ clamp01(baseRate),
167
+ 1 - clamp01(baseRate)
168
+ ]);
168
169
  return mkOpinion(
169
170
  dirichlet.b[0] ?? 0,
170
171
  dirichlet.b[1] ?? 0,
@@ -208,7 +209,7 @@ function dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode = "conti
208
209
  }
209
210
  return beliefOpinion;
210
211
  }
211
- const dampingFactor = Math.pow(dependencyProjection, 0.5);
212
+ const dampingFactor = dependencyProjection ** 0.5;
212
213
  return opinion(
213
214
  beliefOpinion.b * dampingFactor,
214
215
  beliefOpinion.d + beliefOpinion.b * (1 - dampingFactor) * 0.3,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/v1/operations/subjectiveLogic/index.ts","../../../src/v1/operations/bridge/index.ts","../../../src/v1/operations/dynamics/cascade.ts","../../../src/v1/operations/approximation.ts"],"names":["opinion"],"mappings":"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","file":"approximation.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 { DirichletOpinion, Opinion, SLOpinion } from \"../../types\";\nimport { mkOpinion } from \"../subjectiveLogic\";\n\nexport const DEFAULT_NON_INFORMATIVE_WEIGHT = 2;\n\nfunction clamp01(value: number): number {\n return Math.max(0, Math.min(1, value));\n}\n\nfunction clampNonNegative(value: number): number {\n return Number.isFinite(value) ? Math.max(0, value) : 0;\n}\n\nfunction normalizeNonInformativeWeight(weight?: number): number {\n if (weight === undefined) {\n return DEFAULT_NON_INFORMATIVE_WEIGHT;\n }\n return Number.isFinite(weight)\n ? Math.max(0, weight)\n : DEFAULT_NON_INFORMATIVE_WEIGHT;\n}\n\nfunction normalizeBaseRateVector(\n baseRate: readonly number[],\n size: number\n): number[] {\n if (size === 0) {\n return [];\n }\n\n if (baseRate.length !== size) {\n throw new Error(\n `Base-rate vector length ${baseRate.length} must match evidence vector length ${size}.`\n );\n }\n\n const normalized = baseRate.map((value) => clampNonNegative(value));\n const total = normalized.reduce((sum, value) => sum + value, 0);\n\n if (total === 0) {\n throw new Error(\"Base-rate vector must contain at least one positive value.\");\n }\n\n return normalized.map((value) => value / total);\n}\n\nexport function opinionFromDirichlet(\n alpha: readonly number[],\n nonInformativeWeight: number,\n baseRate: readonly number[]\n): DirichletOpinion {\n const evidence = alpha.map((value) => clampNonNegative(value));\n const safeWeight = normalizeNonInformativeWeight(nonInformativeWeight);\n const normalizedBaseRate = normalizeBaseRateVector(baseRate, evidence.length);\n const totalEvidence = evidence.reduce((sum, value) => sum + value, 0);\n const denominator = totalEvidence + safeWeight;\n\n if (denominator === 0) {\n return {\n b: evidence.map(() => 0),\n u: 1,\n a: normalizedBaseRate,\n };\n }\n\n return {\n b: evidence.map((value) => value / denominator),\n u: safeWeight / denominator,\n a: normalizedBaseRate,\n };\n}\n\nexport function projectDirichletOpinion(opinion: DirichletOpinion): number[] {\n return opinion.b.map(\n (belief, index) => belief + (opinion.a[index] ?? 0) * opinion.u\n );\n}\n\nexport function opinionFromBeta(\n alpha: number,\n beta: number,\n nonInformativeWeight: number,\n baseRate: number\n): SLOpinion {\n const dirichlet = opinionFromDirichlet(\n [alpha, beta],\n nonInformativeWeight,\n [clamp01(baseRate), 1 - clamp01(baseRate)]\n );\n\n return mkOpinion(\n dirichlet.b[0] ?? 0,\n dirichlet.b[1] ?? 0,\n dirichlet.u,\n dirichlet.a[0] ?? clamp01(baseRate)\n );\n}\n\nexport function betaFromOpinion(\n opinion: Opinion,\n nonInformativeWeight: number = DEFAULT_NON_INFORMATIVE_WEIGHT\n): { alpha: number; beta: number } {\n if (opinion.u === 0) {\n return {\n alpha: Number.POSITIVE_INFINITY,\n beta: Number.POSITIVE_INFINITY,\n };\n }\n\n const safeWeight = normalizeNonInformativeWeight(nonInformativeWeight);\n const scale = safeWeight / opinion.u;\n\n return {\n alpha: opinion.b * scale,\n beta: opinion.d * scale,\n };\n}\n","import type { Opinion, PropagationMode, PropagationResult } from \"../../types\";\nimport { mkOpinion, opinion, project } from \"../subjectiveLogic\";\n\nexport function dampedDependencyOpinion(\n dependencyOpinion: Opinion,\n beliefOpinion: Opinion,\n mode: PropagationMode = \"continuous\",\n threshold: number = 0.3\n): Opinion {\n const dependencyProjection = project(\n mkOpinion(\n dependencyOpinion.b,\n dependencyOpinion.d,\n dependencyOpinion.u,\n dependencyOpinion.a\n )\n );\n\n if (mode === \"threshold\") {\n if (dependencyProjection < threshold) {\n return opinion(\n 0,\n beliefOpinion.d + beliefOpinion.b * 0.5,\n 0.5,\n beliefOpinion.a\n );\n }\n return beliefOpinion;\n }\n\n const dampingFactor = Math.pow(dependencyProjection, 0.5);\n return opinion(\n beliefOpinion.b * dampingFactor,\n beliefOpinion.d + beliefOpinion.b * (1 - dampingFactor) * 0.3,\n beliefOpinion.u + beliefOpinion.b * (1 - dampingFactor) * 0.7,\n beliefOpinion.a\n );\n}\n\nexport function dampedDependencyCascade(\n dependencyOpinion: Opinion,\n beliefOpinion: Opinion,\n mode: PropagationMode = \"continuous\"\n): PropagationResult {\n return {\n opinion: dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode),\n operator: \"dependency_cascade\",\n rationale: `Damped dependency cascade (${mode}): prerequisite at ${project(\n mkOpinion(\n dependencyOpinion.b,\n dependencyOpinion.d,\n dependencyOpinion.u,\n dependencyOpinion.a\n )\n ).toFixed(2)}`,\n };\n}\n","export const SL_APPROXIMATION_OPERATOR_NAMES = [\n \"cumulativeFusion\",\n \"averagingFusion\",\n \"conditionalAbduction\",\n \"opinionFromDirichlet\",\n \"projectDirichletOpinion\",\n \"opinionFromBeta\",\n \"betaFromOpinion\",\n \"dampedDependencyOpinion\",\n \"dampedDependencyCascade\",\n] as const;\n\nexport {\n cumulativeFusion,\n averagingFusion,\n conditionalAbduction,\n} from \"./subjectiveLogic\";\nexport {\n opinionFromDirichlet,\n projectDirichletOpinion,\n opinionFromBeta,\n betaFromOpinion,\n} from \"./bridge\";\nexport {\n dampedDependencyOpinion,\n dampedDependencyCascade,\n} from \"./dynamics/cascade\";\n"]}
1
+ {"version":3,"sources":["../../../src/v1/operations/subjectiveLogic/index.ts","../../../src/v1/operations/bridge/index.ts","../../../src/v1/operations/dynamics/cascade.ts","../../../src/v1/operations/approximation.ts"],"names":["opinion"],"mappings":"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","file":"approximation.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 { DirichletOpinion, Opinion, SLOpinion } from \"../../types\";\nimport { mkOpinion } from \"../subjectiveLogic\";\n\nexport const DEFAULT_NON_INFORMATIVE_WEIGHT = 2;\n\nfunction clamp01(value: number): number {\n return Math.max(0, Math.min(1, value));\n}\n\nfunction clampNonNegative(value: number): number {\n return Number.isFinite(value) ? Math.max(0, value) : 0;\n}\n\nfunction normalizeNonInformativeWeight(weight?: number): number {\n if (weight === undefined) {\n return DEFAULT_NON_INFORMATIVE_WEIGHT;\n }\n return Number.isFinite(weight)\n ? Math.max(0, weight)\n : DEFAULT_NON_INFORMATIVE_WEIGHT;\n}\n\nfunction normalizeBaseRateVector(\n baseRate: readonly number[],\n size: number\n): number[] {\n if (size === 0) {\n return [];\n }\n\n if (baseRate.length !== size) {\n throw new Error(\n `Base-rate vector length ${baseRate.length} must match evidence vector length ${size}.`\n );\n }\n\n const normalized = baseRate.map((value) => clampNonNegative(value));\n const total = normalized.reduce((sum, value) => sum + value, 0);\n\n if (total === 0) {\n throw new Error(\n \"Base-rate vector must contain at least one positive value.\"\n );\n }\n\n return normalized.map((value) => value / total);\n}\n\nexport function opinionFromDirichlet(\n alpha: readonly number[],\n nonInformativeWeight: number,\n baseRate: readonly number[]\n): DirichletOpinion {\n const evidence = alpha.map((value) => clampNonNegative(value));\n const safeWeight = normalizeNonInformativeWeight(nonInformativeWeight);\n const normalizedBaseRate = normalizeBaseRateVector(baseRate, evidence.length);\n const totalEvidence = evidence.reduce((sum, value) => sum + value, 0);\n const denominator = totalEvidence + safeWeight;\n\n if (denominator === 0) {\n return {\n b: evidence.map(() => 0),\n u: 1,\n a: normalizedBaseRate,\n };\n }\n\n return {\n b: evidence.map((value) => value / denominator),\n u: safeWeight / denominator,\n a: normalizedBaseRate,\n };\n}\n\nexport function projectDirichletOpinion(opinion: DirichletOpinion): number[] {\n return opinion.b.map(\n (belief, index) => belief + (opinion.a[index] ?? 0) * opinion.u\n );\n}\n\nexport function opinionFromBeta(\n alpha: number,\n beta: number,\n nonInformativeWeight: number,\n baseRate: number\n): SLOpinion {\n const dirichlet = opinionFromDirichlet([alpha, beta], nonInformativeWeight, [\n clamp01(baseRate),\n 1 - clamp01(baseRate),\n ]);\n\n return mkOpinion(\n dirichlet.b[0] ?? 0,\n dirichlet.b[1] ?? 0,\n dirichlet.u,\n dirichlet.a[0] ?? clamp01(baseRate)\n );\n}\n\nexport function betaFromOpinion(\n opinion: Opinion,\n nonInformativeWeight: number = DEFAULT_NON_INFORMATIVE_WEIGHT\n): { alpha: number; beta: number } {\n if (opinion.u === 0) {\n return {\n alpha: Number.POSITIVE_INFINITY,\n beta: Number.POSITIVE_INFINITY,\n };\n }\n\n const safeWeight = normalizeNonInformativeWeight(nonInformativeWeight);\n const scale = safeWeight / opinion.u;\n\n return {\n alpha: opinion.b * scale,\n beta: opinion.d * scale,\n };\n}\n","import type { Opinion, PropagationMode, PropagationResult } from \"../../types\";\nimport { mkOpinion, opinion, project } from \"../subjectiveLogic\";\n\nexport function dampedDependencyOpinion(\n dependencyOpinion: Opinion,\n beliefOpinion: Opinion,\n mode: PropagationMode = \"continuous\",\n threshold = 0.3\n): Opinion {\n const dependencyProjection = project(\n mkOpinion(\n dependencyOpinion.b,\n dependencyOpinion.d,\n dependencyOpinion.u,\n dependencyOpinion.a\n )\n );\n\n if (mode === \"threshold\") {\n if (dependencyProjection < threshold) {\n return opinion(\n 0,\n beliefOpinion.d + beliefOpinion.b * 0.5,\n 0.5,\n beliefOpinion.a\n );\n }\n return beliefOpinion;\n }\n\n const dampingFactor = dependencyProjection ** 0.5;\n return opinion(\n beliefOpinion.b * dampingFactor,\n beliefOpinion.d + beliefOpinion.b * (1 - dampingFactor) * 0.3,\n beliefOpinion.u + beliefOpinion.b * (1 - dampingFactor) * 0.7,\n beliefOpinion.a\n );\n}\n\nexport function dampedDependencyCascade(\n dependencyOpinion: Opinion,\n beliefOpinion: Opinion,\n mode: PropagationMode = \"continuous\"\n): PropagationResult {\n return {\n opinion: dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode),\n operator: \"dependency_cascade\",\n rationale: `Damped dependency cascade (${mode}): prerequisite at ${project(\n mkOpinion(\n dependencyOpinion.b,\n dependencyOpinion.d,\n dependencyOpinion.u,\n dependencyOpinion.a\n )\n ).toFixed(2)}`,\n };\n}\n","// biome-ignore-all lint/performance/noBarrelFile: Public confidence approximation facade; splitting requires an export-map migration.\n\nexport const SL_APPROXIMATION_OPERATOR_NAMES = [\n \"cumulativeFusion\",\n \"averagingFusion\",\n \"conditionalAbduction\",\n \"opinionFromDirichlet\",\n \"projectDirichletOpinion\",\n \"opinionFromBeta\",\n \"betaFromOpinion\",\n \"dampedDependencyOpinion\",\n \"dampedDependencyCascade\",\n] as const;\n\nexport {\n betaFromOpinion,\n opinionFromBeta,\n opinionFromDirichlet,\n projectDirichletOpinion,\n} from \"./bridge\";\nexport {\n dampedDependencyCascade,\n dampedDependencyOpinion,\n} from \"./dynamics/cascade\";\nexport {\n averagingFusion,\n conditionalAbduction,\n cumulativeFusion,\n} from \"./subjectiveLogic\";\n"]}
@@ -45,7 +45,9 @@ function normalizeBaseRateVector(baseRate, size) {
45
45
  const normalized = baseRate.map((value) => clampNonNegative(value));
46
46
  const total = normalized.reduce((sum, value) => sum + value, 0);
47
47
  if (total === 0) {
48
- throw new Error("Base-rate vector must contain at least one positive value.");
48
+ throw new Error(
49
+ "Base-rate vector must contain at least one positive value."
50
+ );
49
51
  }
50
52
  return normalized.map((value) => value / total);
51
53
  }
@@ -74,11 +76,10 @@ function projectDirichletOpinion(opinion2) {
74
76
  );
75
77
  }
76
78
  function opinionFromBeta(alpha, beta, nonInformativeWeight, baseRate) {
77
- const dirichlet = opinionFromDirichlet(
78
- [alpha, beta],
79
- nonInformativeWeight,
80
- [clamp01(baseRate), 1 - clamp01(baseRate)]
81
- );
79
+ const dirichlet = opinionFromDirichlet([alpha, beta], nonInformativeWeight, [
80
+ clamp01(baseRate),
81
+ 1 - clamp01(baseRate)
82
+ ]);
82
83
  return mkOpinion(
83
84
  dirichlet.b[0] ?? 0,
84
85
  dirichlet.b[1] ?? 0,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/v1/operations/subjectiveLogic/index.ts","../../../../src/v1/operations/bridge/index.ts"],"names":["opinion"],"mappings":"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","file":"index.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 { DirichletOpinion, Opinion, SLOpinion } from \"../../types\";\nimport { mkOpinion } from \"../subjectiveLogic\";\n\nexport const DEFAULT_NON_INFORMATIVE_WEIGHT = 2;\n\nfunction clamp01(value: number): number {\n return Math.max(0, Math.min(1, value));\n}\n\nfunction clampNonNegative(value: number): number {\n return Number.isFinite(value) ? Math.max(0, value) : 0;\n}\n\nfunction normalizeNonInformativeWeight(weight?: number): number {\n if (weight === undefined) {\n return DEFAULT_NON_INFORMATIVE_WEIGHT;\n }\n return Number.isFinite(weight)\n ? Math.max(0, weight)\n : DEFAULT_NON_INFORMATIVE_WEIGHT;\n}\n\nfunction normalizeBaseRateVector(\n baseRate: readonly number[],\n size: number\n): number[] {\n if (size === 0) {\n return [];\n }\n\n if (baseRate.length !== size) {\n throw new Error(\n `Base-rate vector length ${baseRate.length} must match evidence vector length ${size}.`\n );\n }\n\n const normalized = baseRate.map((value) => clampNonNegative(value));\n const total = normalized.reduce((sum, value) => sum + value, 0);\n\n if (total === 0) {\n throw new Error(\"Base-rate vector must contain at least one positive value.\");\n }\n\n return normalized.map((value) => value / total);\n}\n\nexport function opinionFromDirichlet(\n alpha: readonly number[],\n nonInformativeWeight: number,\n baseRate: readonly number[]\n): DirichletOpinion {\n const evidence = alpha.map((value) => clampNonNegative(value));\n const safeWeight = normalizeNonInformativeWeight(nonInformativeWeight);\n const normalizedBaseRate = normalizeBaseRateVector(baseRate, evidence.length);\n const totalEvidence = evidence.reduce((sum, value) => sum + value, 0);\n const denominator = totalEvidence + safeWeight;\n\n if (denominator === 0) {\n return {\n b: evidence.map(() => 0),\n u: 1,\n a: normalizedBaseRate,\n };\n }\n\n return {\n b: evidence.map((value) => value / denominator),\n u: safeWeight / denominator,\n a: normalizedBaseRate,\n };\n}\n\nexport function projectDirichletOpinion(opinion: DirichletOpinion): number[] {\n return opinion.b.map(\n (belief, index) => belief + (opinion.a[index] ?? 0) * opinion.u\n );\n}\n\nexport function opinionFromBeta(\n alpha: number,\n beta: number,\n nonInformativeWeight: number,\n baseRate: number\n): SLOpinion {\n const dirichlet = opinionFromDirichlet(\n [alpha, beta],\n nonInformativeWeight,\n [clamp01(baseRate), 1 - clamp01(baseRate)]\n );\n\n return mkOpinion(\n dirichlet.b[0] ?? 0,\n dirichlet.b[1] ?? 0,\n dirichlet.u,\n dirichlet.a[0] ?? clamp01(baseRate)\n );\n}\n\nexport function betaFromOpinion(\n opinion: Opinion,\n nonInformativeWeight: number = DEFAULT_NON_INFORMATIVE_WEIGHT\n): { alpha: number; beta: number } {\n if (opinion.u === 0) {\n return {\n alpha: Number.POSITIVE_INFINITY,\n beta: Number.POSITIVE_INFINITY,\n };\n }\n\n const safeWeight = normalizeNonInformativeWeight(nonInformativeWeight);\n const scale = safeWeight / opinion.u;\n\n return {\n alpha: opinion.b * scale,\n beta: opinion.d * scale,\n };\n}\n"]}
1
+ {"version":3,"sources":["../../../../src/v1/operations/subjectiveLogic/index.ts","../../../../src/v1/operations/bridge/index.ts"],"names":["opinion"],"mappings":"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","file":"index.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 { DirichletOpinion, Opinion, SLOpinion } from \"../../types\";\nimport { mkOpinion } from \"../subjectiveLogic\";\n\nexport const DEFAULT_NON_INFORMATIVE_WEIGHT = 2;\n\nfunction clamp01(value: number): number {\n return Math.max(0, Math.min(1, value));\n}\n\nfunction clampNonNegative(value: number): number {\n return Number.isFinite(value) ? Math.max(0, value) : 0;\n}\n\nfunction normalizeNonInformativeWeight(weight?: number): number {\n if (weight === undefined) {\n return DEFAULT_NON_INFORMATIVE_WEIGHT;\n }\n return Number.isFinite(weight)\n ? Math.max(0, weight)\n : DEFAULT_NON_INFORMATIVE_WEIGHT;\n}\n\nfunction normalizeBaseRateVector(\n baseRate: readonly number[],\n size: number\n): number[] {\n if (size === 0) {\n return [];\n }\n\n if (baseRate.length !== size) {\n throw new Error(\n `Base-rate vector length ${baseRate.length} must match evidence vector length ${size}.`\n );\n }\n\n const normalized = baseRate.map((value) => clampNonNegative(value));\n const total = normalized.reduce((sum, value) => sum + value, 0);\n\n if (total === 0) {\n throw new Error(\n \"Base-rate vector must contain at least one positive value.\"\n );\n }\n\n return normalized.map((value) => value / total);\n}\n\nexport function opinionFromDirichlet(\n alpha: readonly number[],\n nonInformativeWeight: number,\n baseRate: readonly number[]\n): DirichletOpinion {\n const evidence = alpha.map((value) => clampNonNegative(value));\n const safeWeight = normalizeNonInformativeWeight(nonInformativeWeight);\n const normalizedBaseRate = normalizeBaseRateVector(baseRate, evidence.length);\n const totalEvidence = evidence.reduce((sum, value) => sum + value, 0);\n const denominator = totalEvidence + safeWeight;\n\n if (denominator === 0) {\n return {\n b: evidence.map(() => 0),\n u: 1,\n a: normalizedBaseRate,\n };\n }\n\n return {\n b: evidence.map((value) => value / denominator),\n u: safeWeight / denominator,\n a: normalizedBaseRate,\n };\n}\n\nexport function projectDirichletOpinion(opinion: DirichletOpinion): number[] {\n return opinion.b.map(\n (belief, index) => belief + (opinion.a[index] ?? 0) * opinion.u\n );\n}\n\nexport function opinionFromBeta(\n alpha: number,\n beta: number,\n nonInformativeWeight: number,\n baseRate: number\n): SLOpinion {\n const dirichlet = opinionFromDirichlet([alpha, beta], nonInformativeWeight, [\n clamp01(baseRate),\n 1 - clamp01(baseRate),\n ]);\n\n return mkOpinion(\n dirichlet.b[0] ?? 0,\n dirichlet.b[1] ?? 0,\n dirichlet.u,\n dirichlet.a[0] ?? clamp01(baseRate)\n );\n}\n\nexport function betaFromOpinion(\n opinion: Opinion,\n nonInformativeWeight: number = DEFAULT_NON_INFORMATIVE_WEIGHT\n): { alpha: number; beta: number } {\n if (opinion.u === 0) {\n return {\n alpha: Number.POSITIVE_INFINITY,\n beta: Number.POSITIVE_INFINITY,\n };\n }\n\n const safeWeight = normalizeNonInformativeWeight(nonInformativeWeight);\n const scale = safeWeight / opinion.u;\n\n return {\n alpha: opinion.b * scale,\n beta: opinion.d * scale,\n };\n}\n"]}
@@ -1,5 +1,5 @@
1
- export { conditionalDeduction, dogmatic, mkOpinion, negate, opinion, project, trustDiscount, vacuous } from './subjectiveLogic/index.js';
2
1
  export { confidenceFromOpinion, confidenceFromSL, opinionFromBaseRate, opinionFromDogmatic, opinionFromProjected } from './scoring.js';
2
+ export { conditionalDeduction, dogmatic, mkOpinion, negate, opinion, project, trustDiscount, vacuous } from './subjectiveLogic/index.js';
3
3
  import '../types.js';
4
4
 
5
5
  declare const SL_CANONICAL_OPERATOR_NAMES: readonly ["mkOpinion", "opinion", "vacuous", "dogmatic", "project", "trustDiscount", "negate", "conditionalDeduction", "confidenceFromOpinion", "confidenceFromSL", "opinionFromBaseRate", "opinionFromDogmatic", "opinionFromProjected"];
@@ -59,14 +59,91 @@ function computeConditionalDeductionBaseRate(opinionA, ifTrue, ifFalse, fallback
59
59
  }
60
60
  function safeCorrectionTerm(numerator, denominator) {
61
61
  if (Math.abs(denominator) <= EPSILON) {
62
- return void 0;
62
+ return;
63
63
  }
64
64
  const value = numerator / denominator;
65
65
  if (!Number.isFinite(value)) {
66
- return void 0;
66
+ return;
67
67
  }
68
68
  return Math.max(0, value);
69
69
  }
70
+ function correctionOrZero(numerator, denominator) {
71
+ return safeCorrectionTerm(numerator, denominator) ?? 0;
72
+ }
73
+ function hasNoConditionalDeductionCorrection(ifTrue, ifFalse) {
74
+ return ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d || ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d;
75
+ }
76
+ function usesLowerVacuousBranch(context) {
77
+ return context.projectedVacuousDeduction <= context.projectedConditionalA;
78
+ }
79
+ function usesLowerAntecedentBranch(context) {
80
+ return context.projectedAntecedent <= context.opinionA.a;
81
+ }
82
+ function computeTrueDominantDeductionCorrection(context) {
83
+ const beliefGap = context.ifTrue.b - context.ifFalse.b;
84
+ const disbeliefGap = context.ifFalse.d - context.ifTrue.d;
85
+ const lowerVacuous = usesLowerVacuousBranch(context);
86
+ const lowerAntecedent = usesLowerAntecedentBranch(context);
87
+ if (lowerVacuous && lowerAntecedent) {
88
+ return correctionOrZero(
89
+ context.opinionA.a * context.opinionA.u * (context.intermediateBelief - context.ifTrue.b),
90
+ context.projectedAntecedent * context.childBaseRate
91
+ );
92
+ }
93
+ if (lowerVacuous) {
94
+ return correctionOrZero(
95
+ context.opinionA.a * context.opinionA.u * (context.intermediateDisbelief - context.ifTrue.d) * beliefGap,
96
+ context.projectedAntecedentComplement * context.childBaseRate * disbeliefGap
97
+ );
98
+ }
99
+ if (lowerAntecedent) {
100
+ return correctionOrZero(
101
+ (1 - context.opinionA.a) * context.opinionA.u * (context.intermediateBelief - context.ifTrue.b) * disbeliefGap,
102
+ context.projectedAntecedent * (1 - context.childBaseRate) * beliefGap
103
+ );
104
+ }
105
+ return correctionOrZero(
106
+ (1 - context.opinionA.a) * context.opinionA.u * (context.intermediateDisbelief - context.ifTrue.d),
107
+ context.projectedAntecedentComplement * (1 - context.childBaseRate)
108
+ );
109
+ }
110
+ function computeFalseDominantDeductionCorrection(context) {
111
+ const beliefGap = context.ifFalse.b - context.ifTrue.b;
112
+ const disbeliefGap = context.ifTrue.d - context.ifFalse.d;
113
+ const lowerVacuous = usesLowerVacuousBranch(context);
114
+ const lowerAntecedent = usesLowerAntecedentBranch(context);
115
+ if (lowerVacuous && lowerAntecedent) {
116
+ return correctionOrZero(
117
+ (1 - context.opinionA.a) * context.opinionA.u * (context.intermediateDisbelief - context.ifTrue.d) * beliefGap,
118
+ context.projectedAntecedent * context.childBaseRate * disbeliefGap
119
+ );
120
+ }
121
+ if (lowerVacuous) {
122
+ return correctionOrZero(
123
+ (1 - context.opinionA.a) * context.opinionA.u * (context.intermediateBelief - context.ifTrue.b),
124
+ context.projectedAntecedentComplement * context.childBaseRate
125
+ );
126
+ }
127
+ if (lowerAntecedent) {
128
+ return correctionOrZero(
129
+ context.opinionA.a * context.opinionA.u * (context.intermediateDisbelief - context.ifTrue.d),
130
+ context.projectedAntecedent * (1 - context.childBaseRate)
131
+ );
132
+ }
133
+ return correctionOrZero(
134
+ context.opinionA.a * context.opinionA.u * (context.intermediateBelief - context.ifTrue.b) * disbeliefGap,
135
+ context.projectedAntecedentComplement * (1 - context.childBaseRate) * beliefGap
136
+ );
137
+ }
138
+ function computeConditionalDeductionCorrection(context) {
139
+ if (hasNoConditionalDeductionCorrection(context.ifTrue, context.ifFalse)) {
140
+ return 0;
141
+ }
142
+ if (context.ifTrue.b > context.ifFalse.b && context.ifTrue.d <= context.ifFalse.d) {
143
+ return computeTrueDominantDeductionCorrection(context);
144
+ }
145
+ return computeFalseDominantDeductionCorrection(context);
146
+ }
70
147
  function conditionalDeduction(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
71
148
  const fallbackChildBaseRate = childBaseRateFallback(
72
149
  ifTrue,
@@ -86,58 +163,18 @@ function conditionalDeduction(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
86
163
  const intermediateUncertainty = opinionA.b * ifTrue.u + opinionA.d * ifFalse.u + opinionA.u * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
87
164
  const projectedVacuousDeduction = ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a) + childBaseRate * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
88
165
  const projectedConditionalA = ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);
89
- let correction = 0;
90
- if (ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d || ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d) {
91
- correction = 0;
92
- } else if (ifTrue.b > ifFalse.b && ifTrue.d <= ifFalse.d) {
93
- const beliefGap = ifTrue.b - ifFalse.b;
94
- const disbeliefGap = ifFalse.d - ifTrue.d;
95
- if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
96
- correction = safeCorrectionTerm(
97
- opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b),
98
- projectedAntecedent * childBaseRate
99
- ) ?? 0;
100
- } else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
101
- correction = safeCorrectionTerm(
102
- opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
103
- projectedAntecedentComplement * childBaseRate * disbeliefGap
104
- ) ?? 0;
105
- } else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
106
- correction = safeCorrectionTerm(
107
- (1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
108
- projectedAntecedent * (1 - childBaseRate) * beliefGap
109
- ) ?? 0;
110
- } else {
111
- correction = safeCorrectionTerm(
112
- (1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d),
113
- projectedAntecedentComplement * (1 - childBaseRate)
114
- ) ?? 0;
115
- }
116
- } else {
117
- const beliefGap = ifFalse.b - ifTrue.b;
118
- const disbeliefGap = ifTrue.d - ifFalse.d;
119
- if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
120
- correction = safeCorrectionTerm(
121
- (1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
122
- projectedAntecedent * childBaseRate * disbeliefGap
123
- ) ?? 0;
124
- } else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
125
- correction = safeCorrectionTerm(
126
- (1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b),
127
- projectedAntecedentComplement * childBaseRate
128
- ) ?? 0;
129
- } else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
130
- correction = safeCorrectionTerm(
131
- opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d),
132
- projectedAntecedent * (1 - childBaseRate)
133
- ) ?? 0;
134
- } else {
135
- correction = safeCorrectionTerm(
136
- opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
137
- projectedAntecedentComplement * (1 - childBaseRate) * beliefGap
138
- ) ?? 0;
139
- }
140
- }
166
+ const correction = computeConditionalDeductionCorrection({
167
+ childBaseRate,
168
+ ifFalse,
169
+ ifTrue,
170
+ intermediateBelief,
171
+ intermediateDisbelief,
172
+ opinionA,
173
+ projectedAntecedent,
174
+ projectedAntecedentComplement,
175
+ projectedConditionalA,
176
+ projectedVacuousDeduction
177
+ });
141
178
  return opinion(
142
179
  intermediateBelief - childBaseRate * correction,
143
180
  intermediateDisbelief - (1 - childBaseRate) * correction,
@@ -169,11 +206,23 @@ function opinionFromBaseRate(probability) {
169
206
  function opinionFromDogmatic(probability, baseRate) {
170
207
  return dogmatic(clamp01(probability), clamp01(baseRate));
171
208
  }
209
+ function representableProjectedUncertainty(probability, uncertainty, baseRate) {
210
+ const maxBeliefUncertainty = baseRate > 0 ? probability / baseRate : Number.POSITIVE_INFINITY;
211
+ const maxDisbeliefUncertainty = baseRate < 1 ? (1 - probability) / (1 - baseRate) : Number.POSITIVE_INFINITY;
212
+ return clamp01(
213
+ Math.min(uncertainty, maxBeliefUncertainty, maxDisbeliefUncertainty)
214
+ );
215
+ }
172
216
  function opinionFromProjected(probability, uncertainty, baseRate) {
173
217
  const p = clamp01(probability);
174
- const u = clamp01(uncertainty);
175
- const remainingMass = 1 - u;
176
- return mkOpinion(p * remainingMass, (1 - p) * remainingMass, u, clamp01(baseRate));
218
+ const a = clamp01(baseRate);
219
+ const u = representableProjectedUncertainty(p, clamp01(uncertainty), a);
220
+ return mkOpinion(
221
+ Math.max(0, p - a * u),
222
+ Math.max(0, 1 - p - (1 - a) * u),
223
+ u,
224
+ a
225
+ );
177
226
  }
178
227
 
179
228
  // src/v1/operations/canonical.ts