1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
25pub enum VisualizationFramework {
26 Matplotlib,
28 Plotly,
30 D3JS,
32 SVG,
34 ASCII,
36 LaTeX,
38 JSON,
40}
41
42#[derive(Debug, Clone)]
44pub struct VisualizationConfig {
45 pub framework: VisualizationFramework,
47 pub real_time: bool,
49 pub max_data_points: usize,
51 pub export_directory: String,
53 pub color_scheme: ColorScheme,
55 pub interactive: bool,
57 pub plot_dimensions: (usize, usize),
59 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum ColorScheme {
81 Default,
82 Dark,
83 Light,
84 Scientific,
85 Quantum,
86 Accessibility,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
91pub enum VisualizationData {
92 StateVector {
94 amplitudes: Vec<Complex64>,
95 basis_labels: Vec<String>,
96 timestamp: f64,
97 },
98 CircuitDiagram {
100 gates: Vec<GateVisualizationData>,
101 num_qubits: usize,
102 circuit_depth: usize,
103 },
104 PerformanceTimeSeries {
106 timestamps: Vec<f64>,
107 execution_times: Vec<f64>,
108 memory_usage: Vec<f64>,
109 gate_counts: Vec<HashMap<String, usize>>,
110 },
111 EntanglementVisualization {
113 entanglement_matrix: Array2<f64>,
114 qubit_labels: Vec<String>,
115 bipartite_entropies: Vec<f64>,
116 },
117 SyndromePattern {
119 syndrome_data: Vec<Vec<bool>>,
120 error_locations: Vec<usize>,
121 correction_success: bool,
122 timestamp: f64,
123 },
124 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#[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
144pub trait VisualizationHook: Send + Sync {
146 fn process_data(&mut self, data: VisualizationData) -> Result<()>;
148
149 fn export(&self, path: &str) -> Result<()>;
151
152 fn clear(&mut self);
154
155 fn framework(&self) -> VisualizationFramework;
157}
158
159pub struct VisualizationManager {
161 config: VisualizationConfig,
163 hooks: Vec<Box<dyn VisualizationHook>>,
165 data_buffer: Arc<Mutex<VecDeque<VisualizationData>>>,
167 performance_history: Arc<Mutex<Vec<PerformanceMetrics>>>,
169 active_visualizations: HashMap<String, Box<dyn VisualizationHook>>,
171}
172
173impl VisualizationManager {
174 #[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 pub fn register_hook(&mut self, hook: Box<dyn VisualizationHook>) {
188 self.hooks.push(hook);
189 }
190
191 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 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 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 if history.len() > self.config.max_data_points {
251 history.remove(0);
252 }
253 }
254
255 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 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 let entanglement_matrix = self.calculate_entanglement_matrix(state, num_qubits)?;
295
296 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 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 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 fn process_visualization_data(&mut self, data: VisualizationData) -> Result<()> {
353 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 for hook in &mut self.hooks {
367 hook.process_data(data.clone())?;
368 }
369
370 Ok(())
371 }
372
373 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 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 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 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 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 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 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 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 fn calculate_mutual_information(
480 &self,
481 state: &Array1<Complex64>,
482 qubit_i: usize,
483 qubit_j: usize,
484 num_qubits: usize,
485 ) -> Result<f64> {
486 if qubit_i >= num_qubits || qubit_j >= num_qubits {
487 return Err(SimulatorError::InvalidQubitIndex {
488 index: qubit_i.max(qubit_j),
489 num_qubits,
490 });
491 }
492
493 let rho_i = Self::reduced_density_matrix(state, &[qubit_i], num_qubits);
494 let rho_j = Self::reduced_density_matrix(state, &[qubit_j], num_qubits);
495 let rho_ij = Self::reduced_density_matrix(state, &[qubit_i, qubit_j], num_qubits);
496
497 let s_i = Self::von_neumann_entropy(&rho_i)?;
498 let s_j = Self::von_neumann_entropy(&rho_j)?;
499 let s_ij = Self::von_neumann_entropy(&rho_ij)?;
500
501 Ok((s_i + s_j - s_ij).max(0.0))
505 }
506
507 fn reduced_density_matrix(
512 state: &Array1<Complex64>,
513 subset_qubits: &[usize],
514 num_qubits: usize,
515 ) -> Array2<Complex64> {
516 let k = subset_qubits.len();
517 let dim = 1usize << k;
518 let other_qubits: Vec<usize> = (0..num_qubits)
519 .filter(|q| !subset_qubits.contains(q))
520 .collect();
521 let num_other = other_qubits.len();
522
523 let mut rho = Array2::zeros((dim, dim));
524 let mut amps = vec![Complex64::new(0.0, 0.0); dim];
525
526 for other_bits in 0..(1usize << num_other) {
527 let mut base = 0usize;
528 for (bit_pos, &q) in other_qubits.iter().enumerate() {
529 if (other_bits >> bit_pos) & 1 == 1 {
530 base |= 1 << q;
531 }
532 }
533
534 for combo in 0..dim {
535 let mut idx = base;
536 for (i, &q) in subset_qubits.iter().enumerate() {
537 if (combo >> (k - 1 - i)) & 1 == 1 {
538 idx |= 1 << q;
539 }
540 }
541 amps[combo] = if idx < state.len() {
542 state[idx]
543 } else {
544 Complex64::new(0.0, 0.0)
545 };
546 }
547
548 for a in 0..dim {
549 for b in 0..dim {
550 rho[[a, b]] += amps[a] * amps[b].conj();
551 }
552 }
553 }
554
555 rho
556 }
557
558 fn von_neumann_entropy(rho: &Array2<Complex64>) -> Result<f64> {
562 let dim = rho.nrows();
563
564 let mut symmetrized = Array2::zeros((dim, dim));
567 for a in 0..dim {
568 for b in 0..dim {
569 symmetrized[[a, b]] = (rho[[a, b]] + rho[[b, a]].conj()) * Complex64::new(0.5, 0.0);
570 }
571 }
572
573 let decomposition =
574 scirs2_linalg::complex::complex_eigh(&symmetrized.view()).map_err(|e| {
575 SimulatorError::InvalidInput(format!(
576 "reduced-density-matrix eigendecomposition failed: {e}"
577 ))
578 })?;
579
580 let mut entropy = 0.0;
581 for &eigenvalue in decomposition.eigenvalues.iter() {
582 let p = eigenvalue.re.clamp(0.0, 1.0);
588 if p > 1e-12 {
589 entropy -= p * p.ln();
590 }
591 }
592 Ok(entropy)
593 }
594
595 fn calculate_bipartite_entropy(
597 &self,
598 state: &Array1<Complex64>,
599 cut: usize,
600 num_qubits: usize,
601 ) -> Result<f64> {
602 let left_size = 1 << cut;
604 let right_size = 1 << (num_qubits - cut);
605
606 let mut reduced_dm = Array2::zeros((left_size, left_size));
608
609 for i in 0..left_size {
610 for j in 0..left_size {
611 let mut trace_val = Complex64::new(0.0, 0.0);
612 for k in 0..right_size {
613 let idx1 = i * right_size + k;
614 let idx2 = j * right_size + k;
615 if idx1 < state.len() && idx2 < state.len() {
616 trace_val += state[idx1] * state[idx2].conj();
617 }
618 }
619 reduced_dm[[i, j]] = trace_val;
620 }
621 }
622
623 let mut entropy = 0.0;
625 for i in 0..left_size {
626 let eigenval = reduced_dm[[i, i]].norm();
627 if eigenval > 1e-10 {
628 entropy -= eigenval * eigenval.ln();
629 }
630 }
631
632 Ok(entropy)
633 }
634
635 const fn get_file_extension(&self, framework: VisualizationFramework) -> &str {
637 match framework {
638 VisualizationFramework::Matplotlib => "py",
639 VisualizationFramework::Plotly => "html",
640 VisualizationFramework::D3JS => "html",
641 VisualizationFramework::SVG => "svg",
642 VisualizationFramework::ASCII => "txt",
643 VisualizationFramework::LaTeX => "tex",
644 VisualizationFramework::JSON => "json",
645 }
646 }
647}
648
649pub struct JSONVisualizationHook {
651 data: Vec<VisualizationData>,
653 config: VisualizationConfig,
655}
656
657impl JSONVisualizationHook {
658 #[must_use]
659 pub const fn new(config: VisualizationConfig) -> Self {
660 Self {
661 data: Vec::new(),
662 config,
663 }
664 }
665}
666
667impl VisualizationHook for JSONVisualizationHook {
668 fn process_data(&mut self, data: VisualizationData) -> Result<()> {
669 self.data.push(data);
670
671 if self.data.len() > self.config.max_data_points {
673 self.data.remove(0);
674 }
675
676 Ok(())
677 }
678
679 fn export(&self, path: &str) -> Result<()> {
680 let json_data = serde_json::to_string_pretty(&self.data)
681 .map_err(|e| SimulatorError::InvalidInput(format!("Failed to serialize data: {e}")))?;
682
683 let mut file = File::create(path)
684 .map_err(|e| SimulatorError::InvalidInput(format!("Failed to create file: {e}")))?;
685
686 file.write_all(json_data.as_bytes())
687 .map_err(|e| SimulatorError::InvalidInput(format!("Failed to write file: {e}")))?;
688
689 Ok(())
690 }
691
692 fn clear(&mut self) {
693 self.data.clear();
694 }
695
696 fn framework(&self) -> VisualizationFramework {
697 VisualizationFramework::JSON
698 }
699}
700
701pub struct ASCIIVisualizationHook {
703 recent_states: VecDeque<Array1<Complex64>>,
705 config: VisualizationConfig,
707}
708
709impl ASCIIVisualizationHook {
710 #[must_use]
711 pub fn new(config: VisualizationConfig) -> Self {
712 Self {
713 recent_states: VecDeque::with_capacity(100),
714 config,
715 }
716 }
717
718 fn state_to_ascii(&self, state: &Array1<Complex64>) -> String {
720 let mut output = String::new();
721 output.push_str("Quantum State Visualization:\n");
722 output.push_str("==========================\n");
723
724 for (i, amplitude) in state.iter().enumerate().take(16) {
725 let magnitude = amplitude.norm();
726 let phase = amplitude.arg();
727
728 let bar_length = (magnitude * 20.0) as usize;
729 let bar = "█".repeat(bar_length) + &"░".repeat(20 - bar_length);
730
731 writeln!(
732 output,
733 "|{:02}⟩: {} {:.4} ∠{:.2}π",
734 i,
735 bar,
736 magnitude,
737 phase / std::f64::consts::PI
738 )
739 .expect("Failed to write to string buffer");
740 }
741
742 if state.len() > 16 {
743 writeln!(output, "... ({} more states)", state.len() - 16)
744 .expect("Failed to write to string buffer");
745 }
746
747 output
748 }
749}
750
751impl VisualizationHook for ASCIIVisualizationHook {
752 fn process_data(&mut self, data: VisualizationData) -> Result<()> {
753 match data {
754 VisualizationData::StateVector { amplitudes, .. } => {
755 let state = Array1::from_vec(amplitudes);
756
757 if self.config.real_time {
758 println!("{}", self.state_to_ascii(&state));
759 }
760
761 self.recent_states.push_back(state);
762 if self.recent_states.len() > 100 {
763 self.recent_states.pop_front();
764 }
765 }
766 VisualizationData::CircuitDiagram {
767 gates, num_qubits, ..
768 } => {
769 if self.config.real_time {
770 println!(
771 "Circuit Diagram ({} qubits, {} gates):",
772 num_qubits,
773 gates.len()
774 );
775 for gate in gates.iter().take(10) {
776 println!(" {} on qubits {:?}", gate.gate_type, gate.qubits);
777 }
778 if gates.len() > 10 {
779 println!(" ... ({} more gates)", gates.len() - 10);
780 }
781 }
782 }
783 _ => {
784 }
786 }
787
788 Ok(())
789 }
790
791 fn export(&self, path: &str) -> Result<()> {
792 let mut output = String::new();
793 output.push_str("ASCII Visualization Export\n");
794 output.push_str("==========================\n\n");
795
796 for (i, state) in self.recent_states.iter().enumerate() {
797 writeln!(output, "State {i}:").expect("Failed to write to string buffer");
798 output.push_str(&self.state_to_ascii(state));
799 output.push('\n');
800 }
801
802 let mut file = File::create(path)
803 .map_err(|e| SimulatorError::InvalidInput(format!("Failed to create file: {e}")))?;
804
805 file.write_all(output.as_bytes())
806 .map_err(|e| SimulatorError::InvalidInput(format!("Failed to write file: {e}")))?;
807
808 Ok(())
809 }
810
811 fn clear(&mut self) {
812 self.recent_states.clear();
813 }
814
815 fn framework(&self) -> VisualizationFramework {
816 VisualizationFramework::ASCII
817 }
818}
819
820pub fn benchmark_visualization() -> Result<HashMap<String, f64>> {
822 let mut results = HashMap::new();
823
824 let start = std::time::Instant::now();
826 let mut json_hook = JSONVisualizationHook::new(VisualizationConfig::default());
827
828 for i in 0..1000 {
829 let test_state = Array1::from_vec(vec![
830 Complex64::new(0.5, 0.0),
831 Complex64::new(0.5, 0.0),
832 Complex64::new(0.5, 0.0),
833 Complex64::new(0.5, 0.0),
834 ]);
835
836 let data = VisualizationData::StateVector {
837 amplitudes: test_state.to_vec(),
838 basis_labels: vec![
839 "00".to_string(),
840 "01".to_string(),
841 "10".to_string(),
842 "11".to_string(),
843 ],
844 timestamp: f64::from(i),
845 };
846
847 json_hook.process_data(data)?;
848 }
849
850 let json_time = start.elapsed().as_millis() as f64;
851 results.insert("json_hook_1000_states".to_string(), json_time);
852
853 let start = std::time::Instant::now();
855 let mut ascii_hook = ASCIIVisualizationHook::new(VisualizationConfig {
856 real_time: false,
857 ..Default::default()
858 });
859
860 for i in 0..100 {
861 let test_state = Array1::from_vec(vec![
862 Complex64::new(0.5, 0.0),
863 Complex64::new(0.5, 0.0),
864 Complex64::new(0.5, 0.0),
865 Complex64::new(0.5, 0.0),
866 ]);
867
868 let data = VisualizationData::StateVector {
869 amplitudes: test_state.to_vec(),
870 basis_labels: vec![
871 "00".to_string(),
872 "01".to_string(),
873 "10".to_string(),
874 "11".to_string(),
875 ],
876 timestamp: f64::from(i),
877 };
878
879 ascii_hook.process_data(data)?;
880 }
881
882 let ascii_time = start.elapsed().as_millis() as f64;
883 results.insert("ascii_hook_100_states".to_string(), ascii_time);
884
885 Ok(results)
886}
887
888#[cfg(test)]
889mod tests {
890 use super::*;
891 use approx::assert_abs_diff_eq;
892
893 #[test]
894 fn test_visualization_manager_creation() {
895 let config = VisualizationConfig::default();
896 let manager = VisualizationManager::new(config);
897 assert_eq!(manager.hooks.len(), 0);
898 }
899
900 #[test]
901 fn test_json_hook() {
902 let mut hook = JSONVisualizationHook::new(VisualizationConfig::default());
903
904 let test_data = VisualizationData::StateVector {
905 amplitudes: vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
906 basis_labels: vec!["0".to_string(), "1".to_string()],
907 timestamp: 0.0,
908 };
909
910 assert!(hook.process_data(test_data).is_ok());
911 assert_eq!(hook.data.len(), 1);
912 }
913
914 #[test]
915 fn test_ascii_hook() {
916 let mut hook = ASCIIVisualizationHook::new(VisualizationConfig {
917 real_time: false,
918 ..Default::default()
919 });
920
921 let test_data = VisualizationData::StateVector {
922 amplitudes: vec![Complex64::new(0.707, 0.0), Complex64::new(0.707, 0.0)],
923 basis_labels: vec!["0".to_string(), "1".to_string()],
924 timestamp: 0.0,
925 };
926
927 assert!(hook.process_data(test_data).is_ok());
928 assert_eq!(hook.recent_states.len(), 1);
929 }
930
931 #[test]
932 fn test_state_visualization() {
933 let config = VisualizationConfig::default();
934 let mut manager = VisualizationManager::new(config);
935
936 let test_state = Array1::from_vec(vec![
937 Complex64::new(0.5, 0.0),
938 Complex64::new(0.5, 0.0),
939 Complex64::new(0.5, 0.0),
940 Complex64::new(0.5, 0.0),
941 ]);
942
943 assert!(manager.visualize_state(&test_state, None).is_ok());
944 }
945
946 #[test]
947 fn test_circuit_visualization() {
948 let config = VisualizationConfig::default();
949 let mut manager = VisualizationManager::new(config);
950
951 let mut circuit = InterfaceCircuit::new(2, 0);
952 circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![0]));
953 circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![0, 1]));
954
955 assert!(manager.visualize_circuit(&circuit).is_ok());
956 }
957
958 #[test]
959 fn test_parameter_extraction() {
960 let config = VisualizationConfig::default();
961 let manager = VisualizationManager::new(config);
962
963 let params = manager.extract_gate_parameters(&InterfaceGateType::RX(1.5));
964 assert_eq!(params, vec![1.5]);
965
966 let params = manager.extract_gate_parameters(&InterfaceGateType::U3(1.0, 2.0, 3.0));
967 assert_eq!(params, vec![1.0, 2.0, 3.0]);
968
969 let params = manager.extract_gate_parameters(&InterfaceGateType::Hadamard);
970 assert_eq!(params.len(), 0);
971 }
972
973 #[test]
974 fn test_basis_label_generation() {
975 let config = VisualizationConfig::default();
976 let manager = VisualizationManager::new(config);
977
978 let labels = manager.generate_basis_labels(4);
979 assert_eq!(labels, vec!["|00⟩", "|01⟩", "|10⟩", "|11⟩"]);
980
981 let labels = manager.generate_basis_labels(8);
982 assert_eq!(labels.len(), 8);
983 assert_eq!(labels[0], "|000⟩");
984 assert_eq!(labels[7], "|111⟩");
985 }
986
987 #[test]
988 fn test_entanglement_calculation() {
989 let config = VisualizationConfig::default();
990 let manager = VisualizationManager::new(config);
991
992 let bell_state = Array1::from_vec(vec![
993 Complex64::new(0.707, 0.0),
994 Complex64::new(0.0, 0.0),
995 Complex64::new(0.0, 0.0),
996 Complex64::new(0.707, 0.0),
997 ]);
998
999 let entanglement_matrix = manager
1000 .calculate_entanglement_matrix(&bell_state, 2)
1001 .expect("Entanglement calculation should succeed in test");
1002 assert_eq!(entanglement_matrix.shape(), [2, 2]);
1003
1004 let entropies = manager
1005 .calculate_bipartite_entropies(&bell_state, 2)
1006 .expect("Entropy calculation should succeed in test");
1007 assert_eq!(entropies.len(), 1);
1008 }
1009}