use crate::advanced_clustering::{AdvancedClusteringResult, AdvancedPerformanceMetrics};
use crate::advanced_visualization::{
AdvancedVisualizationOutput, NeuromorphicAdaptationPlot, QuantumCoherencePlot,
};
use crate::error::{ClusteringError, Result};
use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2, Axis};
use std::collections::HashMap;
use std::f64::consts::PI;
use serde::{Deserialize, Serialize};
#[derive(Debug)]
pub struct AdvancedNativePlotter {
config: NativePlotConfig,
svg_canvas: SvgCanvas,
animation_engine: AnimationEngine,
interactive_controller: InteractiveController,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NativePlotConfig {
pub width: usize,
pub height: usize,
pub enable_interactivity: bool,
pub enable_animations: bool,
pub animation_fps: f64,
pub color_scheme: PlotColorScheme,
pub export_quality: ExportQuality,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PlotColorScheme {
Quantum,
Neuromorphic,
AI,
Scientific,
Custom(Vec<[u8; 3]>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ExportQuality {
Draft,
Standard,
High,
Publication,
}
#[derive(Debug)]
pub struct SvgCanvas {
width: usize,
height: usize,
elements: Vec<SvgElement>,
styles: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub enum SvgElement {
Circle {
cx: f64,
cy: f64,
r: f64,
fill: String,
stroke: String,
stroke_width: f64,
opacity: f64,
},
Line {
x1: f64,
y1: f64,
x2: f64,
y2: f64,
stroke: String,
stroke_width: f64,
opacity: f64,
},
Path {
d: String,
fill: String,
stroke: String,
stroke_width: f64,
opacity: f64,
},
Text {
x: f64,
y: f64,
content: String,
font_size: f64,
fill: String,
text_anchor: String,
},
Group {
id: String,
elements: Vec<SvgElement>,
transform: String,
},
}
#[derive(Debug)]
pub struct AnimationEngine {
frames: Vec<AnimationFrame>,
current_frame: usize,
frame_duration: f64,
total_duration: f64,
}
#[derive(Debug, Clone)]
pub struct AnimationFrame {
timestamp: f64,
elements: Vec<SvgElement>,
transformations: Vec<Transformation>,
}
#[derive(Debug, Clone)]
pub enum Transformation {
Translate { dx: f64, dy: f64 },
Rotate { angle: f64, cx: f64, cy: f64 },
Scale { sx: f64, sy: f64 },
Fade { from: f64, to: f64 },
ColorTransition { from: String, to: String },
}
#[derive(Debug)]
pub struct InteractiveController {
zoom_level: f64,
pan_offset: (f64, f64),
selected_elements: Vec<String>,
hover_element: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct NativeDendrogramPlot {
pub tree: DendrogramTree,
pub node_positions: HashMap<String, (f64, f64)>,
pub branch_lengths: HashMap<String, f64>,
pub quantum_enhancements: HashMap<String, f64>,
pub interactive_features: Vec<InteractiveFeature>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DendrogramTree {
pub root: DendrogramNode,
pub height: f64,
pub leaf_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DendrogramNode {
pub id: String,
pub height: f64,
pub children: Vec<DendrogramNode>,
pub data_indices: Vec<usize>,
pub quantum_coherence: f64,
pub neuromorphic_activity: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum InteractiveFeature {
ZoomPan,
NodeSelection,
Tooltip,
RealTimeFilter,
AnimationControls,
ExportOptions,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Native3DClusterPlot {
pub points_3d: Array2<f64>,
pub point_colors: Vec<[u8; 3]>,
pub centroids_3d: Array2<f64>,
pub camera: Camera3D,
pub lighting: Lighting3D,
pub quantum_field: QuantumField3D,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Camera3D {
pub position: [f64; 3],
pub target: [f64; 3],
pub up: [f64; 3],
pub fov: f64,
pub near: f64,
pub far: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Lighting3D {
pub ambient: f64,
pub directional_lights: Vec<DirectionalLight>,
pub point_lights: Vec<PointLight>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DirectionalLight {
pub direction: [f64; 3],
pub intensity: f64,
pub color: [f64; 3],
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PointLight {
pub position: [f64; 3],
pub intensity: f64,
pub color: [f64; 3],
pub attenuation: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuantumField3D {
pub field_strength: Array2<f64>,
pub coherence: Array2<f64>,
pub phase: Array2<f64>,
pub entanglement_lines: Vec<([f64; 3], [f64; 3], f64)>,
}
impl Default for NativePlotConfig {
fn default() -> Self {
Self {
width: 1200,
height: 800,
enable_interactivity: true,
enable_animations: true,
animation_fps: 30.0,
color_scheme: PlotColorScheme::Quantum,
export_quality: ExportQuality::High,
}
}
}
impl AdvancedNativePlotter {
pub fn new(config: NativePlotConfig) -> Self {
Self {
svg_canvas: SvgCanvas::new(config.width, config.height),
animation_engine: AnimationEngine::new(config.animation_fps),
interactive_controller: InteractiveController::new(),
config,
}
}
pub fn create_comprehensive_plot(
&mut self,
data: &ArrayView2<f64>,
result: &AdvancedClusteringResult,
) -> Result<NativeVisualizationOutput> {
self.svg_canvas.clear();
let cluster_plot = self.create_native_cluster_plot(data, result)?;
let dendrogram = if result.selected_algorithm.contains("hierarchical") {
Some(self.create_native_dendrogram(data, result)?)
} else {
None
};
let plot_3d = if data.ncols() > 2 {
Some(self.create_native_3d_plot(data, result)?)
} else {
None
};
let quantum_animation = if self.config.enable_animations {
Some(self.create_quantum_coherence_animation(result)?)
} else {
None
};
let neuromorphic_plot = self.create_neuromorphic_activity_plot(result)?;
let performance_dashboard = self.create_interactive_performance_dashboard(result)?;
Ok(NativeVisualizationOutput {
cluster_plot,
dendrogram,
plot_3d,
quantum_animation,
neuromorphic_plot,
performance_dashboard,
svg_content: self.svg_canvas.to_svg(),
interactive_script: self.generate_interactive_script(),
})
}
fn create_native_cluster_plot(
&mut self,
data: &ArrayView2<f64>,
result: &AdvancedClusteringResult,
) -> Result<NativeClusterPlot> {
let n_samples = data.nrows();
let n_features = data.ncols();
let plot_data = if n_features > 2 {
self.apply_native_pca(data, 2)?
} else {
data.to_owned()
};
let (x_min, x_max, y_min, y_max) = self.calculate_plot_bounds(&plot_data);
let margin = 50.0;
let plot_width = self.config.width as f64 - 2.0 * margin;
let plot_height = self.config.height as f64 - 2.0 * margin;
let x_scale = plot_width / (x_max - x_min);
let y_scale = plot_height / (y_max - y_min);
let mut point_elements = Vec::new();
let mut quantum_enhancements = Vec::new();
for i in 0..n_samples {
let x = margin + (plot_data[[i, 0]] - x_min) * x_scale;
let y = margin + (plot_data[[i, 1]] - y_min) * y_scale;
let cluster_id = result.clusters[i];
let quantum_factor = self.calculate_point_quantum_enhancement(i, cluster_id, result);
quantum_enhancements.push(quantum_factor);
let base_color = self.get_cluster_color(cluster_id);
let enhanced_color = self.apply_quantum_color_enhancement(base_color, quantum_factor);
let point_radius = 3.0 + quantum_factor * 2.0;
let circle = SvgElement::Circle {
cx: x,
cy: y,
r: point_radius,
fill: enhanced_color.clone(),
stroke: "#000000".to_string(),
stroke_width: 0.5,
opacity: 0.8 + quantum_factor * 0.2,
};
point_elements.push(circle);
}
let mut centroid_elements = Vec::new();
for (cluster_id, centroid) in result.centroids.outer_iter().enumerate() {
if centroid.len() >= 2 {
let x = margin + (centroid[0] - x_min) * x_scale;
let y = margin + (centroid[1] - y_min) * y_scale;
let aura_radius = 15.0;
let aura = SvgElement::Circle {
cx: x,
cy: y,
r: aura_radius,
fill: "none".to_string(),
stroke: self.get_cluster_color(cluster_id),
stroke_width: 2.0,
opacity: 0.3,
};
let centroid_circle = SvgElement::Circle {
cx: x,
cy: y,
r: 6.0,
fill: self.get_cluster_color(cluster_id),
stroke: "#FFFFFF".to_string(),
stroke_width: 2.0,
opacity: 1.0,
};
centroid_elements.push(aura);
centroid_elements.push(centroid_circle);
}
}
for element in &point_elements {
self.svg_canvas.add_element(element.clone());
}
for element in ¢roid_elements {
self.svg_canvas.add_element(element.clone());
}
self.add_plot_axes_and_labels(x_min, x_max, y_min, y_max, margin)?;
Ok(NativeClusterPlot {
data: plot_data,
point_elements,
centroid_elements,
quantum_enhancements,
bounds: (x_min, x_max, y_min, y_max),
scale: (x_scale, y_scale),
})
}
fn create_native_dendrogram(
&mut self,
data: &ArrayView2<f64>,
result: &AdvancedClusteringResult,
) -> Result<NativeDendrogramPlot> {
let tree = self.build_dendrogram_tree(data, result)?;
let node_positions = self.calculate_dendrogram_layout(&tree)?;
let branch_lengths = self.calculate_quantum_branch_lengths(&tree, result)?;
let quantum_enhancements = self.calculate_dendrogram_quantum_enhancements(&tree, result)?;
let interactive_features = vec![
InteractiveFeature::ZoomPan,
InteractiveFeature::NodeSelection,
InteractiveFeature::Tooltip,
InteractiveFeature::RealTimeFilter,
];
self.render_dendrogram_to_svg(
&tree,
&node_positions,
&branch_lengths,
&quantum_enhancements,
)?;
Ok(NativeDendrogramPlot {
tree,
node_positions,
branch_lengths,
quantum_enhancements,
interactive_features,
})
}
fn create_native_3d_plot(
&mut self,
data: &ArrayView2<f64>,
result: &AdvancedClusteringResult,
) -> Result<Native3DClusterPlot> {
let points_3d = if data.ncols() > 3 {
self.apply_native_pca(data, 3)?
} else if data.ncols() == 2 {
let mut points_3d = Array2::zeros((data.nrows(), 3));
points_3d
.slice_mut(scirs2_core::ndarray::s![.., 0..2])
.assign(data);
for i in 0..data.nrows() {
let cluster_id = result.clusters[i];
let quantum_factor =
self.calculate_point_quantum_enhancement(i, cluster_id, result);
points_3d[[i, 2]] = quantum_factor * 5.0; }
points_3d
} else {
data.to_owned()
};
let mut point_colors = Vec::new();
for i in 0..points_3d.nrows() {
let cluster_id = result.clusters[i];
let base_color = self.get_cluster_color_rgb(cluster_id);
let quantum_factor = self.calculate_point_quantum_enhancement(i, cluster_id, result);
let enhanced_color =
self.apply_quantum_color_enhancement_rgb(base_color, quantum_factor);
point_colors.push(enhanced_color);
}
let centroids_3d = if result.centroids.ncols() >= 3 {
result
.centroids
.slice(scirs2_core::ndarray::s![.., 0..3])
.to_owned()
} else {
let mut centroids_3d = Array2::zeros((result.centroids.nrows(), 3));
centroids_3d
.slice_mut(scirs2_core::ndarray::s![.., 0..result.centroids.ncols()])
.assign(&result.centroids);
centroids_3d
};
let camera = Camera3D {
position: [10.0, 10.0, 10.0],
target: [0.0, 0.0, 0.0],
up: [0.0, 1.0, 0.0],
fov: 45.0,
near: 0.1,
far: 100.0,
};
let lighting = Lighting3D {
ambient: 0.3,
directional_lights: vec![DirectionalLight {
direction: [-1.0, -1.0, -1.0],
intensity: 0.7,
color: [1.0, 1.0, 1.0],
}],
point_lights: vec![PointLight {
position: [5.0, 5.0, 5.0],
intensity: 0.5,
color: [0.0, 1.0, 1.0], attenuation: 0.1,
}],
};
let quantum_field = self.create_quantum_field_3d(&points_3d, result)?;
Ok(Native3DClusterPlot {
points_3d,
point_colors,
centroids_3d,
camera,
lighting,
quantum_field,
})
}
fn create_quantum_coherence_animation(
&mut self,
result: &AdvancedClusteringResult,
) -> Result<QuantumCoherenceAnimation> {
let num_frames = (self.config.animation_fps * 5.0) as usize; let mut frames = Vec::new();
for frame_idx in 0..num_frames {
let time = frame_idx as f64 / self.config.animation_fps;
let coherence_frame = self.create_quantum_coherence_frame(result, time)?;
frames.push(coherence_frame);
}
Ok(QuantumCoherenceAnimation {
frames,
duration: 5.0,
fps: self.config.animation_fps,
})
}
fn create_neuromorphic_activity_plot(
&mut self,
result: &AdvancedClusteringResult,
) -> Result<NeuromorphicActivityPlot> {
let n_neurons = result.centroids.nrows();
let time_steps = 100;
let mut activity_matrix = Array2::zeros((time_steps, n_neurons));
let mut spike_trains = Array2::zeros((time_steps, n_neurons));
for t in 0..time_steps {
let time = t as f64 / time_steps as f64;
for neuron in 0..n_neurons {
let base_activity = result.performance.neural_adaptation_rate;
let quantum_modulation =
result.performance.quantum_coherence * (2.0 * PI * time * 3.0).sin();
let noise = 0.1 * (time * 47.0 + neuron as f64 * 13.0).sin();
let activity = base_activity + 0.2 * quantum_modulation + noise;
activity_matrix[[t, neuron]] = activity.max(0.0).min(1.0);
let spike_threshold = 0.7;
let spike_prob = if activity > spike_threshold { 1.0 } else { 0.0 };
spike_trains[[t, neuron]] = spike_prob;
}
}
let mut plasticity_changes = Array2::zeros((n_neurons, n_neurons));
for i in 0..n_neurons {
for j in 0..n_neurons {
if i != j {
let distance = ((i as f64 - j as f64).abs() / n_neurons as f64).min(1.0);
let plasticity = result.performance.neural_adaptation_rate * (1.0 - distance);
plasticity_changes[[i, j]] = plasticity;
}
}
}
Ok(NeuromorphicActivityPlot {
activity_matrix,
spike_trains,
plasticity_changes,
time_resolution: 1.0 / time_steps as f64,
})
}
fn create_interactive_performance_dashboard(
&mut self,
result: &AdvancedClusteringResult,
) -> Result<InteractivePerformanceDashboard> {
let metrics = &result.performance;
let mut performance_metrics = HashMap::new();
performance_metrics.insert("Silhouette Score".to_string(), metrics.silhouette_score);
performance_metrics.insert("Quantum Coherence".to_string(), metrics.quantum_coherence);
performance_metrics.insert(
"Neural Adaptation".to_string(),
metrics.neural_adaptation_rate,
);
performance_metrics.insert("Energy Efficiency".to_string(), metrics.energy_efficiency);
let mut improvements = HashMap::new();
improvements.insert("AI Speedup".to_string(), result.ai_speedup);
improvements.insert("Quantum Advantage".to_string(), result.quantum_advantage);
improvements.insert(
"Neuromorphic Benefit".to_string(),
result.neuromorphic_benefit,
);
improvements.insert(
"Meta-learning Improvement".to_string(),
result.meta_learning_improvement,
);
let mut metrics_timeline = Vec::new();
for i in 0..metrics.ai_iterations {
let progress = i as f64 / metrics.ai_iterations as f64;
let timestamp = progress * metrics.execution_time;
let coherence = metrics.quantum_coherence * (1.0 - 0.3 * (-progress * 5.0).exp());
let adaptation = metrics.neural_adaptation_rate * (1.0 + 0.5 * progress);
metrics_timeline.push(MetricTimelinePoint {
timestamp,
quantum_coherence: coherence,
neural_adaptation: adaptation,
ai_confidence: result.confidence * (1.0 - (-progress * 3.0).exp()),
});
}
Ok(InteractivePerformanceDashboard {
performance_metrics,
improvements,
metrics_timeline,
execution_summary: ExecutionSummary {
total_time: metrics.execution_time,
memory_usage: metrics.memory_usage,
iterations: metrics.ai_iterations,
algorithm: result.selected_algorithm.clone(),
confidence: result.confidence,
},
})
}
fn calculate_point_quantum_enhancement(
&self,
point_idx: usize,
cluster_id: usize,
result: &AdvancedClusteringResult,
) -> f64 {
let base_quantum = result.quantum_advantage / 10.0;
let coherence_factor = result.performance.quantum_coherence;
let confidence_factor = result.confidence;
let quantum_phase = 2.0 * PI * (point_idx as f64 + cluster_id as f64) / 100.0;
let phase_modulation = quantum_phase.cos() * 0.2;
(base_quantum + coherence_factor * 0.3 + confidence_factor * 0.2 + phase_modulation)
.max(0.0)
.min(1.0)
}
fn get_cluster_color(&self, cluster_id: usize) -> String {
match self.config.color_scheme {
PlotColorScheme::Quantum => {
let hue = (cluster_id as f64 * 137.5) % 360.0; format!("hsl({}, 70%, 60%)", hue)
}
PlotColorScheme::Neuromorphic => {
let colors = ["#00FF00", "#FFD700", "#FF4500", "#FF1493", "#00CED1"];
colors[cluster_id % colors.len()].to_string()
}
PlotColorScheme::AI => {
let colors = ["#FFD700", "#FF8C00", "#FF4500", "#DC143C", "#B22222"];
colors[cluster_id % colors.len()].to_string()
}
PlotColorScheme::Scientific => {
let intensity = 128 + (cluster_id * 32) % 128;
format!("rgb({}, {}, {})", intensity, intensity, intensity)
}
PlotColorScheme::Custom(ref colors) => {
if colors.is_empty() {
"#0088FF".to_string()
} else {
let color = colors[cluster_id % colors.len()];
format!("rgb({}, {}, {})", color[0], color[1], color[2])
}
}
}
}
fn get_cluster_color_rgb(&self, cluster_id: usize) -> [u8; 3] {
match self.config.color_scheme {
PlotColorScheme::Quantum => {
let hue = (cluster_id as f64 * 137.5) % 360.0;
self.hsl_to_rgb(hue, 0.7, 0.6)
}
PlotColorScheme::Neuromorphic => {
let colors = [
[0, 255, 0],
[255, 215, 0],
[255, 69, 0],
[255, 20, 147],
[0, 206, 209],
];
colors[cluster_id % colors.len()]
}
PlotColorScheme::AI => {
let colors = [
[255, 215, 0],
[255, 140, 0],
[255, 69, 0],
[220, 20, 60],
[178, 34, 34],
];
colors[cluster_id % colors.len()]
}
PlotColorScheme::Scientific => {
let intensity = 128 + (cluster_id * 32) % 128;
[intensity as u8, intensity as u8, intensity as u8]
}
PlotColorScheme::Custom(ref colors) => {
if colors.is_empty() {
[0, 136, 255]
} else {
colors[cluster_id % colors.len()]
}
}
}
}
fn apply_quantum_color_enhancement(&self, base_color: String, quantum_factor: f64) -> String {
if base_color.starts_with("hsl") {
if let Some(hsl_part) = base_color
.strip_prefix("hsl(")
.and_then(|s| s.strip_suffix(")"))
{
let parts: Vec<&str> = hsl_part.split(", ").collect();
if parts.len() == 3 {
if let (Ok(h), Ok(s), Ok(l)) = (
parts[0].parse::<f64>(),
parts[1].strip_suffix("%").unwrap_or("0").parse::<f64>(),
parts[2].strip_suffix("%").unwrap_or("0").parse::<f64>(),
) {
let enhanced_s = (s + quantum_factor * 20.0).min(100.0);
let enhanced_l = (l + quantum_factor * 10.0).min(90.0);
return format!("hsl({}, {}%, {}%)", h, enhanced_s, enhanced_l);
}
}
}
}
base_color }
fn apply_quantum_color_enhancement_rgb(
&self,
base_color: [u8; 3],
quantum_factor: f64,
) -> [u8; 3] {
let enhancement = (quantum_factor * 50.0) as u8;
[
(base_color[0] as u16 + enhancement as u16).min(255) as u8,
base_color[1],
(base_color[2] as u16 + enhancement as u16).min(255) as u8,
]
}
fn hsl_to_rgb(&self, h: f64, s: f64, l: f64) -> [u8; 3] {
let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
let m = l - c / 2.0;
let (r_prime, g_prime, b_prime) = match h as u32 {
0..=59 => (c, x, 0.0),
60..=119 => (x, c, 0.0),
120..=179 => (0.0, c, x),
180..=239 => (0.0, x, c),
240..=299 => (x, 0.0, c),
_ => (c, 0.0, x),
};
[
((r_prime + m) * 255.0) as u8,
((g_prime + m) * 255.0) as u8,
((b_prime + m) * 255.0) as u8,
]
}
fn calculate_plot_bounds(&self, data: &Array2<f64>) -> (f64, f64, f64, f64) {
let x_values = data.column(0);
let y_values = data.column(1);
let x_min = x_values.iter().fold(f64::INFINITY, |a, &b| a.min(b));
let x_max = x_values.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
let y_min = y_values.iter().fold(f64::INFINITY, |a, &b| a.min(b));
let y_max = y_values.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
let x_padding = (x_max - x_min) * 0.1;
let y_padding = (y_max - y_min) * 0.1;
(
x_min - x_padding,
x_max + x_padding,
y_min - y_padding,
y_max + y_padding,
)
}
fn apply_native_pca(&self, data: &ArrayView2<f64>, target_dims: usize) -> Result<Array2<f64>> {
let n_samples = data.nrows();
let n_features = data.ncols();
if target_dims >= n_features {
return Ok(data.to_owned());
}
let mean = data.mean_axis(Axis(0)).expect("Operation failed");
let centered = data - &mean.insert_axis(Axis(0));
let mut reduced = Array2::zeros((n_samples, target_dims));
for i in 0..n_samples {
for j in 0..target_dims {
let mut component = 0.0;
for k in 0..n_features {
let weight = (k as f64 * PI / n_features as f64
+ j as f64 * PI / target_dims as f64)
.cos();
component += centered[[i, k]] * weight;
}
reduced[[i, j]] = component / (n_features as f64).sqrt();
}
}
Ok(reduced)
}
fn add_plot_axes_and_labels(
&mut self,
x_min: f64,
x_max: f64,
y_min: f64,
ymax: f64,
margin: f64,
) -> Result<()> {
let plot_width = self.config.width as f64 - 2.0 * margin;
let plot_height = self.config.height as f64 - 2.0 * margin;
let x_axis = SvgElement::Line {
x1: margin,
y1: margin + plot_height,
x2: margin + plot_width,
y2: margin + plot_height,
stroke: "#333333".to_string(),
stroke_width: 2.0,
opacity: 1.0,
};
let y_axis = SvgElement::Line {
x1: margin,
y1: margin,
x2: margin,
y2: margin + plot_height,
stroke: "#333333".to_string(),
stroke_width: 2.0,
opacity: 1.0,
};
let x_label = SvgElement::Text {
x: margin + plot_width / 2.0,
y: margin + plot_height + 30.0,
content: "Principal Component 1".to_string(),
font_size: 14.0,
fill: "#333333".to_string(),
text_anchor: "middle".to_string(),
};
let y_label = SvgElement::Text {
x: margin - 30.0,
y: margin + plot_height / 2.0,
content: "Principal Component 2".to_string(),
font_size: 14.0,
fill: "#333333".to_string(),
text_anchor: "middle".to_string(),
};
self.svg_canvas.add_element(x_axis);
self.svg_canvas.add_element(y_axis);
self.svg_canvas.add_element(x_label);
self.svg_canvas.add_element(y_label);
Ok(())
}
fn generate_interactive_script(&self) -> String {
r#"
// Advanced Native Plotting Interactive Script
(function() {
let zoom = 1.0;
let panX = 0, panY = 0;
let selectedElements = [];
// Initialize interactive features
function initInteractivity() {
const svg = document.querySelector('svg');
if (!svg) return;
// Zoom and pan
svg.addEventListener('wheel', handleZoom);
svg.addEventListener('mousedown', handlePanStart);
svg.addEventListener('mousemove', handlePanMove);
svg.addEventListener('mouseup', handlePanEnd);
// Element selection
svg.addEventListener('click', handleElementClick);
svg.addEventListener('mouseover', handleElementHover);
svg.addEventListener('mouseout', handleElementOut);
}
function handleZoom(event) {
event.preventDefault();
const delta = event.deltaY > 0 ? 0.9 : 1.1;
zoom *= delta;
updateTransform();
}
function handleElementClick(event) {
const target = event.target;
if (target.tagName === 'circle' || target.tagName === 'path') {
toggleSelection(target);
}
}
function toggleSelection(element) {
const index = selectedElements.indexOf(element);
if (index > -1) {
selectedElements.splice(index, 1);
element.classList.remove('selected');
} else {
selectedElements.push(element);
element.classList.add('selected');
}
}
function updateTransform() {
const svg = document.querySelector('svg');
const mainGroup = svg.querySelector('g.main-group');
if (mainGroup) {
mainGroup.setAttribute('transform',
`translate(${panX}, ${panY}) scale(${zoom})`);
}
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initInteractivity);
} else {
initInteractivity();
}
})();
"#
.to_string()
}
fn build_dendrogram_tree(
&self,
data: &ArrayView2<f64>,
result: &AdvancedClusteringResult,
) -> Result<DendrogramTree> {
let n_samples = data.nrows();
if n_samples == 0 {
return Err(ClusteringError::InvalidInput(
"Cannot build dendrogram from empty data".into(),
));
}
let mut nodes: Vec<DendrogramNode> = (0..n_samples)
.map(|i| {
let cluster_id = if i < result.clusters.len() {
result.clusters[i]
} else {
0
};
let quantum_coherence = result.performance.quantum_coherence
* ((i as f64 * PI / n_samples as f64).cos().abs());
DendrogramNode {
id: format!("leaf_{i}"),
height: 0.0,
children: Vec::new(),
data_indices: vec![i],
quantum_coherence,
neuromorphic_activity: result.performance.neural_adaptation_rate
* (1.0 - (cluster_id as f64 / (result.centroids.nrows().max(1) as f64))),
}
})
.collect();
let mut merge_height = 0.0_f64;
while nodes.len() > 1 {
let n = nodes.len();
let centroid = |node: &DendrogramNode| -> Array1<f64> {
let cols = data.ncols();
let mut sum: Array1<f64> = Array1::zeros(cols);
for &idx in &node.data_indices {
if idx < data.nrows() {
for c in 0..cols {
sum[c] += data[[idx, c]];
}
}
}
let cnt = node.data_indices.len().max(1) as f64;
sum.mapv(|v: f64| v / cnt)
};
let mut best_dist = f64::INFINITY;
let mut best_i = 0;
let mut best_j = 1;
for i in 0..n {
let ci = centroid(&nodes[i]);
for j in (i + 1)..n {
let cj = centroid(&nodes[j]);
let dist: f64 = ci
.iter()
.zip(cj.iter())
.map(|(a, b)| (a - b) * (a - b))
.sum::<f64>()
.sqrt();
if dist < best_dist {
best_dist = dist;
best_i = i;
best_j = j;
}
}
}
merge_height += best_dist;
let node_j = nodes.remove(best_j);
let node_i = nodes.remove(best_i);
let mut merged_indices = node_i.data_indices.clone();
merged_indices.extend_from_slice(&node_j.data_indices);
let avg_coherence = (node_i.quantum_coherence + node_j.quantum_coherence) / 2.0;
let avg_neuro = (node_i.neuromorphic_activity + node_j.neuromorphic_activity) / 2.0;
let merged = DendrogramNode {
id: format!("merge_{}_{}", node_i.id, node_j.id),
height: merge_height,
children: vec![node_i, node_j],
data_indices: merged_indices,
quantum_coherence: avg_coherence,
neuromorphic_activity: avg_neuro,
};
nodes.push(merged);
}
let root = nodes.remove(0);
let leaf_count = root.data_indices.len();
let total_height = root.height;
Ok(DendrogramTree {
root,
height: total_height,
leaf_count,
})
}
fn calculate_dendrogram_layout(
&self,
tree: &DendrogramTree,
) -> Result<HashMap<String, (f64, f64)>> {
let mut positions: HashMap<String, (f64, f64)> = HashMap::new();
let plot_width = self.config.width as f64;
let plot_height = self.config.height as f64;
let max_height = tree.height.max(1.0);
let mut leaf_counter = 0usize;
fn assign_leaves(
node: &DendrogramNode,
positions: &mut HashMap<String, (f64, f64)>,
leaf_counter: &mut usize,
plot_width: f64,
plot_height: f64,
max_height: f64,
leaf_count: usize,
) {
if node.children.is_empty() {
let x = if leaf_count > 1 {
(*leaf_counter as f64 / (leaf_count - 1) as f64) * plot_width
} else {
plot_width / 2.0
};
let y = plot_height; positions.insert(node.id.clone(), (x, y));
*leaf_counter += 1;
} else {
for child in &node.children {
assign_leaves(
child,
positions,
leaf_counter,
plot_width,
plot_height,
max_height,
leaf_count,
);
}
let child_x: f64 = node
.children
.iter()
.filter_map(|c| positions.get(&c.id).map(|(x, _)| *x))
.sum::<f64>()
/ node.children.len().max(1) as f64;
let y = plot_height * (1.0 - node.height / max_height);
positions.insert(node.id.clone(), (child_x, y));
}
}
assign_leaves(
&tree.root,
&mut positions,
&mut leaf_counter,
plot_width,
plot_height,
max_height,
tree.leaf_count.max(1),
);
Ok(positions)
}
fn calculate_quantum_branch_lengths(
&self,
tree: &DendrogramTree,
result: &AdvancedClusteringResult,
) -> Result<HashMap<String, f64>> {
let mut branch_lengths: HashMap<String, f64> = HashMap::new();
let quantum_scale = result.quantum_advantage.max(1.0);
fn traverse(
node: &DendrogramNode,
parent_height: f64,
branch_lengths: &mut HashMap<String, f64>,
quantum_scale: f64,
) {
let raw_length = (node.height - parent_height).abs();
let quantum_enhanced = raw_length * (1.0 + node.quantum_coherence / quantum_scale);
branch_lengths.insert(node.id.clone(), quantum_enhanced);
for child in &node.children {
traverse(child, node.height, branch_lengths, quantum_scale);
}
}
traverse(&tree.root, 0.0, &mut branch_lengths, quantum_scale);
Ok(branch_lengths)
}
fn calculate_dendrogram_quantum_enhancements(
&self,
tree: &DendrogramTree,
result: &AdvancedClusteringResult,
) -> Result<HashMap<String, f64>> {
let mut enhancements: HashMap<String, f64> = HashMap::new();
fn traverse(
node: &DendrogramNode,
enhancements: &mut HashMap<String, f64>,
base_quantum: f64,
) {
let enhancement = (base_quantum * node.quantum_coherence).clamp(0.0, 1.0);
enhancements.insert(node.id.clone(), enhancement);
for child in &node.children {
traverse(child, enhancements, base_quantum);
}
}
let base_quantum = result.quantum_advantage.clamp(0.01, 10.0);
traverse(&tree.root, &mut enhancements, base_quantum);
Ok(enhancements)
}
fn render_dendrogram_to_svg(
&mut self,
tree: &DendrogramTree,
positions: &HashMap<String, (f64, f64)>,
_branch_lengths: &HashMap<String, f64>,
enhancements: &HashMap<String, f64>,
) -> Result<()> {
fn draw_node(
node: &DendrogramNode,
positions: &HashMap<String, (f64, f64)>,
enhancements: &HashMap<String, f64>,
elements: &mut Vec<SvgElement>,
) {
if let Some(&(x, y)) = positions.get(&node.id) {
for child in &node.children {
if let Some(&(cx, cy)) = positions.get(&child.id) {
let enhancement = enhancements.get(&child.id).copied().unwrap_or(0.0);
let blue = ((enhancement * 200.0) as u8).saturating_add(55);
let color = format!("rgb(0, {}, {})", blue, blue);
elements.push(SvgElement::Line {
x1: x,
y1: y,
x2: cx,
y2: y,
stroke: color.clone(),
stroke_width: 1.5,
opacity: 0.85,
});
elements.push(SvgElement::Line {
x1: cx,
y1: y,
x2: cx,
y2: cy,
stroke: color,
stroke_width: 1.5,
opacity: 0.85,
});
draw_node(child, positions, enhancements, elements);
}
}
if !node.children.is_empty() {
elements.push(SvgElement::Circle {
cx: x,
cy: y,
r: 3.0,
fill: "#00CCFF".to_string(),
stroke: "#0088BB".to_string(),
stroke_width: 1.0,
opacity: 0.9,
});
}
}
}
let mut elements: Vec<SvgElement> = Vec::new();
draw_node(&tree.root, positions, enhancements, &mut elements);
for el in elements {
self.svg_canvas.add_element(el);
}
Ok(())
}
fn create_quantum_field_3d(
&self,
points_3d: &Array2<f64>,
result: &AdvancedClusteringResult,
) -> Result<QuantumField3D> {
let grid = 10usize;
let n_points = points_3d.nrows();
let (mut x_min, mut x_max, mut y_min, mut y_max) = (
f64::INFINITY,
f64::NEG_INFINITY,
f64::INFINITY,
f64::NEG_INFINITY,
);
for i in 0..n_points {
let x = points_3d[[i, 0]];
let y = points_3d[[i, 1]];
x_min = x_min.min(x);
x_max = x_max.max(x);
y_min = y_min.min(y);
y_max = y_max.max(y);
}
x_min -= 1.0;
x_max += 1.0;
y_min -= 1.0;
y_max += 1.0;
let mut field_strength = Array2::zeros((grid, grid));
let mut coherence = Array2::zeros((grid, grid));
let mut phase = Array2::zeros((grid, grid));
let base_coherence = result.performance.quantum_coherence;
for gi in 0..grid {
for gj in 0..grid {
let gx = x_min + (gi as f64 / (grid - 1) as f64) * (x_max - x_min);
let gy = y_min + (gj as f64 / (grid - 1) as f64) * (y_max - y_min);
let mut strength = 0.0_f64;
for i in 0..n_points {
let dx = gx - points_3d[[i, 0]];
let dy = gy - points_3d[[i, 1]];
let dz = if points_3d.ncols() > 2 {
gx - points_3d[[i, 2]]
} else {
0.0
};
let dist2 = (dx * dx + dy * dy + dz * dz).max(1e-6);
strength += 1.0 / dist2;
}
field_strength[[gi, gj]] = strength.min(100.0);
coherence[[gi, gj]] = base_coherence * (1.0 - strength / (strength + 10.0));
phase[[gi, gj]] = (gx * 0.5 + gy * 0.3).sin() * PI;
}
}
let n_centroids = result.centroids.nrows().min(5);
let mut entanglement_lines = Vec::new();
for i in 0..n_centroids {
for j in (i + 1)..n_centroids {
let coherence_ij = base_coherence * 0.8;
if coherence_ij > 0.3 {
let pi_x = result.centroids[[i, 0]];
let pi_y = result.centroids[[i, 1]];
let pi_z = if result.centroids.ncols() > 2 {
result.centroids[[i, 2]]
} else {
0.0
};
let pj_x = result.centroids[[j, 0]];
let pj_y = result.centroids[[j, 1]];
let pj_z = if result.centroids.ncols() > 2 {
result.centroids[[j, 2]]
} else {
0.0
};
entanglement_lines.push(([pi_x, pi_y, pi_z], [pj_x, pj_y, pj_z], coherence_ij));
}
}
}
Ok(QuantumField3D {
field_strength,
coherence,
phase,
entanglement_lines,
})
}
fn create_quantum_coherence_frame(
&self,
result: &AdvancedClusteringResult,
t: f64,
) -> Result<QuantumCoherenceFrame> {
let grid = 8usize;
let mut field_strength = Array2::zeros((grid, grid));
let base = result.performance.quantum_coherence;
for gi in 0..grid {
for gj in 0..grid {
let wave_x = (gi as f64 / grid as f64 * 2.0 * PI + t * 2.5).cos();
let wave_y = (gj as f64 / grid as f64 * 2.0 * PI + t * 1.7).sin();
field_strength[[gi, gj]] = (base + 0.3 * wave_x * wave_y).clamp(0.0, 1.0);
}
}
let n_centroids = result.centroids.nrows().min(8);
let mut elements: Vec<SvgElement> = Vec::new();
let w = self.config.width as f64;
let h = self.config.height as f64;
for k in 0..n_centroids {
let cx_val = result.centroids[[k, 0]];
let cy_val = result.centroids[[k, 1]];
let sx = (cx_val + 5.0) / 10.0 * w;
let sy = (cy_val + 5.0) / 10.0 * h;
let pulse = (t * 2.0 * PI + k as f64 * 0.5).sin() * 0.5 + 0.5;
let r = 5.0 + pulse * 10.0;
let opacity = 0.4 + pulse * 0.5;
let blue_val = ((base * 200.0) as u8).saturating_add(55);
let color = format!("rgba(0, {blue_val}, 255, {opacity:.2})");
elements.push(SvgElement::Circle {
cx: sx,
cy: sy,
r,
fill: color.clone(),
stroke: "#00FFFF".to_string(),
stroke_width: 1.0,
opacity,
});
}
Ok(QuantumCoherenceFrame {
timestamp: t,
elements,
field_strength,
})
}
}
#[derive(Debug)]
pub struct NativeClusterPlot {
pub data: Array2<f64>,
pub point_elements: Vec<SvgElement>,
pub centroid_elements: Vec<SvgElement>,
pub quantum_enhancements: Vec<f64>,
pub bounds: (f64, f64, f64, f64),
pub scale: (f64, f64),
}
#[derive(Debug)]
pub struct NativeVisualizationOutput {
pub cluster_plot: NativeClusterPlot,
pub dendrogram: Option<NativeDendrogramPlot>,
pub plot_3d: Option<Native3DClusterPlot>,
pub quantum_animation: Option<QuantumCoherenceAnimation>,
pub neuromorphic_plot: NeuromorphicActivityPlot,
pub performance_dashboard: InteractivePerformanceDashboard,
pub svg_content: String,
pub interactive_script: String,
}
#[derive(Debug)]
pub struct QuantumCoherenceAnimation {
pub frames: Vec<QuantumCoherenceFrame>,
pub duration: f64,
pub fps: f64,
}
#[derive(Debug, Clone)]
pub struct QuantumCoherenceFrame {
pub timestamp: f64,
pub elements: Vec<SvgElement>,
pub field_strength: Array2<f64>,
}
#[derive(Debug)]
pub struct NeuromorphicActivityPlot {
pub activity_matrix: Array2<f64>,
pub spike_trains: Array2<f64>,
pub plasticity_changes: Array2<f64>,
pub time_resolution: f64,
}
#[derive(Debug)]
pub struct InteractivePerformanceDashboard {
pub performance_metrics: HashMap<String, f64>,
pub improvements: HashMap<String, f64>,
pub metrics_timeline: Vec<MetricTimelinePoint>,
pub execution_summary: ExecutionSummary,
}
#[derive(Debug, Clone)]
pub struct MetricTimelinePoint {
pub timestamp: f64,
pub quantum_coherence: f64,
pub neural_adaptation: f64,
pub ai_confidence: f64,
}
#[derive(Debug, Clone)]
pub struct ExecutionSummary {
pub total_time: f64,
pub memory_usage: f64,
pub iterations: usize,
pub algorithm: String,
pub confidence: f64,
}
impl SvgCanvas {
pub fn new(width: usize, height: usize) -> Self {
Self {
width,
height,
elements: Vec::new(),
styles: HashMap::new(),
}
}
pub fn clear(&mut self) {
self.elements.clear();
}
pub fn add_element(&mut self, element: SvgElement) {
self.elements.push(element);
}
pub fn to_svg(&self) -> String {
let mut svg = format!(
r#"<svg width="{}" height="{}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">"#,
self.width, self.height
);
svg.push_str("<defs><style>");
for (selector, style) in &self.styles {
svg.push_str(&format!("{} {{ {} }}", selector, style));
}
svg.push_str("</style></defs>");
svg.push_str(r#"<g class="main-group">"#);
for element in &self.elements {
svg.push_str(&element.to_svg());
}
svg.push_str("</g></svg>");
svg
}
}
impl SvgElement {
pub fn to_svg(&self) -> String {
match self {
SvgElement::Circle {
cx,
cy,
r,
fill,
stroke,
stroke_width,
opacity,
} => {
format!(
r#"<circle cx="{}" cy="{}" r="{}" fill="{}" stroke="{}" stroke-width="{}" opacity="{}" />"#,
cx, cy, r, fill, stroke, stroke_width, opacity
)
}
SvgElement::Line {
x1,
y1,
x2,
y2,
stroke,
stroke_width,
opacity,
} => {
format!(
r#"<line x1="{}" y1="{}" x2="{}" y2="{}" stroke="{}" stroke-width="{}" opacity="{}" />"#,
x1, y1, x2, y2, stroke, stroke_width, opacity
)
}
SvgElement::Path {
d,
fill,
stroke,
stroke_width,
opacity,
} => {
format!(
r#"<path d="{}" fill="{}" stroke="{}" stroke-width="{}" opacity="{}" />"#,
d, fill, stroke, stroke_width, opacity
)
}
SvgElement::Text {
x,
y,
content,
font_size,
fill,
text_anchor,
} => {
format!(
r#"<text x="{}" y="{}" font-size="{}" fill="{}" text-anchor="{}">{}</text>"#,
x, y, font_size, fill, text_anchor, content
)
}
SvgElement::Group {
id,
elements,
transform,
} => {
let mut group = format!(r#"<g id="{}" transform="{}">"#, id, transform);
for element in elements {
group.push_str(&element.to_svg());
}
group.push_str("</g>");
group
}
}
}
}
impl AnimationEngine {
pub fn new(fps: f64) -> Self {
Self {
frames: Vec::new(),
current_frame: 0,
frame_duration: 1000.0 / fps,
total_duration: 0.0,
}
}
}
impl InteractiveController {
pub fn new() -> Self {
Self {
zoom_level: 1.0,
pan_offset: (0.0, 0.0),
selected_elements: Vec::new(),
hover_element: None,
}
}
}
#[allow(dead_code)]
pub fn create_native_advanced_plot(
data: &ArrayView2<f64>,
result: &AdvancedClusteringResult,
config: Option<NativePlotConfig>,
) -> Result<NativeVisualizationOutput> {
let config = config.unwrap_or_default();
let mut plotter = AdvancedNativePlotter::new(config);
plotter.create_comprehensive_plot(data, result)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::advanced_clustering::{AdvancedClusteringResult, AdvancedPerformanceMetrics};
use scirs2_core::ndarray::{array, Array1, Array2};
fn make_result() -> AdvancedClusteringResult {
let centroids = array![[0.5, 0.5], [5.5, 5.5]];
let clusters = Array1::from_vec(vec![0, 0, 1, 1]);
AdvancedClusteringResult {
clusters,
centroids,
ai_speedup: 1.0,
quantum_advantage: 1.0,
neuromorphic_benefit: 1.0,
meta_learning_improvement: 0.0,
selected_algorithm: "test".to_string(),
confidence: 0.9,
performance: AdvancedPerformanceMetrics {
silhouette_score: 0.8,
execution_time: 0.001,
memory_usage: 1.0,
quantum_coherence: 0.7,
neural_adaptation_rate: 0.5,
ai_iterations: 5,
energy_efficiency: 0.9,
},
}
}
fn make_data() -> Array2<f64> {
array![[0.0, 0.0], [1.0, 1.0], [5.0, 5.0], [6.0, 6.0]]
}
#[test]
fn test_build_dendrogram_tree_basic() {
let plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
let data = make_data();
let result = make_result();
let tree = plotter
.build_dendrogram_tree(&data.view(), &result)
.expect("build_dendrogram_tree failed");
assert_eq!(tree.leaf_count, 4, "leaf_count should equal n_samples");
assert!(
tree.root.height >= 0.0,
"root height must be non-negative, got {}",
tree.root.height
);
}
#[test]
fn test_calculate_dendrogram_layout_all_nodes_have_positions() {
let plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
let data = make_data();
let result = make_result();
let tree = plotter
.build_dendrogram_tree(&data.view(), &result)
.expect("build_dendrogram_tree failed");
let layout = plotter
.calculate_dendrogram_layout(&tree)
.expect("calculate_dendrogram_layout failed");
assert!(
!layout.is_empty(),
"layout must contain at least one position"
);
for (id, (x, y)) in &layout {
assert!(
x.is_finite() && y.is_finite(),
"position for node '{id}' is not finite: ({x}, {y})"
);
}
}
#[test]
fn test_calculate_quantum_branch_lengths_all_nodes() {
let plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
let data = make_data();
let result = make_result();
let tree = plotter
.build_dendrogram_tree(&data.view(), &result)
.expect("build_dendrogram_tree failed");
let lengths = plotter
.calculate_quantum_branch_lengths(&tree, &result)
.expect("calculate_quantum_branch_lengths failed");
assert!(!lengths.is_empty(), "branch lengths map must not be empty");
for (id, length) in &lengths {
assert!(
length.is_finite(),
"branch length for '{id}' is not finite: {length}"
);
}
}
#[test]
fn test_calculate_dendrogram_quantum_enhancements_in_range() {
let plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
let data = make_data();
let result = make_result();
let tree = plotter
.build_dendrogram_tree(&data.view(), &result)
.expect("build_dendrogram_tree failed");
let enhancements = plotter
.calculate_dendrogram_quantum_enhancements(&tree, &result)
.expect("calculate_dendrogram_quantum_enhancements failed");
assert!(
!enhancements.is_empty(),
"enhancements map must not be empty"
);
for (id, enh) in &enhancements {
assert!(
*enh >= 0.0 && *enh <= 1.0,
"enhancement for '{id}' is out of [0, 1]: {enh}"
);
}
}
#[test]
fn test_render_dendrogram_to_svg_adds_elements() {
let mut plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
let data = make_data();
let result = make_result();
let tree = plotter
.build_dendrogram_tree(&data.view(), &result)
.expect("build_dendrogram_tree failed");
let positions = plotter
.calculate_dendrogram_layout(&tree)
.expect("calculate_dendrogram_layout failed");
let branch_lengths = plotter
.calculate_quantum_branch_lengths(&tree, &result)
.expect("calculate_quantum_branch_lengths failed");
let enhancements = plotter
.calculate_dendrogram_quantum_enhancements(&tree, &result)
.expect("calculate_dendrogram_quantum_enhancements failed");
plotter
.render_dendrogram_to_svg(&tree, &positions, &branch_lengths, &enhancements)
.expect("render_dendrogram_to_svg failed");
let svg = plotter.svg_canvas.to_svg();
assert!(
!svg.is_empty(),
"SVG canvas must be non-empty after rendering"
);
}
#[test]
fn test_create_quantum_field_3d_grid_dimensions() {
let plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
let data = make_data();
let result = make_result();
let data_3d: Array2<f64> = array![
[0.0, 0.0, 0.0],
[1.0, 1.0, 1.0],
[5.0, 5.0, 5.0],
[6.0, 6.0, 6.0]
];
let field = plotter
.create_quantum_field_3d(&data_3d, &result)
.expect("create_quantum_field_3d failed");
let shape = field.field_strength.shape();
assert_eq!(shape[0], 10, "field grid rows should be 10");
assert_eq!(shape[1], 10, "field grid cols should be 10");
for v in field.field_strength.iter() {
assert!(
v.is_finite() && *v >= 0.0,
"field value should be finite and >= 0, got {v}"
);
}
let _ = data;
}
#[test]
fn test_create_quantum_coherence_frame_shape_and_timestamp() {
let plotter = AdvancedNativePlotter::new(NativePlotConfig::default());
let result = make_result();
let frame = plotter
.create_quantum_coherence_frame(&result, 0.0)
.expect("create_quantum_coherence_frame failed");
assert_eq!(
frame.timestamp, 0.0,
"timestamp should match argument t=0.0"
);
let shape = frame.field_strength.shape();
assert_eq!(shape[0], 8, "field_strength rows should be 8");
assert_eq!(shape[1], 8, "field_strength cols should be 8");
assert_eq!(
frame.elements.len(),
2,
"should have one element per centroid"
);
}
#[test]
fn test_create_comprehensive_plot_runs_ok() {
let data = make_data();
let result = make_result();
let output = create_native_advanced_plot(&data.view(), &result, None)
.expect("create_native_advanced_plot failed");
assert!(
!output.svg_content.is_empty(),
"svg_content must be non-empty"
);
assert!(
output.plot_3d.is_none(),
"plot_3d should be None for 2-D data"
);
}
#[test]
fn test_comprehensive_plot_hierarchical_triggers_dendrogram() {
let data = make_data();
let mut result = make_result();
result.selected_algorithm = "hierarchical".to_string();
let output = create_native_advanced_plot(&data.view(), &result, None)
.expect("create_native_advanced_plot (hierarchical) failed");
assert!(
output.dendrogram.is_some(),
"dendrogram should be Some when selected_algorithm contains 'hierarchical'"
);
}
}
#[allow(dead_code)]
pub fn export_native_visualization(
output: &NativeVisualizationOutput,
filename: &str,
format: &str,
) -> Result<()> {
match format.to_lowercase().as_str() {
"svg" => {
use std::fs::File;
use std::io::Write;
let mut file = File::create(format!("{}.svg", filename)).map_err(|e| {
ClusteringError::InvalidInput(format!("Failed to create SVG file: {}", e))
})?;
file.write_all(output.svg_content.as_bytes()).map_err(|e| {
ClusteringError::InvalidInput(format!("Failed to write SVG file: {}", e))
})?;
println!("✅ Exported native Advanced visualization to {filename}.svg");
}
"html" => {
use std::fs::File;
use std::io::Write;
let html_content = format!(
r#"<!DOCTYPE html>
<html>
<head>
<title>Advanced Native Visualization</title>
<style>
body {{ margin: 0; padding: 20px; background: #1a1a2e; }}
.selected {{ stroke: #FFD700 !important; stroke-width: 3px !important; }}
</style>
</head>
<body>
{}
<script>{}</script>
</body>
</html>"#,
output.svg_content, output.interactive_script
);
let mut file = File::create(format!("{}.html", filename)).map_err(|e| {
ClusteringError::InvalidInput(format!("Failed to create HTML file: {}", e))
})?;
file.write_all(html_content.as_bytes()).map_err(|e| {
ClusteringError::InvalidInput(format!("Failed to write HTML file: {}", e))
})?;
println!("🌐 Exported interactive Advanced visualization to {filename}.html");
}
_ => {
return Err(ClusteringError::InvalidInput(format!(
"Unsupported export format: {}",
format
)));
}
}
Ok(())
}