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