quantrs2_sim/
visualization_hooks.rs

1//! Visualization hooks for quantum simulation debugging and analysis.
2//!
3//! This module provides comprehensive visualization capabilities for quantum
4//! simulations, including real-time state visualization, circuit diagrams,
5//! performance plots, and debugging interfaces. It integrates with various
6//! visualization frameworks and provides export capabilities for analysis.
7
8use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
9use scirs2_core::Complex64;
10use serde::{Deserialize, Serialize};
11use std::collections::{HashMap, VecDeque};
12use std::fs::File;
13use std::io::Write as IoWrite;
14use std::path::Path;
15use std::sync::{Arc, Mutex};
16use std::time::{Duration, SystemTime, UNIX_EPOCH};
17
18use crate::circuit_interfaces::{InterfaceCircuit, InterfaceGate, InterfaceGateType};
19use crate::debugger::{ExecutionSnapshot, PerformanceMetrics};
20use crate::error::{Result, SimulatorError};
21
22use std::fmt::Write;
23/// Visualization framework types
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
25pub enum VisualizationFramework {
26    /// Matplotlib-compatible output
27    Matplotlib,
28    /// Plotly interactive plots
29    Plotly,
30    /// D3.js web visualization
31    D3JS,
32    /// Custom SVG output
33    SVG,
34    /// ASCII art for terminal
35    ASCII,
36    /// LaTeX/TikZ for publications
37    LaTeX,
38    /// JSON data export
39    JSON,
40}
41
42/// Visualization configuration
43#[derive(Debug, Clone)]
44pub struct VisualizationConfig {
45    /// Target visualization framework
46    pub framework: VisualizationFramework,
47    /// Enable real-time visualization
48    pub real_time: bool,
49    /// Maximum data points to keep in memory
50    pub max_data_points: usize,
51    /// Export directory for plots
52    pub export_directory: String,
53    /// Color scheme for plots
54    pub color_scheme: ColorScheme,
55    /// Enable interactive features
56    pub interactive: bool,
57    /// Plot dimensions (width, height)
58    pub plot_dimensions: (usize, usize),
59    /// Enable animation for time series
60    pub enable_animation: bool,
61}
62
63impl Default for VisualizationConfig {
64    fn default() -> Self {
65        Self {
66            framework: VisualizationFramework::JSON,
67            real_time: false,
68            max_data_points: 10_000,
69            export_directory: "./visualization_output".to_string(),
70            color_scheme: ColorScheme::Default,
71            interactive: false,
72            plot_dimensions: (800, 600),
73            enable_animation: false,
74        }
75    }
76}
77
78/// Color schemes for visualization
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum ColorScheme {
81    Default,
82    Dark,
83    Light,
84    Scientific,
85    Quantum,
86    Accessibility,
87}
88
89/// Visualization data types
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub enum VisualizationData {
92    /// Quantum state vector amplitudes
93    StateVector {
94        amplitudes: Vec<Complex64>,
95        basis_labels: Vec<String>,
96        timestamp: f64,
97    },
98    /// Circuit diagram data
99    CircuitDiagram {
100        gates: Vec<GateVisualizationData>,
101        num_qubits: usize,
102        circuit_depth: usize,
103    },
104    /// Performance metrics over time
105    PerformanceTimeSeries {
106        timestamps: Vec<f64>,
107        execution_times: Vec<f64>,
108        memory_usage: Vec<f64>,
109        gate_counts: Vec<HashMap<String, usize>>,
110    },
111    /// Entanglement structure
112    EntanglementVisualization {
113        entanglement_matrix: Array2<f64>,
114        qubit_labels: Vec<String>,
115        bipartite_entropies: Vec<f64>,
116    },
117    /// Error correction syndrome patterns
118    SyndromePattern {
119        syndrome_data: Vec<Vec<bool>>,
120        error_locations: Vec<usize>,
121        correction_success: bool,
122        timestamp: f64,
123    },
124    /// Optimization landscape
125    OptimizationLandscape {
126        parameter_values: Vec<Vec<f64>>,
127        cost_values: Vec<f64>,
128        gradient_norms: Vec<f64>,
129        parameter_names: Vec<String>,
130    },
131}
132
133/// Gate visualization data
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct GateVisualizationData {
136    pub gate_type: String,
137    pub qubits: Vec<usize>,
138    pub parameters: Vec<f64>,
139    pub position: usize,
140    pub execution_time: Option<f64>,
141    pub label: Option<String>,
142}
143
144/// Visualization hook interface
145pub trait VisualizationHook: Send + Sync {
146    /// Process visualization data
147    fn process_data(&mut self, data: VisualizationData) -> Result<()>;
148
149    /// Export accumulated data
150    fn export(&self, path: &str) -> Result<()>;
151
152    /// Clear accumulated data
153    fn clear(&mut self);
154
155    /// Get visualization framework name
156    fn framework(&self) -> VisualizationFramework;
157}
158
159/// Main visualization manager
160pub struct VisualizationManager {
161    /// Configuration
162    config: VisualizationConfig,
163    /// Registered visualization hooks
164    hooks: Vec<Box<dyn VisualizationHook>>,
165    /// Data buffer for real-time visualization
166    data_buffer: Arc<Mutex<VecDeque<VisualizationData>>>,
167    /// Performance metrics history
168    performance_history: Arc<Mutex<Vec<PerformanceMetrics>>>,
169    /// Active visualizations
170    active_visualizations: HashMap<String, Box<dyn VisualizationHook>>,
171}
172
173impl VisualizationManager {
174    /// Create new visualization manager
175    #[must_use]
176    pub fn new(config: VisualizationConfig) -> Self {
177        Self {
178            config,
179            hooks: Vec::new(),
180            data_buffer: Arc::new(Mutex::new(VecDeque::with_capacity(1000))),
181            performance_history: Arc::new(Mutex::new(Vec::new())),
182            active_visualizations: HashMap::new(),
183        }
184    }
185
186    /// Register a visualization hook
187    pub fn register_hook(&mut self, hook: Box<dyn VisualizationHook>) {
188        self.hooks.push(hook);
189    }
190
191    /// Visualize quantum state
192    pub fn visualize_state(
193        &mut self,
194        state: &Array1<Complex64>,
195        label: Option<String>,
196    ) -> Result<()> {
197        let amplitudes = state.to_vec();
198        let basis_labels = self.generate_basis_labels(state.len());
199        let timestamp = SystemTime::now()
200            .duration_since(UNIX_EPOCH)
201            .map_err(|e| SimulatorError::InvalidOperation(format!("System time error: {e}")))?
202            .as_secs_f64();
203
204        let data = VisualizationData::StateVector {
205            amplitudes,
206            basis_labels,
207            timestamp,
208        };
209
210        self.process_visualization_data(data)?;
211        Ok(())
212    }
213
214    /// Visualize circuit structure
215    pub fn visualize_circuit(&mut self, circuit: &InterfaceCircuit) -> Result<()> {
216        let gates = circuit
217            .gates
218            .iter()
219            .enumerate()
220            .map(|(pos, gate)| GateVisualizationData {
221                gate_type: format!("{:?}", gate.gate_type),
222                qubits: gate.qubits.clone(),
223                parameters: self.extract_gate_parameters(&gate.gate_type),
224                position: pos,
225                execution_time: None,
226                label: gate.label.clone(),
227            })
228            .collect();
229
230        let data = VisualizationData::CircuitDiagram {
231            gates,
232            num_qubits: circuit.num_qubits,
233            circuit_depth: circuit.gates.len(),
234        };
235
236        self.process_visualization_data(data)?;
237        Ok(())
238    }
239
240    /// Visualize performance metrics
241    pub fn visualize_performance(&mut self, metrics: &PerformanceMetrics) -> Result<()> {
242        {
243            let mut history = self
244                .performance_history
245                .lock()
246                .map_err(|e| SimulatorError::InvalidOperation(format!("Lock poisoned: {e}")))?;
247            history.push(metrics.clone());
248
249            // Keep only recent data
250            if history.len() > self.config.max_data_points {
251                history.remove(0);
252            }
253        }
254
255        // Create time series data
256        let data = {
257            let history = self
258                .performance_history
259                .lock()
260                .map_err(|e| SimulatorError::InvalidOperation(format!("Lock poisoned: {e}")))?;
261            let timestamps: Vec<f64> = (0..history.len()).map(|i| i as f64).collect();
262            let execution_times: Vec<f64> =
263                history.iter().map(|m| m.total_time.as_secs_f64()).collect();
264            let memory_usage: Vec<f64> = history
265                .iter()
266                .map(|m| m.memory_usage.peak_statevector_memory as f64)
267                .collect();
268            let gate_counts: Vec<HashMap<String, usize>> =
269                history.iter().map(|m| m.gate_counts.clone()).collect();
270
271            VisualizationData::PerformanceTimeSeries {
272                timestamps,
273                execution_times,
274                memory_usage,
275                gate_counts,
276            }
277        };
278
279        self.process_visualization_data(data)?;
280        Ok(())
281    }
282
283    /// Visualize entanglement structure
284    pub fn visualize_entanglement(
285        &mut self,
286        state: &Array1<Complex64>,
287        qubit_labels: Option<Vec<String>>,
288    ) -> Result<()> {
289        let num_qubits = (state.len() as f64).log2().round() as usize;
290        let labels =
291            qubit_labels.unwrap_or_else(|| (0..num_qubits).map(|i| format!("q{i}")).collect());
292
293        // Calculate entanglement matrix (simplified)
294        let entanglement_matrix = self.calculate_entanglement_matrix(state, num_qubits)?;
295
296        // Calculate bipartite entropies
297        let bipartite_entropies = self.calculate_bipartite_entropies(state, num_qubits)?;
298
299        let data = VisualizationData::EntanglementVisualization {
300            entanglement_matrix,
301            qubit_labels: labels,
302            bipartite_entropies,
303        };
304
305        self.process_visualization_data(data)?;
306        Ok(())
307    }
308
309    /// Visualize syndrome patterns (for error correction)
310    pub fn visualize_syndrome_pattern(
311        &mut self,
312        syndrome_data: Vec<Vec<bool>>,
313        error_locations: Vec<usize>,
314        correction_success: bool,
315    ) -> Result<()> {
316        let timestamp = SystemTime::now()
317            .duration_since(UNIX_EPOCH)
318            .map_err(|e| SimulatorError::InvalidOperation(format!("System time error: {e}")))?
319            .as_secs_f64();
320
321        let data = VisualizationData::SyndromePattern {
322            syndrome_data,
323            error_locations,
324            correction_success,
325            timestamp,
326        };
327
328        self.process_visualization_data(data)?;
329        Ok(())
330    }
331
332    /// Visualize optimization landscape
333    pub fn visualize_optimization_landscape(
334        &mut self,
335        parameter_values: Vec<Vec<f64>>,
336        cost_values: Vec<f64>,
337        gradient_norms: Vec<f64>,
338        parameter_names: Vec<String>,
339    ) -> Result<()> {
340        let data = VisualizationData::OptimizationLandscape {
341            parameter_values,
342            cost_values,
343            gradient_norms,
344            parameter_names,
345        };
346
347        self.process_visualization_data(data)?;
348        Ok(())
349    }
350
351    /// Process visualization data through all hooks
352    fn process_visualization_data(&mut self, data: VisualizationData) -> Result<()> {
353        // Add to buffer for real-time processing
354        if self.config.real_time {
355            let mut buffer = self
356                .data_buffer
357                .lock()
358                .map_err(|e| SimulatorError::InvalidOperation(format!("Lock poisoned: {e}")))?;
359            buffer.push_back(data.clone());
360            if buffer.len() > self.config.max_data_points {
361                buffer.pop_front();
362            }
363        }
364
365        // Process through all hooks
366        for hook in &mut self.hooks {
367            hook.process_data(data.clone())?;
368        }
369
370        Ok(())
371    }
372
373    /// Export all visualization data
374    pub fn export_all(&self, base_path: &str) -> Result<()> {
375        std::fs::create_dir_all(base_path).map_err(|e| {
376            SimulatorError::InvalidInput(format!("Failed to create export directory: {e}"))
377        })?;
378
379        for (i, hook) in self.hooks.iter().enumerate() {
380            let export_path = format!(
381                "{}/visualization_{}.{}",
382                base_path,
383                i,
384                self.get_file_extension(hook.framework())
385            );
386            hook.export(&export_path)?;
387        }
388
389        Ok(())
390    }
391
392    /// Clear all visualization data
393    pub fn clear_all(&mut self) {
394        for hook in &mut self.hooks {
395            hook.clear();
396        }
397
398        if let Ok(mut buffer) = self.data_buffer.lock() {
399            buffer.clear();
400        }
401        if let Ok(mut history) = self.performance_history.lock() {
402            history.clear();
403        }
404    }
405
406    /// Generate basis state labels
407    fn generate_basis_labels(&self, state_size: usize) -> Vec<String> {
408        let num_qubits = (state_size as f64).log2().round() as usize;
409        (0..state_size)
410            .map(|i| format!("|{i:0num_qubits$b}⟩"))
411            .collect()
412    }
413
414    /// Extract parameters from gate type
415    fn extract_gate_parameters(&self, gate_type: &InterfaceGateType) -> Vec<f64> {
416        match gate_type {
417            InterfaceGateType::Phase(angle) => vec![*angle],
418            InterfaceGateType::RX(angle) => vec![*angle],
419            InterfaceGateType::RY(angle) => vec![*angle],
420            InterfaceGateType::RZ(angle) => vec![*angle],
421            InterfaceGateType::U1(angle) => vec![*angle],
422            InterfaceGateType::U2(theta, phi) => vec![*theta, *phi],
423            InterfaceGateType::U3(theta, phi, lambda) => vec![*theta, *phi, *lambda],
424            InterfaceGateType::CRX(angle) => vec![*angle],
425            InterfaceGateType::CRY(angle) => vec![*angle],
426            InterfaceGateType::CRZ(angle) => vec![*angle],
427            InterfaceGateType::CPhase(angle) => vec![*angle],
428            _ => Vec::new(),
429        }
430    }
431
432    /// Calculate entanglement matrix between qubits
433    fn calculate_entanglement_matrix(
434        &self,
435        state: &Array1<Complex64>,
436        num_qubits: usize,
437    ) -> Result<Array2<f64>> {
438        let mut entanglement_matrix = Array2::zeros((num_qubits, num_qubits));
439
440        // Simplified entanglement measure (mutual information approximation)
441        for i in 0..num_qubits {
442            for j in i..num_qubits {
443                if i == j {
444                    entanglement_matrix[[i, j]] = 1.0;
445                } else {
446                    // Calculate mutual information between qubits i and j
447                    let mutual_info = self.calculate_mutual_information(state, i, j, num_qubits)?;
448                    entanglement_matrix[[i, j]] = mutual_info;
449                    entanglement_matrix[[j, i]] = mutual_info;
450                }
451            }
452        }
453
454        Ok(entanglement_matrix)
455    }
456
457    /// Calculate bipartite entropies for different cuts
458    fn calculate_bipartite_entropies(
459        &self,
460        state: &Array1<Complex64>,
461        num_qubits: usize,
462    ) -> Result<Vec<f64>> {
463        let mut entropies = Vec::new();
464
465        for cut in 1..num_qubits {
466            let entropy = self.calculate_bipartite_entropy(state, cut, num_qubits)?;
467            entropies.push(entropy);
468        }
469
470        Ok(entropies)
471    }
472
473    /// Calculate mutual information between two qubits (simplified)
474    const fn calculate_mutual_information(
475        &self,
476        _state: &Array1<Complex64>,
477        _qubit_i: usize,
478        _qubit_j: usize,
479        _num_qubits: usize,
480    ) -> Result<f64> {
481        // Simplified placeholder - in practice would compute reduced density matrices
482        Ok(0.5)
483    }
484
485    /// Calculate bipartite entropy for a given cut
486    fn calculate_bipartite_entropy(
487        &self,
488        state: &Array1<Complex64>,
489        cut: usize,
490        num_qubits: usize,
491    ) -> Result<f64> {
492        // Simplified von Neumann entropy calculation
493        let left_size = 1 << cut;
494        let right_size = 1 << (num_qubits - cut);
495
496        // Calculate reduced density matrix for left subsystem (simplified)
497        let mut reduced_dm = Array2::zeros((left_size, left_size));
498
499        for i in 0..left_size {
500            for j in 0..left_size {
501                let mut trace_val = Complex64::new(0.0, 0.0);
502                for k in 0..right_size {
503                    let idx1 = i * right_size + k;
504                    let idx2 = j * right_size + k;
505                    if idx1 < state.len() && idx2 < state.len() {
506                        trace_val += state[idx1] * state[idx2].conj();
507                    }
508                }
509                reduced_dm[[i, j]] = trace_val;
510            }
511        }
512
513        // Calculate von Neumann entropy (simplified)
514        let mut entropy = 0.0;
515        for i in 0..left_size {
516            let eigenval = reduced_dm[[i, i]].norm();
517            if eigenval > 1e-10 {
518                entropy -= eigenval * eigenval.ln();
519            }
520        }
521
522        Ok(entropy)
523    }
524
525    /// Get file extension for visualization framework
526    const fn get_file_extension(&self, framework: VisualizationFramework) -> &str {
527        match framework {
528            VisualizationFramework::Matplotlib => "py",
529            VisualizationFramework::Plotly => "html",
530            VisualizationFramework::D3JS => "html",
531            VisualizationFramework::SVG => "svg",
532            VisualizationFramework::ASCII => "txt",
533            VisualizationFramework::LaTeX => "tex",
534            VisualizationFramework::JSON => "json",
535        }
536    }
537}
538
539/// JSON export visualization hook
540pub struct JSONVisualizationHook {
541    /// Accumulated visualization data
542    data: Vec<VisualizationData>,
543    /// Export configuration
544    config: VisualizationConfig,
545}
546
547impl JSONVisualizationHook {
548    #[must_use]
549    pub const fn new(config: VisualizationConfig) -> Self {
550        Self {
551            data: Vec::new(),
552            config,
553        }
554    }
555}
556
557impl VisualizationHook for JSONVisualizationHook {
558    fn process_data(&mut self, data: VisualizationData) -> Result<()> {
559        self.data.push(data);
560
561        // Keep only recent data
562        if self.data.len() > self.config.max_data_points {
563            self.data.remove(0);
564        }
565
566        Ok(())
567    }
568
569    fn export(&self, path: &str) -> Result<()> {
570        let json_data = serde_json::to_string_pretty(&self.data)
571            .map_err(|e| SimulatorError::InvalidInput(format!("Failed to serialize data: {e}")))?;
572
573        let mut file = File::create(path)
574            .map_err(|e| SimulatorError::InvalidInput(format!("Failed to create file: {e}")))?;
575
576        file.write_all(json_data.as_bytes())
577            .map_err(|e| SimulatorError::InvalidInput(format!("Failed to write file: {e}")))?;
578
579        Ok(())
580    }
581
582    fn clear(&mut self) {
583        self.data.clear();
584    }
585
586    fn framework(&self) -> VisualizationFramework {
587        VisualizationFramework::JSON
588    }
589}
590
591/// ASCII visualization hook for terminal output
592pub struct ASCIIVisualizationHook {
593    /// Recent state vectors for display
594    recent_states: VecDeque<Array1<Complex64>>,
595    /// Configuration
596    config: VisualizationConfig,
597}
598
599impl ASCIIVisualizationHook {
600    #[must_use]
601    pub fn new(config: VisualizationConfig) -> Self {
602        Self {
603            recent_states: VecDeque::with_capacity(100),
604            config,
605        }
606    }
607
608    /// Generate ASCII representation of quantum state
609    fn state_to_ascii(&self, state: &Array1<Complex64>) -> String {
610        let mut output = String::new();
611        output.push_str("Quantum State Visualization:\n");
612        output.push_str("==========================\n");
613
614        for (i, amplitude) in state.iter().enumerate().take(16) {
615            let magnitude = amplitude.norm();
616            let phase = amplitude.arg();
617
618            let bar_length = (magnitude * 20.0) as usize;
619            let bar = "█".repeat(bar_length) + &"░".repeat(20 - bar_length);
620
621            writeln!(
622                output,
623                "|{:02}⟩: {} {:.4} ∠{:.2}π",
624                i,
625                bar,
626                magnitude,
627                phase / std::f64::consts::PI
628            )
629            .expect("Failed to write to string buffer");
630        }
631
632        if state.len() > 16 {
633            writeln!(output, "... ({} more states)", state.len() - 16)
634                .expect("Failed to write to string buffer");
635        }
636
637        output
638    }
639}
640
641impl VisualizationHook for ASCIIVisualizationHook {
642    fn process_data(&mut self, data: VisualizationData) -> Result<()> {
643        match data {
644            VisualizationData::StateVector { amplitudes, .. } => {
645                let state = Array1::from_vec(amplitudes);
646
647                if self.config.real_time {
648                    println!("{}", self.state_to_ascii(&state));
649                }
650
651                self.recent_states.push_back(state);
652                if self.recent_states.len() > 100 {
653                    self.recent_states.pop_front();
654                }
655            }
656            VisualizationData::CircuitDiagram {
657                gates, num_qubits, ..
658            } => {
659                if self.config.real_time {
660                    println!(
661                        "Circuit Diagram ({} qubits, {} gates):",
662                        num_qubits,
663                        gates.len()
664                    );
665                    for gate in gates.iter().take(10) {
666                        println!("  {} on qubits {:?}", gate.gate_type, gate.qubits);
667                    }
668                    if gates.len() > 10 {
669                        println!("  ... ({} more gates)", gates.len() - 10);
670                    }
671                }
672            }
673            _ => {
674                // Handle other data types
675            }
676        }
677
678        Ok(())
679    }
680
681    fn export(&self, path: &str) -> Result<()> {
682        let mut output = String::new();
683        output.push_str("ASCII Visualization Export\n");
684        output.push_str("==========================\n\n");
685
686        for (i, state) in self.recent_states.iter().enumerate() {
687            writeln!(output, "State {i}:").expect("Failed to write to string buffer");
688            output.push_str(&self.state_to_ascii(state));
689            output.push('\n');
690        }
691
692        let mut file = File::create(path)
693            .map_err(|e| SimulatorError::InvalidInput(format!("Failed to create file: {e}")))?;
694
695        file.write_all(output.as_bytes())
696            .map_err(|e| SimulatorError::InvalidInput(format!("Failed to write file: {e}")))?;
697
698        Ok(())
699    }
700
701    fn clear(&mut self) {
702        self.recent_states.clear();
703    }
704
705    fn framework(&self) -> VisualizationFramework {
706        VisualizationFramework::ASCII
707    }
708}
709
710/// Benchmark visualization performance
711pub fn benchmark_visualization() -> Result<HashMap<String, f64>> {
712    let mut results = HashMap::new();
713
714    // Test JSON hook performance
715    let start = std::time::Instant::now();
716    let mut json_hook = JSONVisualizationHook::new(VisualizationConfig::default());
717
718    for i in 0..1000 {
719        let test_state = Array1::from_vec(vec![
720            Complex64::new(0.5, 0.0),
721            Complex64::new(0.5, 0.0),
722            Complex64::new(0.5, 0.0),
723            Complex64::new(0.5, 0.0),
724        ]);
725
726        let data = VisualizationData::StateVector {
727            amplitudes: test_state.to_vec(),
728            basis_labels: vec![
729                "00".to_string(),
730                "01".to_string(),
731                "10".to_string(),
732                "11".to_string(),
733            ],
734            timestamp: f64::from(i),
735        };
736
737        json_hook.process_data(data)?;
738    }
739
740    let json_time = start.elapsed().as_millis() as f64;
741    results.insert("json_hook_1000_states".to_string(), json_time);
742
743    // Test ASCII hook performance
744    let start = std::time::Instant::now();
745    let mut ascii_hook = ASCIIVisualizationHook::new(VisualizationConfig {
746        real_time: false,
747        ..Default::default()
748    });
749
750    for i in 0..100 {
751        let test_state = Array1::from_vec(vec![
752            Complex64::new(0.5, 0.0),
753            Complex64::new(0.5, 0.0),
754            Complex64::new(0.5, 0.0),
755            Complex64::new(0.5, 0.0),
756        ]);
757
758        let data = VisualizationData::StateVector {
759            amplitudes: test_state.to_vec(),
760            basis_labels: vec![
761                "00".to_string(),
762                "01".to_string(),
763                "10".to_string(),
764                "11".to_string(),
765            ],
766            timestamp: f64::from(i),
767        };
768
769        ascii_hook.process_data(data)?;
770    }
771
772    let ascii_time = start.elapsed().as_millis() as f64;
773    results.insert("ascii_hook_100_states".to_string(), ascii_time);
774
775    Ok(results)
776}
777
778#[cfg(test)]
779mod tests {
780    use super::*;
781    use approx::assert_abs_diff_eq;
782
783    #[test]
784    fn test_visualization_manager_creation() {
785        let config = VisualizationConfig::default();
786        let manager = VisualizationManager::new(config);
787        assert_eq!(manager.hooks.len(), 0);
788    }
789
790    #[test]
791    fn test_json_hook() {
792        let mut hook = JSONVisualizationHook::new(VisualizationConfig::default());
793
794        let test_data = VisualizationData::StateVector {
795            amplitudes: vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
796            basis_labels: vec!["0".to_string(), "1".to_string()],
797            timestamp: 0.0,
798        };
799
800        assert!(hook.process_data(test_data).is_ok());
801        assert_eq!(hook.data.len(), 1);
802    }
803
804    #[test]
805    fn test_ascii_hook() {
806        let mut hook = ASCIIVisualizationHook::new(VisualizationConfig {
807            real_time: false,
808            ..Default::default()
809        });
810
811        let test_data = VisualizationData::StateVector {
812            amplitudes: vec![Complex64::new(0.707, 0.0), Complex64::new(0.707, 0.0)],
813            basis_labels: vec!["0".to_string(), "1".to_string()],
814            timestamp: 0.0,
815        };
816
817        assert!(hook.process_data(test_data).is_ok());
818        assert_eq!(hook.recent_states.len(), 1);
819    }
820
821    #[test]
822    fn test_state_visualization() {
823        let config = VisualizationConfig::default();
824        let mut manager = VisualizationManager::new(config);
825
826        let test_state = Array1::from_vec(vec![
827            Complex64::new(0.5, 0.0),
828            Complex64::new(0.5, 0.0),
829            Complex64::new(0.5, 0.0),
830            Complex64::new(0.5, 0.0),
831        ]);
832
833        assert!(manager.visualize_state(&test_state, None).is_ok());
834    }
835
836    #[test]
837    fn test_circuit_visualization() {
838        let config = VisualizationConfig::default();
839        let mut manager = VisualizationManager::new(config);
840
841        let mut circuit = InterfaceCircuit::new(2, 0);
842        circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![0]));
843        circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![0, 1]));
844
845        assert!(manager.visualize_circuit(&circuit).is_ok());
846    }
847
848    #[test]
849    fn test_parameter_extraction() {
850        let config = VisualizationConfig::default();
851        let manager = VisualizationManager::new(config);
852
853        let params = manager.extract_gate_parameters(&InterfaceGateType::RX(1.5));
854        assert_eq!(params, vec![1.5]);
855
856        let params = manager.extract_gate_parameters(&InterfaceGateType::U3(1.0, 2.0, 3.0));
857        assert_eq!(params, vec![1.0, 2.0, 3.0]);
858
859        let params = manager.extract_gate_parameters(&InterfaceGateType::Hadamard);
860        assert_eq!(params.len(), 0);
861    }
862
863    #[test]
864    fn test_basis_label_generation() {
865        let config = VisualizationConfig::default();
866        let manager = VisualizationManager::new(config);
867
868        let labels = manager.generate_basis_labels(4);
869        assert_eq!(labels, vec!["|00⟩", "|01⟩", "|10⟩", "|11⟩"]);
870
871        let labels = manager.generate_basis_labels(8);
872        assert_eq!(labels.len(), 8);
873        assert_eq!(labels[0], "|000⟩");
874        assert_eq!(labels[7], "|111⟩");
875    }
876
877    #[test]
878    fn test_entanglement_calculation() {
879        let config = VisualizationConfig::default();
880        let manager = VisualizationManager::new(config);
881
882        let bell_state = Array1::from_vec(vec![
883            Complex64::new(0.707, 0.0),
884            Complex64::new(0.0, 0.0),
885            Complex64::new(0.0, 0.0),
886            Complex64::new(0.707, 0.0),
887        ]);
888
889        let entanglement_matrix = manager
890            .calculate_entanglement_matrix(&bell_state, 2)
891            .expect("Entanglement calculation should succeed in test");
892        assert_eq!(entanglement_matrix.shape(), [2, 2]);
893
894        let entropies = manager
895            .calculate_bipartite_entropies(&bell_state, 2)
896            .expect("Entropy calculation should succeed in test");
897        assert_eq!(entropies.len(), 1);
898    }
899}