Files
boc/iom/intelligence/prediction_engine.py
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- Add NFC ePassport roadmap (ICAO 9303, eIDAS)
- Add TensorFlow.js edge face detection (BlazeFace)
- Add structured audit logger (GDPR-compliant)
- Risk scoring support

Part of KYC Apple Native UX v1.1.0
2026-06-29 16:24:48 +00:00

309 lines
11 KiB
Python

"""
Prediction Engine (PRI - Predictive Reality Index)
Forecasts what will happen in 30, 90, and 365 days
"""
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
from datetime import datetime, timedelta
import json
@dataclass
class Prediction:
"""A single prediction"""
target: str
current_value: float
predicted_value: float
confidence: float
horizon_days: int
drivers: List[Dict]
scenario: Optional[str] = None
@property
def change_percent(self) -> float:
return round(((self.predicted_value - self.current_value) / max(self.current_value, 1)) * 100, 2)
def to_dict(self) -> Dict:
return {
"target": self.target,
"current_value": round(self.current_value, 2),
"predicted_value": round(self.predicted_value, 2),
"change_percent": self.change_percent,
"confidence": round(self.confidence, 2),
"horizon_days": self.horizon_days,
"drivers": self.drivers,
"scenario": self.scenario
}
class PredictionEngine:
"""Predicts future states from reality signals"""
def __init__(self):
self.models = self._load_prediction_models()
def _load_prediction_models(self) -> Dict:
"""Load prediction models for different signals"""
return {
"vacancy_rate": {
"drivers": ["retail_density", "pedestrian_flow", "rent_burden", "maintenance_quality"],
"trend_factor": 0.02, # 2% monthly trend
"seasonality": True
},
"crime_risk": {
"drivers": ["lighting", "pedestrian_flow", "maintenance_quality", "informal_economy"],
"trend_factor": 0.015,
"seasonality": False
},
"family_friendly": {
"drivers": ["playground", "greenery", "safety_index", "schools", "noise_level"],
"trend_factor": 0.01,
"seasonality": False
},
"property_value": {
"drivers": ["walkability", "transit_access", "greenery", "safety_index", "retail_density"],
"trend_factor": 0.03,
"seasonality": True
}
}
def predict(
self,
signal_history: List[Dict],
target: str,
horizon_days: int = 90,
scenario: Optional[Dict] = None
) -> Prediction:
"""
Predict future value of a signal
Args:
signal_history: Historical signal values
target: Signal type to predict
horizon_days: Prediction horizon (30, 90, 365)
scenario: Optional scenario (e.g., "add 40 trees")
Returns:
Prediction result
"""
model = self.models.get(target, {})
# Get current value
current_value = self._get_current_value(signal_history, target)
# Calculate trend
trend = self._calculate_trend(signal_history, target)
# Apply scenario effects
scenario_effect = 0
if scenario:
scenario_effect = self._calculate_scenario_effect(scenario, target)
# Predict
months = horizon_days / 30
predicted_value = current_value + (trend * months) + scenario_effect
# Calculate confidence based on data quality
confidence = self._calculate_confidence(signal_history, target)
# Identify drivers
drivers = self._identify_drivers(signal_history, target, model)
return Prediction(
target=target,
current_value=current_value,
predicted_value=predicted_value,
confidence=confidence,
horizon_days=horizon_days,
drivers=drivers,
scenario=scenario.get("name") if scenario else None
)
def predict_multiple(
self,
signal_history: List[Dict],
targets: List[str],
horizon_days: int = 90
) -> Dict:
"""Predict multiple targets"""
predictions = {}
for target in targets:
pred = self.predict(signal_history, target, horizon_days)
predictions[target] = pred.to_dict()
return {
"horizon_days": horizon_days,
"predictions": predictions,
"timestamp": datetime.utcnow().isoformat()
}
def scenario_analysis(
self,
signal_history: List[Dict],
target: str,
scenarios: List[Dict]
) -> Dict:
"""
Compare multiple scenarios
Example scenarios:
- {"name": "add_40_trees", "changes": {"tree_coverage": 40}}
- {"name": "improve_lighting", "changes": {"lighting": 20}}
"""
results = []
# Baseline prediction
baseline = self.predict(signal_history, target, 365)
for scenario in scenarios:
pred = self.predict(signal_history, target, 365, scenario)
improvement = pred.predicted_value - baseline.predicted_value
results.append({
"scenario": scenario["name"],
"predicted_value": round(pred.predicted_value, 2),
"improvement": round(improvement, 2),
"improvement_percent": round((improvement / max(baseline.predicted_value, 1)) * 100, 2),
"cost_estimate": scenario.get("cost_estimate", "unknown"),
"roi": self._calculate_roi(improvement, scenario.get("cost_estimate"))
})
# Sort by improvement
results.sort(key=lambda x: x["improvement"], reverse=True)
return {
"target": target,
"baseline": baseline.to_dict(),
"scenarios": results,
"best_scenario": results[0]["scenario"] if results else None
}
def _get_current_value(self, signal_history: List[Dict], target: str) -> float:
"""Get current value from history"""
matching = [s for s in signal_history if s.get("signal_type") == target]
if matching:
return matching[-1].get("value", 50)
return 50 # Default
def _calculate_trend(self, signal_history: List[Dict], target: str) -> float:
"""Calculate trend from historical data"""
matching = [s for s in signal_history if s.get("signal_type") == target]
if len(matching) < 2:
return 0 # No trend
# Simple linear trend
values = [s.get("value", 50) for s in matching]
return (values[-1] - values[0]) / len(values)
def _calculate_scenario_effect(self, scenario: Dict, target: str) -> float:
"""Calculate effect of a scenario"""
changes = scenario.get("changes", {})
# Simple heuristic: each unit change affects target by 0.5
total_effect = 0
for signal_type, change in changes.items():
total_effect += change * 0.5
return total_effect
def _calculate_confidence(self, signal_history: List[Dict], target: str) -> float:
"""Calculate prediction confidence"""
matching = [s for s in signal_history if s.get("signal_type") == target]
# More data = higher confidence
data_confidence = min(0.9, len(matching) / 10)
# Recent data = higher confidence
if matching:
latest = matching[-1].get("timestamp", "")
# Check if data is recent (within 30 days)
# Simplified: assume recent
recency_confidence = 0.8
else:
recency_confidence = 0.3
return (data_confidence + recency_confidence) / 2
def _identify_drivers(self, signal_history: List[Dict], target: str, model: Dict) -> List[Dict]:
"""Identify key drivers for prediction"""
drivers = []
for driver_type in model.get("drivers", []):
driver_signals = [s for s in signal_history if s.get("signal_type") == driver_type]
if driver_signals:
avg_value = sum(s.get("value", 50) for s in driver_signals) / len(driver_signals)
drivers.append({
"signal_type": driver_type,
"current_value": round(avg_value, 2),
"impact": "high" if avg_value < 30 or avg_value > 70 else "medium"
})
return drivers
def _calculate_roi(self, improvement: float, cost_estimate: Optional[float]) -> Optional[float]:
"""Calculate ROI for a scenario"""
if cost_estimate and cost_estimate > 0:
return round(improvement / cost_estimate, 2)
return None
# Example usage
def example_predictions():
"""Example: Predict future states"""
engine = PredictionEngine()
# Historical signals
signal_history = [
{"signal_type": "vacancy_rate", "value": 25, "timestamp": "2026-01-01"},
{"signal_type": "vacancy_rate", "value": 28, "timestamp": "2026-02-01"},
{"signal_type": "vacancy_rate", "value": 32, "timestamp": "2026-03-01"},
{"signal_type": "retail_density", "value": 45, "timestamp": "2026-03-01"},
{"signal_type": "pedestrian_flow", "value": 60, "timestamp": "2026-03-01"},
{"signal_type": "rent_burden", "value": 75, "timestamp": "2026-03-01"},
{"signal_type": "maintenance_quality", "value": 40, "timestamp": "2026-03-01"},
]
# Predict vacancy rate in 90 days
pred = engine.predict(signal_history, "vacancy_rate", 90)
print("=== Prediction: Vacancy Rate ===")
print(f"Current: {pred.current_value}%")
print(f"Predicted (90 days): {pred.predicted_value}%")
print(f"Change: {pred.change_percent}%")
print(f"Confidence: {pred.confidence}")
print("\nKey Drivers:")
for driver in pred.drivers:
print(f" {driver['signal_type']}: {driver['current_value']} ({driver['impact']} impact)")
# Scenario analysis
print("\n=== Scenario Analysis ===")
scenarios = [
{"name": "add_40_trees", "changes": {"tree_coverage": 40}, "cost_estimate": 50000},
{"name": "improve_lighting", "changes": {"lighting": 30}, "cost_estimate": 30000},
{"name": "reduce_rent", "changes": {"rent_burden": -20}, "cost_estimate": 0}
]
result = engine.scenario_analysis(signal_history, "vacancy_rate", scenarios)
print(f"Baseline (365 days): {result['baseline']['predicted_value']}%")
print("\nScenarios:")
for scenario in result['scenarios']:
print(f" {scenario['scenario']}:")
print(f" Predicted: {scenario['predicted_value']}%")
print(f" Improvement: {scenario['improvement_percent']}%")
if scenario['roi']:
print(f" ROI: {scenario['roi']}")
print(f"\nBest scenario: {result['best_scenario']}")
return pred
if __name__ == '__main__':
example_predictions()