use super::types::{ProblemInfo, Solution};
use serde::Serialize;
use std::collections::HashMap;
pub struct SolutionVisualizer {
options: VisualizationOptions,
color_schemes: HashMap<String, ColorScheme>,
}
#[derive(Debug, Clone, Serialize)]
pub struct VisualizationOptions {
pub show_values: bool,
pub show_energy: bool,
pub show_violations: bool,
pub show_relationships: bool,
pub layout: LayoutAlgorithm,
}
#[derive(Debug, Clone, Serialize)]
pub enum LayoutAlgorithm {
Grid,
Circular,
ForceDirected,
Hierarchical,
Custom,
}
#[derive(Debug, Clone, Serialize)]
pub struct ColorScheme {
pub variable_colors: HashMap<bool, String>,
pub constraint_colors: HashMap<String, String>,
pub energy_gradient: Vec<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct Visualization {
pub viz_type: VisualizationType,
pub title: String,
pub data: VisualizationData,
pub options: VisualizationOptions,
}
#[derive(Debug, Clone, Serialize)]
pub enum VisualizationType {
SolutionMatrix,
EnergyLandscape,
ConstraintGraph,
InteractionGraph,
EnergyBreakdown,
}
#[derive(Debug, Clone, Serialize)]
pub struct VisualizationData {
pub nodes: Vec<Node>,
pub edges: Vec<Edge>,
pub metadata: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize)]
pub struct Node {
pub id: String,
pub label: String,
pub node_type: String,
pub position: Option<(f64, f64)>,
pub size: f64,
pub color: String,
pub properties: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize)]
pub struct Edge {
pub source: String,
pub target: String,
pub edge_type: String,
pub weight: f64,
pub color: String,
pub properties: HashMap<String, serde_json::Value>,
}
impl SolutionVisualizer {
pub fn new() -> Self {
let mut color_schemes = HashMap::new();
let mut default_scheme = ColorScheme {
variable_colors: HashMap::new(),
constraint_colors: HashMap::new(),
energy_gradient: vec![
"#FF0000".to_string(), "#FFFF00".to_string(), "#00FF00".to_string(), ],
};
default_scheme
.variable_colors
.insert(true, "#0066CC".to_string());
default_scheme
.variable_colors
.insert(false, "#CCCCCC".to_string());
color_schemes.insert("default".to_string(), default_scheme);
Self {
options: VisualizationOptions::default(),
color_schemes,
}
}
pub fn visualize_solution_matrix(
&self,
solution: &Solution,
problem_info: &ProblemInfo,
) -> Visualization {
let mut nodes = Vec::new();
let mut edges = Vec::new();
for (var, &value) in &solution.assignments {
let color = if value {
self.color_schemes["default"].variable_colors[&true].clone()
} else {
self.color_schemes["default"].variable_colors[&false].clone()
};
nodes.push(Node {
id: var.clone(),
label: format!("{}: {}", var, if value { "1" } else { "0" }),
node_type: "variable".to_string(),
position: None,
size: 1.0,
color,
properties: HashMap::new(),
});
}
for i in 0..problem_info.qubo.nrows() {
for j in i + 1..problem_info.qubo.ncols() {
if problem_info.qubo[[i, j]].abs() > 1e-10 {
if let (Some(var1), Some(var2)) = (
problem_info.reverse_var_map.get(&i),
problem_info.reverse_var_map.get(&j),
) {
edges.push(Edge {
source: var1.clone(),
target: var2.clone(),
edge_type: "interaction".to_string(),
weight: problem_info.qubo[[i, j]].abs(),
color: if problem_info.qubo[[i, j]] > 0.0 {
"#FF0000".to_string()
} else {
"#0000FF".to_string()
},
properties: HashMap::new(),
});
}
}
}
}
Visualization {
viz_type: VisualizationType::SolutionMatrix,
title: "Solution Variable Matrix".to_string(),
data: VisualizationData {
nodes,
edges,
metadata: HashMap::new(),
},
options: self.options.clone(),
}
}
pub fn visualize_energy_landscape(
&self,
solution: &Solution,
_problem_info: &ProblemInfo,
) -> Visualization {
let mut nodes = Vec::new();
let edges = Vec::new();
nodes.push(Node {
id: "current".to_string(),
label: format!("Current Solution (E={:.2})", solution.energy),
node_type: "solution".to_string(),
position: Some((0.0, 0.0)),
size: 2.0,
color: "#FF0000".to_string(),
properties: HashMap::new(),
});
Visualization {
viz_type: VisualizationType::EnergyLandscape,
title: "Energy Landscape".to_string(),
data: VisualizationData {
nodes,
edges,
metadata: HashMap::new(),
},
options: self.options.clone(),
}
}
pub fn visualize_constraint_graph(
&self,
solution: &Solution,
problem_info: &ProblemInfo,
) -> Visualization {
let mut nodes = Vec::new();
let mut edges = Vec::new();
for (var, &value) in &solution.assignments {
let color = if value {
self.color_schemes["default"].variable_colors[&true].clone()
} else {
self.color_schemes["default"].variable_colors[&false].clone()
};
nodes.push(Node {
id: var.clone(),
label: var.clone(),
node_type: "variable".to_string(),
position: None,
size: 1.0,
color,
properties: HashMap::new(),
});
}
for (i, constraint) in problem_info.constraints.iter().enumerate() {
let constraint_id = format!("constraint_{i}");
nodes.push(Node {
id: constraint_id.clone(),
label: constraint.name.as_ref().unwrap_or(&constraint_id).clone(),
node_type: "constraint".to_string(),
position: None,
size: 1.5,
color: "#FFAA00".to_string(),
properties: HashMap::new(),
});
for var in &constraint.variables {
edges.push(Edge {
source: constraint_id.clone(),
target: var.clone(),
edge_type: "constrains".to_string(),
weight: 1.0,
color: "#888888".to_string(),
properties: HashMap::new(),
});
}
}
Visualization {
viz_type: VisualizationType::ConstraintGraph,
title: "Constraint Graph".to_string(),
data: VisualizationData {
nodes,
edges,
metadata: HashMap::new(),
},
options: self.options.clone(),
}
}
}
impl Default for VisualizationOptions {
fn default() -> Self {
Self {
show_values: true,
show_energy: true,
show_violations: true,
show_relationships: true,
layout: LayoutAlgorithm::ForceDirected,
}
}
}
impl Default for SolutionVisualizer {
fn default() -> Self {
Self::new()
}
}