quantrs2_sim/
quantum_cellular_automata.rs

1//! Quantum Cellular Automata (QCA) simulation for novel quantum algorithms.
2//!
3//! This module implements quantum cellular automata, which are quantum analogues
4//! of classical cellular automata. QCA evolve quantum states according to local
5//! unitary rules, enabling exploration of novel quantum algorithms, cryptographic
6//! applications, and quantum computation models with inherent locality properties.
7
8use ndarray::{Array1, Array2};
9use num_complex::Complex64;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13use crate::error::{Result, SimulatorError};
14use crate::scirs2_integration::SciRS2Backend;
15
16/// Quantum cellular automaton configuration
17#[derive(Debug, Clone)]
18pub struct QCAConfig {
19    /// Lattice dimensions (1D, 2D, or 3D)
20    pub dimensions: Vec<usize>,
21    /// Boundary conditions
22    pub boundary_conditions: BoundaryConditions,
23    /// Local neighborhood type
24    pub neighborhood: NeighborhoodType,
25    /// Evolution rule type
26    pub rule_type: QCARuleType,
27    /// Number of evolution steps
28    pub evolution_steps: usize,
29    /// Enable parallel evolution
30    pub parallel_evolution: bool,
31    /// Measurement strategy
32    pub measurement_strategy: MeasurementStrategy,
33}
34
35impl Default for QCAConfig {
36    fn default() -> Self {
37        Self {
38            dimensions: vec![16], // 1D lattice with 16 cells
39            boundary_conditions: BoundaryConditions::Periodic,
40            neighborhood: NeighborhoodType::Moore,
41            rule_type: QCARuleType::Partitioned,
42            evolution_steps: 100,
43            parallel_evolution: true,
44            measurement_strategy: MeasurementStrategy::Probabilistic,
45        }
46    }
47}
48
49/// Boundary conditions for the lattice
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum BoundaryConditions {
52    /// Periodic boundaries (toroidal topology)
53    Periodic,
54    /// Fixed boundaries (cells on boundary are fixed)
55    Fixed,
56    /// Open boundaries (no interaction beyond boundary)
57    Open,
58    /// Reflective boundaries
59    Reflective,
60}
61
62/// Neighborhood types for local evolution
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum NeighborhoodType {
65    /// Von Neumann neighborhood (nearest neighbors only)
66    VonNeumann,
67    /// Moore neighborhood (includes diagonal neighbors)
68    Moore,
69    /// Custom neighborhood pattern
70    Custom,
71    /// Extended neighborhood (next-nearest neighbors)
72    Extended,
73}
74
75/// QCA rule types
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum QCARuleType {
78    /// Partitioned QCA (update disjoint partitions simultaneously)
79    Partitioned,
80    /// Global QCA (single global unitary evolution)
81    Global,
82    /// Sequential QCA (update cells one by one)
83    Sequential,
84    /// Margolus neighborhood QCA
85    Margolus,
86    /// Custom rule implementation
87    Custom,
88}
89
90/// Measurement strategy for QCA observation
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub enum MeasurementStrategy {
93    /// Probabilistic measurements
94    Probabilistic,
95    /// Deterministic state observation
96    Deterministic,
97    /// Partial measurements (subset of cells)
98    Partial,
99    /// Continuous weak measurements
100    Continuous,
101}
102
103/// Quantum cellular automaton simulator
104pub struct QuantumCellularAutomaton {
105    /// Configuration
106    config: QCAConfig,
107    /// Current quantum state of the lattice
108    state: Array1<Complex64>,
109    /// Evolution rules for each partition/neighborhood
110    evolution_rules: Vec<QCARule>,
111    /// Lattice cell mapping
112    cell_mapping: CellMapping,
113    /// SciRS2 backend for optimization
114    backend: Option<SciRS2Backend>,
115    /// Evolution history
116    evolution_history: Vec<QCASnapshot>,
117    /// Statistics
118    stats: QCAStats,
119}
120
121/// QCA evolution rule
122#[derive(Debug, Clone)]
123pub struct QCARule {
124    /// Rule identifier
125    pub id: String,
126    /// Unitary matrix for local evolution
127    pub unitary: Array2<Complex64>,
128    /// Cells affected by this rule
129    pub affected_cells: Vec<usize>,
130    /// Neighborhood pattern
131    pub neighborhood_pattern: Vec<(i32, i32, i32)>, // Relative positions (x, y, z)
132    /// Rule parameters
133    pub parameters: HashMap<String, f64>,
134}
135
136/// Cell mapping for different lattice geometries
137#[derive(Debug, Clone)]
138pub struct CellMapping {
139    /// Total number of cells
140    pub total_cells: usize,
141    /// Dimension sizes
142    pub dimensions: Vec<usize>,
143    /// Cell coordinates to linear index mapping
144    pub coord_to_index: HashMap<Vec<usize>, usize>,
145    /// Linear index to coordinates mapping
146    pub index_to_coord: HashMap<usize, Vec<usize>>,
147    /// Neighbor mappings for each cell
148    pub neighbors: HashMap<usize, Vec<usize>>,
149}
150
151/// QCA state snapshot
152#[derive(Debug, Clone)]
153pub struct QCASnapshot {
154    /// Time step
155    pub time_step: usize,
156    /// Quantum state at this time
157    pub state: Array1<Complex64>,
158    /// Measurement results (if any)
159    pub measurements: Option<Vec<f64>>,
160    /// Entropy measures
161    pub entanglement_entropy: Option<f64>,
162    /// Local observables
163    pub local_observables: HashMap<String, f64>,
164}
165
166/// QCA simulation statistics
167#[derive(Debug, Clone, Default, Serialize, Deserialize)]
168pub struct QCAStats {
169    /// Total evolution steps performed
170    pub evolution_steps: usize,
171    /// Total evolution time
172    pub total_evolution_time_ms: f64,
173    /// Average step time
174    pub avg_step_time_ms: f64,
175    /// Number of measurements performed
176    pub measurements_performed: usize,
177    /// Entropy evolution statistics
178    pub entropy_stats: EntropyStats,
179    /// Rule application counts
180    pub rule_applications: HashMap<String, usize>,
181}
182
183/// Entropy statistics
184#[derive(Debug, Clone, Default, Serialize, Deserialize)]
185pub struct EntropyStats {
186    /// Maximum entropy observed
187    pub max_entropy: f64,
188    /// Minimum entropy observed
189    pub min_entropy: f64,
190    /// Average entropy
191    pub avg_entropy: f64,
192    /// Entropy variance
193    pub entropy_variance: f64,
194}
195
196impl QuantumCellularAutomaton {
197    /// Create new quantum cellular automaton
198    pub fn new(config: QCAConfig) -> Result<Self> {
199        let cell_mapping = Self::create_cell_mapping(&config)?;
200        let total_cells = cell_mapping.total_cells;
201
202        // Initialize quantum state |0...0⟩
203        let state_size = 1 << total_cells;
204        let mut state = Array1::zeros(state_size);
205        state[0] = Complex64::new(1.0, 0.0);
206
207        let evolution_rules = Self::create_default_rules(&config, &cell_mapping)?;
208
209        Ok(Self {
210            config,
211            state,
212            evolution_rules,
213            cell_mapping,
214            backend: None,
215            evolution_history: Vec::new(),
216            stats: QCAStats::default(),
217        })
218    }
219
220    /// Initialize with SciRS2 backend
221    pub fn with_backend(mut self) -> Result<Self> {
222        self.backend = Some(SciRS2Backend::new());
223        Ok(self)
224    }
225
226    /// Set initial state
227    pub fn set_initial_state(&mut self, initial_state: Array1<Complex64>) -> Result<()> {
228        if initial_state.len() != self.state.len() {
229            return Err(SimulatorError::InvalidInput(format!(
230                "Initial state size {} doesn't match expected size {}",
231                initial_state.len(),
232                self.state.len()
233            )));
234        }
235
236        self.state = initial_state;
237        self.evolution_history.clear();
238        self.stats = QCAStats::default();
239
240        Ok(())
241    }
242
243    /// Add custom evolution rule
244    pub fn add_rule(&mut self, rule: QCARule) -> Result<()> {
245        // Validate rule unitary
246        if !Self::is_unitary(&rule.unitary) {
247            return Err(SimulatorError::InvalidInput(
248                "Rule matrix is not unitary".to_string(),
249            ));
250        }
251
252        self.evolution_rules.push(rule);
253        Ok(())
254    }
255
256    /// Evolve the QCA for specified number of steps
257    pub fn evolve(&mut self, steps: usize) -> Result<QCAEvolutionResult> {
258        let start_time = std::time::Instant::now();
259
260        for step in 0..steps {
261            let step_start = std::time::Instant::now();
262
263            // Apply evolution rules based on rule type
264            match self.config.rule_type {
265                QCARuleType::Partitioned => self.evolve_partitioned()?,
266                QCARuleType::Global => self.evolve_global()?,
267                QCARuleType::Sequential => self.evolve_sequential()?,
268                QCARuleType::Margolus => self.evolve_margolus()?,
269                QCARuleType::Custom => self.evolve_custom()?,
270            }
271
272            // Take snapshot if requested
273            if step % 10 == 0 || step == steps - 1 {
274                let snapshot = self.take_snapshot(step)?;
275                self.evolution_history.push(snapshot);
276            }
277
278            // Apply measurements if configured
279            if let MeasurementStrategy::Continuous = self.config.measurement_strategy {
280                self.apply_weak_measurements()?;
281            }
282
283            let step_time = step_start.elapsed().as_secs_f64() * 1000.0;
284            self.stats.avg_step_time_ms =
285                (self.stats.avg_step_time_ms * self.stats.evolution_steps as f64 + step_time)
286                    / (self.stats.evolution_steps + 1) as f64;
287            self.stats.evolution_steps += 1;
288        }
289
290        let total_time = start_time.elapsed().as_secs_f64() * 1000.0;
291        self.stats.total_evolution_time_ms += total_time;
292
293        Ok(QCAEvolutionResult {
294            final_state: self.state.clone(),
295            evolution_history: self.evolution_history.clone(),
296            total_steps: steps,
297            total_time_ms: total_time,
298            final_entropy: self.calculate_entanglement_entropy()?,
299        })
300    }
301
302    /// Evolve using partitioned updates
303    fn evolve_partitioned(&mut self) -> Result<()> {
304        match self.config.dimensions.len() {
305            1 => self.evolve_1d_partitioned(),
306            2 => self.evolve_2d_partitioned(),
307            3 => self.evolve_3d_partitioned(),
308            _ => Err(SimulatorError::UnsupportedOperation(
309                "QCA supports only 1D, 2D, and 3D lattices".to_string(),
310            )),
311        }
312    }
313
314    /// 1D partitioned evolution
315    fn evolve_1d_partitioned(&mut self) -> Result<()> {
316        let lattice_size = self.config.dimensions[0];
317
318        // Create partitions for parallel update
319        let mut partitions = Vec::new();
320        let partition_size = 2; // Update pairs of neighboring cells
321
322        for start in (0..lattice_size).step_by(partition_size) {
323            let end = (start + partition_size).min(lattice_size);
324            partitions.push((start, end));
325        }
326
327        // Apply evolution to each partition
328        for (start, end) in partitions {
329            for cell in start..end - 1 {
330                let neighbor = match self.config.boundary_conditions {
331                    BoundaryConditions::Periodic => (cell + 1) % lattice_size,
332                    BoundaryConditions::Fixed | BoundaryConditions::Open => {
333                        if cell + 1 < lattice_size {
334                            cell + 1
335                        } else {
336                            continue;
337                        }
338                    }
339                    BoundaryConditions::Reflective => {
340                        if cell + 1 < lattice_size {
341                            cell + 1
342                        } else {
343                            lattice_size - 1
344                        }
345                    }
346                };
347
348                self.apply_two_cell_rule(cell, neighbor)?;
349            }
350        }
351
352        Ok(())
353    }
354
355    /// 2D partitioned evolution
356    fn evolve_2d_partitioned(&mut self) -> Result<()> {
357        let (width, height) = (self.config.dimensions[0], self.config.dimensions[1]);
358
359        // Use checkerboard pattern for 2D partitioned updates
360        for parity in 0..2 {
361            for y in 0..height {
362                for x in (parity..width).step_by(2) {
363                    let cell = y * width + x;
364                    let neighbors = self.get_neighbors_2d(x, y, width, height);
365
366                    if !neighbors.is_empty() {
367                        self.apply_neighborhood_rule(cell, &neighbors)?;
368                    }
369                }
370            }
371        }
372
373        Ok(())
374    }
375
376    /// 3D partitioned evolution
377    fn evolve_3d_partitioned(&mut self) -> Result<()> {
378        let (width, height, depth) = (
379            self.config.dimensions[0],
380            self.config.dimensions[1],
381            self.config.dimensions[2],
382        );
383
384        // Use 3D checkerboard pattern
385        for parity in 0..2 {
386            for z in 0..depth {
387                for y in 0..height {
388                    for x in (parity..width).step_by(2) {
389                        let cell = z * width * height + y * width + x;
390                        let neighbors = self.get_neighbors_3d(x, y, z, width, height, depth);
391
392                        if !neighbors.is_empty() {
393                            self.apply_neighborhood_rule(cell, &neighbors)?;
394                        }
395                    }
396                }
397            }
398        }
399
400        Ok(())
401    }
402
403    /// Global evolution (single unitary on entire system)
404    fn evolve_global(&mut self) -> Result<()> {
405        if let Some(global_rule) = self.evolution_rules.first() {
406            // Apply global unitary evolution
407            let new_state = global_rule.unitary.dot(&self.state);
408            self.state = new_state;
409
410            // Update rule application statistics
411            let rule_id = global_rule.id.clone();
412            *self.stats.rule_applications.entry(rule_id).or_insert(0) += 1;
413        }
414
415        Ok(())
416    }
417
418    /// Sequential evolution (update one cell at a time)
419    fn evolve_sequential(&mut self) -> Result<()> {
420        let total_cells = self.cell_mapping.total_cells;
421        for cell in 0..total_cells {
422            let neighbors = self
423                .cell_mapping
424                .neighbors
425                .get(&cell)
426                .cloned()
427                .unwrap_or_default();
428            if !neighbors.is_empty() {
429                self.apply_neighborhood_rule(cell, &neighbors)?;
430            }
431        }
432        Ok(())
433    }
434
435    /// Margolus neighborhood evolution
436    fn evolve_margolus(&mut self) -> Result<()> {
437        // Margolus neighborhood: 2x2 blocks that alternate between offset configurations
438        if self.config.dimensions.len() != 2 {
439            return Err(SimulatorError::UnsupportedOperation(
440                "Margolus QCA only supports 2D lattices".to_string(),
441            ));
442        }
443
444        let (width, height) = (self.config.dimensions[0], self.config.dimensions[1]);
445        let offset = self.stats.evolution_steps % 2; // Alternate between two configurations
446
447        for y in (offset..height - 1).step_by(2) {
448            for x in (offset..width - 1).step_by(2) {
449                // 2x2 Margolus block
450                let cells = vec![
451                    y * width + x,
452                    y * width + (x + 1),
453                    (y + 1) * width + x,
454                    (y + 1) * width + (x + 1),
455                ];
456
457                self.apply_margolus_rule(&cells)?;
458            }
459        }
460
461        Ok(())
462    }
463
464    /// Custom evolution rule
465    fn evolve_custom(&mut self) -> Result<()> {
466        // Apply all custom rules in sequence
467        for rule in &self.evolution_rules.clone() {
468            if rule.affected_cells.len() == 1 {
469                self.apply_single_cell_rule(rule.affected_cells[0], &rule.unitary)?;
470            } else if rule.affected_cells.len() == 2 {
471                self.apply_two_cell_rule(rule.affected_cells[0], rule.affected_cells[1])?;
472            } else {
473                self.apply_neighborhood_rule(rule.affected_cells[0], &rule.affected_cells[1..])?;
474            }
475
476            // Update statistics
477            *self
478                .stats
479                .rule_applications
480                .entry(rule.id.clone())
481                .or_insert(0) += 1;
482        }
483
484        Ok(())
485    }
486
487    /// Apply rule to two neighboring cells
488    fn apply_two_cell_rule(&mut self, cell1: usize, cell2: usize) -> Result<()> {
489        // Find appropriate two-cell rule and clone what we need
490        let rule_data = self
491            .evolution_rules
492            .iter()
493            .find(|r| r.unitary.dim() == (4, 4))
494            .map(|r| (r.unitary.clone(), r.id.clone()))
495            .ok_or_else(|| {
496                SimulatorError::UnsupportedOperation("No two-cell rule available".to_string())
497            })?;
498
499        // Apply two-qubit unitary to the pair
500        self.apply_two_qubit_unitary(cell1, cell2, &rule_data.0)?;
501
502        // Update statistics
503        *self.stats.rule_applications.entry(rule_data.1).or_insert(0) += 1;
504
505        Ok(())
506    }
507
508    /// Apply rule to a neighborhood of cells
509    fn apply_neighborhood_rule(&mut self, center_cell: usize, neighbors: &[usize]) -> Result<()> {
510        // For simplicity, apply pairwise interactions between center and each neighbor
511        for &neighbor in neighbors {
512            self.apply_two_cell_rule(center_cell, neighbor)?;
513        }
514        Ok(())
515    }
516
517    /// Apply Margolus rule to a 2x2 block
518    fn apply_margolus_rule(&mut self, cells: &[usize]) -> Result<()> {
519        if cells.len() != 4 {
520            return Err(SimulatorError::InvalidInput(
521                "Margolus rule requires exactly 4 cells".to_string(),
522            ));
523        }
524
525        // Find appropriate 4-cell rule or create default
526        let rule_unitary = self
527            .evolution_rules
528            .iter()
529            .find(|r| r.unitary.dim() == (16, 16))
530            .map(|r| r.unitary.clone())
531            .unwrap_or_else(Self::create_margolus_rotation_unitary);
532
533        // Apply four-qubit unitary
534        self.apply_four_qubit_unitary(cells, &rule_unitary)?;
535
536        Ok(())
537    }
538
539    /// Apply single-cell rule
540    fn apply_single_cell_rule(&mut self, cell: usize, unitary: &Array2<Complex64>) -> Result<()> {
541        self.apply_single_qubit_unitary(cell, unitary)
542    }
543
544    /// Apply unitary to single qubit
545    fn apply_single_qubit_unitary(
546        &mut self,
547        qubit: usize,
548        unitary: &Array2<Complex64>,
549    ) -> Result<()> {
550        let state_size = self.state.len();
551        let qubit_mask = 1 << qubit;
552
553        let mut new_state = self.state.clone();
554
555        for i in 0..state_size {
556            if i & qubit_mask == 0 {
557                let j = i | qubit_mask;
558                if j < state_size {
559                    let amp_0 = self.state[i];
560                    let amp_1 = self.state[j];
561
562                    new_state[i] = unitary[[0, 0]] * amp_0 + unitary[[0, 1]] * amp_1;
563                    new_state[j] = unitary[[1, 0]] * amp_0 + unitary[[1, 1]] * amp_1;
564                }
565            }
566        }
567
568        self.state = new_state;
569        Ok(())
570    }
571
572    /// Apply unitary to two qubits
573    fn apply_two_qubit_unitary(
574        &mut self,
575        qubit1: usize,
576        qubit2: usize,
577        unitary: &Array2<Complex64>,
578    ) -> Result<()> {
579        let state_size = self.state.len();
580        let mask1 = 1 << qubit1;
581        let mask2 = 1 << qubit2;
582
583        let mut new_state = self.state.clone();
584
585        for i in 0..state_size {
586            let i00 = i & !(mask1 | mask2);
587            let i01 = i00 | mask2;
588            let i10 = i00 | mask1;
589            let i11 = i00 | mask1 | mask2;
590
591            if i == i00 {
592                let amp_00 = self.state[i00];
593                let amp_01 = self.state[i01];
594                let amp_10 = self.state[i10];
595                let amp_11 = self.state[i11];
596
597                new_state[i00] = unitary[[0, 0]] * amp_00
598                    + unitary[[0, 1]] * amp_01
599                    + unitary[[0, 2]] * amp_10
600                    + unitary[[0, 3]] * amp_11;
601                new_state[i01] = unitary[[1, 0]] * amp_00
602                    + unitary[[1, 1]] * amp_01
603                    + unitary[[1, 2]] * amp_10
604                    + unitary[[1, 3]] * amp_11;
605                new_state[i10] = unitary[[2, 0]] * amp_00
606                    + unitary[[2, 1]] * amp_01
607                    + unitary[[2, 2]] * amp_10
608                    + unitary[[2, 3]] * amp_11;
609                new_state[i11] = unitary[[3, 0]] * amp_00
610                    + unitary[[3, 1]] * amp_01
611                    + unitary[[3, 2]] * amp_10
612                    + unitary[[3, 3]] * amp_11;
613            }
614        }
615
616        self.state = new_state;
617        Ok(())
618    }
619
620    /// Apply unitary to four qubits
621    fn apply_four_qubit_unitary(
622        &mut self,
623        qubits: &[usize],
624        unitary: &Array2<Complex64>,
625    ) -> Result<()> {
626        if qubits.len() != 4 {
627            return Err(SimulatorError::InvalidInput(
628                "Four qubits required".to_string(),
629            ));
630        }
631
632        let state_size = self.state.len();
633        let mut new_state = self.state.clone();
634
635        // Create masks for the four qubits
636        let masks: Vec<usize> = qubits.iter().map(|&q| 1 << q).collect();
637
638        for i in 0..state_size {
639            // Extract base index (all qubits in |0⟩ state)
640            let base = i & !(masks[0] | masks[1] | masks[2] | masks[3]);
641
642            // Only process if this is the base index for this group
643            if i == base {
644                // Collect amplitudes for all 16 basis states
645                let mut amplitudes = Vec::new();
646                for state_idx in 0..16 {
647                    let full_idx = base
648                        | (if state_idx & 1 != 0 { masks[0] } else { 0 })
649                        | (if state_idx & 2 != 0 { masks[1] } else { 0 })
650                        | (if state_idx & 4 != 0 { masks[2] } else { 0 })
651                        | (if state_idx & 8 != 0 { masks[3] } else { 0 });
652                    amplitudes.push(self.state[full_idx]);
653                }
654
655                // Apply unitary transformation
656                for out_state in 0..16 {
657                    let mut new_amplitude = Complex64::new(0.0, 0.0);
658                    for (in_state, &amplitude) in amplitudes.iter().enumerate() {
659                        new_amplitude += unitary[[out_state, in_state]] * amplitude;
660                    }
661
662                    let full_idx = base
663                        | (if out_state & 1 != 0 { masks[0] } else { 0 })
664                        | (if out_state & 2 != 0 { masks[1] } else { 0 })
665                        | (if out_state & 4 != 0 { masks[2] } else { 0 })
666                        | (if out_state & 8 != 0 { masks[3] } else { 0 });
667                    new_state[full_idx] = new_amplitude;
668                }
669            }
670        }
671
672        self.state = new_state;
673        Ok(())
674    }
675
676    /// Get 2D neighbors
677    fn get_neighbors_2d(&self, x: usize, y: usize, width: usize, height: usize) -> Vec<usize> {
678        let mut neighbors = Vec::new();
679
680        let deltas: &[(i32, i32)] = match self.config.neighborhood {
681            NeighborhoodType::VonNeumann => &[(-1, 0), (1, 0), (0, -1), (0, 1)],
682            NeighborhoodType::Moore => &[
683                (-1, -1),
684                (-1, 0),
685                (-1, 1),
686                (0, -1),
687                (0, 1),
688                (1, -1),
689                (1, 0),
690                (1, 1),
691            ],
692            _ => &[(-1, 0), (1, 0), (0, -1), (0, 1)], // Default to Von Neumann
693        };
694
695        for (dx, dy) in deltas {
696            let nx = x as i32 + dx;
697            let ny = y as i32 + dy;
698
699            let (nx, ny) = match self.config.boundary_conditions {
700                BoundaryConditions::Periodic => {
701                    let nx = ((nx % width as i32) + width as i32) % width as i32;
702                    let ny = ((ny % height as i32) + height as i32) % height as i32;
703                    (nx as usize, ny as usize)
704                }
705                BoundaryConditions::Fixed | BoundaryConditions::Open => {
706                    if nx >= 0 && nx < width as i32 && ny >= 0 && ny < height as i32 {
707                        (nx as usize, ny as usize)
708                    } else {
709                        continue;
710                    }
711                }
712                BoundaryConditions::Reflective => {
713                    let nx = if nx < 0 {
714                        0
715                    } else if nx >= width as i32 {
716                        width - 1
717                    } else {
718                        nx as usize
719                    };
720                    let ny = if ny < 0 {
721                        0
722                    } else if ny >= height as i32 {
723                        height - 1
724                    } else {
725                        ny as usize
726                    };
727                    (nx, ny)
728                }
729            };
730
731            neighbors.push(ny * width + nx);
732        }
733
734        neighbors
735    }
736
737    /// Get 3D neighbors
738    fn get_neighbors_3d(
739        &self,
740        x: usize,
741        y: usize,
742        z: usize,
743        width: usize,
744        height: usize,
745        depth: usize,
746    ) -> Vec<usize> {
747        let mut neighbors = Vec::new();
748
749        // Von Neumann neighborhood in 3D (6 neighbors)
750        let deltas = [
751            (-1, 0, 0),
752            (1, 0, 0),
753            (0, -1, 0),
754            (0, 1, 0),
755            (0, 0, -1),
756            (0, 0, 1),
757        ];
758
759        for (dx, dy, dz) in deltas {
760            let nx = x as i32 + dx;
761            let ny = y as i32 + dy;
762            let nz = z as i32 + dz;
763
764            let (nx, ny, nz) = match self.config.boundary_conditions {
765                BoundaryConditions::Periodic => {
766                    let nx = ((nx % width as i32) + width as i32) % width as i32;
767                    let ny = ((ny % height as i32) + height as i32) % height as i32;
768                    let nz = ((nz % depth as i32) + depth as i32) % depth as i32;
769                    (nx as usize, ny as usize, nz as usize)
770                }
771                BoundaryConditions::Fixed | BoundaryConditions::Open => {
772                    if nx >= 0
773                        && nx < width as i32
774                        && ny >= 0
775                        && ny < height as i32
776                        && nz >= 0
777                        && nz < depth as i32
778                    {
779                        (nx as usize, ny as usize, nz as usize)
780                    } else {
781                        continue;
782                    }
783                }
784                BoundaryConditions::Reflective => {
785                    let nx = if nx < 0 {
786                        0
787                    } else if nx >= width as i32 {
788                        width - 1
789                    } else {
790                        nx as usize
791                    };
792                    let ny = if ny < 0 {
793                        0
794                    } else if ny >= height as i32 {
795                        height - 1
796                    } else {
797                        ny as usize
798                    };
799                    let nz = if nz < 0 {
800                        0
801                    } else if nz >= depth as i32 {
802                        depth - 1
803                    } else {
804                        nz as usize
805                    };
806                    (nx, ny, nz)
807                }
808            };
809
810            neighbors.push(nz * width * height + ny * width + nx);
811        }
812
813        neighbors
814    }
815
816    /// Apply weak measurements for continuous monitoring
817    fn apply_weak_measurements(&mut self) -> Result<()> {
818        // Implement weak measurement on a subset of cells
819        let measurement_strength = 0.01; // Weak measurement parameter
820        let cells_to_measure = (0..self.cell_mapping.total_cells)
821            .step_by(4)
822            .collect::<Vec<_>>();
823
824        for &cell in &cells_to_measure {
825            // Apply weak Z measurement
826            let measurement_operator =
827                self.create_weak_measurement_operator(cell, measurement_strength);
828            self.state = measurement_operator.dot(&self.state);
829
830            // Renormalize
831            let norm: f64 = self.state.iter().map(|x| x.norm_sqr()).sum::<f64>().sqrt();
832            if norm > 1e-15 {
833                self.state.mapv_inplace(|x| x / norm);
834            }
835        }
836
837        self.stats.measurements_performed += cells_to_measure.len();
838        Ok(())
839    }
840
841    /// Create weak measurement operator
842    fn create_weak_measurement_operator(&self, cell: usize, strength: f64) -> Array2<Complex64> {
843        let state_size = self.state.len();
844        let mut operator = Array2::eye(state_size);
845        let cell_mask = 1 << cell;
846
847        for i in 0..state_size {
848            if i & cell_mask != 0 {
849                // Apply weak dephasing to |1⟩ states
850                let factor = Complex64::new((1.0 - strength).sqrt(), 0.0);
851                operator[[i, i]] = factor;
852            }
853        }
854
855        operator
856    }
857
858    /// Take a snapshot of the current state
859    fn take_snapshot(&mut self, time_step: usize) -> Result<QCASnapshot> {
860        let entropy = self.calculate_entanglement_entropy()?;
861
862        // Update entropy statistics
863        if self.stats.entropy_stats.max_entropy == 0.0 {
864            self.stats.entropy_stats.max_entropy = entropy;
865            self.stats.entropy_stats.min_entropy = entropy;
866        } else {
867            self.stats.entropy_stats.max_entropy =
868                self.stats.entropy_stats.max_entropy.max(entropy);
869            self.stats.entropy_stats.min_entropy =
870                self.stats.entropy_stats.min_entropy.min(entropy);
871        }
872
873        let snapshot_count = self.evolution_history.len() as f64;
874        self.stats.entropy_stats.avg_entropy =
875            (self.stats.entropy_stats.avg_entropy * snapshot_count + entropy)
876                / (snapshot_count + 1.0);
877
878        Ok(QCASnapshot {
879            time_step,
880            state: self.state.clone(),
881            measurements: None,
882            entanglement_entropy: Some(entropy),
883            local_observables: self.calculate_local_observables()?,
884        })
885    }
886
887    /// Calculate entanglement entropy
888    fn calculate_entanglement_entropy(&self) -> Result<f64> {
889        // For simplicity, calculate von Neumann entropy of reduced density matrix for first half of system
890        let half_size = self.cell_mapping.total_cells / 2;
891        if half_size == 0 {
892            return Ok(0.0);
893        }
894
895        let reduced_dm = self.compute_reduced_density_matrix(half_size)?;
896        let eigenvalues = self.compute_eigenvalues(&reduced_dm)?;
897
898        let entropy = eigenvalues
899            .iter()
900            .filter(|&&lambda| lambda > 1e-15)
901            .map(|&lambda| -lambda * lambda.ln())
902            .sum();
903
904        Ok(entropy)
905    }
906
907    /// Compute reduced density matrix for first n qubits
908    fn compute_reduced_density_matrix(&self, n_qubits: usize) -> Result<Array2<f64>> {
909        let reduced_size = 1 << n_qubits;
910        let mut reduced_dm = Array2::zeros((reduced_size, reduced_size));
911
912        let total_qubits = self.cell_mapping.total_cells;
913        let env_size = 1 << (total_qubits - n_qubits);
914
915        for i in 0..reduced_size {
916            for j in 0..reduced_size {
917                let mut element = 0.0;
918
919                for k in 0..env_size {
920                    let full_i = i | (k << n_qubits);
921                    let full_j = j | (k << n_qubits);
922
923                    if full_i < self.state.len() && full_j < self.state.len() {
924                        element += (self.state[full_i] * self.state[full_j].conj()).re;
925                    }
926                }
927
928                reduced_dm[[i, j]] = element;
929            }
930        }
931
932        Ok(reduced_dm)
933    }
934
935    /// Compute eigenvalues of a real symmetric matrix
936    fn compute_eigenvalues(&self, matrix: &Array2<f64>) -> Result<Vec<f64>> {
937        // Simplified eigenvalue computation - in practice would use LAPACK
938        let mut eigenvalues = Vec::new();
939
940        // For small matrices, use power iteration or analytical solutions
941        if matrix.dim().0 <= 4 {
942            // Analytical or simple numerical methods
943            for i in 0..matrix.dim().0 {
944                eigenvalues.push(matrix[[i, i]]); // Approximation using diagonal elements
945            }
946        } else {
947            // For larger matrices, would need proper eigenvalue solver
948            for i in 0..matrix.dim().0 {
949                eigenvalues.push(matrix[[i, i]]);
950            }
951        }
952
953        Ok(eigenvalues)
954    }
955
956    /// Calculate local observables
957    fn calculate_local_observables(&self) -> Result<HashMap<String, f64>> {
958        let mut observables = HashMap::new();
959
960        // Calculate local magnetization (average Z measurement) for each cell
961        for cell in 0..self.cell_mapping.total_cells {
962            let magnetization = self.calculate_local_magnetization(cell)?;
963            observables.insert(format!("magnetization_{}", cell), magnetization);
964        }
965
966        // Calculate correlation functions for nearest neighbors
967        for cell in 0..self.cell_mapping.total_cells {
968            if let Some(neighbors) = self.cell_mapping.neighbors.get(&cell) {
969                for &neighbor in neighbors {
970                    if neighbor > cell {
971                        // Avoid double counting
972                        let correlation = self.calculate_correlation(cell, neighbor)?;
973                        observables
974                            .insert(format!("correlation_{}_{}", cell, neighbor), correlation);
975                    }
976                }
977            }
978        }
979
980        Ok(observables)
981    }
982
983    /// Calculate local magnetization (Z expectation value)
984    fn calculate_local_magnetization(&self, cell: usize) -> Result<f64> {
985        let cell_mask = 1 << cell;
986        let mut expectation = 0.0;
987
988        for (i, &amplitude) in self.state.iter().enumerate() {
989            let prob = amplitude.norm_sqr();
990            let z_value = if i & cell_mask != 0 { -1.0 } else { 1.0 };
991            expectation += prob * z_value;
992        }
993
994        Ok(expectation)
995    }
996
997    /// Calculate correlation between two cells
998    fn calculate_correlation(&self, cell1: usize, cell2: usize) -> Result<f64> {
999        let mask1 = 1 << cell1;
1000        let mask2 = 1 << cell2;
1001        let mut correlation = 0.0;
1002
1003        for (i, &amplitude) in self.state.iter().enumerate() {
1004            let prob = amplitude.norm_sqr();
1005            let z1 = if i & mask1 != 0 { -1.0 } else { 1.0 };
1006            let z2 = if i & mask2 != 0 { -1.0 } else { 1.0 };
1007            correlation += prob * z1 * z2;
1008        }
1009
1010        Ok(correlation)
1011    }
1012
1013    /// Measure specific cells
1014    pub fn measure_cells(&mut self, cells: &[usize]) -> Result<Vec<bool>> {
1015        let mut results = Vec::new();
1016
1017        for &cell in cells {
1018            let prob_one = self.calculate_measurement_probability(cell)?;
1019            let result = fastrand::f64() < prob_one;
1020            results.push(result);
1021
1022            // Apply measurement collapse
1023            self.collapse_state_after_measurement(cell, result)?;
1024        }
1025
1026        self.stats.measurements_performed += cells.len();
1027        Ok(results)
1028    }
1029
1030    /// Calculate measurement probability for a cell
1031    fn calculate_measurement_probability(&self, cell: usize) -> Result<f64> {
1032        let cell_mask = 1 << cell;
1033        let mut prob_one = 0.0;
1034
1035        for (i, &amplitude) in self.state.iter().enumerate() {
1036            if i & cell_mask != 0 {
1037                prob_one += amplitude.norm_sqr();
1038            }
1039        }
1040
1041        Ok(prob_one)
1042    }
1043
1044    /// Collapse state after measurement
1045    fn collapse_state_after_measurement(&mut self, cell: usize, result: bool) -> Result<()> {
1046        let cell_mask = 1 << cell;
1047        let mut norm = 0.0;
1048
1049        // Zero out incompatible amplitudes and calculate new norm
1050        for (i, amplitude) in self.state.iter_mut().enumerate() {
1051            let cell_value = (i & cell_mask) != 0;
1052            if cell_value != result {
1053                *amplitude = Complex64::new(0.0, 0.0);
1054            } else {
1055                norm += amplitude.norm_sqr();
1056            }
1057        }
1058
1059        // Renormalize
1060        if norm > 1e-15 {
1061            let norm_factor = 1.0 / norm.sqrt();
1062            self.state.mapv_inplace(|x| x * norm_factor);
1063        }
1064
1065        Ok(())
1066    }
1067
1068    /// Get current state
1069    pub fn get_state(&self) -> &Array1<Complex64> {
1070        &self.state
1071    }
1072
1073    /// Get evolution history
1074    pub fn get_evolution_history(&self) -> &[QCASnapshot] {
1075        &self.evolution_history
1076    }
1077
1078    /// Get statistics
1079    pub fn get_stats(&self) -> &QCAStats {
1080        &self.stats
1081    }
1082
1083    /// Reset the QCA to initial state
1084    pub fn reset(&mut self) -> Result<()> {
1085        let state_size = self.state.len();
1086        self.state = Array1::zeros(state_size);
1087        self.state[0] = Complex64::new(1.0, 0.0);
1088        self.evolution_history.clear();
1089        self.stats = QCAStats::default();
1090        Ok(())
1091    }
1092
1093    /// Helper methods
1094    fn create_cell_mapping(config: &QCAConfig) -> Result<CellMapping> {
1095        let total_cells = config.dimensions.iter().product();
1096        let mut coord_to_index = HashMap::new();
1097        let mut index_to_coord = HashMap::new();
1098        let mut neighbors = HashMap::new();
1099
1100        match config.dimensions.len() {
1101            1 => {
1102                let size = config.dimensions[0];
1103                for i in 0..size {
1104                    coord_to_index.insert(vec![i], i);
1105                    index_to_coord.insert(i, vec![i]);
1106
1107                    let mut cell_neighbors = Vec::new();
1108                    match config.boundary_conditions {
1109                        BoundaryConditions::Periodic => {
1110                            cell_neighbors.push((i + size - 1) % size);
1111                            cell_neighbors.push((i + 1) % size);
1112                        }
1113                        _ => {
1114                            if i > 0 {
1115                                cell_neighbors.push(i - 1);
1116                            }
1117                            if i < size - 1 {
1118                                cell_neighbors.push(i + 1);
1119                            }
1120                        }
1121                    }
1122                    neighbors.insert(i, cell_neighbors);
1123                }
1124            }
1125            2 => {
1126                let (width, height) = (config.dimensions[0], config.dimensions[1]);
1127                for y in 0..height {
1128                    for x in 0..width {
1129                        let index = y * width + x;
1130                        coord_to_index.insert(vec![x, y], index);
1131                        index_to_coord.insert(index, vec![x, y]);
1132                        // Neighbors will be computed dynamically
1133                        neighbors.insert(index, Vec::new());
1134                    }
1135                }
1136            }
1137            3 => {
1138                let (width, height, depth) = (
1139                    config.dimensions[0],
1140                    config.dimensions[1],
1141                    config.dimensions[2],
1142                );
1143                for z in 0..depth {
1144                    for y in 0..height {
1145                        for x in 0..width {
1146                            let index = z * width * height + y * width + x;
1147                            coord_to_index.insert(vec![x, y, z], index);
1148                            index_to_coord.insert(index, vec![x, y, z]);
1149                            neighbors.insert(index, Vec::new());
1150                        }
1151                    }
1152                }
1153            }
1154            _ => {
1155                return Err(SimulatorError::UnsupportedOperation(
1156                    "QCA supports only 1D, 2D, and 3D lattices".to_string(),
1157                ))
1158            }
1159        }
1160
1161        Ok(CellMapping {
1162            total_cells,
1163            dimensions: config.dimensions.clone(),
1164            coord_to_index,
1165            index_to_coord,
1166            neighbors,
1167        })
1168    }
1169
1170    fn create_default_rules(
1171        config: &QCAConfig,
1172        cell_mapping: &CellMapping,
1173    ) -> Result<Vec<QCARule>> {
1174        let mut rules = Vec::new();
1175
1176        match config.rule_type {
1177            QCARuleType::Global => {
1178                // Create random unitary for global evolution
1179                let state_size = 1 << cell_mapping.total_cells.min(10); // Limit for practicality
1180                let unitary = Self::create_random_unitary(state_size);
1181                rules.push(QCARule {
1182                    id: "global_evolution".to_string(),
1183                    unitary,
1184                    affected_cells: (0..cell_mapping.total_cells).collect(),
1185                    neighborhood_pattern: Vec::new(),
1186                    parameters: HashMap::new(),
1187                });
1188            }
1189            _ => {
1190                // Create local rules
1191                rules.push(QCARule {
1192                    id: "two_cell_interaction".to_string(),
1193                    unitary: Self::create_cnot_unitary(),
1194                    affected_cells: Vec::new(),
1195                    neighborhood_pattern: vec![(0, 0, 0), (1, 0, 0)],
1196                    parameters: HashMap::new(),
1197                });
1198
1199                rules.push(QCARule {
1200                    id: "single_cell_rotation".to_string(),
1201                    unitary: Self::create_rotation_unitary(std::f64::consts::PI / 4.0),
1202                    affected_cells: Vec::new(),
1203                    neighborhood_pattern: vec![(0, 0, 0)],
1204                    parameters: HashMap::new(),
1205                });
1206            }
1207        }
1208
1209        Ok(rules)
1210    }
1211
1212    fn create_cnot_unitary() -> Array2<Complex64> {
1213        Array2::from_shape_vec(
1214            (4, 4),
1215            vec![
1216                Complex64::new(1.0, 0.0),
1217                Complex64::new(0.0, 0.0),
1218                Complex64::new(0.0, 0.0),
1219                Complex64::new(0.0, 0.0),
1220                Complex64::new(0.0, 0.0),
1221                Complex64::new(1.0, 0.0),
1222                Complex64::new(0.0, 0.0),
1223                Complex64::new(0.0, 0.0),
1224                Complex64::new(0.0, 0.0),
1225                Complex64::new(0.0, 0.0),
1226                Complex64::new(0.0, 0.0),
1227                Complex64::new(1.0, 0.0),
1228                Complex64::new(0.0, 0.0),
1229                Complex64::new(0.0, 0.0),
1230                Complex64::new(1.0, 0.0),
1231                Complex64::new(0.0, 0.0),
1232            ],
1233        )
1234        .unwrap()
1235    }
1236
1237    fn create_rotation_unitary(angle: f64) -> Array2<Complex64> {
1238        let cos_half = (angle / 2.0).cos();
1239        let sin_half = (angle / 2.0).sin();
1240        Array2::from_shape_vec(
1241            (2, 2),
1242            vec![
1243                Complex64::new(cos_half, 0.0),
1244                Complex64::new(0.0, -sin_half),
1245                Complex64::new(0.0, -sin_half),
1246                Complex64::new(cos_half, 0.0),
1247            ],
1248        )
1249        .unwrap()
1250    }
1251
1252    fn create_margolus_rotation_unitary() -> Array2<Complex64> {
1253        // 4-cell Margolus rotation unitary (rotates the 2x2 block)
1254        let mut unitary = Array2::zeros((16, 16));
1255
1256        // Identity for most states, with specific rotations for the 2x2 block
1257        for i in 0..16 {
1258            // Apply rotation pattern: |ab⟩|cd⟩ → |da⟩|bc⟩
1259            let a = (i >> 3) & 1;
1260            let b = (i >> 2) & 1;
1261            let c = (i >> 1) & 1;
1262            let d = i & 1;
1263
1264            let rotated = (d << 3) | (a << 2) | (b << 1) | c;
1265            unitary[[rotated, i]] = Complex64::new(1.0, 0.0);
1266        }
1267
1268        unitary
1269    }
1270
1271    fn create_random_unitary(size: usize) -> Array2<Complex64> {
1272        // Create a random unitary matrix using Gram-Schmidt process
1273        let mut matrix = Array2::zeros((size, size));
1274
1275        // Start with random complex matrix
1276        for i in 0..size {
1277            for j in 0..size {
1278                matrix[[i, j]] = Complex64::new(fastrand::f64() - 0.5, fastrand::f64() - 0.5);
1279            }
1280        }
1281
1282        // Apply Gram-Schmidt orthogonalization
1283        for j in 0..size {
1284            // Normalize column j
1285            let mut norm_sq = 0.0;
1286            for i in 0..size {
1287                norm_sq += matrix[[i, j]].norm_sqr();
1288            }
1289            let norm = norm_sq.sqrt();
1290
1291            if norm > 1e-15 {
1292                for i in 0..size {
1293                    matrix[[i, j]] /= norm;
1294                }
1295            }
1296
1297            // Orthogonalize subsequent columns
1298            for k in j + 1..size {
1299                let mut inner_product = Complex64::new(0.0, 0.0);
1300                for i in 0..size {
1301                    inner_product += matrix[[i, j]].conj() * matrix[[i, k]];
1302                }
1303
1304                for i in 0..size {
1305                    let correction = inner_product * matrix[[i, j]];
1306                    matrix[[i, k]] -= correction;
1307                }
1308            }
1309        }
1310
1311        matrix
1312    }
1313
1314    fn is_unitary(matrix: &Array2<Complex64>) -> bool {
1315        let (rows, cols) = matrix.dim();
1316        if rows != cols {
1317            return false;
1318        }
1319
1320        // Check if U†U = I (approximately)
1321        let mut identity_check = Array2::zeros((rows, cols));
1322
1323        for i in 0..rows {
1324            for j in 0..cols {
1325                let mut sum = Complex64::new(0.0, 0.0);
1326                for k in 0..rows {
1327                    sum += matrix[[k, i]].conj() * matrix[[k, j]];
1328                }
1329                identity_check[[i, j]] = sum;
1330            }
1331        }
1332
1333        // Check if close to identity
1334        for i in 0..rows {
1335            for j in 0..cols {
1336                let expected = if i == j {
1337                    Complex64::new(1.0, 0.0)
1338                } else {
1339                    Complex64::new(0.0, 0.0)
1340                };
1341                let diff = (identity_check[[i, j]] - expected).norm();
1342                if diff > 1e-10 {
1343                    return false;
1344                }
1345            }
1346        }
1347
1348        true
1349    }
1350}
1351
1352/// QCA evolution result
1353#[derive(Debug, Clone)]
1354pub struct QCAEvolutionResult {
1355    /// Final quantum state
1356    pub final_state: Array1<Complex64>,
1357    /// Evolution history snapshots
1358    pub evolution_history: Vec<QCASnapshot>,
1359    /// Total evolution steps
1360    pub total_steps: usize,
1361    /// Total evolution time
1362    pub total_time_ms: f64,
1363    /// Final entanglement entropy
1364    pub final_entropy: f64,
1365}
1366
1367/// QCA utilities
1368pub struct QCAUtils;
1369
1370impl QCAUtils {
1371    /// Create a predefined QCA configuration
1372    pub fn create_predefined_config(config_type: &str, size: usize) -> QCAConfig {
1373        match config_type {
1374            "game_of_life" => QCAConfig {
1375                dimensions: vec![size, size],
1376                boundary_conditions: BoundaryConditions::Periodic,
1377                neighborhood: NeighborhoodType::Moore,
1378                rule_type: QCARuleType::Partitioned,
1379                evolution_steps: 50,
1380                parallel_evolution: true,
1381                measurement_strategy: MeasurementStrategy::Probabilistic,
1382            },
1383            "elementary_ca" => QCAConfig {
1384                dimensions: vec![size],
1385                boundary_conditions: BoundaryConditions::Periodic,
1386                neighborhood: NeighborhoodType::VonNeumann,
1387                rule_type: QCARuleType::Sequential,
1388                evolution_steps: 100,
1389                parallel_evolution: false,
1390                measurement_strategy: MeasurementStrategy::Deterministic,
1391            },
1392            "margolus_ca" => QCAConfig {
1393                dimensions: vec![size, size],
1394                boundary_conditions: BoundaryConditions::Periodic,
1395                neighborhood: NeighborhoodType::Custom,
1396                rule_type: QCARuleType::Margolus,
1397                evolution_steps: 25,
1398                parallel_evolution: true,
1399                measurement_strategy: MeasurementStrategy::Partial,
1400            },
1401            _ => QCAConfig::default(),
1402        }
1403    }
1404
1405    /// Create initial pattern for QCA
1406    pub fn create_initial_pattern(pattern_type: &str, dimensions: &[usize]) -> Array1<Complex64> {
1407        let total_cells = dimensions.iter().product::<usize>();
1408        let state_size = 1 << total_cells;
1409        let mut state = Array1::zeros(state_size);
1410
1411        match pattern_type {
1412            "random" => {
1413                // Random superposition
1414                for i in 0..state_size {
1415                    state[i] = Complex64::new(fastrand::f64() - 0.5, fastrand::f64() - 0.5);
1416                }
1417                // Normalize
1418                let norm: f64 = state.iter().map(|x| x.norm_sqr()).sum::<f64>().sqrt();
1419                state.mapv_inplace(|x| x / norm);
1420            }
1421            "glider" if dimensions.len() == 2 => {
1422                // Conway's Game of Life glider pattern
1423                let width = dimensions[0];
1424                let height = dimensions[1];
1425
1426                if width >= 3 && height >= 3 {
1427                    // Glider pattern: positions (1,0), (2,1), (0,2), (1,2), (2,2)
1428                    let glider_positions = [
1429                        width,
1430                        2 * width + 1,
1431                        2, // 0 * width + 2, simplified to avoid clippy warning
1432                        width + 2,
1433                        2 * width + 2,
1434                    ];
1435
1436                    let mut glider_state = 0;
1437                    for &pos in &glider_positions {
1438                        if pos < total_cells {
1439                            glider_state |= 1 << pos;
1440                        }
1441                    }
1442
1443                    if glider_state < state_size {
1444                        state[glider_state] = Complex64::new(1.0, 0.0);
1445                    }
1446                }
1447            }
1448            "uniform" => {
1449                // Uniform superposition
1450                let amplitude = 1.0 / (state_size as f64).sqrt();
1451                state.fill(Complex64::new(amplitude, 0.0));
1452            }
1453            _ => {
1454                // Default: |0...0⟩ state
1455                state[0] = Complex64::new(1.0, 0.0);
1456            }
1457        }
1458
1459        state
1460    }
1461
1462    /// Benchmark QCA performance
1463    pub fn benchmark_qca() -> Result<QCABenchmarkResults> {
1464        let mut results = QCABenchmarkResults::default();
1465
1466        let configs = vec![
1467            (
1468                "1d_elementary",
1469                QCAUtils::create_predefined_config("elementary_ca", 8),
1470            ),
1471            (
1472                "2d_game_of_life",
1473                QCAUtils::create_predefined_config("game_of_life", 4),
1474            ),
1475            (
1476                "2d_margolus",
1477                QCAUtils::create_predefined_config("margolus_ca", 4),
1478            ),
1479        ];
1480
1481        for (name, mut config) in configs {
1482            config.evolution_steps = 20; // Limit for benchmarking
1483
1484            let mut qca = QuantumCellularAutomaton::new(config)?;
1485            let initial_state = QCAUtils::create_initial_pattern("random", &qca.config.dimensions);
1486            qca.set_initial_state(initial_state)?;
1487
1488            let start = std::time::Instant::now();
1489            let _result = qca.evolve(20)?;
1490            let time = start.elapsed().as_secs_f64() * 1000.0;
1491
1492            results.benchmark_times.push((name.to_string(), time));
1493            results
1494                .qca_stats
1495                .insert(name.to_string(), qca.get_stats().clone());
1496        }
1497
1498        Ok(results)
1499    }
1500}
1501
1502/// QCA benchmark results
1503#[derive(Debug, Clone, Default)]
1504pub struct QCABenchmarkResults {
1505    /// Benchmark times by configuration
1506    pub benchmark_times: Vec<(String, f64)>,
1507    /// QCA statistics by configuration
1508    pub qca_stats: HashMap<String, QCAStats>,
1509}
1510
1511#[cfg(test)]
1512mod tests {
1513    use super::*;
1514    use approx::assert_abs_diff_eq;
1515
1516    #[test]
1517    fn test_qca_creation() {
1518        let config = QCAConfig::default();
1519        let qca = QuantumCellularAutomaton::new(config);
1520        assert!(qca.is_ok());
1521    }
1522
1523    #[test]
1524    fn test_qca_1d_evolution() {
1525        let config = QCAConfig {
1526            dimensions: vec![4],
1527            evolution_steps: 5,
1528            ..Default::default()
1529        };
1530
1531        let mut qca = QuantumCellularAutomaton::new(config).unwrap();
1532        let result = qca.evolve(5);
1533        assert!(result.is_ok());
1534
1535        let evolution_result = result.unwrap();
1536        assert_eq!(evolution_result.total_steps, 5);
1537        assert!(evolution_result.total_time_ms > 0.0);
1538    }
1539
1540    #[test]
1541    fn test_qca_2d_evolution() {
1542        let config = QCAConfig {
1543            dimensions: vec![3, 3],
1544            evolution_steps: 3,
1545            rule_type: QCARuleType::Partitioned,
1546            ..Default::default()
1547        };
1548
1549        let mut qca = QuantumCellularAutomaton::new(config).unwrap();
1550        let result = qca.evolve(3);
1551        assert!(result.is_ok());
1552    }
1553
1554    #[test]
1555    fn test_qca_measurement() {
1556        let config = QCAConfig {
1557            dimensions: vec![3],
1558            ..Default::default()
1559        };
1560
1561        let mut qca = QuantumCellularAutomaton::new(config).unwrap();
1562
1563        // Measure all cells
1564        let results = qca.measure_cells(&[0, 1, 2]);
1565        assert!(results.is_ok());
1566        assert_eq!(results.unwrap().len(), 3);
1567    }
1568
1569    #[test]
1570    fn test_boundary_conditions() {
1571        let configs = vec![
1572            BoundaryConditions::Periodic,
1573            BoundaryConditions::Fixed,
1574            BoundaryConditions::Open,
1575            BoundaryConditions::Reflective,
1576        ];
1577
1578        for boundary in configs {
1579            let config = QCAConfig {
1580                dimensions: vec![4],
1581                boundary_conditions: boundary,
1582                evolution_steps: 2,
1583                ..Default::default()
1584            };
1585
1586            let mut qca = QuantumCellularAutomaton::new(config).unwrap();
1587            let result = qca.evolve(2);
1588            assert!(result.is_ok());
1589        }
1590    }
1591
1592    #[test]
1593    fn test_neighborhood_types() {
1594        let neighborhoods = vec![NeighborhoodType::VonNeumann, NeighborhoodType::Moore];
1595
1596        for neighborhood in neighborhoods {
1597            let config = QCAConfig {
1598                dimensions: vec![3, 3],
1599                neighborhood,
1600                evolution_steps: 2,
1601                ..Default::default()
1602            };
1603
1604            let mut qca = QuantumCellularAutomaton::new(config).unwrap();
1605            let result = qca.evolve(2);
1606            assert!(result.is_ok());
1607        }
1608    }
1609
1610    #[test]
1611    fn test_predefined_configs() {
1612        let config_types = vec!["game_of_life", "elementary_ca", "margolus_ca"];
1613
1614        for config_type in config_types {
1615            let config = QCAUtils::create_predefined_config(config_type, 4);
1616            let qca = QuantumCellularAutomaton::new(config);
1617            assert!(qca.is_ok());
1618        }
1619    }
1620
1621    #[test]
1622    fn test_initial_patterns() {
1623        let dimensions = vec![4, 4];
1624        let patterns = vec!["random", "glider", "uniform"];
1625
1626        for pattern in patterns {
1627            let state = QCAUtils::create_initial_pattern(pattern, &dimensions);
1628            assert_eq!(state.len(), 1 << 16); // 2^16 for 16 cells
1629
1630            // Check normalization
1631            let norm: f64 = state.iter().map(|x| x.norm_sqr()).sum();
1632            assert_abs_diff_eq!(norm, 1.0, epsilon = 1e-10);
1633        }
1634    }
1635
1636    #[test]
1637    fn test_entanglement_entropy_calculation() {
1638        let config = QCAConfig {
1639            dimensions: vec![4],
1640            ..Default::default()
1641        };
1642
1643        let mut qca = QuantumCellularAutomaton::new(config).unwrap();
1644
1645        // Create entangled state
1646        let state_size = qca.state.len();
1647        qca.state = Array1::zeros(state_size);
1648        qca.state[0] = Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0); // |0000⟩
1649        qca.state[15] = Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0); // |1111⟩
1650
1651        let entropy = qca.calculate_entanglement_entropy().unwrap();
1652        assert!(entropy >= 0.0);
1653    }
1654
1655    #[test]
1656    fn test_local_observables() {
1657        let config = QCAConfig {
1658            dimensions: vec![3],
1659            ..Default::default()
1660        };
1661
1662        let qca = QuantumCellularAutomaton::new(config).unwrap();
1663        let observables = qca.calculate_local_observables().unwrap();
1664
1665        // Should have magnetization for each cell
1666        assert!(observables.contains_key("magnetization_0"));
1667        assert!(observables.contains_key("magnetization_1"));
1668        assert!(observables.contains_key("magnetization_2"));
1669    }
1670
1671    #[test]
1672    fn test_unitary_check() {
1673        let cnot = QuantumCellularAutomaton::create_cnot_unitary();
1674        assert!(QuantumCellularAutomaton::is_unitary(&cnot));
1675
1676        let rotation =
1677            QuantumCellularAutomaton::create_rotation_unitary(std::f64::consts::PI / 4.0);
1678        assert!(QuantumCellularAutomaton::is_unitary(&rotation));
1679    }
1680}