use crate::ir::ConstraintIR;
use crate::{ReasoningStrategy, SolutionResult};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub struct SpeculateVerifyRouter {
config: RouterConfig,
performance_history: PerformanceHistory,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RouterConfig {
pub speculation_threshold: f32,
pub verification_threshold: f32,
pub time_budget_ms: f64,
pub required_accuracy: f32,
pub adaptive_thresholds: bool,
}
impl Default for RouterConfig {
fn default() -> Self {
Self {
speculation_threshold: 0.9,
verification_threshold: 0.5,
time_budget_ms: 1000.0,
required_accuracy: 0.95,
adaptive_thresholds: true,
}
}
}
#[derive(Debug, Default)]
struct PerformanceHistory {
speculation_accuracy: HashMap<ProblemSignature, AccuracyStats>,
solver_performance: HashMap<ProblemSignature, PerformanceStats>,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct ProblemSignature {
num_variables: usize,
num_constraints: usize,
theory_tags: Vec<String>, complexity_class: ComplexityClass,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
enum ComplexityClass {
Simple, Medium, Complex, Unknown, }
#[derive(Debug, Default)]
struct AccuracyStats {
correct_predictions: u64,
total_predictions: u64,
average_confidence: f32,
}
#[derive(Debug, Default)]
struct PerformanceStats {
average_solve_time_ms: f64,
success_rate: f32,
timeout_rate: f32,
total_attempts: u64,
total_timeouts: u64,
}
impl PerformanceStats {
pub fn record_attempt(&mut self, solve_time_ms: f64, timed_out: bool) {
self.total_attempts += 1;
if timed_out {
self.total_timeouts += 1;
}
self.timeout_rate = self.total_timeouts as f32 / self.total_attempts as f32;
self.success_rate = 1.0 - self.timeout_rate;
let alpha = 0.2; if self.total_attempts == 1 {
self.average_solve_time_ms = solve_time_ms;
} else {
self.average_solve_time_ms =
(1.0 - alpha) * self.average_solve_time_ms + alpha * solve_time_ms;
}
}
pub fn analytics_summary(&self) -> AnalyticsSummary {
AnalyticsSummary {
avg_solve_time_ms: self.average_solve_time_ms,
success_rate: self.success_rate,
timeout_rate: self.timeout_rate,
total_attempts: self.total_attempts,
reliability_score: self.calculate_reliability(),
}
}
fn calculate_reliability(&self) -> f32 {
if self.total_attempts == 0 {
return 0.5; }
let sample_confidence = (self.total_attempts as f32 / 100.0).min(1.0);
self.success_rate * sample_confidence
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct AnalyticsSummary {
pub avg_solve_time_ms: f64,
pub success_rate: f32,
pub timeout_rate: f32,
pub total_attempts: u64,
pub reliability_score: f32,
}
impl SpeculateVerifyRouter {
pub fn new() -> Self {
Self {
config: RouterConfig::default(),
performance_history: PerformanceHistory::default(),
}
}
pub fn new_with_config(config: RouterConfig) -> Self {
Self {
config,
performance_history: PerformanceHistory::default(),
}
}
pub fn route(&self, constraints: &ConstraintIR) -> Result<ReasoningStrategy> {
let signature = self.compute_problem_signature(constraints);
let speculation_confidence = self.estimate_speculation_confidence(&signature);
let estimated_solver_time = self.estimate_solver_time(&signature);
let strategy = if speculation_confidence >= self.config.speculation_threshold {
ReasoningStrategy::Speculate
} else if speculation_confidence < self.config.verification_threshold
|| estimated_solver_time > self.config.time_budget_ms
{
ReasoningStrategy::Speculate
} else if estimated_solver_time < self.config.time_budget_ms * 0.1 {
ReasoningStrategy::Verify
} else {
ReasoningStrategy::Hybrid
};
Ok(strategy)
}
pub fn update_performance(
&mut self,
constraints: &ConstraintIR,
strategy_used: ReasoningStrategy,
speculation_result: Option<&SolutionResult>,
verification_result: Option<&SolutionResult>,
actual_solver_time_ms: f64,
) {
let signature = self.compute_problem_signature(constraints);
if let (Some(spec_result), Some(verify_result)) = (speculation_result, verification_result)
{
let correct = self.solutions_match(spec_result, verify_result);
self.update_speculation_accuracy(&signature, correct, spec_result.confidence);
}
if matches!(
strategy_used,
ReasoningStrategy::Verify | ReasoningStrategy::Hybrid
) {
self.update_solver_performance(&signature, actual_solver_time_ms, true);
}
}
fn compute_problem_signature(&self, constraints: &ConstraintIR) -> ProblemSignature {
let num_variables = constraints.variables.len();
let num_constraints = constraints.constraints.len();
let theory_tags = constraints
.theory_tags
.iter()
.map(|tag| format!("{:?}", tag))
.collect();
let complexity_class = self.classify_complexity(constraints);
ProblemSignature {
num_variables,
num_constraints,
theory_tags,
complexity_class,
}
}
fn classify_complexity(&self, constraints: &ConstraintIR) -> ComplexityClass {
let num_vars = constraints.variables.len();
let num_constraints = constraints.constraints.len();
if num_vars <= 10 && num_constraints <= 10 {
ComplexityClass::Simple
} else if num_vars <= 50 && num_constraints <= 50 {
ComplexityClass::Medium
} else if num_vars <= 200 && num_constraints <= 200 {
ComplexityClass::Complex
} else {
ComplexityClass::Complex
}
}
fn estimate_speculation_confidence(&self, signature: &ProblemSignature) -> f32 {
if let Some(stats) = self.performance_history.speculation_accuracy.get(signature) {
if stats.total_predictions > 0 {
return stats.correct_predictions as f32 / stats.total_predictions as f32;
}
}
match signature.complexity_class {
ComplexityClass::Simple => 0.9,
ComplexityClass::Medium => 0.7,
ComplexityClass::Complex => 0.5,
ComplexityClass::Unknown => 0.6,
}
}
fn estimate_solver_time(&self, signature: &ProblemSignature) -> f64 {
if let Some(stats) = self.performance_history.solver_performance.get(signature) {
return stats.average_solve_time_ms;
}
let base_time = match signature.complexity_class {
ComplexityClass::Simple => 10.0,
ComplexityClass::Medium => 100.0,
ComplexityClass::Complex => 1000.0,
ComplexityClass::Unknown => 500.0,
};
let size_factor = (signature.num_variables + signature.num_constraints) as f64;
base_time + size_factor * 0.1
}
fn solutions_match(
&self,
spec_result: &SolutionResult,
verify_result: &SolutionResult,
) -> bool {
spec_result.satisfiable == verify_result.satisfiable
}
fn update_speculation_accuracy(
&mut self,
signature: &ProblemSignature,
correct: bool,
confidence: f32,
) {
let stats = self
.performance_history
.speculation_accuracy
.entry(signature.clone())
.or_default();
if correct {
stats.correct_predictions += 1;
}
stats.total_predictions += 1;
let alpha = 0.1_f32; stats.average_confidence =
stats.average_confidence * (1.0_f32 - alpha) + confidence * alpha;
}
fn update_solver_performance(
&mut self,
signature: &ProblemSignature,
solve_time_ms: f64,
success: bool,
) {
let stats = self
.performance_history
.solver_performance
.entry(signature.clone())
.or_default();
let alpha = 0.1_f64; stats.average_solve_time_ms =
stats.average_solve_time_ms * (1.0_f64 - alpha) + solve_time_ms * alpha;
let alpha_f32 = alpha as f32;
stats.success_rate = stats.success_rate * (1.0_f32 - alpha_f32)
+ if success { 1.0_f32 } else { 0.0_f32 } * alpha_f32;
}
}
impl Default for SpeculateVerifyRouter {
fn default() -> Self {
Self::new()
}
}