Skip to main content

quantrs2_core/state_visualization_3d/
mod.rs

1//! Advanced 3D quantum state visualization.
2//!
3//! Provides five visualization types for quantum state vectors:
4//!
5//! - **Bloch sphere array**: per-qubit Bloch vectors on a sphere grid
6//! - **Q-sphere**: Qiskit-style global phase-weighted amplitude map
7//! - **Discrete Wigner**: Wootters phase-space function (n=1, 2 only)
8//! - **Husimi Q**: SU(2) coherent-state projection on the sphere
9//! - **Density matrix bars**: 3D bar plots of Re(ρ) and Im(ρ)
10//!
11//! All renderers return Plotly-JSON strings that can be wrapped in
12//! a self-contained HTML page or consumed by the Plotly.js library.
13
14#![allow(clippy::missing_const_for_fn)]
15
16pub mod bloch;
17pub mod density_bars;
18pub mod husimi;
19pub mod qsphere;
20pub mod wigner;
21
22#[cfg(feature = "python")]
23use pyo3::prelude::*;
24#[cfg(feature = "python")]
25use scirs2_core::ndarray::Array1;
26#[cfg(feature = "python")]
27use scirs2_core::Complex64;
28
29/// Wraps a Plotly-JSON object in a self-contained HTML page.
30///
31/// The resulting HTML pulls Plotly.js from CDN and renders the
32/// figure in a full-page `<div>`.
33pub fn make_plotly_html(plotly_json: &str) -> String {
34    format!(
35        r#"<!DOCTYPE html>
36<html lang="en">
37<head>
38<meta charset="UTF-8">
39<meta name="viewport" content="width=device-width, initial-scale=1.0">
40<title>QuantRS2 Quantum State Visualization</title>
41<script src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script>
42<style>
43  body {{ margin: 0; padding: 10px; font-family: Arial, sans-serif; }}
44  #plot {{ width: 100%; height: calc(100vh - 20px); }}
45</style>
46</head>
47<body>
48<div id="plot"></div>
49<script>
50var figure = {json};
51Plotly.newPlot('plot', figure.data, figure.layout, {{responsive: true}});
52</script>
53</body>
54</html>"#,
55        json = plotly_json
56    )
57}
58
59/// Python class for 3D quantum state visualization.
60///
61/// Accepts state amplitudes as a list of `(re, im)` tuples and
62/// exposes methods for each visualization type returning HTML strings.
63#[cfg(feature = "python")]
64#[pyclass(name = "QuantumState3DVisualizer")]
65pub struct PyQuantumState3DVisualizer {
66    state: Array1<Complex64>,
67    n_qubits: usize,
68}
69
70#[cfg(feature = "python")]
71#[pymethods]
72impl PyQuantumState3DVisualizer {
73    /// Create a new visualizer from state amplitudes.
74    ///
75    /// Parameters
76    /// ----------
77    /// state_amplitudes : list[tuple[float, float]]
78    ///     List of `(re, im)` pairs for each computational basis state.
79    ///     Length must equal `2**n_qubits`.
80    /// n_qubits : int
81    ///     Number of qubits.
82    #[new]
83    pub fn new(state_amplitudes: Vec<(f64, f64)>, n_qubits: usize) -> PyResult<Self> {
84        let expected = 1usize << n_qubits;
85        if state_amplitudes.len() != expected {
86            return Err(pyo3::exceptions::PyValueError::new_err(format!(
87                "state_amplitudes length {} does not match 2^{} = {}",
88                state_amplitudes.len(),
89                n_qubits,
90                expected
91            )));
92        }
93        let state: Array1<Complex64> = state_amplitudes
94            .into_iter()
95            .map(|(re, im)| Complex64::new(re, im))
96            .collect();
97        Ok(Self { state, n_qubits })
98    }
99
100    /// Generate a multi-qubit Bloch sphere array visualization.
101    ///
102    /// Returns an HTML string with Plotly 3D scenes, one per qubit.
103    pub fn bloch_array_html(&self) -> PyResult<String> {
104        let json = bloch::bloch_array_plotly_json(&self.state, self.n_qubits)
105            .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?;
106        Ok(make_plotly_html(&json))
107    }
108
109    /// Generate a Q-sphere visualization.
110    ///
111    /// Returns an HTML string with a Plotly 3D scatter on a sphere,
112    /// each marker encoding amplitude magnitude and phase.
113    pub fn qsphere_html(&self) -> PyResult<String> {
114        let json = qsphere::qsphere_plotly_json(&self.state, self.n_qubits)
115            .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?;
116        Ok(make_plotly_html(&json))
117    }
118
119    /// Generate a discrete Wigner function visualization.
120    ///
121    /// Returns an HTML string with a Plotly heatmap.
122    /// Only n=1 and n=2 are supported; raises ValueError for n ≥ 3.
123    pub fn wigner_html(&self) -> PyResult<String> {
124        let json = wigner::wigner_plotly_json(&self.state, self.n_qubits)
125            .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?;
126        Ok(make_plotly_html(&json))
127    }
128
129    /// Generate a Husimi Q-distribution visualization.
130    ///
131    /// Returns an HTML string with a Plotly 3D surface over the sphere.
132    pub fn husimi_html(&self) -> PyResult<String> {
133        let json = husimi::husimi_plotly_json(&self.state, self.n_qubits)
134            .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?;
135        Ok(make_plotly_html(&json))
136    }
137
138    /// Generate density matrix 3D bar plots.
139    ///
140    /// Returns an HTML string with two side-by-side 3D bar charts
141    /// showing Re(ρ) and Im(ρ).
142    pub fn density_bars_html(&self) -> PyResult<String> {
143        let json = density_bars::density_matrix_bars_plotly_json(&self.state, self.n_qubits)
144            .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?;
145        Ok(make_plotly_html(&json))
146    }
147
148    /// String representation showing qubit count and state dimension.
149    pub fn __repr__(&self) -> String {
150        format!(
151            "QuantumState3DVisualizer(n_qubits={}, dim={})",
152            self.n_qubits,
153            self.state.len()
154        )
155    }
156}