use super::types::{ProblemInfo, Solution};
use serde::Serialize;
use std::collections::HashMap;
pub struct SolutionComparator {
metrics: Vec<ComparisonMetric>,
reference_solutions: Vec<Solution>,
}
#[derive(Debug, Clone, Serialize)]
pub enum ComparisonMetric {
HammingDistance,
EnergyDifference,
ConstraintSatisfaction,
StructuralSimilarity,
Custom { name: String },
}
#[derive(Debug, Clone, Serialize)]
pub struct ComparisonResult {
pub solution1: String,
pub solution2: String,
pub metrics: HashMap<String, f64>,
pub differences: Vec<Difference>,
pub similarity: f64,
}
#[derive(Debug, Clone, Serialize)]
pub struct Difference {
pub variable: String,
pub value1: bool,
pub value2: bool,
pub objective_impact: f64,
pub constraint_impact: Vec<String>,
}
impl SolutionComparator {
pub fn new() -> Self {
Self {
metrics: vec![
ComparisonMetric::HammingDistance,
ComparisonMetric::EnergyDifference,
ComparisonMetric::ConstraintSatisfaction,
ComparisonMetric::StructuralSimilarity,
],
reference_solutions: Vec::new(),
}
}
pub fn add_reference(&mut self, solution: Solution) {
self.reference_solutions.push(solution);
}
pub fn compare(
&self,
sol1: &Solution,
sol2: &Solution,
problem_info: &ProblemInfo,
) -> ComparisonResult {
let mut metrics = HashMap::new();
let mut differences = Vec::new();
for metric in &self.metrics {
let value = match metric {
ComparisonMetric::HammingDistance => self.hamming_distance(sol1, sol2),
ComparisonMetric::EnergyDifference => (sol1.energy - sol2.energy).abs(),
ComparisonMetric::ConstraintSatisfaction => {
self.constraint_satisfaction_diff(sol1, sol2)
}
ComparisonMetric::StructuralSimilarity => self.structural_similarity(sol1, sol2),
ComparisonMetric::Custom { name } => self.custom_metric(sol1, sol2, name),
};
metrics.insert(self.metric_name(metric), value);
}
for var in sol1.assignments.keys() {
let val1 = sol1.assignments.get(var).copied().unwrap_or(false);
let val2 = sol2.assignments.get(var).copied().unwrap_or(false);
if val1 != val2 {
differences.push(Difference {
variable: var.clone(),
value1: val1,
value2: val2,
objective_impact: self.calculate_objective_impact(
var,
val1,
val2,
problem_info,
),
constraint_impact: self.calculate_constraint_impact(
var,
val1,
val2,
problem_info,
),
});
}
}
let max_distance = sol1.assignments.len() as f64;
let hamming_dist = metrics.get("hamming_distance").copied().unwrap_or(0.0);
let similarity = 1.0 - (hamming_dist / max_distance);
ComparisonResult {
solution1: "sol1".to_string(), solution2: "sol2".to_string(),
metrics,
differences,
similarity,
}
}
fn hamming_distance(&self, sol1: &Solution, sol2: &Solution) -> f64 {
let mut distance = 0;
for var in sol1.assignments.keys() {
let val1 = sol1.assignments.get(var).copied().unwrap_or(false);
let val2 = sol2.assignments.get(var).copied().unwrap_or(false);
if val1 != val2 {
distance += 1;
}
}
distance as f64
}
const fn constraint_satisfaction_diff(&self, _sol1: &Solution, _sol2: &Solution) -> f64 {
0.0
}
fn structural_similarity(&self, sol1: &Solution, sol2: &Solution) -> f64 {
let total_vars = sol1.assignments.len() as f64;
let same_vars = sol1
.assignments
.iter()
.filter(|(var, &val1)| sol2.assignments.get(*var).copied().unwrap_or(false) == val1)
.count() as f64;
same_vars / total_vars
}
const fn custom_metric(&self, _sol1: &Solution, _sol2: &Solution, _name: &str) -> f64 {
0.0
}
fn metric_name(&self, metric: &ComparisonMetric) -> String {
match metric {
ComparisonMetric::HammingDistance => "hamming_distance".to_string(),
ComparisonMetric::EnergyDifference => "energy_difference".to_string(),
ComparisonMetric::ConstraintSatisfaction => "constraint_satisfaction".to_string(),
ComparisonMetric::StructuralSimilarity => "structural_similarity".to_string(),
ComparisonMetric::Custom { name } => name.clone(),
}
}
const fn calculate_objective_impact(
&self,
_var: &str,
_val1: bool,
_val2: bool,
_problem_info: &ProblemInfo,
) -> f64 {
0.0
}
const fn calculate_constraint_impact(
&self,
_var: &str,
_val1: bool,
_val2: bool,
_problem_info: &ProblemInfo,
) -> Vec<String> {
Vec::new()
}
}
impl Default for SolutionComparator {
fn default() -> Self {
Self::new()
}
}