///|
pub fn default_calibration_config() -> CalibrationConfig {
{ min_congestion: 0.0, max_congestion: 1.0, steps: 10 }
}
///|
fn score_congestion(
route : RouteProfile,
observations : Array[Observation],
actuals : Array[Double],
congestion : Double,
) -> ErrorSummary {
let results : Array[EtaResult] = []
let mut engine = EtaEngine::new(route, initial_speed=8.0)
for obs in observations {
let (next, result) = engine.observe(obs, congestion~)
engine = next
results.push(result)
}
summarize_errors(eta_results_to_points(results, actuals))
}
///|
pub fn calibrate_congestion(
route : RouteProfile,
observations : Array[Observation],
actuals : Array[Double],
config? : CalibrationConfig = default_calibration_config(),
) -> CalibrationResult {
let candidates : Array[ForecastPoint] = []
let steps = if config.steps <= 0 { 1 } else { config.steps }
let mut best_congestion = config.min_congestion
let mut best_error = score_congestion(
route,
observations,
actuals,
config.min_congestion,
)
for i in 0..<=steps {
let ratio = i.to_double() / steps.to_double()
let congestion = config.min_congestion +
(config.max_congestion - config.min_congestion) * ratio
let summary = score_congestion(route, observations, actuals, congestion)
candidates.push({ predicted: congestion, actual: summary.mae })
if i == 0 || summary.mae < best_error.mae {
best_congestion = congestion
best_error = summary
}
}
{ best_congestion, best_error, candidates }
}
///|
pub fn calibrate_demo() -> CalibrationResult {
let trace = noisy_demo_trace()
calibrate_congestion(demo_route(), trace.observations, trace.actual_remaining)
}