feat: math core v3 engine upgrade
This commit is contained in:
@@ -11,6 +11,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime, timezone
|
||||
|
||||
@@ -955,3 +956,107 @@ def apply_accelerated_decay(
|
||||
return accelerated
|
||||
|
||||
return standard_decay
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# V3 Macro Layer — Noisy-OR Exposure & LLR Emission (Requirements: 9.1–9.5)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
# Noisy-OR weights per dimension
|
||||
_V3_MACRO_WEIGHTS: dict[str, float] = {
|
||||
"geo": 0.35,
|
||||
"supply": 0.25,
|
||||
"commodity": 0.25,
|
||||
"sector": 0.15,
|
||||
}
|
||||
|
||||
# Resilience dampener per tier
|
||||
_V3_RESILIENCE_DAMPENER: dict[str, float] = {
|
||||
"global_leader": 0.70,
|
||||
"multinational": 0.85,
|
||||
"regional": 1.00,
|
||||
"domestic": 1.20,
|
||||
}
|
||||
|
||||
|
||||
def compute_normalized_macro_exposure(
|
||||
overlaps: dict[str, float],
|
||||
tier: str = "regional",
|
||||
) -> float:
|
||||
"""Compute normalized macro exposure via noisy-OR.
|
||||
|
||||
E_raw = 1 - product(1 - w_k × O_k)
|
||||
E_max = 1 - product(1 - w_k)
|
||||
E_macro = E_raw / E_max × resilience_dampener
|
||||
|
||||
Args:
|
||||
overlaps: Dimension overlap values keyed by 'geo', 'supply',
|
||||
'commodity', 'sector'. Missing keys treated as 0.0.
|
||||
tier: Market position tier for resilience dampening.
|
||||
|
||||
Returns:
|
||||
Normalized macro exposure in [0, ∞) (can exceed 1.0 for domestic
|
||||
tier due to 1.20 dampener, but typically in [0, ~1.2]).
|
||||
|
||||
Requirements: 9.1, 9.2, 9.3
|
||||
"""
|
||||
# E_raw = 1 - product(1 - w_k × O_k)
|
||||
product_raw = 1.0
|
||||
for dim, weight in _V3_MACRO_WEIGHTS.items():
|
||||
o_k = max(0.0, min(1.0, overlaps.get(dim, 0.0)))
|
||||
product_raw *= (1.0 - weight * o_k)
|
||||
e_raw = 1.0 - product_raw
|
||||
|
||||
# E_max = 1 - product(1 - w_k) — theoretical max when all overlaps = 1.0
|
||||
product_max = 1.0
|
||||
for weight in _V3_MACRO_WEIGHTS.values():
|
||||
product_max *= (1.0 - weight)
|
||||
e_max = 1.0 - product_max
|
||||
|
||||
# Guard against zero (should never happen with default weights)
|
||||
if e_max <= 0.0:
|
||||
return 0.0
|
||||
|
||||
# Normalize to [0, 1]
|
||||
e_macro = e_raw / e_max
|
||||
|
||||
# Apply resilience dampener per tier
|
||||
dampener = _V3_RESILIENCE_DAMPENER.get(tier, 1.0)
|
||||
return e_macro * dampener
|
||||
|
||||
|
||||
def compute_macro_llr(
|
||||
macro_impact: float,
|
||||
event_confidence: float,
|
||||
q_recency: float,
|
||||
macro_direction: int,
|
||||
) -> float:
|
||||
"""Compute macro LLR for shared posterior.
|
||||
|
||||
p_macro = clamp(0.50 + 0.30 × macro_impact × event_confidence × q_recency, 0.501, 0.80)
|
||||
LLR_macro = macro_direction × ln(p_macro / (1 - p_macro))
|
||||
|
||||
When macro_direction == 0, returns 0.0 (neutral — no directional signal).
|
||||
|
||||
Args:
|
||||
macro_impact: Normalized macro impact score (typically [0, 1]).
|
||||
event_confidence: Event classification confidence [0, 1].
|
||||
q_recency: Recency quality factor [0, 1].
|
||||
macro_direction: +1 for positive, -1 for negative, 0 for neutral.
|
||||
|
||||
Returns:
|
||||
Log-likelihood ratio for the macro signal. Feeds directly into the
|
||||
shared posterior without separate post-hoc modifier.
|
||||
|
||||
Requirements: 9.4, 9.5
|
||||
"""
|
||||
if macro_direction == 0:
|
||||
return 0.0
|
||||
|
||||
# p_macro = clamp(0.50 + 0.30 × macro_impact × event_confidence × q_recency, 0.501, 0.80)
|
||||
p_macro = 0.50 + 0.30 * macro_impact * event_confidence * q_recency
|
||||
p_macro = max(0.501, min(0.80, p_macro))
|
||||
|
||||
# LLR_macro = macro_direction × ln(p_macro / (1 - p_macro))
|
||||
llr = macro_direction * math.log(p_macro / (1.0 - p_macro))
|
||||
return llr
|
||||
|
||||
Reference in New Issue
Block a user