Files
boc/iom/intelligence/causal_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

352 lines
13 KiB
Python

"""
Causal Engine
Understands cause-and-effect relationships, not just correlations
"""
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
from datetime import datetime
@dataclass
class CausalRelationship:
"""A causal relationship between signals"""
cause: str
effect: str
strength: float # 0-1
mechanism: str # Explanation of how cause leads to effect
evidence: List[Dict]
confidence: float
def to_dict(self) -> Dict:
return {
"cause": self.cause,
"effect": self.effect,
"strength": round(self.strength, 2),
"mechanism": self.mechanism,
"evidence_count": len(self.evidence),
"confidence": round(self.confidence, 2)
}
class CausalEngine:
"""Identifies causal relationships in urban systems"""
def __init__(self):
self.causal_graph = self._build_causal_graph()
def _build_causal_graph(self) -> Dict:
"""Build knowledge graph of causal relationships"""
return {
# Lighting → Safety
"lighting": {
"safety_index": {
"strength": 0.71,
"mechanism": "Better lighting increases visibility, deters criminal activity, and improves pedestrian confidence",
"evidence": [
{"study": "Chicago Alley Lighting", "effect": "7% crime reduction"},
{"study": "NYC Street Lighting", "effect": "36% reduction in outdoor nighttime index crimes"}
]
},
"night_activity": {
"strength": 0.65,
"mechanism": "Improved lighting extends usable hours for commercial and social activities",
"evidence": []
}
},
# Trees → Temperature
"tree_coverage": {
"heat_stress": {
"strength": 0.82,
"mechanism": "Trees provide shade and evapotranspiration, reducing surface and air temperature",
"evidence": [
{"study": "Urban Heat Island", "effect": "2-8°C cooling effect"},
{"study": "Barcelona Tree Strategy", "effect": "4°C reduction in pedestrian areas"}
]
},
"walkability": {
"strength": 0.58,
"mechanism": "Shaded walkways encourage walking and extend comfortable walking hours",
"evidence": []
},
"property_value": {
"strength": 0.45,
"mechanism": "Tree-lined streets are associated with higher property values and desirability",
"evidence": [
{"study": "Portland Trees", "effect": "$1.35B total property value increase"}
]
}
},
# Pedestrian Flow → Retail
"pedestrian_flow": {
"retail_revenue": {
"strength": 0.78,
"mechanism": "More pedestrians = more potential customers = higher sales",
"evidence": [
{"study": "High Street Retail", "effect": "1% footfall increase = 1.3% sales increase"}
]
},
"safety_index": {
"strength": 0.52,
"mechanism": "Eyes on the street effect - more people watching increases natural surveillance",
"evidence": [
{"study": "Jane Jacobs", "effect": "Natural surveillance reduces crime"}
]
}
},
# Maintenance → Everything
"maintenance_quality": {
"property_value": {
"strength": 0.63,
"mechanism": "Well-maintained areas signal investment and care, attracting residents and businesses",
"evidence": []
},
"safety_index": {
"strength": 0.55,
"mechanism": "Broken windows theory - visible neglect signals low enforcement and invites more disorder",
"evidence": [
{"study": "Broken Windows", "effect": "Maintenance prevents escalation of disorder"}
]
},
"tourism": {
"strength": 0.48,
"mechanism": "Tourists avoid areas that appear neglected or unsafe",
"evidence": []
}
},
# Public Transit → Accessibility
"transit_access": {
"property_value": {
"strength": 0.67,
"mechanism": "Transit access reduces commute costs and increases location desirability",
"evidence": [
{"study": "TOD Value", "effect": "10-20% property value premium near transit"}
]
},
"walkability": {
"strength": 0.44,
"mechanism": "Transit hubs create walkable destinations and mixed-use development",
"evidence": []
}
}
}
def explain_causality(self, cause: str, effect: str) -> Optional[Dict]:
"""
Explain why A causes B
Returns:
Causal explanation or None if no relationship known
"""
if cause in self.causal_graph and effect in self.causal_graph[cause]:
relationship = self.causal_graph[cause][effect]
return {
"cause": cause,
"effect": effect,
"strength": relationship["strength"],
"mechanism": relationship["mechanism"],
"evidence": relationship["evidence"],
"interpretation": self._interpret_strength(relationship["strength"])
}
return None
def find_causes(self, effect: str) -> List[Dict]:
"""Find all known causes of an effect"""
causes = []
for cause, effects in self.causal_graph.items():
if effect in effects:
relationship = effects[effect]
causes.append({
"cause": cause,
"strength": relationship["strength"],
"mechanism": relationship["mechanism"][:100] + "..."
})
# Sort by strength
causes.sort(key=lambda x: x["strength"], reverse=True)
return causes
def find_effects(self, cause: str) -> List[Dict]:
"""Find all known effects of a cause"""
if cause not in self.causal_graph:
return []
effects = []
for effect, relationship in self.causal_graph[cause].items():
effects.append({
"effect": effect,
"strength": relationship["strength"],
"mechanism": relationship["mechanism"][:100] + "..."
})
# Sort by strength
effects.sort(key=lambda x: x["strength"], reverse=True)
return effects
def calculate_attribution(
self,
effect: str,
signal_values: Dict[str, float]
) -> Dict:
"""
Calculate how much each cause contributes to an effect
Example:
"Safety Index = 45. What causes this?"
→ Lighting: 31% of variation
→ Maintenance: 22% of variation
→ Pedestrian flow: 18% of variation
"""
causes = self.find_causes(effect)
if not causes:
return {
"effect": effect,
"status": "unknown",
"message": f"No causal model for {effect}"
}
# Calculate attribution
attributions = []
total_strength = sum(c["strength"] for c in causes)
for cause in causes:
cause_name = cause["cause"]
current_value = signal_values.get(cause_name, 50)
# Attribution = strength * (deviation from optimal)
deviation = abs(50 - current_value) / 50 # 0 = optimal, 1 = worst
attribution = cause["strength"] / total_strength * (1 - deviation)
attributions.append({
"cause": cause_name,
"attribution_percent": round(attribution * 100, 1),
"current_value": current_value,
"potential_improvement": round(deviation * 100, 1),
"mechanism": cause["mechanism"]
})
# Sort by attribution
attributions.sort(key=lambda x: x["attribution_percent"], reverse=True)
return {
"effect": effect,
"current_value": signal_values.get(effect, 50),
"total_attribution": round(sum(a["attribution_percent"] for a in attributions), 1),
"attributions": attributions,
"top_driver": attributions[0]["cause"] if attributions else None
}
def recommend_interventions(
self,
target: str,
current_value: float,
target_value: float
) -> List[Dict]:
"""
Recommend interventions to achieve a target
Example:
"Improve safety from 45 to 70"
→ Install lighting (expected improvement: +15)
→ Increase maintenance (expected improvement: +8)
→ Activate pedestrian flow (expected improvement: +5)
"""
causes = self.find_causes(target)
interventions = []
for cause in causes:
cause_name = cause["cause"]
strength = cause["strength"]
# Expected improvement
gap = target_value - current_value
expected_improvement = gap * strength
interventions.append({
"intervention": f"Improve {cause_name}",
"target_cause": cause_name,
"expected_improvement": round(expected_improvement, 1),
"confidence": round(strength, 2),
"mechanism": cause["mechanism"],
"priority": "high" if expected_improvement > gap * 0.3 else "medium"
})
# Sort by expected improvement
interventions.sort(key=lambda x: x["expected_improvement"], reverse=True)
return interventions
def _interpret_strength(self, strength: float) -> str:
"""Interpret causal strength"""
if strength >= 0.7:
return "Strong causal relationship"
elif strength >= 0.5:
return "Moderate causal relationship"
elif strength >= 0.3:
return "Weak causal relationship"
else:
return "Very weak causal relationship"
# Example usage
def example_causal_analysis():
"""Example: Causal analysis"""
engine = CausalEngine()
# Explain causality
print("=== Causal Explanation ===")
explanation = engine.explain_causality("lighting", "safety_index")
if explanation:
print(f"{explanation['cause']}{explanation['effect']}")
print(f"Strength: {explanation['strength']} ({explanation['interpretation']})")
print(f"Mechanism: {explanation['mechanism']}")
print("Evidence:")
for ev in explanation['evidence']:
print(f" - {ev['study']}: {ev['effect']}")
# Find causes of safety
print("\n=== Causes of Safety Index ===")
causes = engine.find_causes("safety_index")
for cause in causes:
print(f" {cause['cause']}: {cause['strength']}")
# Attribution analysis
print("\n=== Attribution Analysis ===")
signal_values = {
"lighting": 35,
"maintenance_quality": 40,
"pedestrian_flow": 60,
"safety_index": 45
}
attribution = engine.calculate_attribution("safety_index", signal_values)
print(f"Safety Index = {attribution['current_value']}")
print("Attributions:")
for attr in attribution['attributions']:
print(f" {attr['cause']}: {attr['attribution_percent']}%")
print(f" Current: {attr['current_value']}, Potential: {attr['potential_improvement']}%")
# Recommend interventions
print("\n=== Recommended Interventions ===")
interventions = engine.recommend_interventions("safety_index", 45, 70)
for intervention in interventions:
print(f" {intervention['intervention']}")
print(f" Expected improvement: +{intervention['expected_improvement']}")
print(f" Priority: {intervention['priority']}")
return engine
if __name__ == '__main__':
example_causal_analysis()