Spaces:
Sleeping
Sleeping
File size: 3,905 Bytes
a4b5ecb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | # 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}")
@classmethod
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
|