|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| use ndarray::{Array2, Array3};
|
| use std::fs::File;
|
| use std::io::{self, Write};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| pub fn export_trajectory_to_bin(
|
| trajectory_tensor: &Array3<f32>,
|
| output_path: &str,
|
| ) -> io::Result<()> {
|
| let (time_steps, batch_size, coords) = trajectory_tensor.dim();
|
| assert_eq!(coords, 3, "Expected 3 coordinates per vertex, got {coords}");
|
|
|
|
|
|
|
| let total_floats = batch_size * time_steps * 3;
|
| let mut flat = Vec::with_capacity(total_floats);
|
|
|
| for b in 0..batch_size {
|
| for t in 0..time_steps {
|
| flat.push(trajectory_tensor[[t, b, 0]]);
|
| flat.push(trajectory_tensor[[t, b, 1]]);
|
| flat.push(trajectory_tensor[[t, b, 2]]);
|
| }
|
| }
|
|
|
|
|
| let byte_slice: &[u8] = bytemuck::cast_slice(&flat);
|
|
|
| let mut file = File::create(output_path)?;
|
| file.write_all(byte_slice)?;
|
| file.flush()?;
|
|
|
| eprintln!(
|
| "Exported {} trajectories × {} steps = {} vertices to {}",
|
| batch_size, time_steps, batch_size * time_steps, output_path
|
| );
|
| Ok(())
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| pub fn density_matrix_to_bloch(rho: &Array2<f64>) -> (f32, f32, f32) {
|
| assert_eq!(rho.dim(), (2, 2), "Bloch conversion requires 2×2 density matrix");
|
|
|
| let x = 2.0 * rho[[0, 1]];
|
| let y = 0.0f64;
|
| let z = rho[[0, 0]] - rho[[1, 1]];
|
|
|
| (x as f32, y as f32, z as f32)
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| pub fn generate_demo_data(time_steps: usize, batch_size: usize, dt: f32, diffusion: f32) -> Array3<f32> {
|
| use std::f32::consts::PI;
|
|
|
| let phi: f32 = (1.0 + 5.0f32.sqrt()) / 2.0;
|
| let contraction = 1.0 / phi;
|
|
|
| let mut data = Array3::zeros((time_steps, batch_size, 3));
|
|
|
|
|
| let mut seed: u64 = 42;
|
| let mut rng = || -> f32 {
|
| seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
|
| let bits = ((seed >> 33) as u32) as f32 / (u32::MAX as f32);
|
| bits * 2.0 - 1.0
|
| };
|
|
|
| for b in 0..batch_size {
|
|
|
| let theta = (rng() + 1.0) * 0.5 * PI;
|
| let phi0 = (rng() + 1.0) * PI;
|
|
|
| let mut x = theta.sin() * phi0.cos();
|
| let mut y = theta.sin() * phi0.sin();
|
| let mut z = theta.cos();
|
|
|
| for t in 0..time_steps {
|
| data[[t, b, 0]] = x;
|
| data[[t, b, 1]] = y;
|
| data[[t, b, 2]] = z;
|
|
|
|
|
| let drift = contraction * dt;
|
| x -= x * drift;
|
| y -= y * drift;
|
| z -= z * drift;
|
|
|
|
|
| let noise_scale = (diffusion * dt).sqrt();
|
| let nx = rng() * noise_scale;
|
| let ny = rng() * noise_scale;
|
| let nz = rng() * noise_scale;
|
|
|
|
|
| let dot = nx * x + ny * y + nz * z;
|
| let r2 = x * x + y * y + z * z;
|
| if r2 > 1e-8 {
|
| x += nx - dot * x / r2;
|
| y += ny - dot * y / r2;
|
| z += nz - dot * z / r2;
|
| }
|
|
|
|
|
| let r = (x * x + y * y + z * z).sqrt();
|
| if r > 1e-8 {
|
| let target_r = (1.0 - (t as f32) * contraction * dt * 0.5).max(0.01);
|
| x = x / r * target_r;
|
| y = y / r * target_r;
|
| z = z / r * target_r;
|
| }
|
| }
|
| }
|
|
|
| data
|
| }
|
|
|
| #[cfg(test)]
|
| mod tests {
|
| use super::*;
|
| use std::path::Path;
|
|
|
| #[test]
|
| fn test_demo_data_shape() {
|
| let data = generate_demo_data(100, 10, 0.01, 0.3);
|
| assert_eq!(data.dim(), (100, 10, 3));
|
| }
|
|
|
| #[test]
|
| fn test_demo_data_bounded() {
|
| let data = generate_demo_data(200, 50, 0.01, 0.2);
|
| for val in data.iter() {
|
| assert!(val.abs() <= 1.5, "Coordinate out of bounds: {val}");
|
| }
|
| }
|
|
|
| #[test]
|
| fn test_export_creates_file() {
|
| let data = generate_demo_data(10, 5, 0.01, 0.1);
|
| let path = "test_trajectory_output.bin";
|
| export_trajectory_to_bin(&data, path).expect("Export failed");
|
|
|
| let metadata = std::fs::metadata(path).expect("File not found");
|
|
|
| assert_eq!(metadata.len(), 600);
|
|
|
| std::fs::remove_file(path).ok();
|
| }
|
|
|
| #[test]
|
| fn test_bloch_conversion_pure_state() {
|
|
|
| let rho = Array2::from_shape_vec((2, 2), vec![1.0, 0.0, 0.0, 0.0]).unwrap();
|
| let (x, y, z) = density_matrix_to_bloch(&rho);
|
| assert!((x - 0.0).abs() < 1e-6);
|
| assert!((y - 0.0).abs() < 1e-6);
|
| assert!((z - 1.0).abs() < 1e-6);
|
| }
|
|
|
| #[test]
|
| fn test_bloch_conversion_mixed_state() {
|
|
|
| let rho = Array2::from_shape_vec((2, 2), vec![0.5, 0.0, 0.0, 0.5]).unwrap();
|
| let (x, y, z) = density_matrix_to_bloch(&rho);
|
| assert!((x - 0.0).abs() < 1e-6);
|
| assert!((y - 0.0).abs() < 1e-6);
|
| assert!((z - 0.0).abs() < 1e-6);
|
| }
|
| }
|
|
|