quantrs2_tytan/solution_debugger/
mod.rs

1//! Solution debugger for quantum optimization.
2//!
3//! This module provides comprehensive debugging tools for analyzing
4//! quantum optimization solutions, constraint violations, and solution quality.
5
6pub mod analysis;
7pub mod comparison;
8pub mod config;
9pub mod constraint_analyzer;
10pub mod energy_analyzer;
11pub mod reporting;
12pub mod types;
13pub mod visualization;
14
15#[cfg(feature = "dwave")]
16use crate::compile::{Compile, CompiledModel};
17
18// Re-export main types
19pub use analysis::*;
20pub use comparison::*;
21pub use config::*;
22pub use constraint_analyzer::*;
23pub use energy_analyzer::*;
24pub use reporting::*;
25pub use types::*;
26pub use visualization::*;
27
28/// Solution debugger
29pub struct SolutionDebugger {
30    /// Configuration
31    config: config::DebuggerConfig,
32    /// Problem information
33    problem_info: types::ProblemInfo,
34    /// Constraint analyzer
35    constraint_analyzer: constraint_analyzer::ConstraintAnalyzer,
36    /// Energy analyzer
37    energy_analyzer: energy_analyzer::EnergyAnalyzer,
38    /// Solution comparator
39    comparator: comparison::SolutionComparator,
40    /// Visualization engine
41    visualizer: visualization::SolutionVisualizer,
42}
43
44impl SolutionDebugger {
45    /// Create new debugger
46    pub fn new(problem_info: types::ProblemInfo, config: config::DebuggerConfig) -> Self {
47        Self {
48            config,
49            problem_info,
50            constraint_analyzer: constraint_analyzer::ConstraintAnalyzer::new(1e-6),
51            energy_analyzer: energy_analyzer::EnergyAnalyzer::new(2),
52            comparator: comparison::SolutionComparator::new(),
53            visualizer: visualization::SolutionVisualizer::new(),
54        }
55    }
56
57    /// Debug solution
58    pub fn debug_solution(&mut self, solution: &types::Solution) -> reporting::DebugReport {
59        let mut report = reporting::DebugReport {
60            solution: solution.clone(),
61            constraint_analysis: None,
62            energy_analysis: None,
63            comparison_results: Vec::new(),
64            visualizations: Vec::new(),
65            issues: Vec::new(),
66            suggestions: Vec::new(),
67            summary: reporting::DebugSummary::default(),
68        };
69
70        // Analyze constraints
71        if self.config.check_constraints {
72            report.constraint_analysis = Some(self.analyze_constraints(solution));
73        }
74
75        // Analyze energy
76        if self.config.analyze_energy {
77            report.energy_analysis = Some(self.analyze_energy(solution));
78        }
79
80        // Compare with known solutions
81        if self.config.compare_solutions {
82            if let Some(ref optimal) = self.problem_info.optimal_solution {
83                report
84                    .comparison_results
85                    .push(self.compare_solutions(solution, optimal));
86            }
87        }
88
89        // Generate visualizations
90        if self.config.generate_visuals {
91            report.visualizations = self.generate_visualizations(solution);
92        }
93
94        // Identify issues
95        report.issues = self.identify_issues(&report);
96
97        // Generate suggestions
98        report.suggestions = self.generate_suggestions(&report);
99
100        // Generate summary
101        report.summary = self.generate_summary(&report);
102
103        report
104    }
105
106    /// Analyze constraints
107    fn analyze_constraints(&mut self, solution: &types::Solution) -> analysis::ConstraintAnalysis {
108        let violations = self
109            .constraint_analyzer
110            .analyze(&self.problem_info.constraints, &solution.assignments);
111
112        let satisfied_count = self.problem_info.constraints.len() - violations.len();
113        let satisfaction_rate = satisfied_count as f64 / self.problem_info.constraints.len() as f64;
114
115        analysis::ConstraintAnalysis {
116            total_constraints: self.problem_info.constraints.len(),
117            satisfied: satisfied_count,
118            violated: violations.len(),
119            satisfaction_rate,
120            penalty_incurred: violations
121                .iter()
122                .map(|v| v.constraint.penalty * v.violation_amount)
123                .sum(),
124            violations,
125        }
126    }
127
128    /// Analyze energy
129    fn analyze_energy(&mut self, solution: &types::Solution) -> analysis::EnergyAnalysis {
130        let breakdown = self.energy_analyzer.analyze(
131            &self.problem_info.qubo,
132            &solution.assignments,
133            &self.problem_info.var_map,
134        );
135
136        // Find critical variables
137        let mut critical_vars: Vec<_> = breakdown
138            .variable_contributions
139            .iter()
140            .map(|(var, contrib)| (var.clone(), contrib.abs()))
141            .collect();
142        critical_vars.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
143        critical_vars.truncate(10);
144
145        // Find critical interactions
146        let mut critical_interactions: Vec<_> = breakdown
147            .interaction_contributions
148            .iter()
149            .map(|(vars, contrib)| (vars.clone(), contrib.abs()))
150            .collect();
151        critical_interactions
152            .sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
153        critical_interactions.truncate(10);
154
155        analysis::EnergyAnalysis {
156            total_energy: breakdown.total_energy,
157            breakdown: breakdown.clone(),
158            critical_variables: critical_vars,
159            critical_interactions,
160            improvement_potential: self.estimate_improvement_potential(&breakdown),
161        }
162    }
163
164    /// Compare solutions
165    fn compare_solutions(
166        &self,
167        sol1: &types::Solution,
168        sol2: &types::Solution,
169    ) -> comparison::ComparisonResult {
170        self.comparator.compare(sol1, sol2, &self.problem_info)
171    }
172
173    /// Generate visualizations
174    fn generate_visualizations(
175        &self,
176        solution: &types::Solution,
177    ) -> Vec<visualization::Visualization> {
178        vec![
179            // Solution matrix visualization
180            self.visualizer
181                .visualize_solution_matrix(solution, &self.problem_info),
182            // Energy landscape visualization
183            self.visualizer
184                .visualize_energy_landscape(solution, &self.problem_info),
185            // Constraint graph visualization
186            self.visualizer
187                .visualize_constraint_graph(solution, &self.problem_info),
188        ]
189    }
190
191    /// Identify issues
192    fn identify_issues(&self, report: &reporting::DebugReport) -> Vec<reporting::Issue> {
193        let mut issues = Vec::new();
194
195        // Check constraint violations
196        if let Some(ref constraint_analysis) = report.constraint_analysis {
197            if constraint_analysis.satisfaction_rate < 0.95 {
198                issues.push(reporting::Issue {
199                    severity: reporting::IssueSeverity::High,
200                    category: "Constraints".to_string(),
201                    description: format!(
202                        "Only {:.1}% of constraints satisfied",
203                        constraint_analysis.satisfaction_rate * 100.0
204                    ),
205                    location: "Constraint analysis".to_string(),
206                    suggested_action: "Review constraint violations and adjust solution"
207                        .to_string(),
208                });
209            }
210        }
211
212        // Check energy analysis
213        if let Some(ref energy_analysis) = report.energy_analysis {
214            if energy_analysis.improvement_potential > 0.1 {
215                issues.push(reporting::Issue {
216                    severity: reporting::IssueSeverity::Medium,
217                    category: "Energy".to_string(),
218                    description: "Significant energy improvement potential detected".to_string(),
219                    location: "Energy analysis".to_string(),
220                    suggested_action: "Consider local optimization or different sampling strategy"
221                        .to_string(),
222                });
223            }
224        }
225
226        issues
227    }
228
229    /// Generate suggestions
230    fn generate_suggestions(&self, report: &reporting::DebugReport) -> Vec<reporting::Suggestion> {
231        let mut suggestions = Vec::new();
232
233        // Suggestions based on constraint violations
234        if let Some(ref constraint_analysis) = report.constraint_analysis {
235            for violation in &constraint_analysis.violations {
236                if !violation.suggested_fixes.is_empty() {
237                    suggestions.push(reporting::Suggestion {
238                        category: "Constraint Fix".to_string(),
239                        description: format!(
240                            "Fix violation in constraint: {}",
241                            violation
242                                .constraint
243                                .name
244                                .as_ref()
245                                .unwrap_or(&"unnamed".to_string())
246                        ),
247                        impact: violation.violation_amount,
248                        feasibility: 0.8,
249                        action_steps: violation
250                            .suggested_fixes
251                            .iter()
252                            .map(|fix| fix.description.clone())
253                            .collect(),
254                    });
255                }
256            }
257        }
258
259        suggestions
260    }
261
262    /// Generate summary
263    fn generate_summary(&self, report: &reporting::DebugReport) -> reporting::DebugSummary {
264        let total_issues = report.issues.len();
265        let critical_issues = report
266            .issues
267            .iter()
268            .filter(|i| matches!(i.severity, reporting::IssueSeverity::Critical))
269            .count();
270        let suggestions_count = report.suggestions.len();
271
272        let mut summary = reporting::DebugSummary {
273            total_issues,
274            critical_issues,
275            suggestions_count,
276            ..Default::default()
277        };
278
279        if let Some(ref constraint_analysis) = report.constraint_analysis {
280            summary.constraint_satisfaction_rate = constraint_analysis.satisfaction_rate;
281        }
282
283        if let Some(ref energy_analysis) = report.energy_analysis {
284            summary.total_energy = energy_analysis.total_energy;
285            summary.improvement_potential = energy_analysis.improvement_potential;
286        }
287
288        // Calculate overall score
289        summary.overall_score = self.calculate_overall_score(&summary);
290
291        summary
292    }
293
294    /// Calculate overall solution score
295    fn calculate_overall_score(&self, summary: &reporting::DebugSummary) -> f64 {
296        let mut score = 1.0;
297
298        // Penalty for constraint violations
299        score *= summary.constraint_satisfaction_rate;
300
301        // Penalty for critical issues
302        if summary.critical_issues > 0 {
303            score *= 0.5;
304        }
305
306        // Penalty for high improvement potential
307        score *= (1.0 - summary.improvement_potential).max(0.0);
308
309        score.max(0.0).min(1.0)
310    }
311
312    /// Estimate improvement potential
313    fn estimate_improvement_potential(&self, breakdown: &energy_analyzer::EnergyBreakdown) -> f64 {
314        // Simple heuristic: if energy landscape shows nearby local minima with lower energy
315        let current_energy = breakdown.total_energy;
316        let best_nearby = breakdown
317            .energy_landscape
318            .local_minima
319            .iter()
320            .map(|m| m.energy)
321            .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
322            .unwrap_or(current_energy);
323
324        if best_nearby < current_energy {
325            ((current_energy - best_nearby) / current_energy.abs()).min(1.0)
326        } else {
327            0.0
328        }
329    }
330}
331
332/// Interactive debugger for real-time solution analysis
333pub struct InteractiveDebugger {
334    /// Problem information
335    problem_info: types::ProblemInfo,
336    /// Current solution being debugged
337    pub current_solution: Option<types::Solution>,
338    /// Debugger instance
339    debugger: SolutionDebugger,
340    /// Watch variables
341    pub watch_variables: Vec<String>,
342}
343
344impl InteractiveDebugger {
345    /// Create new interactive debugger
346    pub fn new(problem_info: types::ProblemInfo) -> Self {
347        let config = config::DebuggerConfig {
348            detailed_analysis: true,
349            check_constraints: true,
350            analyze_energy: true,
351            compare_solutions: false,
352            generate_visuals: false,
353            output_format: config::DebugOutputFormat::Console,
354            verbosity: config::VerbosityLevel::Normal,
355        };
356
357        Self {
358            debugger: SolutionDebugger::new(problem_info.clone(), config),
359            problem_info,
360            current_solution: None,
361            watch_variables: Vec::new(),
362        }
363    }
364
365    /// Load a solution for debugging
366    pub fn load_solution(&mut self, solution: types::Solution) {
367        self.current_solution = Some(solution);
368    }
369
370    /// Add a variable to watch list
371    pub fn add_watch(&mut self, variable: String) {
372        if !self.watch_variables.contains(&variable) {
373            self.watch_variables.push(variable);
374        }
375    }
376
377    /// Execute a debug command
378    pub fn execute_command(&mut self, command: &str) -> String {
379        match command {
380            "help" => "Available commands: help, energy, constraints, watch".to_string(),
381            "energy" => {
382                if let Some(ref solution) = self.current_solution {
383                    format!("Solution energy: {}", solution.energy)
384                } else {
385                    "No solution loaded".to_string()
386                }
387            }
388            "constraints" => {
389                if let Some(ref solution) = self.current_solution {
390                    let report = self.debugger.debug_solution(solution);
391                    if let Some(constraint_analysis) = report.constraint_analysis {
392                        format!(
393                            "Constraint satisfaction rate: {:.2}%",
394                            constraint_analysis.satisfaction_rate * 100.0
395                        )
396                    } else {
397                        "No constraint analysis available".to_string()
398                    }
399                } else {
400                    "No solution loaded".to_string()
401                }
402            }
403            "watch" => {
404                format!("Watched variables: {:?}", self.watch_variables)
405            }
406            _ => format!("Unknown command: {command}"),
407        }
408    }
409}