1use crate::ReasoningStrategy;
10use crate::ir::{ConstraintIR, TheoryTag};
11use crate::meta_reasoning::SessionPerformance;
12use anyhow::Result;
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15use std::time::Instant;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct OptimizationConfig {
20 pub enable_backend_selection: bool,
22 pub enable_parameter_tuning: bool,
24 pub enable_performance_prediction: bool,
26 pub backend_switch_threshold: f32,
28 pub max_analysis_time_ms: f64,
30 pub calibration_sample_size: usize,
32}
33
34impl Default for OptimizationConfig {
35 fn default() -> Self {
36 Self {
37 enable_backend_selection: true,
38 enable_parameter_tuning: true,
39 enable_performance_prediction: true,
40 backend_switch_threshold: 0.3, max_analysis_time_ms: 50.0,
42 calibration_sample_size: 100,
43 }
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
49pub enum ComputeBackend {
50 CPU,
52 GPU,
54 SMT,
56 Neural,
58 Hybrid {
60 primary: Box<ComputeBackend>,
61 fallback: Box<ComputeBackend>,
62 },
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct BackendPerformance {
68 pub avg_time_ms: f64,
70 pub success_rate: f32,
72 pub memory_usage_mb: f64,
74 pub throughput_ops_per_sec: f64,
76 pub accuracy_confidence: f32,
78 pub sample_count: u32,
80 pub last_updated: u64,
82}
83
84impl Default for BackendPerformance {
85 fn default() -> Self {
86 Self {
87 avg_time_ms: 1000.0,
88 success_rate: 0.7,
89 memory_usage_mb: 100.0,
90 throughput_ops_per_sec: 1.0,
91 accuracy_confidence: 0.8,
92 sample_count: 0,
93 last_updated: 0,
94 }
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct WorkloadProfile {
101 pub complexity: f32,
103 pub variable_count: u32,
105 pub constraint_count: u32,
107 pub theories: Vec<TheoryTag>,
109 pub compute_intensity: ComputeIntensity,
111 pub memory_requirements: MemoryRequirements,
113 pub time_sensitivity: TimeSensitivity,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub enum ComputeIntensity {
119 Light, Medium, Heavy, Extreme, }
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub enum MemoryRequirements {
127 Small, Medium, Large, Huge, }
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub enum TimeSensitivity {
135 RealTime, Interactive, Batch, Offline, }
140
141#[derive(Debug, Clone)]
143pub struct OptimizationRecommendation {
144 pub backend: ComputeBackend,
146 pub strategy: ReasoningStrategy,
148 pub parameters: OptimizationParameters,
150 pub confidence: f32,
152 pub reasoning: String,
154 pub expected_performance: ExpectedPerformance,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct OptimizationParameters {
160 pub timeout_ms: f64,
162 pub worker_threads: u32,
164 pub memory_budget_mb: f64,
166 pub precision_level: PrecisionLevel,
168 pub caching_strategy: CachingStrategy,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub enum PrecisionLevel {
174 Low, Medium, High, }
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub enum CachingStrategy {
181 None, Aggressive, Selective, Adaptive, }
186
187#[derive(Debug, Clone)]
188pub struct ExpectedPerformance {
189 pub estimated_time_ms: f64,
190 pub success_probability: f32,
191 pub memory_usage_mb: f64,
192 pub accuracy_confidence: f32,
193}
194
195pub struct PerformanceOptimizer {
197 config: OptimizationConfig,
198 backend_stats: HashMap<ComputeBackend, BackendPerformance>,
199 workload_history: Vec<(WorkloadProfile, SessionPerformance)>,
200 parameter_tuner: ParameterTuner,
201 backend_selector: BackendSelector,
202}
203
204impl PerformanceOptimizer {
205 pub fn new(config: OptimizationConfig) -> Self {
206 Self {
207 config,
208 backend_stats: HashMap::new(),
209 workload_history: Vec::new(),
210 parameter_tuner: ParameterTuner::new(),
211 backend_selector: BackendSelector::new(),
212 }
213 }
214
215 pub fn optimize(
217 &mut self,
218 constraints: &ConstraintIR,
219 verbose: bool,
220 ) -> Result<OptimizationRecommendation> {
221 let start_time = Instant::now();
222
223 if verbose {
224 println!("🚀 Performance Optimizer: Analyzing constraints...");
225 }
226
227 let profile = self.analyze_workload(constraints)?;
229
230 if verbose {
231 println!(
232 " Workload: {} variables, {} constraints, complexity {:.2}",
233 profile.variable_count, profile.constraint_count, profile.complexity
234 );
235 }
236
237 let backend = if self.config.enable_backend_selection {
239 match (&profile.compute_intensity, profile.variable_count) {
240 (ComputeIntensity::Light, _) => ComputeBackend::CPU,
241 (ComputeIntensity::Medium, v) if v > 10 => ComputeBackend::GPU,
242 (ComputeIntensity::Heavy, _) => ComputeBackend::GPU,
243 (ComputeIntensity::Extreme, _) => ComputeBackend::GPU,
244 _ => {
245 if profile.theories.contains(&TheoryTag::NLA)
247 || profile.theories.contains(&TheoryTag::BV)
248 {
249 ComputeBackend::GPU } else {
251 ComputeBackend::CPU
252 }
253 }
254 }
255 } else {
256 ComputeBackend::CPU };
258
259 if verbose {
260 println!(" Recommended backend: {:?}", backend);
261 }
262
263 let parameters = if self.config.enable_parameter_tuning {
265 self.parameter_tuner
266 .tune_parameters(&profile, &backend, &self.workload_history)?
267 } else {
268 OptimizationParameters::default(&profile)
269 };
270
271 let expected_performance = if self.config.enable_performance_prediction {
273 self.predict_performance(&profile, &backend, ¶meters)?
274 } else {
275 ExpectedPerformance::default()
276 };
277
278 let strategy = self.select_strategy(&profile, &backend)?;
280
281 let elapsed = start_time.elapsed().as_millis() as f64;
282 if verbose {
283 println!(" Optimization analysis completed in {:.1}ms", elapsed);
284 }
285
286 Ok(OptimizationRecommendation {
287 backend,
288 strategy,
289 parameters,
290 confidence: 0.85, reasoning: format!(
292 "Selected based on workload analysis: {:?} complexity, {:?} time sensitivity",
293 profile.compute_intensity, profile.time_sensitivity
294 ),
295 expected_performance,
296 })
297 }
298
299 pub fn record_performance(
301 &mut self,
302 constraints: &ConstraintIR,
303 backend: &ComputeBackend,
304 performance: &SessionPerformance,
305 ) -> Result<()> {
306 let profile = self.analyze_workload(constraints)?;
307
308 {
310 let stats = self.backend_stats.entry(backend.clone()).or_default();
311 let alpha = 0.1f64; stats.avg_time_ms =
313 (1.0 - alpha) * stats.avg_time_ms + alpha * performance.total_solve_time_ms;
314 stats.success_rate = ((1.0 - alpha) * stats.success_rate as f64
315 + alpha * if performance.success { 1.0 } else { 0.0 })
316 as f32;
317 stats.accuracy_confidence = ((1.0 - alpha) * stats.accuracy_confidence as f64
318 + alpha * performance.confidence_achieved as f64)
319 as f32;
320 stats.sample_count += 1;
321 stats.last_updated = std::time::SystemTime::now()
322 .duration_since(std::time::UNIX_EPOCH)
323 .unwrap_or_default()
324 .as_secs();
325 }
326
327 self.workload_history.push((profile, performance.clone()));
329
330 if self.workload_history.len() > self.config.calibration_sample_size {
332 self.workload_history.remove(0);
333 }
334
335 Ok(())
338 }
339
340 fn analyze_workload(&self, constraints: &ConstraintIR) -> Result<WorkloadProfile> {
341 let variable_count = constraints.variables.len() as u32;
342 let constraint_count = constraints.constraints.len() as u32;
343
344 let complexity = self.compute_complexity(constraints);
346
347 let compute_intensity = match (variable_count, constraint_count, complexity) {
349 (v, c, _) if v <= 5 && c <= 3 => ComputeIntensity::Light,
350 (v, c, x) if v <= 20 && c <= 10 && x < 0.5 => ComputeIntensity::Medium,
351 (v, c, x) if v <= 100 && c <= 50 && x < 0.8 => ComputeIntensity::Heavy,
352 _ => ComputeIntensity::Extreme,
353 };
354
355 let memory_requirements = match variable_count + constraint_count {
357 n if n <= 10 => MemoryRequirements::Small,
358 n if n <= 100 => MemoryRequirements::Medium,
359 n if n <= 1000 => MemoryRequirements::Large,
360 _ => MemoryRequirements::Huge,
361 };
362
363 let time_sensitivity = TimeSensitivity::Interactive;
365
366 Ok(WorkloadProfile {
367 complexity,
368 variable_count,
369 constraint_count,
370 theories: constraints.theory_tags.to_vec(),
371 compute_intensity,
372 memory_requirements,
373 time_sensitivity,
374 })
375 }
376
377 fn compute_complexity(&self, constraints: &ConstraintIR) -> f32 {
378 let var_factor = (constraints.variables.len() as f32).ln() / 10.0;
379 let constraint_factor = (constraints.constraints.len() as f32).ln() / 5.0;
380
381 let theory_complexity: f32 = constraints
383 .theory_tags
384 .iter()
385 .map(|tag| match tag {
386 TheoryTag::LIA | TheoryTag::LRA => 0.2,
387 TheoryTag::NLA => 0.8,
388 TheoryTag::BV => 0.4,
389 TheoryTag::Array => 0.6,
390 TheoryTag::String => 0.5,
391 _ => 0.3,
392 })
393 .sum();
394
395 ((var_factor + constraint_factor + theory_complexity) / 3.0).min(1.0)
396 }
397
398 fn select_strategy(
399 &self,
400 profile: &WorkloadProfile,
401 backend: &ComputeBackend,
402 ) -> Result<ReasoningStrategy> {
403 match (
404 backend,
405 &profile.time_sensitivity,
406 &profile.compute_intensity,
407 ) {
408 (ComputeBackend::Neural, _, _) => Ok(ReasoningStrategy::Speculate),
409 (ComputeBackend::SMT, _, _) => Ok(ReasoningStrategy::Verify),
410 (_, TimeSensitivity::RealTime, _) => Ok(ReasoningStrategy::Speculate),
411 (_, _, ComputeIntensity::Extreme) => Ok(ReasoningStrategy::Verify),
412 _ => Ok(ReasoningStrategy::Hybrid),
413 }
414 }
415
416 fn predict_performance(
417 &self,
418 profile: &WorkloadProfile,
419 backend: &ComputeBackend,
420 parameters: &OptimizationParameters,
421 ) -> Result<ExpectedPerformance> {
422 let timeout_factor = if parameters.timeout_ms > 0.0 {
424 1.0 + (parameters.timeout_ms / 1000.0).ln() / 10.0
425 } else {
426 1.0
427 };
428
429 let parallel_factor = if parameters.worker_threads > 1 {
430 0.7 } else {
432 1.0
433 };
434
435 if let Some(stats) = self.backend_stats.get(backend) {
437 let complexity_factor = 1.0 + profile.complexity as f64;
438 let size_factor = 1.0 + (profile.variable_count as f64).ln() / 10.0;
439
440 Ok(ExpectedPerformance {
441 estimated_time_ms: stats.avg_time_ms
442 * complexity_factor
443 * size_factor
444 * timeout_factor
445 * parallel_factor,
446 success_probability: stats.success_rate,
447 memory_usage_mb: stats.memory_usage_mb * size_factor,
448 accuracy_confidence: stats.accuracy_confidence,
449 })
450 } else {
451 let mut fallback = ExpectedPerformance::default();
453 fallback.estimated_time_ms *= timeout_factor * parallel_factor;
454 Ok(fallback)
455 }
456 }
457}
458
459impl OptimizationParameters {
460 fn default(profile: &WorkloadProfile) -> Self {
461 let timeout_ms = match profile.compute_intensity {
462 ComputeIntensity::Light => 100.0,
463 ComputeIntensity::Medium => 1000.0,
464 ComputeIntensity::Heavy => 5000.0,
465 ComputeIntensity::Extreme => 30000.0,
466 };
467
468 let worker_threads = match profile.compute_intensity {
469 ComputeIntensity::Light => 1,
470 ComputeIntensity::Medium => 2,
471 ComputeIntensity::Heavy => 4,
472 ComputeIntensity::Extreme => 8,
473 };
474
475 let memory_budget_mb = match profile.memory_requirements {
476 MemoryRequirements::Small => 50.0,
477 MemoryRequirements::Medium => 200.0,
478 MemoryRequirements::Large => 1000.0,
479 MemoryRequirements::Huge => 4000.0,
480 };
481
482 Self {
483 timeout_ms,
484 worker_threads,
485 memory_budget_mb,
486 precision_level: PrecisionLevel::Medium,
487 caching_strategy: CachingStrategy::Selective,
488 }
489 }
490}
491
492impl Default for ExpectedPerformance {
493 fn default() -> Self {
494 Self {
495 estimated_time_ms: 1000.0,
496 success_probability: 0.8,
497 memory_usage_mb: 100.0,
498 accuracy_confidence: 0.85,
499 }
500 }
501}
502
503pub struct ParameterTuner {
505 }
507
508impl ParameterTuner {
509 pub fn new() -> Self {
510 Self {}
511 }
512
513 pub fn tune_parameters(
514 &self,
515 profile: &WorkloadProfile,
516 _backend: &ComputeBackend,
517 history: &[(WorkloadProfile, SessionPerformance)],
518 ) -> Result<OptimizationParameters> {
519 let similar_workloads: Vec<_> = history
521 .iter()
522 .filter(|(p, _)| self.workload_similarity(profile, p) > 0.7)
523 .collect();
524
525 if similar_workloads.is_empty() {
526 return Ok(OptimizationParameters::default(profile));
527 }
528
529 let avg_success_rate: f32 = similar_workloads
531 .iter()
532 .map(|(_, perf)| if perf.success { 1.0 } else { 0.0 })
533 .sum::<f32>()
534 / similar_workloads.len() as f32;
535
536 let avg_time: f64 = similar_workloads
537 .iter()
538 .map(|(_, perf)| perf.total_solve_time_ms)
539 .sum::<f64>()
540 / similar_workloads.len() as f64;
541
542 let mut params = OptimizationParameters::default(profile);
544
545 if avg_success_rate < 0.7 {
546 params.timeout_ms *= 1.5;
548 params.worker_threads = (params.worker_threads * 2).min(16);
549 }
550
551 if avg_time > 5000.0 {
552 params.precision_level = PrecisionLevel::Medium;
554 params.caching_strategy = CachingStrategy::Aggressive;
555 }
556
557 Ok(params)
558 }
559
560 fn workload_similarity(&self, a: &WorkloadProfile, b: &WorkloadProfile) -> f32 {
561 let complexity_sim = 1.0 - (a.complexity - b.complexity).abs();
562 let var_sim = 1.0
563 - ((a.variable_count as f32 - b.variable_count as f32).abs()
564 / a.variable_count.max(b.variable_count).max(1) as f32);
565 let theory_sim = self.theory_similarity(&a.theories, &b.theories);
566
567 (complexity_sim + var_sim + theory_sim) / 3.0
568 }
569
570 fn theory_similarity(&self, a: &[TheoryTag], b: &[TheoryTag]) -> f32 {
571 if a.is_empty() && b.is_empty() {
572 return 1.0;
573 }
574
575 let intersection: usize = a.iter().filter(|tag| b.contains(tag)).count();
576 let union: usize = a.len() + b.len() - intersection;
577
578 if union == 0 {
579 1.0
580 } else {
581 intersection as f32 / union as f32
582 }
583 }
584}
585
586pub struct BackendSelector {
588 backend_stats: HashMap<ComputeBackend, BackendPerformance>,
590 gpu_available: bool,
592 smt_available: bool,
594}
595
596impl Default for BackendSelector {
597 fn default() -> Self {
598 Self::new()
599 }
600}
601
602impl BackendSelector {
603 pub fn new() -> Self {
604 let gpu_available = Self::detect_gpu();
606 let smt_available = Self::detect_smt();
607
608 Self {
609 backend_stats: HashMap::new(),
610 gpu_available,
611 smt_available,
612 }
613 }
614
615 fn detect_gpu() -> bool {
617 #[cfg(feature = "gpu")]
619 {
620 true
623 }
624 #[cfg(not(feature = "gpu"))]
625 {
626 false
627 }
628 }
629
630 fn detect_smt() -> bool {
632 #[cfg(feature = "z3-solver")]
633 {
634 true
635 }
636 #[cfg(not(feature = "z3-solver"))]
637 {
638 false
639 }
640 }
641
642 pub fn update_stats(&mut self, backend: ComputeBackend, perf: BackendPerformance) {
644 self.backend_stats.insert(backend, perf);
645 }
646
647 pub fn select_backend(&self, profile: &WorkloadProfile) -> Result<ComputeBackend> {
648 let stats = &self.backend_stats;
650
651 let problem_size = profile.variable_count * profile.constraint_count;
653
654 match (&profile.time_sensitivity, &profile.compute_intensity) {
656 (TimeSensitivity::RealTime, _) => {
657 if self.is_fast_enough(&ComputeBackend::Neural, stats, 10.0) {
659 Ok(ComputeBackend::Neural)
660 } else {
661 Ok(ComputeBackend::CPU)
662 }
663 }
664 (TimeSensitivity::Interactive, ComputeIntensity::Light) => {
665 Ok(ComputeBackend::CPU)
667 }
668 (TimeSensitivity::Interactive, ComputeIntensity::Medium) => {
669 if self.gpu_available
671 && problem_size > 500
672 && self.is_available(&ComputeBackend::GPU, stats)
673 {
674 Ok(ComputeBackend::GPU)
675 } else {
676 Ok(ComputeBackend::CPU)
677 }
678 }
679 (_, ComputeIntensity::Heavy | ComputeIntensity::Extreme) => {
680 if self.smt_available && profile.theories.contains(&TheoryTag::NLA) {
682 Ok(ComputeBackend::SMT)
683 } else if self.gpu_available && problem_size > 1000 {
684 Ok(ComputeBackend::Hybrid {
686 primary: Box::new(ComputeBackend::GPU),
687 fallback: Box::new(ComputeBackend::CPU),
688 })
689 } else {
690 Ok(ComputeBackend::CPU)
691 }
692 }
693 _ => Ok(ComputeBackend::CPU), }
695 }
696
697 fn is_fast_enough(
698 &self,
699 backend: &ComputeBackend,
700 stats: &HashMap<ComputeBackend, BackendPerformance>,
701 threshold_ms: f64,
702 ) -> bool {
703 stats
704 .get(backend)
705 .map(|s| s.avg_time_ms < threshold_ms)
706 .unwrap_or(false)
707 }
708
709 fn is_available(
710 &self,
711 backend: &ComputeBackend,
712 stats: &HashMap<ComputeBackend, BackendPerformance>,
713 ) -> bool {
714 stats
715 .get(backend)
716 .map(|s| s.success_rate > 0.5)
717 .unwrap_or(false)
718 }
719}