scirs2_cluster/native_plotting/
mod.rs1mod plotter;
8mod svg;
9mod types;
10
11pub use plotter::AdvancedNativePlotter;
13pub use types::{
14 AnimationEngine, AnimationFrame, Camera3D, DendrogramNode, DendrogramTree, DirectionalLight,
15 ExecutionSummary, ExportQuality, InteractiveController, InteractiveFeature,
16 InteractivePerformanceDashboard, Lighting3D, MetricTimelinePoint, Native3DClusterPlot,
17 NativeClusterPlot, NativeDendrogramPlot, NativePlotConfig, NativeVisualizationOutput,
18 NeuromorphicActivityPlot, PlotColorScheme, PointLight, QuantumCoherenceAnimation,
19 QuantumCoherenceFrame, QuantumField3D, SvgCanvas, SvgElement, Transformation,
20};
21
22use crate::advanced_clustering::AdvancedClusteringResult;
23use crate::error::{ClusteringError, Result};
24use scirs2_core::ndarray::ArrayView2;
25
26#[allow(dead_code)]
28pub fn create_native_advanced_plot(
29 data: &ArrayView2<f64>,
30 result: &AdvancedClusteringResult,
31 config: Option<NativePlotConfig>,
32) -> Result<NativeVisualizationOutput> {
33 let config = config.unwrap_or_default();
34 let mut plotter = AdvancedNativePlotter::new(config);
35 plotter.create_comprehensive_plot(data, result)
36}
37
38#[allow(dead_code)]
40pub fn export_native_visualization(
41 output: &NativeVisualizationOutput,
42 filename: &str,
43 format: &str,
44) -> Result<()> {
45 match format.to_lowercase().as_str() {
46 "svg" => {
47 use std::fs::File;
48 use std::io::Write;
49
50 let mut file = File::create(format!("{}.svg", filename)).map_err(|e| {
51 ClusteringError::InvalidInput(format!("Failed to create SVG file: {}", e))
52 })?;
53
54 file.write_all(output.svg_content.as_bytes()).map_err(|e| {
55 ClusteringError::InvalidInput(format!("Failed to write SVG file: {}", e))
56 })?;
57
58 println!("Exported native Advanced visualization to {filename}.svg");
59 }
60 "html" => {
61 use std::fs::File;
62 use std::io::Write;
63
64 let html_content = format!(
65 r#"<!DOCTYPE html>
66<html>
67<head>
68 <title>Advanced Native Visualization</title>
69 <style>
70 body {{ margin: 0; padding: 20px; background: #1a1a2e; }}
71 .selected {{ stroke: #FFD700 !important; stroke-width: 3px !important; }}
72 </style>
73</head>
74<body>
75 {}
76 <script>{}</script>
77</body>
78</html>"#,
79 output.svg_content, output.interactive_script
80 );
81
82 let mut file = File::create(format!("{}.html", filename)).map_err(|e| {
83 ClusteringError::InvalidInput(format!("Failed to create HTML file: {}", e))
84 })?;
85
86 file.write_all(html_content.as_bytes()).map_err(|e| {
87 ClusteringError::InvalidInput(format!("Failed to write HTML file: {}", e))
88 })?;
89
90 println!("Exported interactive Advanced visualization to {filename}.html");
91 }
92 _ => {
93 return Err(ClusteringError::InvalidInput(format!(
94 "Unsupported export format: {}",
95 format
96 )));
97 }
98 }
99
100 Ok(())
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use crate::advanced_clustering::{AdvancedClusteringResult, AdvancedPerformanceMetrics};
107 use scirs2_core::ndarray::{array, Array1, Array2};
108
109 fn make_result() -> AdvancedClusteringResult {
112 let centroids = array![[0.5, 0.5], [5.5, 5.5]];
113 let clusters = Array1::from_vec(vec![0, 0, 1, 1]);
114 AdvancedClusteringResult {
115 clusters,
116 centroids,
117 ai_speedup: 1.0,
118 quantum_advantage: 1.0,
119 neuromorphic_benefit: 1.0,
120 meta_learning_improvement: 0.0,
121 selected_algorithm: "test".to_string(),
122 confidence: 0.9,
123 performance: AdvancedPerformanceMetrics {
124 silhouette_score: 0.8,
125 execution_time: 0.001,
126 memory_usage: 1.0,
127 quantum_coherence: 0.7,
128 neural_adaptation_rate: 0.5,
129 ai_iterations: 5,
130 energy_efficiency: 0.9,
131 },
132 }
133 }
134
135 fn make_data() -> Array2<f64> {
137 array![[0.0, 0.0], [1.0, 1.0], [5.0, 5.0], [6.0, 6.0]]
138 }
139
140 #[test]
144 fn test_build_dendrogram_tree_basic() {
145 let plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
146 let data = make_data();
147 let result = make_result();
148 let tree = plotter
149 .build_dendrogram_tree(&data.view(), &result)
150 .expect("build_dendrogram_tree failed");
151
152 assert_eq!(tree.leaf_count, 4, "leaf_count should equal n_samples");
153 assert!(
154 tree.root.height >= 0.0,
155 "root height must be non-negative, got {}",
156 tree.root.height
157 );
158 }
159
160 #[test]
164 fn test_calculate_dendrogram_layout_all_nodes_have_positions() {
165 let plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
166 let data = make_data();
167 let result = make_result();
168 let tree = plotter
169 .build_dendrogram_tree(&data.view(), &result)
170 .expect("build_dendrogram_tree failed");
171 let layout = plotter
172 .calculate_dendrogram_layout(&tree)
173 .expect("calculate_dendrogram_layout failed");
174
175 assert!(
177 !layout.is_empty(),
178 "layout must contain at least one position"
179 );
180 for (id, (x, y)) in &layout {
181 assert!(
182 x.is_finite() && y.is_finite(),
183 "position for node '{id}' is not finite: ({x}, {y})"
184 );
185 }
186 }
187
188 #[test]
192 fn test_calculate_quantum_branch_lengths_all_nodes() {
193 let plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
194 let data = make_data();
195 let result = make_result();
196 let tree = plotter
197 .build_dendrogram_tree(&data.view(), &result)
198 .expect("build_dendrogram_tree failed");
199 let lengths = plotter
200 .calculate_quantum_branch_lengths(&tree, &result)
201 .expect("calculate_quantum_branch_lengths failed");
202
203 assert!(!lengths.is_empty(), "branch lengths map must not be empty");
204 for (id, length) in &lengths {
205 assert!(
206 length.is_finite(),
207 "branch length for '{id}' is not finite: {length}"
208 );
209 }
210 }
211
212 #[test]
216 fn test_calculate_dendrogram_quantum_enhancements_in_range() {
217 let plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
218 let data = make_data();
219 let result = make_result();
220 let tree = plotter
221 .build_dendrogram_tree(&data.view(), &result)
222 .expect("build_dendrogram_tree failed");
223 let enhancements = plotter
224 .calculate_dendrogram_quantum_enhancements(&tree, &result)
225 .expect("calculate_dendrogram_quantum_enhancements failed");
226
227 assert!(
228 !enhancements.is_empty(),
229 "enhancements map must not be empty"
230 );
231 for (id, enh) in &enhancements {
232 assert!(
233 *enh >= 0.0 && *enh <= 1.0,
234 "enhancement for '{id}' is out of [0, 1]: {enh}"
235 );
236 }
237 }
238
239 #[test]
243 fn test_render_dendrogram_to_svg_adds_elements() {
244 let mut plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
245 let data = make_data();
246 let result = make_result();
247 let tree = plotter
248 .build_dendrogram_tree(&data.view(), &result)
249 .expect("build_dendrogram_tree failed");
250 let positions = plotter
251 .calculate_dendrogram_layout(&tree)
252 .expect("calculate_dendrogram_layout failed");
253 let branch_lengths = plotter
254 .calculate_quantum_branch_lengths(&tree, &result)
255 .expect("calculate_quantum_branch_lengths failed");
256 let enhancements = plotter
257 .calculate_dendrogram_quantum_enhancements(&tree, &result)
258 .expect("calculate_dendrogram_quantum_enhancements failed");
259
260 plotter
261 .render_dendrogram_to_svg(&tree, &positions, &branch_lengths, &enhancements)
262 .expect("render_dendrogram_to_svg failed");
263
264 let svg = plotter.svg_canvas.to_svg();
266 assert!(
267 !svg.is_empty(),
268 "SVG canvas must be non-empty after rendering"
269 );
270 }
271
272 #[test]
276 fn test_create_quantum_field_3d_grid_dimensions() {
277 let plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
278 let data = make_data();
279 let result = make_result();
280 let data_3d: Array2<f64> = array![
282 [0.0, 0.0, 0.0],
283 [1.0, 1.0, 1.0],
284 [5.0, 5.0, 5.0],
285 [6.0, 6.0, 6.0]
286 ];
287 let field = plotter
288 .create_quantum_field_3d(&data_3d, &result)
289 .expect("create_quantum_field_3d failed");
290
291 let shape = field.field_strength.shape();
293 assert_eq!(shape[0], 10, "field grid rows should be 10");
294 assert_eq!(shape[1], 10, "field grid cols should be 10");
295 for v in field.field_strength.iter() {
297 assert!(
298 v.is_finite() && *v >= 0.0,
299 "field value should be finite and >= 0, got {v}"
300 );
301 }
302 let _ = data;
304 }
305
306 #[test]
310 fn test_create_quantum_coherence_frame_shape_and_timestamp() {
311 let plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
312 let result = make_result();
313 let frame = plotter
314 .create_quantum_coherence_frame(&result, 0.0)
315 .expect("create_quantum_coherence_frame failed");
316
317 assert_eq!(
318 frame.timestamp, 0.0,
319 "timestamp should match argument t=0.0"
320 );
321 let shape = frame.field_strength.shape();
322 assert_eq!(shape[0], 8, "field_strength rows should be 8");
323 assert_eq!(shape[1], 8, "field_strength cols should be 8");
324 assert_eq!(
326 frame.elements.len(),
327 2,
328 "should have one element per centroid"
329 );
330 }
331
332 #[test]
336 fn test_create_comprehensive_plot_runs_ok() {
337 let data = make_data();
338 let result = make_result();
339 let output = create_native_advanced_plot(&data.view(), &result, None)
340 .expect("create_native_advanced_plot failed");
341
342 assert!(
343 !output.svg_content.is_empty(),
344 "svg_content must be non-empty"
345 );
346 assert!(
348 output.plot_3d.is_none(),
349 "plot_3d should be None for 2-D data"
350 );
351 }
352
353 #[test]
357 fn test_comprehensive_plot_hierarchical_triggers_dendrogram() {
358 let data = make_data();
359 let mut result = make_result();
360 result.selected_algorithm = "hierarchical".to_string();
361
362 let output = create_native_advanced_plot(&data.view(), &result, None)
363 .expect("create_native_advanced_plot (hierarchical) failed");
364
365 assert!(
366 output.dendrogram.is_some(),
367 "dendrogram should be Some when selected_algorithm contains 'hierarchical'"
368 );
369 }
370}