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
835                .state
836                .iter()
837                .map(scirs2_core::Complex::norm_sqr)
838                .sum::<f64>()
839                .sqrt();
840            if norm > 1e-15 {
841                self.state.mapv_inplace(|x| x / norm);
842            }
843        }
844
845        self.stats.measurements_performed += cells_to_measure.len();
846        Ok(())
847    }
848
849    /// Create weak measurement operator
850    fn create_weak_measurement_operator(&self, cell: usize, strength: f64) -> Array2<Complex64> {
851        let state_size = self.state.len();
852        let mut operator = Array2::eye(state_size);
853        let cell_mask = 1 << cell;
854
855        for i in 0..state_size {
856            if i & cell_mask != 0 {
857                // Apply weak dephasing to |1⟩ states
858                let factor = Complex64::new((1.0 - strength).sqrt(), 0.0);
859                operator[[i, i]] = factor;
860            }
861        }
862
863        operator
864    }
865
866    /// Take a snapshot of the current state
867    fn take_snapshot(&mut self, time_step: usize) -> Result<QCASnapshot> {
868        let entropy = self.calculate_entanglement_entropy()?;
869
870        // Update entropy statistics
871        if self.stats.entropy_stats.max_entropy == 0.0 {
872            self.stats.entropy_stats.max_entropy = entropy;
873            self.stats.entropy_stats.min_entropy = entropy;
874        } else {
875            self.stats.entropy_stats.max_entropy =
876                self.stats.entropy_stats.max_entropy.max(entropy);
877            self.stats.entropy_stats.min_entropy =
878                self.stats.entropy_stats.min_entropy.min(entropy);
879        }
880
881        let snapshot_count = self.evolution_history.len() as f64;
882        self.stats.entropy_stats.avg_entropy = self
883            .stats
884            .entropy_stats
885            .avg_entropy
886            .mul_add(snapshot_count, entropy)
887            / (snapshot_count + 1.0);
888
889        Ok(QCASnapshot {
890            time_step,
891            state: self.state.clone(),
892            measurements: None,
893            entanglement_entropy: Some(entropy),
894            local_observables: self.calculate_local_observables()?,
895        })
896    }
897
898    /// Calculate entanglement entropy
899    fn calculate_entanglement_entropy(&self) -> Result<f64> {
900        // For simplicity, calculate von Neumann entropy of reduced density matrix for first half of system
901        let half_size = self.cell_mapping.total_cells / 2;
902        if half_size == 0 {
903            return Ok(0.0);
904        }
905
906        let reduced_dm = self.compute_reduced_density_matrix(half_size)?;
907        let eigenvalues = self.compute_eigenvalues(&reduced_dm)?;
908
909        let entropy = eigenvalues
910            .iter()
911            .filter(|&&lambda| lambda > 1e-15)
912            .map(|&lambda| -lambda * lambda.ln())
913            .sum();
914
915        Ok(entropy)
916    }
917
918    /// Compute reduced density matrix for first n qubits
919    fn compute_reduced_density_matrix(&self, n_qubits: usize) -> Result<Array2<f64>> {
920        let reduced_size = 1 << n_qubits;
921        let mut reduced_dm = Array2::zeros((reduced_size, reduced_size));
922
923        let total_qubits = self.cell_mapping.total_cells;
924        let env_size = 1 << (total_qubits - n_qubits);
925
926        for i in 0..reduced_size {
927            for j in 0..reduced_size {
928                let mut element = 0.0;
929
930                for k in 0..env_size {
931                    let full_i = i | (k << n_qubits);
932                    let full_j = j | (k << n_qubits);
933
934                    if full_i < self.state.len() && full_j < self.state.len() {
935                        element += (self.state[full_i] * self.state[full_j].conj()).re;
936                    }
937                }
938
939                reduced_dm[[i, j]] = element;
940            }
941        }
942
943        Ok(reduced_dm)
944    }
945
946    /// Compute eigenvalues of a real symmetric matrix
947    fn compute_eigenvalues(&self, matrix: &Array2<f64>) -> Result<Vec<f64>> {
948        // Simplified eigenvalue computation - in practice would use LAPACK
949        let mut eigenvalues = Vec::new();
950
951        // For small matrices, use power iteration or analytical solutions
952        if matrix.dim().0 <= 4 {
953            // Analytical or simple numerical methods
954            for i in 0..matrix.dim().0 {
955                eigenvalues.push(matrix[[i, i]]); // Approximation using diagonal elements
956            }
957        } else {
958            // For larger matrices, would need proper eigenvalue solver
959            for i in 0..matrix.dim().0 {
960                eigenvalues.push(matrix[[i, i]]);
961            }
962        }
963
964        Ok(eigenvalues)
965    }
966
967    /// Calculate local observables
968    fn calculate_local_observables(&self) -> Result<HashMap<String, f64>> {
969        let mut observables = HashMap::new();
970
971        // Calculate local magnetization (average Z measurement) for each cell
972        for cell in 0..self.cell_mapping.total_cells {
973            let magnetization = self.calculate_local_magnetization(cell)?;
974            observables.insert(format!("magnetization_{cell}"), magnetization);
975        }
976
977        // Calculate correlation functions for nearest neighbors
978        for cell in 0..self.cell_mapping.total_cells {
979            if let Some(neighbors) = self.cell_mapping.neighbors.get(&cell) {
980                for &neighbor in neighbors {
981                    if neighbor > cell {
982                        // Avoid double counting
983                        let correlation = self.calculate_correlation(cell, neighbor)?;
984                        observables.insert(format!("correlation_{cell}_{neighbor}"), correlation);
985                    }
986                }
987            }
988        }
989
990        Ok(observables)
991    }
992
993    /// Calculate local magnetization (Z expectation value)
994    fn calculate_local_magnetization(&self, cell: usize) -> Result<f64> {
995        let cell_mask = 1 << cell;
996        let mut expectation = 0.0;
997
998        for (i, &amplitude) in self.state.iter().enumerate() {
999            let prob = amplitude.norm_sqr();
1000            let z_value = if i & cell_mask != 0 { -1.0 } else { 1.0 };
1001            expectation += prob * z_value;
1002        }
1003
1004        Ok(expectation)
1005    }
1006
1007    /// Calculate correlation between two cells
1008    fn calculate_correlation(&self, cell1: usize, cell2: usize) -> Result<f64> {
1009        let mask1 = 1 << cell1;
1010        let mask2 = 1 << cell2;
1011        let mut correlation = 0.0;
1012
1013        for (i, &amplitude) in self.state.iter().enumerate() {
1014            let prob = amplitude.norm_sqr();
1015            let z1 = if i & mask1 != 0 { -1.0 } else { 1.0 };
1016            let z2 = if i & mask2 != 0 { -1.0 } else { 1.0 };
1017            correlation += prob * z1 * z2;
1018        }
1019
1020        Ok(correlation)
1021    }
1022
1023    /// Measure specific cells
1024    pub fn measure_cells(&mut self, cells: &[usize]) -> Result<Vec<bool>> {
1025        let mut results = Vec::new();
1026
1027        for &cell in cells {
1028            let prob_one = self.calculate_measurement_probability(cell)?;
1029            let result = fastrand::f64() < prob_one;
1030            results.push(result);
1031
1032            // Apply measurement collapse
1033            self.collapse_state_after_measurement(cell, result)?;
1034        }
1035
1036        self.stats.measurements_performed += cells.len();
1037        Ok(results)
1038    }
1039
1040    /// Calculate measurement probability for a cell
1041    fn calculate_measurement_probability(&self, cell: usize) -> Result<f64> {
1042        let cell_mask = 1 << cell;
1043        let mut prob_one = 0.0;
1044
1045        for (i, &amplitude) in self.state.iter().enumerate() {
1046            if i & cell_mask != 0 {
1047                prob_one += amplitude.norm_sqr();
1048            }
1049        }
1050
1051        Ok(prob_one)
1052    }
1053
1054    /// Collapse state after measurement
1055    fn collapse_state_after_measurement(&mut self, cell: usize, result: bool) -> Result<()> {
1056        let cell_mask = 1 << cell;
1057        let mut norm = 0.0;
1058
1059        // Zero out incompatible amplitudes and calculate new norm
1060        for (i, amplitude) in self.state.iter_mut().enumerate() {
1061            let cell_value = (i & cell_mask) != 0;
1062            if cell_value == result {
1063                norm += amplitude.norm_sqr();
1064            } else {
1065                *amplitude = Complex64::new(0.0, 0.0);
1066            }
1067        }
1068
1069        // Renormalize
1070        if norm > 1e-15 {
1071            let norm_factor = 1.0 / norm.sqrt();
1072            self.state.mapv_inplace(|x| x * norm_factor);
1073        }
1074
1075        Ok(())
1076    }
1077
1078    /// Get current state
1079    #[must_use]
1080    pub const fn get_state(&self) -> &Array1<Complex64> {
1081        &self.state
1082    }
1083
1084    /// Get evolution history
1085    #[must_use]
1086    pub fn get_evolution_history(&self) -> &[QCASnapshot] {
1087        &self.evolution_history
1088    }
1089
1090    /// Get statistics
1091    #[must_use]
1092    pub const fn get_stats(&self) -> &QCAStats {
1093        &self.stats
1094    }
1095
1096    /// Reset the QCA to initial state
1097    pub fn reset(&mut self) -> Result<()> {
1098        let state_size = self.state.len();
1099        self.state = Array1::zeros(state_size);
1100        self.state[0] = Complex64::new(1.0, 0.0);
1101        self.evolution_history.clear();
1102        self.stats = QCAStats::default();
1103        Ok(())
1104    }
1105
1106    /// Helper methods
1107    fn create_cell_mapping(config: &QCAConfig) -> Result<CellMapping> {
1108        let total_cells = config.dimensions.iter().product();
1109        let mut coord_to_index = HashMap::new();
1110        let mut index_to_coord = HashMap::new();
1111        let mut neighbors = HashMap::new();
1112
1113        match config.dimensions.len() {
1114            1 => {
1115                let size = config.dimensions[0];
1116                for i in 0..size {
1117                    coord_to_index.insert(vec![i], i);
1118                    index_to_coord.insert(i, vec![i]);
1119
1120                    let mut cell_neighbors = Vec::new();
1121                    if config.boundary_conditions == BoundaryConditions::Periodic {
1122                        cell_neighbors.push((i + size - 1) % size);
1123                        cell_neighbors.push((i + 1) % size);
1124                    } else {
1125                        if i > 0 {
1126                            cell_neighbors.push(i - 1);
1127                        }
1128                        if i < size - 1 {
1129                            cell_neighbors.push(i + 1);
1130                        }
1131                    }
1132                    neighbors.insert(i, cell_neighbors);
1133                }
1134            }
1135            2 => {
1136                let (width, height) = (config.dimensions[0], config.dimensions[1]);
1137                for y in 0..height {
1138                    for x in 0..width {
1139                        let index = y * width + x;
1140                        coord_to_index.insert(vec![x, y], index);
1141                        index_to_coord.insert(index, vec![x, y]);
1142                        // Neighbors will be computed dynamically
1143                        neighbors.insert(index, Vec::new());
1144                    }
1145                }
1146            }
1147            3 => {
1148                let (width, height, depth) = (
1149                    config.dimensions[0],
1150                    config.dimensions[1],
1151                    config.dimensions[2],
1152                );
1153                for z in 0..depth {
1154                    for y in 0..height {
1155                        for x in 0..width {
1156                            let index = z * width * height + y * width + x;
1157                            coord_to_index.insert(vec![x, y, z], index);
1158                            index_to_coord.insert(index, vec![x, y, z]);
1159                            neighbors.insert(index, Vec::new());
1160                        }
1161                    }
1162                }
1163            }
1164            _ => {
1165                return Err(SimulatorError::UnsupportedOperation(
1166                    "QCA supports only 1D, 2D, and 3D lattices".to_string(),
1167                ))
1168            }
1169        }
1170
1171        Ok(CellMapping {
1172            total_cells,
1173            dimensions: config.dimensions.clone(),
1174            coord_to_index,
1175            index_to_coord,
1176            neighbors,
1177        })
1178    }
1179
1180    fn create_default_rules(
1181        config: &QCAConfig,
1182        cell_mapping: &CellMapping,
1183    ) -> Result<Vec<QCARule>> {
1184        let mut rules = Vec::new();
1185
1186        if config.rule_type == QCARuleType::Global {
1187            // Create random unitary for global evolution
1188            let state_size = 1 << cell_mapping.total_cells.min(10); // Limit for practicality
1189            let unitary = Self::create_random_unitary(state_size);
1190            rules.push(QCARule {
1191                id: "global_evolution".to_string(),
1192                unitary,
1193                affected_cells: (0..cell_mapping.total_cells).collect(),
1194                neighborhood_pattern: Vec::new(),
1195                parameters: HashMap::new(),
1196            });
1197        } else {
1198            // Create local rules
1199            rules.push(QCARule {
1200                id: "two_cell_interaction".to_string(),
1201                unitary: Self::create_cnot_unitary(),
1202                affected_cells: Vec::new(),
1203                neighborhood_pattern: vec![(0, 0, 0), (1, 0, 0)],
1204                parameters: HashMap::new(),
1205            });
1206
1207            rules.push(QCARule {
1208                id: "single_cell_rotation".to_string(),
1209                unitary: Self::create_rotation_unitary(std::f64::consts::PI / 4.0),
1210                affected_cells: Vec::new(),
1211                neighborhood_pattern: vec![(0, 0, 0)],
1212                parameters: HashMap::new(),
1213            });
1214        }
1215
1216        Ok(rules)
1217    }
1218
1219    fn create_cnot_unitary() -> Array2<Complex64> {
1220        Array2::from_shape_vec(
1221            (4, 4),
1222            vec![
1223                Complex64::new(1.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(0.0, 0.0),
1228                Complex64::new(1.0, 0.0),
1229                Complex64::new(0.0, 0.0),
1230                Complex64::new(0.0, 0.0),
1231                Complex64::new(0.0, 0.0),
1232                Complex64::new(0.0, 0.0),
1233                Complex64::new(0.0, 0.0),
1234                Complex64::new(1.0, 0.0),
1235                Complex64::new(0.0, 0.0),
1236                Complex64::new(0.0, 0.0),
1237                Complex64::new(1.0, 0.0),
1238                Complex64::new(0.0, 0.0),
1239            ],
1240        )
1241        .expect("CNOT unitary shape is always valid")
1242    }
1243
1244    fn create_rotation_unitary(angle: f64) -> Array2<Complex64> {
1245        let cos_half = (angle / 2.0).cos();
1246        let sin_half = (angle / 2.0).sin();
1247        Array2::from_shape_vec(
1248            (2, 2),
1249            vec![
1250                Complex64::new(cos_half, 0.0),
1251                Complex64::new(0.0, -sin_half),
1252                Complex64::new(0.0, -sin_half),
1253                Complex64::new(cos_half, 0.0),
1254            ],
1255        )
1256        .expect("Rotation unitary shape is always valid")
1257    }
1258
1259    fn create_margolus_rotation_unitary() -> Array2<Complex64> {
1260        // 4-cell Margolus rotation unitary (rotates the 2x2 block)
1261        let mut unitary = Array2::zeros((16, 16));
1262
1263        // Identity for most states, with specific rotations for the 2x2 block
1264        for i in 0..16 {
1265            // Apply rotation pattern: |ab⟩|cd⟩ → |da⟩|bc⟩
1266            let a = (i >> 3) & 1;
1267            let b = (i >> 2) & 1;
1268            let c = (i >> 1) & 1;
1269            let d = i & 1;
1270
1271            let rotated = (d << 3) | (a << 2) | (b << 1) | c;
1272            unitary[[rotated, i]] = Complex64::new(1.0, 0.0);
1273        }
1274
1275        unitary
1276    }
1277
1278    fn create_random_unitary(size: usize) -> Array2<Complex64> {
1279        // Create a random unitary matrix using Gram-Schmidt process
1280        let mut matrix = Array2::zeros((size, size));
1281
1282        // Start with random complex matrix
1283        for i in 0..size {
1284            for j in 0..size {
1285                matrix[[i, j]] = Complex64::new(fastrand::f64() - 0.5, fastrand::f64() - 0.5);
1286            }
1287        }
1288
1289        // Apply Gram-Schmidt orthogonalization
1290        for j in 0..size {
1291            // Normalize column j
1292            let mut norm_sq = 0.0;
1293            for i in 0..size {
1294                norm_sq += matrix[[i, j]].norm_sqr();
1295            }
1296            let norm = norm_sq.sqrt();
1297
1298            if norm > 1e-15 {
1299                for i in 0..size {
1300                    matrix[[i, j]] /= norm;
1301                }
1302            }
1303
1304            // Orthogonalize subsequent columns
1305            for k in j + 1..size {
1306                let mut inner_product = Complex64::new(0.0, 0.0);
1307                for i in 0..size {
1308                    inner_product += matrix[[i, j]].conj() * matrix[[i, k]];
1309                }
1310
1311                for i in 0..size {
1312                    let correction = inner_product * matrix[[i, j]];
1313                    matrix[[i, k]] -= correction;
1314                }
1315            }
1316        }
1317
1318        matrix
1319    }
1320
1321    fn is_unitary(matrix: &Array2<Complex64>) -> bool {
1322        let (rows, cols) = matrix.dim();
1323        if rows != cols {
1324            return false;
1325        }
1326
1327        // Check if U†U = I (approximately)
1328        let mut identity_check = Array2::zeros((rows, cols));
1329
1330        for i in 0..rows {
1331            for j in 0..cols {
1332                let mut sum = Complex64::new(0.0, 0.0);
1333                for k in 0..rows {
1334                    sum += matrix[[k, i]].conj() * matrix[[k, j]];
1335                }
1336                identity_check[[i, j]] = sum;
1337            }
1338        }
1339
1340        // Check if close to identity
1341        for i in 0..rows {
1342            for j in 0..cols {
1343                let expected = if i == j {
1344                    Complex64::new(1.0, 0.0)
1345                } else {
1346                    Complex64::new(0.0, 0.0)
1347                };
1348                let diff = (identity_check[[i, j]] - expected).norm();
1349                if diff > 1e-10 {
1350                    return false;
1351                }
1352            }
1353        }
1354
1355        true
1356    }
1357}
1358
1359/// QCA evolution result
1360#[derive(Debug, Clone)]
1361pub struct QCAEvolutionResult {
1362    /// Final quantum state
1363    pub final_state: Array1<Complex64>,
1364    /// Evolution history snapshots
1365    pub evolution_history: Vec<QCASnapshot>,
1366    /// Total evolution steps
1367    pub total_steps: usize,
1368    /// Total evolution time
1369    pub total_time_ms: f64,
1370    /// Final entanglement entropy
1371    pub final_entropy: f64,
1372}
1373
1374/// QCA utilities
1375pub struct QCAUtils;
1376
1377impl QCAUtils {
1378    /// Create a predefined QCA configuration
1379    #[must_use]
1380    pub fn create_predefined_config(config_type: &str, size: usize) -> QCAConfig {
1381        match config_type {
1382            "game_of_life" => QCAConfig {
1383                dimensions: vec![size, size],
1384                boundary_conditions: BoundaryConditions::Periodic,
1385                neighborhood: NeighborhoodType::Moore,
1386                rule_type: QCARuleType::Partitioned,
1387                evolution_steps: 50,
1388                parallel_evolution: true,
1389                measurement_strategy: MeasurementStrategy::Probabilistic,
1390            },
1391            "elementary_ca" => QCAConfig {
1392                dimensions: vec![size],
1393                boundary_conditions: BoundaryConditions::Periodic,
1394                neighborhood: NeighborhoodType::VonNeumann,
1395                rule_type: QCARuleType::Sequential,
1396                evolution_steps: 100,
1397                parallel_evolution: false,
1398                measurement_strategy: MeasurementStrategy::Deterministic,
1399            },
1400            "margolus_ca" => QCAConfig {
1401                dimensions: vec![size, size],
1402                boundary_conditions: BoundaryConditions::Periodic,
1403                neighborhood: NeighborhoodType::Custom,
1404                rule_type: QCARuleType::Margolus,
1405                evolution_steps: 25,
1406                parallel_evolution: true,
1407                measurement_strategy: MeasurementStrategy::Partial,
1408            },
1409            _ => QCAConfig::default(),
1410        }
1411    }
1412
1413    /// Create initial pattern for QCA
1414    #[must_use]
1415    pub fn create_initial_pattern(pattern_type: &str, dimensions: &[usize]) -> Array1<Complex64> {
1416        let total_cells = dimensions.iter().product::<usize>();
1417        let state_size = 1 << total_cells;
1418        let mut state = Array1::zeros(state_size);
1419
1420        match pattern_type {
1421            "random" => {
1422                // Random superposition
1423                for i in 0..state_size {
1424                    state[i] = Complex64::new(fastrand::f64() - 0.5, fastrand::f64() - 0.5);
1425                }
1426                // Normalize
1427                let norm: f64 = state
1428                    .iter()
1429                    .map(scirs2_core::Complex::norm_sqr)
1430                    .sum::<f64>()
1431                    .sqrt();
1432                state.mapv_inplace(|x| x / norm);
1433            }
1434            "glider" if dimensions.len() == 2 => {
1435                // Conway's Game of Life glider pattern
1436                let width = dimensions[0];
1437                let height = dimensions[1];
1438
1439                if width >= 3 && height >= 3 {
1440                    // Glider pattern: positions (1,0), (2,1), (0,2), (1,2), (2,2)
1441                    let glider_positions = [
1442                        width,
1443                        2 * width + 1,
1444                        2, // 0 * width + 2, simplified to avoid clippy warning
1445                        width + 2,
1446                        2 * width + 2,
1447                    ];
1448
1449                    let mut glider_state = 0;
1450                    for &pos in &glider_positions {
1451                        if pos < total_cells {
1452                            glider_state |= 1 << pos;
1453                        }
1454                    }
1455
1456                    if glider_state < state_size {
1457                        state[glider_state] = Complex64::new(1.0, 0.0);
1458                    }
1459                }
1460            }
1461            "uniform" => {
1462                // Uniform superposition
1463                let amplitude = 1.0 / (state_size as f64).sqrt();
1464                state.fill(Complex64::new(amplitude, 0.0));
1465            }
1466            _ => {
1467                // Default: |0...0⟩ state
1468                state[0] = Complex64::new(1.0, 0.0);
1469            }
1470        }
1471
1472        state
1473    }
1474
1475    /// Benchmark QCA performance
1476    pub fn benchmark_qca() -> Result<QCABenchmarkResults> {
1477        let mut results = QCABenchmarkResults::default();
1478
1479        let configs = vec![
1480            (
1481                "1d_elementary",
1482                Self::create_predefined_config("elementary_ca", 8),
1483            ),
1484            (
1485                "2d_game_of_life",
1486                Self::create_predefined_config("game_of_life", 4),
1487            ),
1488            (
1489                "2d_margolus",
1490                Self::create_predefined_config("margolus_ca", 4),
1491            ),
1492        ];
1493
1494        for (name, mut config) in configs {
1495            config.evolution_steps = 20; // Limit for benchmarking
1496
1497            let mut qca = QuantumCellularAutomaton::new(config)?;
1498            let initial_state = Self::create_initial_pattern("random", &qca.config.dimensions);
1499            qca.set_initial_state(initial_state)?;
1500
1501            let start = std::time::Instant::now();
1502            let _result = qca.evolve(20)?;
1503            let time = start.elapsed().as_secs_f64() * 1000.0;
1504
1505            results.benchmark_times.push((name.to_string(), time));
1506            results
1507                .qca_stats
1508                .insert(name.to_string(), qca.get_stats().clone());
1509        }
1510
1511        Ok(results)
1512    }
1513}
1514
1515/// QCA benchmark results
1516#[derive(Debug, Clone, Default)]
1517pub struct QCABenchmarkResults {
1518    /// Benchmark times by configuration
1519    pub benchmark_times: Vec<(String, f64)>,
1520    /// QCA statistics by configuration
1521    pub qca_stats: HashMap<String, QCAStats>,
1522}
1523
1524#[cfg(test)]
1525mod tests {
1526    use super::*;
1527    use approx::assert_abs_diff_eq;
1528
1529    #[test]
1530    fn test_qca_creation() {
1531        let config = QCAConfig::default();
1532        let qca = QuantumCellularAutomaton::new(config);
1533        assert!(qca.is_ok());
1534    }
1535
1536    #[test]
1537    fn test_qca_1d_evolution() {
1538        let config = QCAConfig {
1539            dimensions: vec![4],
1540            evolution_steps: 5,
1541            ..Default::default()
1542        };
1543
1544        let mut qca =
1545            QuantumCellularAutomaton::new(config).expect("QCA creation should succeed in test");
1546        let result = qca.evolve(5);
1547        assert!(result.is_ok());
1548
1549        let evolution_result = result.expect("Evolution should succeed in test");
1550        assert_eq!(evolution_result.total_steps, 5);
1551        assert!(evolution_result.total_time_ms > 0.0);
1552    }
1553
1554    #[test]
1555    fn test_qca_2d_evolution() {
1556        let config = QCAConfig {
1557            dimensions: vec![3, 3],
1558            evolution_steps: 3,
1559            rule_type: QCARuleType::Partitioned,
1560            ..Default::default()
1561        };
1562
1563        let mut qca =
1564            QuantumCellularAutomaton::new(config).expect("QCA creation should succeed in test");
1565        let result = qca.evolve(3);
1566        assert!(result.is_ok());
1567    }
1568
1569    #[test]
1570    fn test_qca_measurement() {
1571        let config = QCAConfig {
1572            dimensions: vec![3],
1573            ..Default::default()
1574        };
1575
1576        let mut qca =
1577            QuantumCellularAutomaton::new(config).expect("QCA creation should succeed in test");
1578
1579        // Measure all cells
1580        let results = qca.measure_cells(&[0, 1, 2]);
1581        assert!(results.is_ok());
1582        assert_eq!(
1583            results.expect("Measurement should succeed in test").len(),
1584            3
1585        );
1586    }
1587
1588    #[test]
1589    fn test_boundary_conditions() {
1590        let configs = vec![
1591            BoundaryConditions::Periodic,
1592            BoundaryConditions::Fixed,
1593            BoundaryConditions::Open,
1594            BoundaryConditions::Reflective,
1595        ];
1596
1597        for boundary in configs {
1598            let config = QCAConfig {
1599                dimensions: vec![4],
1600                boundary_conditions: boundary,
1601                evolution_steps: 2,
1602                ..Default::default()
1603            };
1604
1605            let mut qca =
1606                QuantumCellularAutomaton::new(config).expect("QCA creation should succeed in test");
1607            let result = qca.evolve(2);
1608            assert!(result.is_ok());
1609        }
1610    }
1611
1612    #[test]
1613    fn test_neighborhood_types() {
1614        let neighborhoods = vec![NeighborhoodType::VonNeumann, NeighborhoodType::Moore];
1615
1616        for neighborhood in neighborhoods {
1617            let config = QCAConfig {
1618                dimensions: vec![3, 3],
1619                neighborhood,
1620                evolution_steps: 2,
1621                ..Default::default()
1622            };
1623
1624            let mut qca =
1625                QuantumCellularAutomaton::new(config).expect("QCA creation should succeed in test");
1626            let result = qca.evolve(2);
1627            assert!(result.is_ok());
1628        }
1629    }
1630
1631    #[test]
1632    fn test_predefined_configs() {
1633        let config_types = vec!["game_of_life", "elementary_ca", "margolus_ca"];
1634
1635        for config_type in config_types {
1636            let config = QCAUtils::create_predefined_config(config_type, 4);
1637            let qca = QuantumCellularAutomaton::new(config);
1638            assert!(qca.is_ok());
1639        }
1640    }
1641
1642    #[test]
1643    fn test_initial_patterns() {
1644        let dimensions = vec![4, 4];
1645        let patterns = vec!["random", "glider", "uniform"];
1646
1647        for pattern in patterns {
1648            let state = QCAUtils::create_initial_pattern(pattern, &dimensions);
1649            assert_eq!(state.len(), 1 << 16); // 2^16 for 16 cells
1650
1651            // Check normalization
1652            let norm: f64 = state.iter().map(|x| x.norm_sqr()).sum();
1653            assert_abs_diff_eq!(norm, 1.0, epsilon = 1e-10);
1654        }
1655    }
1656
1657    #[test]
1658    fn test_entanglement_entropy_calculation() {
1659        let config = QCAConfig {
1660            dimensions: vec![4],
1661            ..Default::default()
1662        };
1663
1664        let mut qca =
1665            QuantumCellularAutomaton::new(config).expect("QCA creation should succeed in test");
1666
1667        // Create entangled state
1668        let state_size = qca.state.len();
1669        qca.state = Array1::zeros(state_size);
1670        qca.state[0] = Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0); // |0000⟩
1671        qca.state[15] = Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0); // |1111⟩
1672
1673        let entropy = qca
1674            .calculate_entanglement_entropy()
1675            .expect("Entropy calculation should succeed in test");
1676        assert!(entropy >= 0.0);
1677    }
1678
1679    #[test]
1680    fn test_local_observables() {
1681        let config = QCAConfig {
1682            dimensions: vec![3],
1683            ..Default::default()
1684        };
1685
1686        let qca =
1687            QuantumCellularAutomaton::new(config).expect("QCA creation should succeed in test");
1688        let observables = qca
1689            .calculate_local_observables()
1690            .expect("Observable calculation should succeed in test");
1691
1692        // Should have magnetization for each cell
1693        assert!(observables.contains_key("magnetization_0"));
1694        assert!(observables.contains_key("magnetization_1"));
1695        assert!(observables.contains_key("magnetization_2"));
1696    }
1697
1698    #[test]
1699    fn test_unitary_check() {
1700        let cnot = QuantumCellularAutomaton::create_cnot_unitary();
1701        assert!(QuantumCellularAutomaton::is_unitary(&cnot));
1702
1703        let rotation =
1704            QuantumCellularAutomaton::create_rotation_unitary(std::f64::consts::PI / 4.0);
1705        assert!(QuantumCellularAutomaton::is_unitary(&rotation));
1706    }
1707}