quantrs2_tytan/solution_debugger/
visualization.rs

1//! Visualization functionality for the solution debugger.
2
3use super::types::{ProblemInfo, Solution};
4use serde::Serialize;
5use std::collections::HashMap;
6
7/// Solution visualizer
8pub struct SolutionVisualizer {
9    /// Visualization options
10    options: VisualizationOptions,
11    /// Color schemes
12    color_schemes: HashMap<String, ColorScheme>,
13}
14
15#[derive(Debug, Clone, Serialize)]
16pub struct VisualizationOptions {
17    /// Show variable values
18    pub show_values: bool,
19    /// Show energy contributions
20    pub show_energy: bool,
21    /// Show constraint violations
22    pub show_violations: bool,
23    /// Show relationships
24    pub show_relationships: bool,
25    /// Layout algorithm
26    pub layout: LayoutAlgorithm,
27}
28
29#[derive(Debug, Clone, Serialize)]
30pub enum LayoutAlgorithm {
31    /// Grid layout
32    Grid,
33    /// Circular layout
34    Circular,
35    /// Force-directed layout
36    ForceDirected,
37    /// Hierarchical layout
38    Hierarchical,
39    /// Custom layout
40    Custom,
41}
42
43#[derive(Debug, Clone, Serialize)]
44pub struct ColorScheme {
45    /// Variable colors
46    pub variable_colors: HashMap<bool, String>,
47    /// Constraint colors
48    pub constraint_colors: HashMap<String, String>,
49    /// Energy gradient
50    pub energy_gradient: Vec<String>,
51}
52
53#[derive(Debug, Clone, Serialize)]
54pub struct Visualization {
55    /// Visualization type
56    pub viz_type: VisualizationType,
57    /// Title
58    pub title: String,
59    /// Data for visualization
60    pub data: VisualizationData,
61    /// Rendering options
62    pub options: VisualizationOptions,
63}
64
65#[derive(Debug, Clone, Serialize)]
66pub enum VisualizationType {
67    /// Solution matrix
68    SolutionMatrix,
69    /// Energy landscape
70    EnergyLandscape,
71    /// Constraint graph
72    ConstraintGraph,
73    /// Variable interaction graph
74    InteractionGraph,
75    /// Energy breakdown chart
76    EnergyBreakdown,
77}
78
79#[derive(Debug, Clone, Serialize)]
80pub struct VisualizationData {
81    /// Nodes (variables, constraints, etc.)
82    pub nodes: Vec<Node>,
83    /// Edges (relationships, interactions)
84    pub edges: Vec<Edge>,
85    /// Additional data
86    pub metadata: HashMap<String, serde_json::Value>,
87}
88
89#[derive(Debug, Clone, Serialize)]
90pub struct Node {
91    /// Node ID
92    pub id: String,
93    /// Node label
94    pub label: String,
95    /// Node type
96    pub node_type: String,
97    /// Position (x, y)
98    pub position: Option<(f64, f64)>,
99    /// Size
100    pub size: f64,
101    /// Color
102    pub color: String,
103    /// Additional properties
104    pub properties: HashMap<String, serde_json::Value>,
105}
106
107#[derive(Debug, Clone, Serialize)]
108pub struct Edge {
109    /// Source node ID
110    pub source: String,
111    /// Target node ID
112    pub target: String,
113    /// Edge type
114    pub edge_type: String,
115    /// Weight/strength
116    pub weight: f64,
117    /// Color
118    pub color: String,
119    /// Additional properties
120    pub properties: HashMap<String, serde_json::Value>,
121}
122
123impl SolutionVisualizer {
124    /// Create new solution visualizer
125    pub fn new() -> Self {
126        let mut color_schemes = HashMap::new();
127
128        // Default color scheme
129        let mut default_scheme = ColorScheme {
130            variable_colors: HashMap::new(),
131            constraint_colors: HashMap::new(),
132            energy_gradient: vec![
133                "#FF0000".to_string(), // High energy (red)
134                "#FFFF00".to_string(), // Medium energy (yellow)
135                "#00FF00".to_string(), // Low energy (green)
136            ],
137        };
138        default_scheme
139            .variable_colors
140            .insert(true, "#0066CC".to_string());
141        default_scheme
142            .variable_colors
143            .insert(false, "#CCCCCC".to_string());
144        color_schemes.insert("default".to_string(), default_scheme);
145
146        Self {
147            options: VisualizationOptions::default(),
148            color_schemes,
149        }
150    }
151
152    /// Visualize solution matrix
153    pub fn visualize_solution_matrix(
154        &self,
155        solution: &Solution,
156        problem_info: &ProblemInfo,
157    ) -> Visualization {
158        let mut nodes = Vec::new();
159        let mut edges = Vec::new();
160
161        // Create nodes for each variable
162        for (var, &value) in &solution.assignments {
163            let color = if value {
164                self.color_schemes["default"].variable_colors[&true].clone()
165            } else {
166                self.color_schemes["default"].variable_colors[&false].clone()
167            };
168
169            nodes.push(Node {
170                id: var.clone(),
171                label: format!("{}: {}", var, if value { "1" } else { "0" }),
172                node_type: "variable".to_string(),
173                position: None,
174                size: 1.0,
175                color,
176                properties: HashMap::new(),
177            });
178        }
179
180        // Add edges based on QUBO interactions
181        for i in 0..problem_info.qubo.nrows() {
182            for j in i + 1..problem_info.qubo.ncols() {
183                if problem_info.qubo[[i, j]].abs() > 1e-10 {
184                    if let (Some(var1), Some(var2)) = (
185                        problem_info.reverse_var_map.get(&i),
186                        problem_info.reverse_var_map.get(&j),
187                    ) {
188                        edges.push(Edge {
189                            source: var1.clone(),
190                            target: var2.clone(),
191                            edge_type: "interaction".to_string(),
192                            weight: problem_info.qubo[[i, j]].abs(),
193                            color: if problem_info.qubo[[i, j]] > 0.0 {
194                                "#FF0000".to_string()
195                            } else {
196                                "#0000FF".to_string()
197                            },
198                            properties: HashMap::new(),
199                        });
200                    }
201                }
202            }
203        }
204
205        Visualization {
206            viz_type: VisualizationType::SolutionMatrix,
207            title: "Solution Variable Matrix".to_string(),
208            data: VisualizationData {
209                nodes,
210                edges,
211                metadata: HashMap::new(),
212            },
213            options: self.options.clone(),
214        }
215    }
216
217    /// Visualize energy landscape
218    pub fn visualize_energy_landscape(
219        &self,
220        solution: &Solution,
221        _problem_info: &ProblemInfo,
222    ) -> Visualization {
223        let mut nodes = Vec::new();
224        let edges = Vec::new();
225
226        // Create node for current solution
227        nodes.push(Node {
228            id: "current".to_string(),
229            label: format!("Current Solution (E={:.2})", solution.energy),
230            node_type: "solution".to_string(),
231            position: Some((0.0, 0.0)),
232            size: 2.0,
233            color: "#FF0000".to_string(),
234            properties: HashMap::new(),
235        });
236
237        // Would add neighboring solutions and their energies
238        // This is a placeholder implementation
239
240        Visualization {
241            viz_type: VisualizationType::EnergyLandscape,
242            title: "Energy Landscape".to_string(),
243            data: VisualizationData {
244                nodes,
245                edges,
246                metadata: HashMap::new(),
247            },
248            options: self.options.clone(),
249        }
250    }
251
252    /// Visualize constraint graph
253    pub fn visualize_constraint_graph(
254        &self,
255        solution: &Solution,
256        problem_info: &ProblemInfo,
257    ) -> Visualization {
258        let mut nodes = Vec::new();
259        let mut edges = Vec::new();
260
261        // Create nodes for variables
262        for (var, &value) in &solution.assignments {
263            let color = if value {
264                self.color_schemes["default"].variable_colors[&true].clone()
265            } else {
266                self.color_schemes["default"].variable_colors[&false].clone()
267            };
268
269            nodes.push(Node {
270                id: var.clone(),
271                label: var.clone(),
272                node_type: "variable".to_string(),
273                position: None,
274                size: 1.0,
275                color,
276                properties: HashMap::new(),
277            });
278        }
279
280        // Create nodes for constraints
281        for (i, constraint) in problem_info.constraints.iter().enumerate() {
282            let constraint_id = format!("constraint_{i}");
283            nodes.push(Node {
284                id: constraint_id.clone(),
285                label: constraint.name.as_ref().unwrap_or(&constraint_id).clone(),
286                node_type: "constraint".to_string(),
287                position: None,
288                size: 1.5,
289                color: "#FFAA00".to_string(),
290                properties: HashMap::new(),
291            });
292
293            // Connect constraint to its variables
294            for var in &constraint.variables {
295                edges.push(Edge {
296                    source: constraint_id.clone(),
297                    target: var.clone(),
298                    edge_type: "constrains".to_string(),
299                    weight: 1.0,
300                    color: "#888888".to_string(),
301                    properties: HashMap::new(),
302                });
303            }
304        }
305
306        Visualization {
307            viz_type: VisualizationType::ConstraintGraph,
308            title: "Constraint Graph".to_string(),
309            data: VisualizationData {
310                nodes,
311                edges,
312                metadata: HashMap::new(),
313            },
314            options: self.options.clone(),
315        }
316    }
317}
318
319impl Default for VisualizationOptions {
320    fn default() -> Self {
321        Self {
322            show_values: true,
323            show_energy: true,
324            show_violations: true,
325            show_relationships: true,
326            layout: LayoutAlgorithm::ForceDirected,
327        }
328    }
329}
330
331impl Default for SolutionVisualizer {
332    fn default() -> Self {
333        Self::new()
334    }
335}