Spaces:
Running
Running
| # modules/ai_link_flow_emulator.py | |
| # TripAI β AI Emulator for Link Flows | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from typing import Any, Dict, Tuple | |
| import numpy as np | |
| import pandas as pd | |
| from sklearn.ensemble import RandomForestRegressor | |
| from .route_assignment import aon_assignment, Network | |
| class LinkFlowEmulator: | |
| """ | |
| AI emulator for link flows under scaled OD demand. | |
| Attributes | |
| ---------- | |
| model : RandomForestRegressor | |
| Multi-output regressor mapping demand scale -> link flows. | |
| link_ids : np.ndarray | |
| IDs of links in the same order as training outputs. | |
| base_total_demand : float | |
| Total baseline car OD (for reference). | |
| """ | |
| model: RandomForestRegressor | |
| link_ids: np.ndarray | |
| base_total_demand: float | |
| def _generate_training_scenarios( | |
| base_od: np.ndarray, | |
| network: Network, | |
| n_scenarios: int = 20, | |
| low_scale: float = 0.7, | |
| high_scale: float = 1.3, | |
| ) -> Tuple[np.ndarray, np.ndarray]: | |
| """ | |
| Generate training scenarios by scaling baseline OD and performing | |
| AON assignment to obtain link flows. | |
| Parameters | |
| ---------- | |
| base_od : np.ndarray | |
| Baseline OD matrix (veh/h). | |
| network : Network | |
| n_scenarios : int | |
| Number of random scaling scenarios. | |
| low_scale : float | |
| Minimum demand scale factor. | |
| high_scale : float | |
| Maximum demand scale factor. | |
| Returns | |
| ------- | |
| X : np.ndarray | |
| Feature matrix of shape (n_scenarios, 1) β the demand scale. | |
| Y : np.ndarray | |
| Target matrix of shape (n_scenarios, n_links) β link flows. | |
| """ | |
| n_zones = base_od.shape[0] | |
| n_links = len(network.links) | |
| scales = np.random.uniform(low_scale, high_scale, size=n_scenarios) | |
| X = scales.reshape(-1, 1) | |
| Y = np.zeros((n_scenarios, n_links), dtype=float) | |
| # Build index -> (from_zone, to_zone) map to reuse AON logic | |
| # We will call the existing aon_assignment with scaled OD each time. | |
| # Convert base OD to DataFrame with synthetic zone index 0..n-1. | |
| zones = np.arange(n_zones) | |
| base_od_df = pd.DataFrame(base_od, index=zones, columns=zones) | |
| for idx, s in enumerate(scales): | |
| od_scaled = base_od_df * s | |
| flows_df = aon_assignment(od_scaled, network) | |
| Y[idx, :] = flows_df["flow_vehph"].to_numpy() | |
| return X, Y | |
| def train_link_flow_emulator( | |
| base_car_od: np.ndarray, | |
| network: Network, | |
| n_scenarios: int = 20, | |
| ) -> tuple[LinkFlowEmulator, pd.DataFrame]: | |
| """ | |
| Train a simple RandomForest-based emulator that maps a single | |
| scalar 'demand scale' to resulting link flows. | |
| Parameters | |
| ---------- | |
| base_car_od : np.ndarray | |
| Baseline car OD matrix (veh/h). | |
| network : Network | |
| n_scenarios : int | |
| Number of training scenarios. | |
| Returns | |
| ------- | |
| emulator : LinkFlowEmulator | |
| training_history : pd.DataFrame | |
| Scenario scales and corresponding total flows, for diagnostics. | |
| """ | |
| X, Y = _generate_training_scenarios(base_car_od, network, n_scenarios=n_scenarios) | |
| model = RandomForestRegressor( | |
| n_estimators=200, | |
| max_depth=12, | |
| random_state=42, | |
| ) | |
| model.fit(X, Y) | |
| link_ids = network.links.index.to_numpy() | |
| base_total = float(base_car_od.sum()) | |
| # Build training history for inspection | |
| total_flows = Y.sum(axis=1) | |
| training_history = pd.DataFrame( | |
| { | |
| "scale": X.flatten(), | |
| "total_link_flow": total_flows, | |
| } | |
| ) | |
| emulator = LinkFlowEmulator( | |
| model=model, | |
| link_ids=link_ids, | |
| base_total_demand=base_total, | |
| ) | |
| return emulator, training_history | |
| def predict_link_flows( | |
| emulator: LinkFlowEmulator, | |
| scale: float, | |
| network: Network, | |
| ) -> pd.DataFrame: | |
| """ | |
| Predict link flows for a new demand scale using the trained emulator. | |
| Parameters | |
| ---------- | |
| emulator : LinkFlowEmulator | |
| scale : float | |
| Multiplicative scaling factor relative to baseline OD. | |
| network : Network | |
| Returns | |
| ------- | |
| pd.DataFrame | |
| Link table with predicted flows in column 'flow_vehph_emulated'. | |
| """ | |
| X_new = np.array([[scale]], dtype=float) | |
| y_pred = emulator.model.predict(X_new).flatten() | |
| links = network.links.copy() | |
| links["flow_vehph_emulated"] = y_pred | |
| return links | |