Spaces:
Sleeping
Sleeping
| # Generated by Claude Code -- 2026-02-08 | |
| """Model 1: Naive Baseline -- Orbital Shell Density Prior. | |
| Predicts collision risk based solely on the altitude band of the conjunction, | |
| using historical base rates. This establishes that altitude alone is predictive | |
| (LEO is more crowded) but insufficient for actionable conjunction assessment. | |
| """ | |
| import json | |
| import numpy as np | |
| from pathlib import Path | |
| from collections import defaultdict | |
| class OrbitalShellBaseline: | |
| """ | |
| Altitude-band collision rate baseline. | |
| For any conjunction event, predict the average risk and miss distance | |
| for that altitude regime. Bins events into 50km altitude bands. | |
| """ | |
| def __init__(self, bin_width_km: float = 50.0): | |
| self.bin_width = bin_width_km | |
| self.bins: dict[int, dict] = {} | |
| self.global_stats: dict = {} | |
| def _altitude_to_bin(self, alt_km: float) -> int: | |
| return int(round(alt_km / self.bin_width) * self.bin_width) | |
| def fit(self, altitudes: np.ndarray, y_risk: np.ndarray, y_miss_log: np.ndarray): | |
| """ | |
| Fit baseline from altitude array and labels. | |
| Args: | |
| altitudes: altitude in km for each event | |
| y_risk: binary risk labels | |
| y_miss_log: log1p(miss_distance_km) targets | |
| """ | |
| # Global fallback stats | |
| self.global_stats = { | |
| "mean_risk": float(np.mean(y_risk)), | |
| "mean_miss_log": float(np.mean(y_miss_log)), | |
| "count": int(len(y_risk)), | |
| } | |
| # Per-bin statistics | |
| bin_data = defaultdict(lambda: {"risks": [], "misses": []}) | |
| for alt, risk, miss in zip(altitudes, y_risk, y_miss_log): | |
| b = self._altitude_to_bin(alt) | |
| bin_data[b]["risks"].append(risk) | |
| bin_data[b]["misses"].append(miss) | |
| self.bins = {} | |
| for b, data in bin_data.items(): | |
| self.bins[b] = { | |
| "mean_risk": float(np.mean(data["risks"])), | |
| "mean_miss_log": float(np.mean(data["misses"])), | |
| "count": len(data["risks"]), | |
| "risk_rate": float(np.sum(data["risks"]) / len(data["risks"])), | |
| } | |
| print(f"Baseline fit: {len(self.bins)} altitude bins, " | |
| f"global risk rate = {self.global_stats['mean_risk']:.4f}") | |
| def predict(self, altitudes: np.ndarray) -> tuple[np.ndarray, np.ndarray]: | |
| """ | |
| Predict risk probability and log miss distance for each altitude. | |
| Returns: (risk_probs, miss_log_preds) | |
| """ | |
| risk_preds = [] | |
| miss_preds = [] | |
| for alt in altitudes: | |
| b = self._altitude_to_bin(alt) | |
| if b in self.bins: | |
| risk_preds.append(self.bins[b]["risk_rate"]) | |
| miss_preds.append(self.bins[b]["mean_miss_log"]) | |
| else: | |
| risk_preds.append(self.global_stats["mean_risk"]) | |
| miss_preds.append(self.global_stats["mean_miss_log"]) | |
| return np.array(risk_preds), np.array(miss_preds) | |
| def save(self, path: Path): | |
| """Save model to JSON.""" | |
| data = { | |
| "bin_width": self.bin_width, | |
| "bins": {str(k): v for k, v in self.bins.items()}, | |
| "global_stats": self.global_stats, | |
| } | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| with open(path, "w") as f: | |
| json.dump(data, f, indent=2) | |
| print(f"Baseline saved to {path}") | |
| def load(cls, path: Path) -> "OrbitalShellBaseline": | |
| """Load model from JSON.""" | |
| with open(path) as f: | |
| data = json.load(f) | |
| model = cls(bin_width_km=data["bin_width"]) | |
| model.bins = {int(k): v for k, v in data["bins"].items()} | |
| model.global_stats = data["global_stats"] | |
| return model | |