quantrs2_tytan/solution_debugger/
comparison.rs

1//! Solution comparison functionality for the solution debugger.
2
3use super::types::{ProblemInfo, Solution};
4use serde::Serialize;
5use std::collections::HashMap;
6
7/// Solution comparator
8pub struct SolutionComparator {
9    /// Comparison metrics
10    metrics: Vec<ComparisonMetric>,
11    /// Reference solutions
12    reference_solutions: Vec<Solution>,
13}
14
15#[derive(Debug, Clone, Serialize)]
16pub enum ComparisonMetric {
17    /// Hamming distance
18    HammingDistance,
19    /// Energy difference
20    EnergyDifference,
21    /// Constraint satisfaction
22    ConstraintSatisfaction,
23    /// Structural similarity
24    StructuralSimilarity,
25    /// Custom metric
26    Custom { name: String },
27}
28
29#[derive(Debug, Clone, Serialize)]
30pub struct ComparisonResult {
31    /// Solutions compared
32    pub solution1: String,
33    pub solution2: String,
34    /// Metric results
35    pub metrics: HashMap<String, f64>,
36    /// Differences
37    pub differences: Vec<Difference>,
38    /// Similarity score
39    pub similarity: f64,
40}
41
42#[derive(Debug, Clone, Serialize)]
43pub struct Difference {
44    /// Variable name
45    pub variable: String,
46    /// Value in solution 1
47    pub value1: bool,
48    /// Value in solution 2
49    pub value2: bool,
50    /// Impact on objective
51    pub objective_impact: f64,
52    /// Impact on constraints
53    pub constraint_impact: Vec<String>,
54}
55
56impl SolutionComparator {
57    /// Create new solution comparator
58    pub fn new() -> Self {
59        Self {
60            metrics: vec![
61                ComparisonMetric::HammingDistance,
62                ComparisonMetric::EnergyDifference,
63                ComparisonMetric::ConstraintSatisfaction,
64                ComparisonMetric::StructuralSimilarity,
65            ],
66            reference_solutions: Vec::new(),
67        }
68    }
69
70    /// Add reference solution
71    pub fn add_reference(&mut self, solution: Solution) {
72        self.reference_solutions.push(solution);
73    }
74
75    /// Compare two solutions
76    pub fn compare(
77        &self,
78        sol1: &Solution,
79        sol2: &Solution,
80        problem_info: &ProblemInfo,
81    ) -> ComparisonResult {
82        let mut metrics = HashMap::new();
83        let mut differences = Vec::new();
84
85        // Calculate metrics
86        for metric in &self.metrics {
87            let value = match metric {
88                ComparisonMetric::HammingDistance => self.hamming_distance(sol1, sol2),
89                ComparisonMetric::EnergyDifference => (sol1.energy - sol2.energy).abs(),
90                ComparisonMetric::ConstraintSatisfaction => {
91                    self.constraint_satisfaction_diff(sol1, sol2)
92                }
93                ComparisonMetric::StructuralSimilarity => self.structural_similarity(sol1, sol2),
94                ComparisonMetric::Custom { name } => self.custom_metric(sol1, sol2, name),
95            };
96            metrics.insert(self.metric_name(metric), value);
97        }
98
99        // Find differences
100        for var in sol1.assignments.keys() {
101            let val1 = sol1.assignments.get(var).copied().unwrap_or(false);
102            let val2 = sol2.assignments.get(var).copied().unwrap_or(false);
103
104            if val1 != val2 {
105                differences.push(Difference {
106                    variable: var.clone(),
107                    value1: val1,
108                    value2: val2,
109                    objective_impact: self.calculate_objective_impact(
110                        var,
111                        val1,
112                        val2,
113                        problem_info,
114                    ),
115                    constraint_impact: self.calculate_constraint_impact(
116                        var,
117                        val1,
118                        val2,
119                        problem_info,
120                    ),
121                });
122            }
123        }
124
125        // Calculate overall similarity (1.0 - normalized hamming distance)
126        let max_distance = sol1.assignments.len() as f64;
127        let hamming_dist = metrics.get("hamming_distance").copied().unwrap_or(0.0);
128        let similarity = 1.0 - (hamming_dist / max_distance);
129
130        ComparisonResult {
131            solution1: "sol1".to_string(), // Would use actual solution IDs
132            solution2: "sol2".to_string(),
133            metrics,
134            differences,
135            similarity,
136        }
137    }
138
139    /// Calculate Hamming distance between solutions
140    fn hamming_distance(&self, sol1: &Solution, sol2: &Solution) -> f64 {
141        let mut distance = 0;
142
143        for var in sol1.assignments.keys() {
144            let val1 = sol1.assignments.get(var).copied().unwrap_or(false);
145            let val2 = sol2.assignments.get(var).copied().unwrap_or(false);
146
147            if val1 != val2 {
148                distance += 1;
149            }
150        }
151
152        distance as f64
153    }
154
155    /// Calculate constraint satisfaction difference
156    const fn constraint_satisfaction_diff(&self, _sol1: &Solution, _sol2: &Solution) -> f64 {
157        // Placeholder - would need actual constraint satisfaction scores
158        0.0
159    }
160
161    /// Calculate structural similarity
162    fn structural_similarity(&self, sol1: &Solution, sol2: &Solution) -> f64 {
163        // Calculate similarity based on variable clusters/groups
164        // Placeholder implementation
165        let total_vars = sol1.assignments.len() as f64;
166        let same_vars = sol1
167            .assignments
168            .iter()
169            .filter(|(var, &val1)| sol2.assignments.get(*var).copied().unwrap_or(false) == val1)
170            .count() as f64;
171
172        same_vars / total_vars
173    }
174
175    /// Calculate custom metric
176    const fn custom_metric(&self, _sol1: &Solution, _sol2: &Solution, _name: &str) -> f64 {
177        // Placeholder for custom metrics
178        0.0
179    }
180
181    /// Get metric name
182    fn metric_name(&self, metric: &ComparisonMetric) -> String {
183        match metric {
184            ComparisonMetric::HammingDistance => "hamming_distance".to_string(),
185            ComparisonMetric::EnergyDifference => "energy_difference".to_string(),
186            ComparisonMetric::ConstraintSatisfaction => "constraint_satisfaction".to_string(),
187            ComparisonMetric::StructuralSimilarity => "structural_similarity".to_string(),
188            ComparisonMetric::Custom { name } => name.clone(),
189        }
190    }
191
192    /// Calculate objective impact of variable difference
193    const fn calculate_objective_impact(
194        &self,
195        _var: &str,
196        _val1: bool,
197        _val2: bool,
198        _problem_info: &ProblemInfo,
199    ) -> f64 {
200        // Would calculate actual impact based on QUBO matrix
201        // Placeholder implementation
202        0.0
203    }
204
205    /// Calculate constraint impact of variable difference
206    const fn calculate_constraint_impact(
207        &self,
208        _var: &str,
209        _val1: bool,
210        _val2: bool,
211        _problem_info: &ProblemInfo,
212    ) -> Vec<String> {
213        // Would find which constraints are affected by this variable change
214        // Placeholder implementation
215        Vec::new()
216    }
217}
218
219impl Default for SolutionComparator {
220    fn default() -> Self {
221        Self::new()
222    }
223}