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