Spaces:
Running
Running
| """Pure-math risk scoring engine for the Fake Gang Detection environment. | |
| All functions are stateless — no imports from other project modules. | |
| Implements the formulas from formulas.md exactly. | |
| """ | |
| from __future__ import annotations | |
| import math | |
| def compute_node_risk(photo_reuse: float, bio_template: float) -> float: | |
| """Content-based risk: stolen photos + copy-paste bios.""" | |
| return round(0.60 * photo_reuse + 0.40 * bio_template, 4) | |
| def compute_behavior_risk(account_age_days: int, post_hour_cluster_score: float) -> float: | |
| """Temporal risk: recently created + posting in the gang's time window.""" | |
| age_norm = min(1.0, account_age_days / 365.0) | |
| return round(0.55 * (1.0 - age_norm) + 0.45 * post_hour_cluster_score, 4) | |
| def compute_graph_risk( | |
| flagged_neighbor_ratio: float, | |
| mutual_follow_rate: float, | |
| avg_neighbor_photo_reuse: float, | |
| ) -> float: | |
| """Structural risk: embedded in a flagged cluster + inflated mutual follows.""" | |
| return round( | |
| 0.45 * flagged_neighbor_ratio | |
| + 0.35 * mutual_follow_rate | |
| + 0.20 * avg_neighbor_photo_reuse, | |
| 4, | |
| ) | |
| def compute_hub_legitimacy( | |
| follower_count: int, | |
| following_count: int, | |
| account_age_days: int, | |
| suspicious_mutual_ratio: float, | |
| ) -> float: | |
| """Celebrity/hub discount: large established accounts are unlikely to be fakes. | |
| High value → high legitimacy → subtract from fake_risk. | |
| """ | |
| F_MAX = 1_000_000 | |
| followers_norm = min(1.0, math.log1p(follower_count) / math.log1p(F_MAX)) | |
| follow_ratio_norm = min(1.0, (following_count / max(follower_count, 1)) / 5.0) | |
| age_norm = min(1.0, account_age_days / 365.0) | |
| return round( | |
| 0.45 * followers_norm | |
| + 0.25 * (1.0 - follow_ratio_norm) | |
| + 0.20 * age_norm | |
| + 0.10 * (1.0 - suspicious_mutual_ratio), | |
| 4, | |
| ) | |
| def compute_fake_risk( | |
| node_risk: float, | |
| behavior_risk: float, | |
| graph_risk: float, | |
| hub_legitimacy: float, | |
| ) -> float: | |
| """Composite fake risk score in [0.0, 1.0]. | |
| Graph risk carries the most weight (0.45) because structural signals | |
| are hardest to fake at scale. Hub legitimacy discounts celebrities. | |
| """ | |
| raw = ( | |
| 0.30 * node_risk | |
| + 0.25 * behavior_risk | |
| + 0.45 * graph_risk | |
| - 0.25 * hub_legitimacy | |
| ) | |
| return round(max(0.0, min(1.0, raw)), 4) | |
| def classify_risk(fake_risk: float) -> str: | |
| """Map a fake_risk score to an account status string.""" | |
| if fake_risk < 0.35: | |
| return "normal" | |
| if fake_risk < 0.60: | |
| return "suspect" | |
| return "confirmed_fake" | |
| def grader_score(tp: int, fp: int, fn: int, steps_used: int, max_steps: int) -> float: | |
| """Normalised [0.0, 1.0] submission score used by /grader endpoint. | |
| Win condition (recall >= 0.8 AND precision >= 0.7): | |
| score = 0.55 + 0.20*recall + 0.15*precision + 0.10*efficiency | |
| Otherwise (partial credit): | |
| score = 0.30*recall + 0.10*precision | |
| """ | |
| recall = tp / 10.0 | |
| precision = tp / max(tp + fp, 1) | |
| efficiency = max(0.0, (max_steps - steps_used) / max_steps) | |
| if recall >= 0.8 and precision >= 0.7: | |
| score = 0.55 + 0.20 * recall + 0.15 * precision + 0.10 * efficiency | |
| else: | |
| score = 0.30 * recall + 0.10 * precision | |
| return round(max(0.0, min(1.0, score)), 4) | |