bharatgraph / ai /self_learning /weight_optimizer.py
abinazebinoy's picture
fix(NEW-A3 part1): replace em-dashes/smart-quotes in 56+ Python files -- CI requires pure ASCII source
8a0fba4
Raw
History Blame Contribute Delete
5.45 kB
import os, sys, json
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from datetime import datetime
from loguru import logger
WEIGHTS_FILE = os.path.join(
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
"data", "processed", "indicator_weights.json"
)
DEFAULT_WEIGHTS = {
"politician_company_overlap": 0.35,
"contract_concentration": 0.25,
"audit_mention_frequency": 0.20,
"asset_growth_anomaly": 0.15,
"criminal_case_presence": 0.05,
}
MIN_CONFIRMATIONS = 3
DELTA_INCREASE = 0.01
DELTA_DECREASE = 0.005
class WeightOptimizer:
def __init__(self):
self.weights = self._load_weights()
self.outcomes = self._load_outcomes()
def _load_weights(self) -> dict:
if os.path.exists(WEIGHTS_FILE):
try:
data = json.loads(open(WEIGHTS_FILE, encoding="utf-8").read())
return data.get("weights", DEFAULT_WEIGHTS.copy())
except Exception:
pass
return DEFAULT_WEIGHTS.copy()
def _load_outcomes(self) -> list:
if os.path.exists(WEIGHTS_FILE):
try:
data = json.loads(open(WEIGHTS_FILE, encoding="utf-8").read())
return data.get("outcomes", [])
except Exception:
pass
return []
def record_outcome(self, entity_id: str, indicator_fired: list[str],
confirmed: bool) -> None:
self.outcomes.append({
"entity_id": entity_id,
"indicator_fired":indicator_fired,
"confirmed": confirmed,
"recorded_at": datetime.now().isoformat(),
})
self._save()
logger.info(
f"[WeightOptimizer] Outcome recorded: {entity_id} "
f"confirmed={confirmed} indicators={indicator_fired}"
)
def optimize(self) -> dict:
confirmed = [o for o in self.outcomes if o["confirmed"]]
unconfirmed = [o for o in self.outcomes if not o["confirmed"]]
if len(confirmed) < MIN_CONFIRMATIONS:
logger.info(
f"[WeightOptimizer] Only {len(confirmed)} confirmed outcomes. "
f"Need {MIN_CONFIRMATIONS} before adjusting weights."
)
return {"adjusted": False, "reason": "insufficient_confirmations",
"confirmed_count": len(confirmed)}
changes = {}
for indicator in DEFAULT_WEIGHTS:
fired_confirmed = sum(1 for o in confirmed
if indicator in o.get("indicator_fired", []))
fired_unconfirmed = sum(1 for o in unconfirmed
if indicator in o.get("indicator_fired", []))
old_weight = self.weights.get(indicator, DEFAULT_WEIGHTS[indicator])
if fired_confirmed > fired_unconfirmed:
new_weight = min(0.50, old_weight + DELTA_INCREASE)
elif fired_unconfirmed > fired_confirmed:
new_weight = max(0.01, old_weight - DELTA_DECREASE)
else:
new_weight = old_weight
if abs(new_weight - old_weight) > 0.0001:
changes[indicator] = {
"old": round(old_weight, 4),
"new": round(new_weight, 4),
"delta": round(new_weight - old_weight, 4),
}
self.weights[indicator] = new_weight
total = sum(self.weights.values())
if total > 0:
self.weights = {k: round(v/total, 4) for k,v in self.weights.items()}
self._save()
logger.success(
f"[WeightOptimizer] Weights adjusted: {len(changes)} changes. "
f"Pending human approval."
)
return {
"adjusted": len(changes) > 0,
"changes": changes,
"new_weights": self.weights,
"confirmed_cases": len(confirmed),
"optimized_at": datetime.now().isoformat(),
"note": "Changes require human approval before deployment.",
}
def _save(self):
os.makedirs(os.path.dirname(WEIGHTS_FILE), exist_ok=True)
with open(WEIGHTS_FILE, "w", encoding="utf-8") as f:
json.dump({
"weights": self.weights,
"outcomes": self.outcomes,
"last_updated": datetime.now().isoformat(),
}, f, indent=2, ensure_ascii=False)
if __name__ == "__main__":
print("=" * 55)
print("BharatGraph - Weight Optimizer Test")
print("=" * 55)
opt = WeightOptimizer()
print(f"\n Current weights:")
for k, v in opt.weights.items():
print(f" {k}: {v}")
for i in range(4):
opt.record_outcome(
f"test_entity_{i:03d}",
["politician_company_overlap", "contract_concentration"],
confirmed=True
)
opt.record_outcome("test_entity_004",
["asset_growth_anomaly"], confirmed=False)
result = opt.optimize()
print(f"\n Adjusted: {result['adjusted']}")
print(f" Confirmed cases: {result['confirmed_cases']}")
if result.get("changes"):
for k, v in result["changes"].items():
print(f" {k}: {v['old']} -> {v['new']} ({v['delta']:+.4f})")
print("\nDone!")