use crate::ReasoningStrategy;
use crate::ir::{ConstraintIR, TheoryTag};
use crate::meta_reasoning::SessionPerformance;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Instant;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptimizationConfig {
pub enable_backend_selection: bool,
pub enable_parameter_tuning: bool,
pub enable_performance_prediction: bool,
pub backend_switch_threshold: f32,
pub max_analysis_time_ms: f64,
pub calibration_sample_size: usize,
}
impl Default for OptimizationConfig {
fn default() -> Self {
Self {
enable_backend_selection: true,
enable_parameter_tuning: true,
enable_performance_prediction: true,
backend_switch_threshold: 0.3, max_analysis_time_ms: 50.0,
calibration_sample_size: 100,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ComputeBackend {
CPU,
GPU,
SMT,
Neural,
Hybrid {
primary: Box<ComputeBackend>,
fallback: Box<ComputeBackend>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackendPerformance {
pub avg_time_ms: f64,
pub success_rate: f32,
pub memory_usage_mb: f64,
pub throughput_ops_per_sec: f64,
pub accuracy_confidence: f32,
pub sample_count: u32,
pub last_updated: u64,
}
impl Default for BackendPerformance {
fn default() -> Self {
Self {
avg_time_ms: 1000.0,
success_rate: 0.7,
memory_usage_mb: 100.0,
throughput_ops_per_sec: 1.0,
accuracy_confidence: 0.8,
sample_count: 0,
last_updated: 0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkloadProfile {
pub complexity: f32,
pub variable_count: u32,
pub constraint_count: u32,
pub theories: Vec<TheoryTag>,
pub compute_intensity: ComputeIntensity,
pub memory_requirements: MemoryRequirements,
pub time_sensitivity: TimeSensitivity,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ComputeIntensity {
Light, Medium, Heavy, Extreme, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MemoryRequirements {
Small, Medium, Large, Huge, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TimeSensitivity {
RealTime, Interactive, Batch, Offline, }
#[derive(Debug, Clone)]
pub struct OptimizationRecommendation {
pub backend: ComputeBackend,
pub strategy: ReasoningStrategy,
pub parameters: OptimizationParameters,
pub confidence: f32,
pub reasoning: String,
pub expected_performance: ExpectedPerformance,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptimizationParameters {
pub timeout_ms: f64,
pub worker_threads: u32,
pub memory_budget_mb: f64,
pub precision_level: PrecisionLevel,
pub caching_strategy: CachingStrategy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PrecisionLevel {
Low, Medium, High, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum CachingStrategy {
None, Aggressive, Selective, Adaptive, }
#[derive(Debug, Clone)]
pub struct ExpectedPerformance {
pub estimated_time_ms: f64,
pub success_probability: f32,
pub memory_usage_mb: f64,
pub accuracy_confidence: f32,
}
pub struct PerformanceOptimizer {
config: OptimizationConfig,
backend_stats: HashMap<ComputeBackend, BackendPerformance>,
workload_history: Vec<(WorkloadProfile, SessionPerformance)>,
parameter_tuner: ParameterTuner,
backend_selector: BackendSelector,
}
impl PerformanceOptimizer {
pub fn new(config: OptimizationConfig) -> Self {
Self {
config,
backend_stats: HashMap::new(),
workload_history: Vec::new(),
parameter_tuner: ParameterTuner::new(),
backend_selector: BackendSelector::new(),
}
}
pub fn optimize(
&mut self,
constraints: &ConstraintIR,
verbose: bool,
) -> Result<OptimizationRecommendation> {
let start_time = Instant::now();
if verbose {
println!("🚀 Performance Optimizer: Analyzing constraints...");
}
let profile = self.analyze_workload(constraints)?;
if verbose {
println!(
" Workload: {} variables, {} constraints, complexity {:.2}",
profile.variable_count, profile.constraint_count, profile.complexity
);
}
let backend = if self.config.enable_backend_selection {
match (&profile.compute_intensity, profile.variable_count) {
(ComputeIntensity::Light, _) => ComputeBackend::CPU,
(ComputeIntensity::Medium, v) if v > 10 => ComputeBackend::GPU,
(ComputeIntensity::Heavy, _) => ComputeBackend::GPU,
(ComputeIntensity::Extreme, _) => ComputeBackend::GPU,
_ => {
if profile.theories.contains(&TheoryTag::NLA)
|| profile.theories.contains(&TheoryTag::BV)
{
ComputeBackend::GPU } else {
ComputeBackend::CPU
}
}
}
} else {
ComputeBackend::CPU };
if verbose {
println!(" Recommended backend: {:?}", backend);
}
let parameters = if self.config.enable_parameter_tuning {
self.parameter_tuner
.tune_parameters(&profile, &backend, &self.workload_history)?
} else {
OptimizationParameters::default(&profile)
};
let expected_performance = if self.config.enable_performance_prediction {
self.predict_performance(&profile, &backend, ¶meters)?
} else {
ExpectedPerformance::default()
};
let strategy = self.select_strategy(&profile, &backend)?;
let elapsed = start_time.elapsed().as_millis() as f64;
if verbose {
println!(" Optimization analysis completed in {:.1}ms", elapsed);
}
Ok(OptimizationRecommendation {
backend,
strategy,
parameters,
confidence: 0.85, reasoning: format!(
"Selected based on workload analysis: {:?} complexity, {:?} time sensitivity",
profile.compute_intensity, profile.time_sensitivity
),
expected_performance,
})
}
pub fn record_performance(
&mut self,
constraints: &ConstraintIR,
backend: &ComputeBackend,
performance: &SessionPerformance,
) -> Result<()> {
let profile = self.analyze_workload(constraints)?;
{
let stats = self.backend_stats.entry(backend.clone()).or_default();
let alpha = 0.1f64; stats.avg_time_ms =
(1.0 - alpha) * stats.avg_time_ms + alpha * performance.total_solve_time_ms;
stats.success_rate = ((1.0 - alpha) * stats.success_rate as f64
+ alpha * if performance.success { 1.0 } else { 0.0 })
as f32;
stats.accuracy_confidence = ((1.0 - alpha) * stats.accuracy_confidence as f64
+ alpha * performance.confidence_achieved as f64)
as f32;
stats.sample_count += 1;
stats.last_updated = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
}
self.workload_history.push((profile, performance.clone()));
if self.workload_history.len() > self.config.calibration_sample_size {
self.workload_history.remove(0);
}
Ok(())
}
fn analyze_workload(&self, constraints: &ConstraintIR) -> Result<WorkloadProfile> {
let variable_count = constraints.variables.len() as u32;
let constraint_count = constraints.constraints.len() as u32;
let complexity = self.compute_complexity(constraints);
let compute_intensity = match (variable_count, constraint_count, complexity) {
(v, c, _) if v <= 5 && c <= 3 => ComputeIntensity::Light,
(v, c, x) if v <= 20 && c <= 10 && x < 0.5 => ComputeIntensity::Medium,
(v, c, x) if v <= 100 && c <= 50 && x < 0.8 => ComputeIntensity::Heavy,
_ => ComputeIntensity::Extreme,
};
let memory_requirements = match variable_count + constraint_count {
n if n <= 10 => MemoryRequirements::Small,
n if n <= 100 => MemoryRequirements::Medium,
n if n <= 1000 => MemoryRequirements::Large,
_ => MemoryRequirements::Huge,
};
let time_sensitivity = TimeSensitivity::Interactive;
Ok(WorkloadProfile {
complexity,
variable_count,
constraint_count,
theories: constraints.theory_tags.to_vec(),
compute_intensity,
memory_requirements,
time_sensitivity,
})
}
fn compute_complexity(&self, constraints: &ConstraintIR) -> f32 {
let var_factor = (constraints.variables.len() as f32).ln() / 10.0;
let constraint_factor = (constraints.constraints.len() as f32).ln() / 5.0;
let theory_complexity: f32 = constraints
.theory_tags
.iter()
.map(|tag| match tag {
TheoryTag::LIA | TheoryTag::LRA => 0.2,
TheoryTag::NLA => 0.8,
TheoryTag::BV => 0.4,
TheoryTag::Array => 0.6,
TheoryTag::String => 0.5,
_ => 0.3,
})
.sum();
((var_factor + constraint_factor + theory_complexity) / 3.0).min(1.0)
}
fn select_strategy(
&self,
profile: &WorkloadProfile,
backend: &ComputeBackend,
) -> Result<ReasoningStrategy> {
match (
backend,
&profile.time_sensitivity,
&profile.compute_intensity,
) {
(ComputeBackend::Neural, _, _) => Ok(ReasoningStrategy::Speculate),
(ComputeBackend::SMT, _, _) => Ok(ReasoningStrategy::Verify),
(_, TimeSensitivity::RealTime, _) => Ok(ReasoningStrategy::Speculate),
(_, _, ComputeIntensity::Extreme) => Ok(ReasoningStrategy::Verify),
_ => Ok(ReasoningStrategy::Hybrid),
}
}
fn predict_performance(
&self,
profile: &WorkloadProfile,
backend: &ComputeBackend,
parameters: &OptimizationParameters,
) -> Result<ExpectedPerformance> {
let timeout_factor = if parameters.timeout_ms > 0.0 {
1.0 + (parameters.timeout_ms / 1000.0).ln() / 10.0
} else {
1.0
};
let parallel_factor = if parameters.worker_threads > 1 {
0.7 } else {
1.0
};
if let Some(stats) = self.backend_stats.get(backend) {
let complexity_factor = 1.0 + profile.complexity as f64;
let size_factor = 1.0 + (profile.variable_count as f64).ln() / 10.0;
Ok(ExpectedPerformance {
estimated_time_ms: stats.avg_time_ms
* complexity_factor
* size_factor
* timeout_factor
* parallel_factor,
success_probability: stats.success_rate,
memory_usage_mb: stats.memory_usage_mb * size_factor,
accuracy_confidence: stats.accuracy_confidence,
})
} else {
let mut fallback = ExpectedPerformance::default();
fallback.estimated_time_ms *= timeout_factor * parallel_factor;
Ok(fallback)
}
}
}
impl OptimizationParameters {
fn default(profile: &WorkloadProfile) -> Self {
let timeout_ms = match profile.compute_intensity {
ComputeIntensity::Light => 100.0,
ComputeIntensity::Medium => 1000.0,
ComputeIntensity::Heavy => 5000.0,
ComputeIntensity::Extreme => 30000.0,
};
let worker_threads = match profile.compute_intensity {
ComputeIntensity::Light => 1,
ComputeIntensity::Medium => 2,
ComputeIntensity::Heavy => 4,
ComputeIntensity::Extreme => 8,
};
let memory_budget_mb = match profile.memory_requirements {
MemoryRequirements::Small => 50.0,
MemoryRequirements::Medium => 200.0,
MemoryRequirements::Large => 1000.0,
MemoryRequirements::Huge => 4000.0,
};
Self {
timeout_ms,
worker_threads,
memory_budget_mb,
precision_level: PrecisionLevel::Medium,
caching_strategy: CachingStrategy::Selective,
}
}
}
impl Default for ExpectedPerformance {
fn default() -> Self {
Self {
estimated_time_ms: 1000.0,
success_probability: 0.8,
memory_usage_mb: 100.0,
accuracy_confidence: 0.85,
}
}
}
pub struct ParameterTuner {
}
impl ParameterTuner {
pub fn new() -> Self {
Self {}
}
pub fn tune_parameters(
&self,
profile: &WorkloadProfile,
_backend: &ComputeBackend,
history: &[(WorkloadProfile, SessionPerformance)],
) -> Result<OptimizationParameters> {
let similar_workloads: Vec<_> = history
.iter()
.filter(|(p, _)| self.workload_similarity(profile, p) > 0.7)
.collect();
if similar_workloads.is_empty() {
return Ok(OptimizationParameters::default(profile));
}
let avg_success_rate: f32 = similar_workloads
.iter()
.map(|(_, perf)| if perf.success { 1.0 } else { 0.0 })
.sum::<f32>()
/ similar_workloads.len() as f32;
let avg_time: f64 = similar_workloads
.iter()
.map(|(_, perf)| perf.total_solve_time_ms)
.sum::<f64>()
/ similar_workloads.len() as f64;
let mut params = OptimizationParameters::default(profile);
if avg_success_rate < 0.7 {
params.timeout_ms *= 1.5;
params.worker_threads = (params.worker_threads * 2).min(16);
}
if avg_time > 5000.0 {
params.precision_level = PrecisionLevel::Medium;
params.caching_strategy = CachingStrategy::Aggressive;
}
Ok(params)
}
fn workload_similarity(&self, a: &WorkloadProfile, b: &WorkloadProfile) -> f32 {
let complexity_sim = 1.0 - (a.complexity - b.complexity).abs();
let var_sim = 1.0
- ((a.variable_count as f32 - b.variable_count as f32).abs()
/ a.variable_count.max(b.variable_count).max(1) as f32);
let theory_sim = self.theory_similarity(&a.theories, &b.theories);
(complexity_sim + var_sim + theory_sim) / 3.0
}
fn theory_similarity(&self, a: &[TheoryTag], b: &[TheoryTag]) -> f32 {
if a.is_empty() && b.is_empty() {
return 1.0;
}
let intersection: usize = a.iter().filter(|tag| b.contains(tag)).count();
let union: usize = a.len() + b.len() - intersection;
if union == 0 {
1.0
} else {
intersection as f32 / union as f32
}
}
}
pub struct BackendSelector {
backend_stats: HashMap<ComputeBackend, BackendPerformance>,
gpu_available: bool,
smt_available: bool,
}
impl Default for BackendSelector {
fn default() -> Self {
Self::new()
}
}
impl BackendSelector {
pub fn new() -> Self {
let gpu_available = Self::detect_gpu();
let smt_available = Self::detect_smt();
Self {
backend_stats: HashMap::new(),
gpu_available,
smt_available,
}
}
fn detect_gpu() -> bool {
#[cfg(feature = "gpu")]
{
true
}
#[cfg(not(feature = "gpu"))]
{
false
}
}
fn detect_smt() -> bool {
#[cfg(feature = "z3-solver")]
{
true
}
#[cfg(not(feature = "z3-solver"))]
{
false
}
}
pub fn update_stats(&mut self, backend: ComputeBackend, perf: BackendPerformance) {
self.backend_stats.insert(backend, perf);
}
pub fn select_backend(&self, profile: &WorkloadProfile) -> Result<ComputeBackend> {
let stats = &self.backend_stats;
let problem_size = profile.variable_count * profile.constraint_count;
match (&profile.time_sensitivity, &profile.compute_intensity) {
(TimeSensitivity::RealTime, _) => {
if self.is_fast_enough(&ComputeBackend::Neural, stats, 10.0) {
Ok(ComputeBackend::Neural)
} else {
Ok(ComputeBackend::CPU)
}
}
(TimeSensitivity::Interactive, ComputeIntensity::Light) => {
Ok(ComputeBackend::CPU)
}
(TimeSensitivity::Interactive, ComputeIntensity::Medium) => {
if self.gpu_available
&& problem_size > 500
&& self.is_available(&ComputeBackend::GPU, stats)
{
Ok(ComputeBackend::GPU)
} else {
Ok(ComputeBackend::CPU)
}
}
(_, ComputeIntensity::Heavy | ComputeIntensity::Extreme) => {
if self.smt_available && profile.theories.contains(&TheoryTag::NLA) {
Ok(ComputeBackend::SMT)
} else if self.gpu_available && problem_size > 1000 {
Ok(ComputeBackend::Hybrid {
primary: Box::new(ComputeBackend::GPU),
fallback: Box::new(ComputeBackend::CPU),
})
} else {
Ok(ComputeBackend::CPU)
}
}
_ => Ok(ComputeBackend::CPU), }
}
fn is_fast_enough(
&self,
backend: &ComputeBackend,
stats: &HashMap<ComputeBackend, BackendPerformance>,
threshold_ms: f64,
) -> bool {
stats
.get(backend)
.map(|s| s.avg_time_ms < threshold_ms)
.unwrap_or(false)
}
fn is_available(
&self,
backend: &ComputeBackend,
stats: &HashMap<ComputeBackend, BackendPerformance>,
) -> bool {
stats
.get(backend)
.map(|s| s.success_rate > 0.5)
.unwrap_or(false)
}
}